]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBoot.c
CommitLineData
067ed98a
RN
1/** @file\r
2 Library functions which relates with booting.\r
3\r
b4e1ad87 4Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>\r
e58f1ae5 5(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>\r
9d510e61 6SPDX-License-Identifier: BSD-2-Clause-Patent\r
067ed98a
RN
7\r
8**/\r
9\r
10#include "InternalBm.h"\r
11\r
339ae051
RN
12EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;\r
13\r
067ed98a
RN
14EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL;\r
15EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL;\r
16\r
17///\r
18/// This GUID is used for an EFI Variable that stores the front device pathes\r
19/// for a partial device path that starts with the HD node.\r
20///\r
21EFI_GUID mBmHardDriveBootVariableGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } };\r
22EFI_GUID mBmAutoCreateBootOptionGuid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } };\r
23\r
3a039a56
DB
24/**\r
25\r
26 End Perf entry of BDS\r
27\r
28 @param Event The triggered event.\r
29 @param Context Context for this event.\r
30\r
31**/\r
32VOID\r
33EFIAPI\r
34BmEndOfBdsPerfCode (\r
35 IN EFI_EVENT Event,\r
36 IN VOID *Context\r
37 )\r
38{\r
39 //\r
40 // Record the performance data for End of BDS\r
41 //\r
67e9ab84 42 PERF_CROSSMODULE_END("BDS");\r
3a039a56
DB
43\r
44 return ;\r
45}\r
46\r
067ed98a
RN
47/**\r
48 The function registers the legacy boot support capabilities.\r
49\r
50 @param RefreshLegacyBootOption The function pointer to create all the legacy boot options.\r
51 @param LegacyBoot The function pointer to boot the legacy boot option.\r
52**/\r
53VOID\r
54EFIAPI\r
55EfiBootManagerRegisterLegacyBootSupport (\r
56 EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption,\r
57 EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot\r
58 )\r
59{\r
60 mBmRefreshLegacyBootOption = RefreshLegacyBootOption;\r
61 mBmLegacyBoot = LegacyBoot;\r
62}\r
63\r
404bd442
RN
64/**\r
65 Return TRUE when the boot option is auto-created instead of manually added.\r
66\r
67 @param BootOption Pointer to the boot option to check.\r
68\r
69 @retval TRUE The boot option is auto-created.\r
70 @retval FALSE The boot option is manually added.\r
71**/\r
72BOOLEAN\r
73BmIsAutoCreateBootOption (\r
74 EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
75 )\r
76{\r
77 if ((BootOption->OptionalDataSize == sizeof (EFI_GUID)) &&\r
78 CompareGuid ((EFI_GUID *) BootOption->OptionalData, &mBmAutoCreateBootOptionGuid)\r
79 ) {\r
80 return TRUE;\r
81 } else {\r
82 return FALSE;\r
83 }\r
84}\r
85\r
067ed98a
RN
86/**\r
87 Find the boot option in the NV storage and return the option number.\r
88\r
89 @param OptionToFind Boot option to be checked.\r
90\r
91 @return The option number of the found boot option.\r
92\r
93**/\r
94UINTN\r
95BmFindBootOptionInVariable (\r
96 IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind\r
97 )\r
98{\r
99 EFI_STATUS Status;\r
100 EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
101 UINTN OptionNumber;\r
102 CHAR16 OptionName[BM_OPTION_NAME_LEN];\r
103 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
104 UINTN BootOptionCount;\r
105 UINTN Index;\r
d1102dba 106\r
067ed98a
RN
107 OptionNumber = LoadOptionNumberUnassigned;\r
108\r
109 //\r
110 // Try to match the variable exactly if the option number is assigned\r
111 //\r
112 if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) {\r
113 UnicodeSPrint (\r
114 OptionName, sizeof (OptionName), L"%s%04x",\r
115 mBmLoadOptionName[OptionToFind->OptionType], OptionToFind->OptionNumber\r
116 );\r
117 Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);\r
118\r
119 if (!EFI_ERROR (Status)) {\r
120 ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber);\r
121 if ((OptionToFind->Attributes == BootOption.Attributes) &&\r
122 (StrCmp (OptionToFind->Description, BootOption.Description) == 0) &&\r
123 (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) &&\r
124 (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) &&\r
125 (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0)\r
126 ) {\r
127 OptionNumber = OptionToFind->OptionNumber;\r
128 }\r
129 EfiBootManagerFreeLoadOption (&BootOption);\r
130 }\r
131 }\r
132\r
133 //\r
134 // The option number assigned is either incorrect or unassigned.\r
135 //\r
136 if (OptionNumber == LoadOptionNumberUnassigned) {\r
137 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
138\r
5d3a9896 139 Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount);\r
067ed98a
RN
140 if (Index != -1) {\r
141 OptionNumber = BootOptions[Index].OptionNumber;\r
142 }\r
143\r
144 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
145 }\r
146\r
147 return OptionNumber;\r
148}\r
149\r
150/**\r
08eff917 151 Return the correct FV file path.\r
067ed98a
RN
152 FV address may change across reboot. This routine promises the FV file device path is right.\r
153\r
525839ed 154 @param FilePath The Memory Mapped Device Path to get the file buffer.\r
067ed98a 155\r
08eff917 156 @return The updated FV Device Path pointint to the file.\r
067ed98a 157**/\r
08eff917
RN
158EFI_DEVICE_PATH_PROTOCOL *\r
159BmAdjustFvFilePath (\r
160 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
067ed98a
RN
161 )\r
162{\r
163 EFI_STATUS Status;\r
164 UINTN Index;\r
165 EFI_DEVICE_PATH_PROTOCOL *FvFileNode;\r
166 EFI_HANDLE FvHandle;\r
167 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
067ed98a
RN
168 UINTN FvHandleCount;\r
169 EFI_HANDLE *FvHandles;\r
170 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
08eff917 171 EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
525839ed
RN
172\r
173 //\r
174 // Get the file buffer by using the exactly FilePath.\r
175 //\r
176 FvFileNode = FilePath;\r
067ed98a
RN
177 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);\r
178 if (!EFI_ERROR (Status)) {\r
08eff917 179 return DuplicateDevicePath (FilePath);\r
067ed98a
RN
180 }\r
181\r
525839ed
RN
182 //\r
183 // Only wide match other FVs if it's a memory mapped FV file path.\r
184 //\r
185 if ((DevicePathType (FilePath) != HARDWARE_DEVICE_PATH) || (DevicePathSubType (FilePath) != HW_MEMMAP_DP)) {\r
186 return NULL;\r
187 }\r
188\r
189 FvFileNode = NextDevicePathNode (FilePath);\r
067ed98a
RN
190\r
191 //\r
192 // Firstly find the FV file in current FV\r
193 //\r
194 gBS->HandleProtocol (\r
195 gImageHandle,\r
196 &gEfiLoadedImageProtocolGuid,\r
197 (VOID **) &LoadedImage\r
198 );\r
199 NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);\r
08eff917 200 FullPath = BmAdjustFvFilePath (NewDevicePath);\r
067ed98a 201 FreePool (NewDevicePath);\r
08eff917
RN
202 if (FullPath != NULL) {\r
203 return FullPath;\r
067ed98a
RN
204 }\r
205\r
206 //\r
207 // Secondly find the FV file in all other FVs\r
208 //\r
209 gBS->LocateHandleBuffer (\r
210 ByProtocol,\r
211 &gEfiFirmwareVolume2ProtocolGuid,\r
212 NULL,\r
213 &FvHandleCount,\r
214 &FvHandles\r
215 );\r
08eff917 216 for (Index = 0; Index < FvHandleCount; Index++) {\r
067ed98a
RN
217 if (FvHandles[Index] == LoadedImage->DeviceHandle) {\r
218 //\r
08eff917 219 // Skip current FV, it was handed in first step.\r
067ed98a
RN
220 //\r
221 continue;\r
222 }\r
223 NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);\r
08eff917 224 FullPath = BmAdjustFvFilePath (NewDevicePath);\r
067ed98a 225 FreePool (NewDevicePath);\r
08eff917
RN
226 if (FullPath != NULL) {\r
227 break;\r
228 }\r
067ed98a 229 }\r
d1102dba 230\r
067ed98a
RN
231 if (FvHandles != NULL) {\r
232 FreePool (FvHandles);\r
233 }\r
08eff917 234 return FullPath;\r
067ed98a
RN
235}\r
236\r
237/**\r
525839ed 238 Check if it's a Device Path pointing to FV file.\r
d1102dba 239\r
067ed98a
RN
240 The function doesn't garentee the device path points to existing FV file.\r
241\r
242 @param DevicePath Input device path.\r
243\r
525839ed
RN
244 @retval TRUE The device path is a FV File Device Path.\r
245 @retval FALSE The device path is NOT a FV File Device Path.\r
067ed98a
RN
246**/\r
247BOOLEAN\r
525839ed 248BmIsFvFilePath (\r
067ed98a
RN
249 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
250 )\r
251{\r
525839ed
RN
252 EFI_STATUS Status;\r
253 EFI_HANDLE Handle;\r
254 EFI_DEVICE_PATH_PROTOCOL *Node;\r
255\r
256 Node = DevicePath;\r
257 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &Handle);\r
258 if (!EFI_ERROR (Status)) {\r
259 return TRUE;\r
260 }\r
067ed98a
RN
261\r
262 if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {\r
525839ed
RN
263 DevicePath = NextDevicePathNode (DevicePath);\r
264 if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MEDIA_PIWG_FW_FILE_DP)) {\r
265 return IsDevicePathEnd (NextDevicePathNode (DevicePath));\r
067ed98a
RN
266 }\r
267 }\r
067ed98a
RN
268 return FALSE;\r
269}\r
270\r
271/**\r
272 Check whether a USB device match the specified USB Class device path. This\r
273 function follows "Load Option Processing" behavior in UEFI specification.\r
274\r
275 @param UsbIo USB I/O protocol associated with the USB device.\r
276 @param UsbClass The USB Class device path to match.\r
277\r
278 @retval TRUE The USB device match the USB Class device path.\r
279 @retval FALSE The USB device does not match the USB Class device path.\r
280\r
281**/\r
282BOOLEAN\r
283BmMatchUsbClass (\r
284 IN EFI_USB_IO_PROTOCOL *UsbIo,\r
285 IN USB_CLASS_DEVICE_PATH *UsbClass\r
286 )\r
287{\r
288 EFI_STATUS Status;\r
289 EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
290 EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
291 UINT8 DeviceClass;\r
292 UINT8 DeviceSubClass;\r
293 UINT8 DeviceProtocol;\r
294\r
295 if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||\r
296 (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){\r
297 return FALSE;\r
298 }\r
299\r
300 //\r
301 // Check Vendor Id and Product Id.\r
302 //\r
303 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
304 if (EFI_ERROR (Status)) {\r
305 return FALSE;\r
306 }\r
307\r
308 if ((UsbClass->VendorId != 0xffff) &&\r
309 (UsbClass->VendorId != DevDesc.IdVendor)) {\r
310 return FALSE;\r
311 }\r
312\r
313 if ((UsbClass->ProductId != 0xffff) &&\r
314 (UsbClass->ProductId != DevDesc.IdProduct)) {\r
315 return FALSE;\r
316 }\r
317\r
318 DeviceClass = DevDesc.DeviceClass;\r
319 DeviceSubClass = DevDesc.DeviceSubClass;\r
320 DeviceProtocol = DevDesc.DeviceProtocol;\r
321 if (DeviceClass == 0) {\r
322 //\r
323 // If Class in Device Descriptor is set to 0, use the Class, SubClass and\r
324 // Protocol in Interface Descriptor instead.\r
325 //\r
326 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
327 if (EFI_ERROR (Status)) {\r
328 return FALSE;\r
329 }\r
330\r
331 DeviceClass = IfDesc.InterfaceClass;\r
332 DeviceSubClass = IfDesc.InterfaceSubClass;\r
333 DeviceProtocol = IfDesc.InterfaceProtocol;\r
334 }\r
335\r
336 //\r
337 // Check Class, SubClass and Protocol.\r
338 //\r
339 if ((UsbClass->DeviceClass != 0xff) &&\r
340 (UsbClass->DeviceClass != DeviceClass)) {\r
341 return FALSE;\r
342 }\r
343\r
344 if ((UsbClass->DeviceSubClass != 0xff) &&\r
345 (UsbClass->DeviceSubClass != DeviceSubClass)) {\r
346 return FALSE;\r
347 }\r
348\r
349 if ((UsbClass->DeviceProtocol != 0xff) &&\r
350 (UsbClass->DeviceProtocol != DeviceProtocol)) {\r
351 return FALSE;\r
352 }\r
353\r
354 return TRUE;\r
355}\r
356\r
067ed98a
RN
357/**\r
358 Check whether a USB device match the specified USB WWID device path. This\r
359 function follows "Load Option Processing" behavior in UEFI specification.\r
360\r
361 @param UsbIo USB I/O protocol associated with the USB device.\r
362 @param UsbWwid The USB WWID device path to match.\r
363\r
364 @retval TRUE The USB device match the USB WWID device path.\r
365 @retval FALSE The USB device does not match the USB WWID device path.\r
366\r
367**/\r
368BOOLEAN\r
369BmMatchUsbWwid (\r
370 IN EFI_USB_IO_PROTOCOL *UsbIo,\r
371 IN USB_WWID_DEVICE_PATH *UsbWwid\r
372 )\r
373{\r
374 EFI_STATUS Status;\r
375 EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
376 EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
377 UINT16 *LangIdTable;\r
378 UINT16 TableSize;\r
379 UINT16 Index;\r
380 CHAR16 *CompareStr;\r
381 UINTN CompareLen;\r
382 CHAR16 *SerialNumberStr;\r
383 UINTN Length;\r
384\r
385 if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||\r
386 (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {\r
387 return FALSE;\r
388 }\r
389\r
390 //\r
391 // Check Vendor Id and Product Id.\r
392 //\r
393 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
394 if (EFI_ERROR (Status)) {\r
395 return FALSE;\r
396 }\r
397 if ((DevDesc.IdVendor != UsbWwid->VendorId) ||\r
398 (DevDesc.IdProduct != UsbWwid->ProductId)) {\r
399 return FALSE;\r
400 }\r
401\r
402 //\r
403 // Check Interface Number.\r
404 //\r
405 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
406 if (EFI_ERROR (Status)) {\r
407 return FALSE;\r
408 }\r
409 if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {\r
410 return FALSE;\r
411 }\r
412\r
413 //\r
414 // Check Serial Number.\r
415 //\r
416 if (DevDesc.StrSerialNumber == 0) {\r
417 return FALSE;\r
418 }\r
419\r
420 //\r
421 // Get all supported languages.\r
422 //\r
423 TableSize = 0;\r
424 LangIdTable = NULL;\r
425 Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);\r
426 if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {\r
427 return FALSE;\r
428 }\r
429\r
430 //\r
431 // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.\r
432 //\r
433 CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);\r
434 CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);\r
435 if (CompareStr[CompareLen - 1] == L'\0') {\r
436 CompareLen--;\r
437 }\r
438\r
439 //\r
440 // Compare serial number in each supported language.\r
441 //\r
442 for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {\r
443 SerialNumberStr = NULL;\r
444 Status = UsbIo->UsbGetStringDescriptor (\r
445 UsbIo,\r
446 LangIdTable[Index],\r
447 DevDesc.StrSerialNumber,\r
448 &SerialNumberStr\r
449 );\r
450 if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {\r
451 continue;\r
452 }\r
453\r
454 Length = StrLen (SerialNumberStr);\r
455 if ((Length >= CompareLen) &&\r
456 (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {\r
457 FreePool (SerialNumberStr);\r
458 return TRUE;\r
459 }\r
460\r
461 FreePool (SerialNumberStr);\r
462 }\r
463\r
464 return FALSE;\r
465}\r
466\r
467/**\r
d1102dba 468 Find a USB device which match the specified short-form device path start with\r
067ed98a
RN
469 USB Class or USB WWID device path. If ParentDevicePath is NULL, this function\r
470 will search in all USB devices of the platform. If ParentDevicePath is not NULL,\r
471 this function will only search in its child devices.\r
472\r
473 @param DevicePath The device path that contains USB Class or USB WWID device path.\r
d1102dba 474 @param ParentDevicePathSize The length of the device path before the USB Class or\r
067ed98a
RN
475 USB WWID device path.\r
476 @param UsbIoHandleCount A pointer to the count of the returned USB IO handles.\r
477\r
478 @retval NULL The matched USB IO handles cannot be found.\r
479 @retval other The matched USB IO handles.\r
480\r
481**/\r
482EFI_HANDLE *\r
483BmFindUsbDevice (\r
484 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
485 IN UINTN ParentDevicePathSize,\r
486 OUT UINTN *UsbIoHandleCount\r
487 )\r
488{\r
489 EFI_STATUS Status;\r
490 EFI_HANDLE *UsbIoHandles;\r
491 EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;\r
492 EFI_USB_IO_PROTOCOL *UsbIo;\r
493 UINTN Index;\r
067ed98a
RN
494 BOOLEAN Matched;\r
495\r
d1102dba 496 ASSERT (UsbIoHandleCount != NULL);\r
067ed98a
RN
497\r
498 //\r
499 // Get all UsbIo Handles.\r
500 //\r
501 Status = gBS->LocateHandleBuffer (\r
502 ByProtocol,\r
503 &gEfiUsbIoProtocolGuid,\r
504 NULL,\r
505 UsbIoHandleCount,\r
506 &UsbIoHandles\r
507 );\r
508 if (EFI_ERROR (Status)) {\r
509 *UsbIoHandleCount = 0;\r
510 UsbIoHandles = NULL;\r
511 }\r
512\r
513 for (Index = 0; Index < *UsbIoHandleCount; ) {\r
514 //\r
515 // Get the Usb IO interface.\r
516 //\r
517 Status = gBS->HandleProtocol(\r
518 UsbIoHandles[Index],\r
519 &gEfiUsbIoProtocolGuid,\r
520 (VOID **) &UsbIo\r
521 );\r
522 UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);\r
523 Matched = FALSE;\r
524 if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {\r
067ed98a
RN
525\r
526 //\r
527 // Compare starting part of UsbIoHandle's device path with ParentDevicePath.\r
528 //\r
529 if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {\r
530 if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) ||\r
531 BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize))) {\r
532 Matched = TRUE;\r
533 }\r
534 }\r
535 }\r
536\r
537 if (!Matched) {\r
538 (*UsbIoHandleCount) --;\r
539 CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE));\r
540 } else {\r
541 Index++;\r
542 }\r
543 }\r
544\r
545 return UsbIoHandles;\r
546}\r
547\r
548/**\r
549 Expand USB Class or USB WWID device path node to be full device path of a USB\r
550 device in platform.\r
551\r
552 This function support following 4 cases:\r
553 1) Boot Option device path starts with a USB Class or USB WWID device path,\r
554 and there is no Media FilePath device path in the end.\r
555 In this case, it will follow Removable Media Boot Behavior.\r
556 2) Boot Option device path starts with a USB Class or USB WWID device path,\r
557 and ended with Media FilePath device path.\r
558 3) Boot Option device path starts with a full device path to a USB Host Controller,\r
559 contains a USB Class or USB WWID device path node, while not ended with Media\r
560 FilePath device path. In this case, it will follow Removable Media Boot Behavior.\r
561 4) Boot Option device path starts with a full device path to a USB Host Controller,\r
562 contains a USB Class or USB WWID device path node, and ended with Media\r
563 FilePath device path.\r
564\r
565 @param FilePath The device path pointing to a load option.\r
566 It could be a short-form device path.\r
08eff917
RN
567 @param FullPath The full path returned by the routine in last call.\r
568 Set to NULL in first call.\r
067ed98a
RN
569 @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.\r
570\r
08eff917
RN
571 @return The next possible full path pointing to the load option.\r
572 Caller is responsible to free the memory.\r
067ed98a 573**/\r
08eff917 574EFI_DEVICE_PATH_PROTOCOL *\r
067ed98a
RN
575BmExpandUsbDevicePath (\r
576 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
08eff917
RN
577 IN EFI_DEVICE_PATH_PROTOCOL *FullPath,\r
578 IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode\r
067ed98a
RN
579 )\r
580{\r
581 UINTN ParentDevicePathSize;\r
582 EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;\r
08eff917 583 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
067ed98a
RN
584 EFI_HANDLE *Handles;\r
585 UINTN HandleCount;\r
586 UINTN Index;\r
08eff917 587 BOOLEAN GetNext;\r
067ed98a 588\r
08eff917
RN
589 NextFullPath = NULL;\r
590 GetNext = (BOOLEAN)(FullPath == NULL);\r
067ed98a
RN
591 ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;\r
592 RemainingDevicePath = NextDevicePathNode (ShortformNode);\r
067ed98a
RN
593 Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);\r
594\r
08eff917
RN
595 for (Index = 0; Index < HandleCount; Index++) {\r
596 FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);\r
597 if (FilePath == NULL) {\r
598 //\r
599 // Out of memory.\r
600 //\r
601 continue;\r
602 }\r
603 NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL);\r
604 FreePool (FilePath);\r
605 if (NextFullPath == NULL) {\r
606 //\r
607 // No BlockIo or SimpleFileSystem under FilePath.\r
608 //\r
609 continue;\r
610 }\r
611 if (GetNext) {\r
612 break;\r
613 } else {\r
614 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
615 FreePool (NextFullPath);\r
616 NextFullPath = NULL;\r
617 }\r
067ed98a
RN
618 }\r
619\r
620 if (Handles != NULL) {\r
621 FreePool (Handles);\r
622 }\r
623\r
08eff917 624 return NextFullPath;\r
067ed98a
RN
625}\r
626\r
ccb66799
RN
627/**\r
628 Expand File-path device path node to be full device path in platform.\r
629\r
630 @param FilePath The device path pointing to a load option.\r
631 It could be a short-form device path.\r
08eff917
RN
632 @param FullPath The full path returned by the routine in last call.\r
633 Set to NULL in first call.\r
ccb66799 634\r
08eff917
RN
635 @return The next possible full path pointing to the load option.\r
636 Caller is responsible to free the memory.\r
ccb66799 637**/\r
08eff917 638EFI_DEVICE_PATH_PROTOCOL *\r
ccb66799
RN
639BmExpandFileDevicePath (\r
640 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
08eff917 641 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
ccb66799
RN
642 )\r
643{\r
644 EFI_STATUS Status;\r
645 UINTN Index;\r
646 UINTN HandleCount;\r
647 EFI_HANDLE *Handles;\r
648 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
649 UINTN MediaType;\r
08eff917
RN
650 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
651 BOOLEAN GetNext;\r
d1102dba 652\r
ccb66799
RN
653 EfiBootManagerConnectAll ();\r
654 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles);\r
655 if (EFI_ERROR (Status)) {\r
656 HandleCount = 0;\r
657 Handles = NULL;\r
658 }\r
659\r
08eff917
RN
660 GetNext = (BOOLEAN)(FullPath == NULL);\r
661 NextFullPath = NULL;\r
ccb66799
RN
662 //\r
663 // Enumerate all removable media devices followed by all fixed media devices,\r
664 // followed by media devices which don't layer on block io.\r
665 //\r
666 for (MediaType = 0; MediaType < 3; MediaType++) {\r
667 for (Index = 0; Index < HandleCount; Index++) {\r
668 Status = gBS->HandleProtocol (Handles[Index], &gEfiBlockIoProtocolGuid, (VOID *) &BlockIo);\r
669 if (EFI_ERROR (Status)) {\r
670 BlockIo = NULL;\r
671 }\r
672 if ((MediaType == 0 && BlockIo != NULL && BlockIo->Media->RemovableMedia) ||\r
673 (MediaType == 1 && BlockIo != NULL && !BlockIo->Media->RemovableMedia) ||\r
674 (MediaType == 2 && BlockIo == NULL)\r
675 ) {\r
08eff917
RN
676 NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);\r
677 if (GetNext) {\r
678 break;\r
679 } else {\r
680 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
681 FreePool (NextFullPath);\r
682 NextFullPath = NULL;\r
ccb66799 683 }\r
ccb66799
RN
684 }\r
685 }\r
08eff917
RN
686 if (NextFullPath != NULL) {\r
687 break;\r
688 }\r
ccb66799
RN
689 }\r
690\r
691 if (Handles != NULL) {\r
692 FreePool (Handles);\r
693 }\r
694\r
08eff917 695 return NextFullPath;\r
ccb66799
RN
696}\r
697\r
4a285ec1
RN
698/**\r
699 Expand URI device path node to be full device path in platform.\r
700\r
701 @param FilePath The device path pointing to a load option.\r
702 It could be a short-form device path.\r
08eff917
RN
703 @param FullPath The full path returned by the routine in last call.\r
704 Set to NULL in first call.\r
4a285ec1 705\r
08eff917
RN
706 @return The next possible full path pointing to the load option.\r
707 Caller is responsible to free the memory.\r
4a285ec1 708**/\r
08eff917 709EFI_DEVICE_PATH_PROTOCOL *\r
4a285ec1
RN
710BmExpandUriDevicePath (\r
711 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
08eff917 712 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
4a285ec1
RN
713 )\r
714{\r
715 EFI_STATUS Status;\r
716 UINTN Index;\r
717 UINTN HandleCount;\r
718 EFI_HANDLE *Handles;\r
08eff917
RN
719 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
720 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
721 BOOLEAN GetNext;\r
4a285ec1
RN
722\r
723 EfiBootManagerConnectAll ();\r
724 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);\r
725 if (EFI_ERROR (Status)) {\r
726 HandleCount = 0;\r
727 Handles = NULL;\r
728 }\r
729\r
08eff917
RN
730 NextFullPath = NULL;\r
731 GetNext = (BOOLEAN)(FullPath == NULL);\r
4a285ec1 732 for (Index = 0; Index < HandleCount; Index++) {\r
08eff917
RN
733 NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);\r
734\r
735 if (NextFullPath == NULL) {\r
736 continue;\r
737 }\r
738\r
739 if (GetNext) {\r
026ede49 740 break;\r
08eff917
RN
741 } else {\r
742 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
743 //\r
744 // Free the resource occupied by the RAM disk.\r
745 //\r
746 RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath);\r
747 if (RamDiskDevicePath != NULL) {\r
748 BmDestroyRamDisk (RamDiskDevicePath);\r
749 FreePool (RamDiskDevicePath);\r
750 }\r
751 FreePool (NextFullPath);\r
752 NextFullPath = NULL;\r
4a285ec1
RN
753 }\r
754 }\r
755\r
756 if (Handles != NULL) {\r
757 FreePool (Handles);\r
758 }\r
759\r
08eff917 760 return NextFullPath;\r
4a285ec1
RN
761}\r
762\r
067ed98a
RN
763/**\r
764 Save the partition DevicePath to the CachedDevicePath as the first instance.\r
765\r
766 @param CachedDevicePath The device path cache.\r
767 @param DevicePath The partition device path to be cached.\r
768**/\r
769VOID\r
770BmCachePartitionDevicePath (\r
771 IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath,\r
772 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
773 )\r
774{\r
775 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
776 UINTN Count;\r
d1102dba 777\r
067ed98a
RN
778 if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) {\r
779 TempDevicePath = *CachedDevicePath;\r
780 *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath);\r
781 FreePool (TempDevicePath);\r
782 }\r
783\r
784 if (*CachedDevicePath == NULL) {\r
785 *CachedDevicePath = DuplicateDevicePath (DevicePath);\r
786 return;\r
787 }\r
788\r
789 TempDevicePath = *CachedDevicePath;\r
790 *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath);\r
791 if (TempDevicePath != NULL) {\r
792 FreePool (TempDevicePath);\r
793 }\r
794\r
795 //\r
796 // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller\r
797 // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.\r
798 //\r
799 Count = 0;\r
800 TempDevicePath = *CachedDevicePath;\r
801 while (!IsDevicePathEnd (TempDevicePath)) {\r
802 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
803 //\r
804 // Parse one instance\r
805 //\r
806 while (!IsDevicePathEndType (TempDevicePath)) {\r
807 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
808 }\r
809 Count++;\r
810 //\r
811 // If the CachedDevicePath variable contain too much instance, only remain 12 instances.\r
812 //\r
813 if (Count == 12) {\r
814 SetDevicePathEndNode (TempDevicePath);\r
815 break;\r
816 }\r
817 }\r
818}\r
819\r
820/**\r
821 Expand a device path that starts with a hard drive media device path node to be a\r
822 full device path that includes the full hardware path to the device. We need\r
823 to do this so it can be booted. As an optimization the front match (the part point\r
824 to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable\r
825 so a connect all is not required on every boot. All successful history device path\r
826 which point to partition node (the front part) will be saved.\r
827\r
828 @param FilePath The device path pointing to a load option.\r
829 It could be a short-form device path.\r
067ed98a 830\r
08eff917 831 @return The full device path pointing to the load option.\r
067ed98a 832**/\r
08eff917 833EFI_DEVICE_PATH_PROTOCOL *\r
067ed98a 834BmExpandPartitionDevicePath (\r
08eff917 835 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
067ed98a
RN
836 )\r
837{\r
838 EFI_STATUS Status;\r
839 UINTN BlockIoHandleCount;\r
840 EFI_HANDLE *BlockIoBuffer;\r
067ed98a
RN
841 EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;\r
842 UINTN Index;\r
843 EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;\r
844 EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;\r
845 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
08eff917 846 EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
067ed98a
RN
847 UINTN CachedDevicePathSize;\r
848 BOOLEAN NeedAdjust;\r
849 EFI_DEVICE_PATH_PROTOCOL *Instance;\r
850 UINTN Size;\r
851\r
067ed98a
RN
852 //\r
853 // Check if there is prestore 'HDDP' variable.\r
854 // If exist, search the front path which point to partition node in the variable instants.\r
855 // If fail to find or 'HDDP' not exist, reconnect all and search in all system\r
856 //\r
857 GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize);\r
858\r
859 //\r
860 // Delete the invalid 'HDDP' variable.\r
861 //\r
862 if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {\r
863 FreePool (CachedDevicePath);\r
864 CachedDevicePath = NULL;\r
865 Status = gRT->SetVariable (\r
866 L"HDDP",\r
867 &mBmHardDriveBootVariableGuid,\r
868 0,\r
869 0,\r
870 NULL\r
871 );\r
872 ASSERT_EFI_ERROR (Status);\r
873 }\r
874\r
08eff917 875 FullPath = NULL;\r
067ed98a
RN
876 if (CachedDevicePath != NULL) {\r
877 TempNewDevicePath = CachedDevicePath;\r
878 NeedAdjust = FALSE;\r
879 do {\r
880 //\r
881 // Check every instance of the variable\r
882 // First, check whether the instance contain the partition node, which is needed for distinguishing multi\r
883 // partial partition boot option. Second, check whether the instance could be connected.\r
884 //\r
885 Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);\r
886 if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
887 //\r
888 // Connect the device path instance, the device path point to hard drive media device path node\r
889 // e.g. ACPI() /PCI()/ATA()/Partition()\r
890 //\r
891 Status = EfiBootManagerConnectDevicePath (Instance, NULL);\r
892 if (!EFI_ERROR (Status)) {\r
893 TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));\r
08eff917
RN
894 //\r
895 // TempDevicePath = ACPI()/PCI()/ATA()/Partition()\r
896 // or = ACPI()/PCI()/ATA()/Partition()/.../A.EFI\r
897 //\r
898 // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(),\r
899 // it may expand to two potienal full paths (nested partition, rarely happen):\r
900 // 1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI\r
901 // 2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI\r
902 // For simplicity, only #1 is returned.\r
903 //\r
904 FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);\r
067ed98a
RN
905 FreePool (TempDevicePath);\r
906\r
08eff917 907 if (FullPath != NULL) {\r
067ed98a
RN
908 //\r
909 // Adjust the 'HDDP' instances sequence if the matched one is not first one.\r
910 //\r
911 if (NeedAdjust) {\r
912 BmCachePartitionDevicePath (&CachedDevicePath, Instance);\r
913 //\r
914 // Save the matching Device Path so we don't need to do a connect all next time\r
915 // Failing to save only impacts performance next time expanding the short-form device path\r
916 //\r
917 Status = gRT->SetVariable (\r
918 L"HDDP",\r
919 &mBmHardDriveBootVariableGuid,\r
920 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
921 GetDevicePathSize (CachedDevicePath),\r
922 CachedDevicePath\r
923 );\r
924 }\r
925\r
926 FreePool (Instance);\r
927 FreePool (CachedDevicePath);\r
08eff917 928 return FullPath;\r
067ed98a
RN
929 }\r
930 }\r
931 }\r
932 //\r
933 // Come here means the first instance is not matched\r
934 //\r
935 NeedAdjust = TRUE;\r
936 FreePool(Instance);\r
937 } while (TempNewDevicePath != NULL);\r
938 }\r
939\r
940 //\r
941 // If we get here we fail to find or 'HDDP' not exist, and now we need\r
942 // to search all devices in the system for a matched partition\r
943 //\r
944 EfiBootManagerConnectAll ();\r
945 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);\r
946 if (EFI_ERROR (Status)) {\r
947 BlockIoHandleCount = 0;\r
948 BlockIoBuffer = NULL;\r
949 }\r
950 //\r
951 // Loop through all the device handles that support the BLOCK_IO Protocol\r
952 //\r
953 for (Index = 0; Index < BlockIoHandleCount; Index++) {\r
954 BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]);\r
955 if (BlockIoDevicePath == NULL) {\r
956 continue;\r
957 }\r
958\r
959 if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
960 //\r
961 // Find the matched partition device path\r
962 //\r
963 TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));\r
08eff917 964 FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);\r
067ed98a
RN
965 FreePool (TempDevicePath);\r
966\r
08eff917 967 if (FullPath != NULL) {\r
067ed98a
RN
968 BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);\r
969\r
970 //\r
971 // Save the matching Device Path so we don't need to do a connect all next time\r
972 // Failing to save only impacts performance next time expanding the short-form device path\r
973 //\r
974 Status = gRT->SetVariable (\r
975 L"HDDP",\r
976 &mBmHardDriveBootVariableGuid,\r
977 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
978 GetDevicePathSize (CachedDevicePath),\r
979 CachedDevicePath\r
980 );\r
981\r
982 break;\r
983 }\r
984 }\r
985 }\r
986\r
987 if (CachedDevicePath != NULL) {\r
988 FreePool (CachedDevicePath);\r
989 }\r
990 if (BlockIoBuffer != NULL) {\r
991 FreePool (BlockIoBuffer);\r
992 }\r
08eff917 993 return FullPath;\r
067ed98a
RN
994}\r
995\r
996/**\r
997 Expand the media device path which points to a BlockIo or SimpleFileSystem instance\r
998 by appending EFI_REMOVABLE_MEDIA_FILE_NAME.\r
999\r
1000 @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.\r
08eff917
RN
1001 @param FullPath The full path returned by the routine in last call.\r
1002 Set to NULL in first call.\r
067ed98a 1003\r
08eff917
RN
1004 @return The next possible full path pointing to the load option.\r
1005 Caller is responsible to free the memory.\r
067ed98a 1006**/\r
08eff917 1007EFI_DEVICE_PATH_PROTOCOL *\r
067ed98a
RN
1008BmExpandMediaDevicePath (\r
1009 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
08eff917 1010 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
067ed98a
RN
1011 )\r
1012{\r
1013 EFI_STATUS Status;\r
1014 EFI_HANDLE Handle;\r
1015 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
1016 VOID *Buffer;\r
1017 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
08eff917 1018 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
067ed98a
RN
1019 UINTN Size;\r
1020 UINTN TempSize;\r
1021 EFI_HANDLE *SimpleFileSystemHandles;\r
1022 UINTN NumberSimpleFileSystemHandles;\r
1023 UINTN Index;\r
08eff917 1024 BOOLEAN GetNext;\r
067ed98a 1025\r
08eff917 1026 GetNext = (BOOLEAN)(FullPath == NULL);\r
067ed98a
RN
1027 //\r
1028 // Check whether the device is connected\r
1029 //\r
1030 TempDevicePath = DevicePath;\r
1031 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);\r
1032 if (!EFI_ERROR (Status)) {\r
1033 ASSERT (IsDevicePathEnd (TempDevicePath));\r
1034\r
08eff917
RN
1035 NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
1036 //\r
1037 // For device path pointing to simple file system, it only expands to one full path.\r
1038 //\r
1039 if (GetNext) {\r
1040 return NextFullPath;\r
1041 } else {\r
1042 FreePool (NextFullPath);\r
1043 return NULL;\r
067ed98a 1044 }\r
067ed98a
RN
1045 }\r
1046\r
00e39b09
RN
1047 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);\r
1048 ASSERT_EFI_ERROR (Status);\r
1049\r
067ed98a 1050 //\r
00e39b09
RN
1051 // For device boot option only pointing to the removable device handle,\r
1052 // should make sure all its children handles (its child partion or media handles)\r
1053 // are created and connected.\r
067ed98a
RN
1054 //\r
1055 gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
1056\r
1057 //\r
1058 // Issue a dummy read to the device to check for media change.\r
1059 // When the removable media is changed, any Block IO read/write will\r
1060 // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is\r
1061 // returned. After the Block IO protocol is reinstalled, subsequent\r
1062 // Block IO read/write will success.\r
1063 //\r
067ed98a
RN
1064 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
1065 ASSERT_EFI_ERROR (Status);\r
6c27a4d3
MH
1066 if (EFI_ERROR (Status)) {\r
1067 return NULL;\r
1068 }\r
067ed98a
RN
1069 Buffer = AllocatePool (BlockIo->Media->BlockSize);\r
1070 if (Buffer != NULL) {\r
1071 BlockIo->ReadBlocks (\r
1072 BlockIo,\r
1073 BlockIo->Media->MediaId,\r
1074 0,\r
1075 BlockIo->Media->BlockSize,\r
1076 Buffer\r
1077 );\r
1078 FreePool (Buffer);\r
1079 }\r
1080\r
1081 //\r
1082 // Detect the the default boot file from removable Media\r
1083 //\r
08eff917 1084 NextFullPath = NULL;\r
067ed98a
RN
1085 Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;\r
1086 gBS->LocateHandleBuffer (\r
1087 ByProtocol,\r
1088 &gEfiSimpleFileSystemProtocolGuid,\r
1089 NULL,\r
1090 &NumberSimpleFileSystemHandles,\r
1091 &SimpleFileSystemHandles\r
1092 );\r
1093 for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {\r
1094 //\r
1095 // Get the device path size of SimpleFileSystem handle\r
1096 //\r
1097 TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);\r
1098 TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH;\r
1099 //\r
1100 // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path\r
1101 //\r
1102 if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {\r
08eff917
RN
1103 NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);\r
1104 if (GetNext) {\r
067ed98a 1105 break;\r
08eff917
RN
1106 } else {\r
1107 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
1108 FreePool (NextFullPath);\r
1109 NextFullPath = NULL;\r
067ed98a 1110 }\r
067ed98a
RN
1111 }\r
1112 }\r
1113\r
1114 if (SimpleFileSystemHandles != NULL) {\r
1115 FreePool (SimpleFileSystemHandles);\r
1116 }\r
1117\r
08eff917 1118 return NextFullPath;\r
067ed98a
RN
1119}\r
1120\r
bf5328f0
RN
1121/**\r
1122 Check whether Left and Right are the same without matching the specific\r
1123 device path data in IP device path and URI device path node.\r
1124\r
1125 @retval TRUE Left and Right are the same.\r
1126 @retval FALSE Left and Right are the different.\r
1127**/\r
1128BOOLEAN\r
1129BmMatchHttpBootDevicePath (\r
1130 IN EFI_DEVICE_PATH_PROTOCOL *Left,\r
1131 IN EFI_DEVICE_PATH_PROTOCOL *Right\r
1132 )\r
1133{\r
1134 for (; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right)\r
1135 ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right)\r
1136 ) {\r
1137 if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) {\r
1138 if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) {\r
1139 return FALSE;\r
1140 }\r
1141\r
6945837e
JW
1142 if (DevicePathSubType (Left) == MSG_DNS_DP) {\r
1143 Left = NextDevicePathNode (Left);\r
1144 }\r
1145\r
1146 if (DevicePathSubType (Right) == MSG_DNS_DP) {\r
1147 Right = NextDevicePathNode (Right);\r
1148 }\r
1149\r
bf5328f0
RN
1150 if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) &&\r
1151 ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) &&\r
1152 ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP))\r
1153 ) {\r
1154 return FALSE;\r
1155 }\r
1156 }\r
1157 }\r
1158 return (BOOLEAN) (IsDevicePathEnd (Left) && IsDevicePathEnd (Right));\r
1159}\r
1160\r
f5c12172
RN
1161/**\r
1162 Get the file buffer from the file system produced by Load File instance.\r
1163\r
1164 @param LoadFileHandle The handle of LoadFile instance.\r
339ae051 1165 @param RamDiskHandle Return the RAM Disk handle.\r
f5c12172 1166\r
08eff917
RN
1167 @return The next possible full path pointing to the load option.\r
1168 Caller is responsible to free the memory.\r
f5c12172 1169**/\r
08eff917
RN
1170EFI_DEVICE_PATH_PROTOCOL *\r
1171BmExpandNetworkFileSystem (\r
f5c12172 1172 IN EFI_HANDLE LoadFileHandle,\r
339ae051 1173 OUT EFI_HANDLE *RamDiskHandle\r
f5c12172
RN
1174 )\r
1175{\r
1176 EFI_STATUS Status;\r
1177 EFI_HANDLE Handle;\r
1178 EFI_HANDLE *Handles;\r
1179 UINTN HandleCount;\r
1180 UINTN Index;\r
1181 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1182\r
1183 Status = gBS->LocateHandleBuffer (\r
1184 ByProtocol,\r
1185 &gEfiBlockIoProtocolGuid,\r
1186 NULL,\r
1187 &HandleCount,\r
1188 &Handles\r
1189 );\r
1190 if (EFI_ERROR (Status)) {\r
1191 Handles = NULL;\r
1192 HandleCount = 0;\r
1193 }\r
eeab16c8
RN
1194\r
1195 Handle = NULL;\r
f5c12172
RN
1196 for (Index = 0; Index < HandleCount; Index++) {\r
1197 Node = DevicePathFromHandle (Handles[Index]);\r
1198 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);\r
1199 if (!EFI_ERROR (Status) &&\r
1200 (Handle == LoadFileHandle) &&\r
1201 (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) {\r
08eff917
RN
1202 //\r
1203 // Find the BlockIo instance populated from the LoadFile.\r
1204 //\r
f5c12172
RN
1205 Handle = Handles[Index];\r
1206 break;\r
1207 }\r
1208 }\r
1209\r
1210 if (Handles != NULL) {\r
1211 FreePool (Handles);\r
1212 }\r
1213\r
339ae051
RN
1214 if (Index == HandleCount) {\r
1215 Handle = NULL;\r
1216 }\r
1217\r
1218 *RamDiskHandle = Handle;\r
1219\r
1220 if (Handle != NULL) {\r
08eff917
RN
1221 //\r
1222 // Re-use BmExpandMediaDevicePath() to get the full device path of load option.\r
1223 // But assume only one SimpleFileSystem can be found under the BlockIo.\r
1224 //\r
1225 return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL);\r
f5c12172
RN
1226 } else {\r
1227 return NULL;\r
1228 }\r
1229}\r
1230\r
339ae051
RN
1231/**\r
1232 Return the RAM Disk device path created by LoadFile.\r
1233\r
1234 @param FilePath The source file path.\r
1235\r
1236 @return Callee-to-free RAM Disk device path\r
1237**/\r
1238EFI_DEVICE_PATH_PROTOCOL *\r
1239BmGetRamDiskDevicePath (\r
1240 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
1241 )\r
1242{\r
1243 EFI_STATUS Status;\r
1244 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
1245 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1246 EFI_HANDLE Handle;\r
1247\r
1248 Node = FilePath;\r
1249 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);\r
1250 if (!EFI_ERROR (Status) &&\r
1251 (DevicePathType (Node) == MEDIA_DEVICE_PATH) &&\r
1252 (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)\r
1253 ) {\r
1254\r
1255 //\r
1256 // Construct the device path pointing to RAM Disk\r
1257 //\r
1258 Node = NextDevicePathNode (Node);\r
1259 RamDiskDevicePath = DuplicateDevicePath (FilePath);\r
1260 ASSERT (RamDiskDevicePath != NULL);\r
1261 SetDevicePathEndNode ((VOID *) ((UINTN) RamDiskDevicePath + ((UINTN) Node - (UINTN) FilePath)));\r
1262 return RamDiskDevicePath;\r
1263 }\r
1264\r
1265 return NULL;\r
1266}\r
1267\r
1268/**\r
1269 Return the buffer and buffer size occupied by the RAM Disk.\r
1270\r
1271 @param RamDiskDevicePath RAM Disk device path.\r
1272 @param RamDiskSizeInPages Return RAM Disk size in pages.\r
1273\r
1274 @retval RAM Disk buffer.\r
1275**/\r
1276VOID *\r
1277BmGetRamDiskMemoryInfo (\r
1278 IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath,\r
1279 OUT UINTN *RamDiskSizeInPages\r
1280 )\r
1281{\r
1282\r
1283 EFI_STATUS Status;\r
1284 EFI_HANDLE Handle;\r
1285 UINT64 StartingAddr;\r
1286 UINT64 EndingAddr;\r
1287\r
1288 ASSERT (RamDiskDevicePath != NULL);\r
1289\r
1290 *RamDiskSizeInPages = 0;\r
1291\r
1292 //\r
1293 // Get the buffer occupied by RAM Disk.\r
1294 //\r
1295 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle);\r
1296 ASSERT_EFI_ERROR (Status);\r
1297 ASSERT ((DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) &&\r
1298 (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP));\r
1299 StartingAddr = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->StartingAddr);\r
1300 EndingAddr = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->EndingAddr);\r
1301 *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN) (EndingAddr - StartingAddr + 1));\r
1302 return (VOID *) (UINTN) StartingAddr;\r
1303}\r
1304\r
1305/**\r
1306 Destroy the RAM Disk.\r
1307\r
1308 The destroy operation includes to call RamDisk.Unregister to\r
1309 unregister the RAM DISK from RAM DISK driver, free the memory\r
1310 allocated for the RAM Disk.\r
1311\r
1312 @param RamDiskDevicePath RAM Disk device path.\r
1313**/\r
1314VOID\r
1315BmDestroyRamDisk (\r
1316 IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath\r
1317 )\r
1318{\r
1319 EFI_STATUS Status;\r
1320 VOID *RamDiskBuffer;\r
1321 UINTN RamDiskSizeInPages;\r
1322\r
1323 ASSERT (RamDiskDevicePath != NULL);\r
1324\r
1325 RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);\r
1326\r
1327 //\r
1328 // Destroy RAM Disk.\r
1329 //\r
1330 if (mRamDisk == NULL) {\r
1331 Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *) &mRamDisk);\r
1332 ASSERT_EFI_ERROR (Status);\r
1333 }\r
1334 Status = mRamDisk->Unregister (RamDiskDevicePath);\r
1335 ASSERT_EFI_ERROR (Status);\r
1336 FreePages (RamDiskBuffer, RamDiskSizeInPages);\r
1337}\r
1338\r
026ede49
RN
1339/**\r
1340 Get the file buffer from the specified Load File instance.\r
1341\r
1342 @param LoadFileHandle The specified Load File instance.\r
1343 @param FilePath The file path which will pass to LoadFile().\r
026ede49 1344\r
08eff917 1345 @return The full device path pointing to the load option buffer.\r
026ede49 1346**/\r
08eff917
RN
1347EFI_DEVICE_PATH_PROTOCOL *\r
1348BmExpandLoadFile (\r
339ae051 1349 IN EFI_HANDLE LoadFileHandle,\r
08eff917 1350 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
026ede49
RN
1351 )\r
1352{\r
1353 EFI_STATUS Status;\r
1354 EFI_LOAD_FILE_PROTOCOL *LoadFile;\r
1355 VOID *FileBuffer;\r
339ae051 1356 EFI_HANDLE RamDiskHandle;\r
026ede49 1357 UINTN BufferSize;\r
08eff917 1358 EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
026ede49
RN
1359\r
1360 Status = gBS->OpenProtocol (\r
1361 LoadFileHandle,\r
1362 &gEfiLoadFileProtocolGuid,\r
1363 (VOID **) &LoadFile,\r
1364 gImageHandle,\r
1365 NULL,\r
1366 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1367 );\r
1368 ASSERT_EFI_ERROR (Status);\r
1369\r
1370 FileBuffer = NULL;\r
1371 BufferSize = 0;\r
1372 Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
1373 if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) {\r
1374 return NULL;\r
1375 }\r
1376\r
08eff917
RN
1377 if (Status == EFI_BUFFER_TOO_SMALL) {\r
1378 //\r
1379 // The load option buffer is directly returned by LoadFile.\r
1380 //\r
1381 return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));\r
1382 }\r
1383\r
1384 //\r
1385 // The load option resides in a RAM disk.\r
1386 //\r
1387 FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));\r
026ede49
RN
1388 if (FileBuffer == NULL) {\r
1389 return NULL;\r
1390 }\r
1391\r
1392 Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
1393 if (EFI_ERROR (Status)) {\r
08eff917 1394 FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));\r
026ede49
RN
1395 return NULL;\r
1396 }\r
1397\r
08eff917
RN
1398 FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle);\r
1399 if (FullPath == NULL) {\r
1400 //\r
1401 // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.\r
1402 //\r
1403 BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));\r
026ede49
RN
1404 }\r
1405\r
08eff917 1406 return FullPath;\r
026ede49
RN
1407}\r
1408\r
bf5328f0 1409/**\r
08eff917
RN
1410 Return the full device path pointing to the load option.\r
1411\r
1412 FilePath may:\r
1413 1. Exactly matches to a LoadFile instance.\r
1414 2. Cannot match to any LoadFile instance. Wide match is required.\r
1415 In either case, the routine may return:\r
1416 1. A copy of FilePath when FilePath matches to a LoadFile instance and\r
1417 the LoadFile returns a load option buffer.\r
1418 2. A new device path with IP and URI information updated when wide match\r
1419 happens.\r
1420 3. A new device path pointing to a load option in RAM disk.\r
1421 In either case, only one full device path is returned for a specified\r
1422 FilePath.\r
bf5328f0
RN
1423\r
1424 @param FilePath The media device path pointing to a LoadFile instance.\r
bf5328f0
RN
1425\r
1426 @return The load option buffer.\r
1427**/\r
08eff917
RN
1428EFI_DEVICE_PATH_PROTOCOL *\r
1429BmExpandLoadFiles (\r
1430 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
bf5328f0
RN
1431 )\r
1432{\r
1433 EFI_STATUS Status;\r
1434 EFI_HANDLE Handle;\r
bf5328f0
RN
1435 EFI_HANDLE *Handles;\r
1436 UINTN HandleCount;\r
1437 UINTN Index;\r
1438 EFI_DEVICE_PATH_PROTOCOL *Node;\r
bf5328f0
RN
1439\r
1440 //\r
1441 // Get file buffer from load file instance.\r
1442 //\r
1443 Node = FilePath;\r
1444 Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);\r
1445 if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {\r
1446 //\r
1447 // When wide match happens, pass full device path to LoadFile (),\r
1448 // otherwise, pass remaining device path to LoadFile ().\r
1449 //\r
1450 FilePath = Node;\r
1451 } else {\r
1452 Handle = NULL;\r
1453 //\r
1454 // Use wide match algorithm to find one when\r
1455 // cannot find a LoadFile instance to exactly match the FilePath\r
1456 //\r
1457 Status = gBS->LocateHandleBuffer (\r
1458 ByProtocol,\r
1459 &gEfiLoadFileProtocolGuid,\r
1460 NULL,\r
1461 &HandleCount,\r
1462 &Handles\r
1463 );\r
1464 if (EFI_ERROR (Status)) {\r
1465 Handles = NULL;\r
1466 HandleCount = 0;\r
1467 }\r
1468 for (Index = 0; Index < HandleCount; Index++) {\r
1469 if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) {\r
1470 Handle = Handles[Index];\r
1471 break;\r
1472 }\r
1473 }\r
1474 if (Handles != NULL) {\r
1475 FreePool (Handles);\r
1476 }\r
1477 }\r
1478\r
1479 if (Handle == NULL) {\r
1480 return NULL;\r
1481 }\r
1482\r
08eff917 1483 return BmExpandLoadFile (Handle, FilePath);\r
bf5328f0
RN
1484}\r
1485\r
067ed98a
RN
1486/**\r
1487 Get the load option by its device path.\r
1488\r
1489 @param FilePath The device path pointing to a load option.\r
1490 It could be a short-form device path.\r
1491 @param FullPath Return the full device path of the load option after\r
1492 short-form device path expanding.\r
1493 Caller is responsible to free it.\r
1494 @param FileSize Return the load option size.\r
1495\r
1496 @return The load option buffer. Caller is responsible to free the memory.\r
1497**/\r
1498VOID *\r
4ed2440d
RN
1499EFIAPI\r
1500EfiBootManagerGetLoadOptionBuffer (\r
067ed98a
RN
1501 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1502 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1503 OUT UINTN *FileSize\r
1504 )\r
08eff917
RN
1505{\r
1506 *FullPath = NULL;\r
1507\r
1508 EfiBootManagerConnectDevicePath (FilePath, NULL);\r
1509 return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize);\r
1510}\r
1511\r
1512/**\r
1513 Get the next possible full path pointing to the load option.\r
1514 The routine doesn't guarantee the returned full path points to an existing\r
1515 file, and it also doesn't guarantee the existing file is a valid load option.\r
1516 BmGetNextLoadOptionBuffer() guarantees.\r
1517\r
1518 @param FilePath The device path pointing to a load option.\r
1519 It could be a short-form device path.\r
1520 @param FullPath The full path returned by the routine in last call.\r
1521 Set to NULL in first call.\r
1522\r
1523 @return The next possible full path pointing to the load option.\r
1524 Caller is responsible to free the memory.\r
1525**/\r
1526EFI_DEVICE_PATH_PROTOCOL *\r
1527BmGetNextLoadOptionDevicePath (\r
1528 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1529 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
1530 )\r
067ed98a
RN
1531{\r
1532 EFI_HANDLE Handle;\r
067ed98a
RN
1533 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1534 EFI_STATUS Status;\r
1535\r
08eff917 1536 ASSERT (FilePath != NULL);\r
067ed98a
RN
1537\r
1538 //\r
1539 // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI\r
1540 //\r
1541 Node = FilePath;\r
1542 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);\r
1543 if (EFI_ERROR (Status)) {\r
1544 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle);\r
1545 }\r
1546\r
1547 if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {\r
08eff917 1548 return BmExpandMediaDevicePath (FilePath, FullPath);\r
067ed98a
RN
1549 }\r
1550\r
1551 //\r
1552 // Expand the short-form device path to full device path\r
1553 //\r
1554 if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&\r
1555 (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) {\r
1556 //\r
1557 // Expand the Harddrive device path\r
1558 //\r
08eff917
RN
1559 if (FullPath == NULL) {\r
1560 return BmExpandPartitionDevicePath (FilePath);\r
1561 } else {\r
1562 return NULL;\r
1563 }\r
ccb66799
RN
1564 } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&\r
1565 (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) {\r
1566 //\r
1567 // Expand the File-path device path\r
1568 //\r
08eff917 1569 return BmExpandFileDevicePath (FilePath, FullPath);\r
4a285ec1
RN
1570 } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) &&\r
1571 (DevicePathSubType (FilePath) == MSG_URI_DP)) {\r
1572 //\r
1573 // Expand the URI device path\r
1574 //\r
08eff917 1575 return BmExpandUriDevicePath (FilePath, FullPath);\r
067ed98a 1576 } else {\r
21e359dc
RN
1577 Node = FilePath;\r
1578 Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &Node, &Handle);\r
1579 if (EFI_ERROR (Status)) {\r
1580 //\r
1581 // Only expand the USB WWID/Class device path\r
1582 // when FilePath doesn't point to a physical UsbIo controller.\r
1583 // Otherwise, infinite recursion will happen.\r
1584 //\r
1585 for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {\r
1586 if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
1587 ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {\r
1588 break;\r
1589 }\r
067ed98a 1590 }\r
067ed98a 1591\r
21e359dc
RN
1592 //\r
1593 // Expand the USB WWID/Class device path\r
1594 //\r
1595 if (!IsDevicePathEnd (Node)) {\r
1596 if (FilePath == Node) {\r
1597 //\r
1598 // Boot Option device path starts with USB Class or USB WWID device path.\r
1599 // For Boot Option device path which doesn't begin with the USB Class or\r
1600 // USB WWID device path, it's not needed to connect again here.\r
1601 //\r
1602 BmConnectUsbShortFormDevicePath (FilePath);\r
1603 }\r
1604 return BmExpandUsbDevicePath (FilePath, FullPath, Node);\r
067ed98a 1605 }\r
067ed98a
RN
1606 }\r
1607 }\r
1608\r
1609 //\r
08eff917
RN
1610 // For the below cases, FilePath only expands to one Full path.\r
1611 // So just handle the case when FullPath == NULL.\r
1612 //\r
1613 if (FullPath != NULL) {\r
1614 return NULL;\r
1615 }\r
1616\r
1617 //\r
1618 // Load option resides in FV.\r
067ed98a 1619 //\r
525839ed 1620 if (BmIsFvFilePath (FilePath)) {\r
08eff917 1621 return BmAdjustFvFilePath (FilePath);\r
067ed98a
RN
1622 }\r
1623\r
1624 //\r
08eff917 1625 // Load option resides in Simple File System.\r
067ed98a 1626 //\r
de6c0eff 1627 Node = FilePath;\r
bf5328f0
RN
1628 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);\r
1629 if (!EFI_ERROR (Status)) {\r
08eff917 1630 return DuplicateDevicePath (FilePath);\r
067ed98a
RN
1631 }\r
1632\r
08eff917
RN
1633 //\r
1634 // Last chance to try: Load option may be loaded through LoadFile.\r
1635 //\r
1636 return BmExpandLoadFiles (FilePath);\r
067ed98a
RN
1637}\r
1638\r
0aa09042 1639/**\r
7c69fbf2 1640 Check if it's a Device Path pointing to BootManagerMenu.\r
0aa09042
LG
1641\r
1642 @param DevicePath Input device path.\r
1643\r
7c69fbf2
LG
1644 @retval TRUE The device path is BootManagerMenu File Device Path.\r
1645 @retval FALSE The device path is NOT BootManagerMenu File Device Path.\r
0aa09042
LG
1646**/\r
1647BOOLEAN\r
7c69fbf2 1648BmIsBootManagerMenuFilePath (\r
0aa09042
LG
1649 EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
1650)\r
1651{\r
1652 EFI_HANDLE FvHandle;\r
1653 VOID *NameGuid;\r
1654 EFI_STATUS Status;\r
1655\r
1656 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle);\r
1657 if (!EFI_ERROR (Status)) {\r
1658 NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);\r
1659 if (NameGuid != NULL) {\r
1660 return CompareGuid (NameGuid, PcdGetPtr (PcdBootManagerMenuFile));\r
1661 }\r
1662 }\r
1663\r
1664 return FALSE;\r
1665}\r
1666\r
5cc67962
LE
1667/**\r
1668 Report status code with EFI_RETURN_STATUS_EXTENDED_DATA about LoadImage() or\r
1669 StartImage() failure.\r
1670\r
1671 @param[in] ErrorCode An Error Code in the Software Class, DXE Boot\r
1672 Service Driver Subclass. ErrorCode will be used to\r
1673 compose the Value parameter for status code\r
1674 reporting. Must be one of\r
1675 EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR and\r
1676 EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED.\r
1677\r
1678 @param[in] FailureStatus The failure status returned by the boot service\r
1679 that should be reported.\r
1680**/\r
1681VOID\r
1682BmReportLoadFailure (\r
1683 IN UINT32 ErrorCode,\r
1684 IN EFI_STATUS FailureStatus\r
1685 )\r
1686{\r
1687 EFI_RETURN_STATUS_EXTENDED_DATA ExtendedData;\r
1688\r
1689 if (!ReportErrorCodeEnabled ()) {\r
1690 return;\r
1691 }\r
1692\r
1693 ASSERT (\r
1694 (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) ||\r
1695 (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)\r
1696 );\r
1697\r
1698 ZeroMem (&ExtendedData, sizeof (ExtendedData));\r
1699 ExtendedData.ReturnStatus = FailureStatus;\r
1700\r
1701 REPORT_STATUS_CODE_EX (\r
1702 (EFI_ERROR_CODE | EFI_ERROR_MINOR),\r
1703 (EFI_SOFTWARE_DXE_BS_DRIVER | ErrorCode),\r
1704 0,\r
1705 NULL,\r
1706 NULL,\r
1707 &ExtendedData.DataHeader + 1,\r
1708 sizeof (ExtendedData) - sizeof (ExtendedData.DataHeader)\r
1709 );\r
1710}\r
1711\r
067ed98a
RN
1712/**\r
1713 Attempt to boot the EFI boot option. This routine sets L"BootCurent" and\r
1714 also signals the EFI ready to boot event. If the device path for the option\r
d1102dba
LG
1715 starts with a BBS device path a legacy boot is attempted via the registered\r
1716 gLegacyBoot function. Short form device paths are also supported via this\r
067ed98a
RN
1717 rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,\r
1718 MSG_USB_CLASS_DP gets expaned out to find the first device that matches.\r
d1102dba
LG
1719 If the BootOption Device Path fails the removable media boot algorithm\r
1720 is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type\r
067ed98a
RN
1721 is tried per processor type)\r
1722\r
1723 @param BootOption Boot Option to try and boot.\r
1724 On return, BootOption->Status contains the boot status.\r
1725 EFI_SUCCESS BootOption was booted\r
1726 EFI_UNSUPPORTED A BBS device path was found with no valid callback\r
1727 registered via EfiBootManagerInitialize().\r
1728 EFI_NOT_FOUND The BootOption was not found on the system\r
1729 !EFI_SUCCESS BootOption failed with this error status\r
1730\r
1731**/\r
1732VOID\r
1733EFIAPI\r
1734EfiBootManagerBoot (\r
1735 IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
1736 )\r
1737{\r
1738 EFI_STATUS Status;\r
1739 EFI_HANDLE ImageHandle;\r
1740 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;\r
1741 UINT16 Uint16;\r
1742 UINTN OptionNumber;\r
1743 UINTN OriginalOptionNumber;\r
1744 EFI_DEVICE_PATH_PROTOCOL *FilePath;\r
339ae051 1745 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
067ed98a
RN
1746 VOID *FileBuffer;\r
1747 UINTN FileSize;\r
1748 EFI_BOOT_LOGO_PROTOCOL *BootLogo;\r
1749 EFI_EVENT LegacyBootEvent;\r
1750\r
1751 if (BootOption == NULL) {\r
1752 return;\r
1753 }\r
1754\r
1755 if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) {\r
1756 BootOption->Status = EFI_INVALID_PARAMETER;\r
1757 return;\r
1758 }\r
1759\r
1760 //\r
1761 // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File")\r
1762 //\r
1763 OptionNumber = BmFindBootOptionInVariable (BootOption);\r
1764 if (OptionNumber == LoadOptionNumberUnassigned) {\r
1765 Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);\r
1766 if (!EFI_ERROR (Status)) {\r
1767 //\r
1768 // Save the BootOption->OptionNumber to restore later\r
1769 //\r
1770 OptionNumber = Uint16;\r
1771 OriginalOptionNumber = BootOption->OptionNumber;\r
1772 BootOption->OptionNumber = OptionNumber;\r
1773 Status = EfiBootManagerLoadOptionToVariable (BootOption);\r
1774 BootOption->OptionNumber = OriginalOptionNumber;\r
1775 }\r
1776\r
1777 if (EFI_ERROR (Status)) {\r
1778 DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));\r
1779 BootOption->Status = Status;\r
1780 return ;\r
1781 }\r
1782 }\r
1783\r
1784 //\r
1785 // 2. Set BootCurrent\r
1786 //\r
1787 Uint16 = (UINT16) OptionNumber;\r
1788 BmSetVariableAndReportStatusCodeOnError (\r
1789 L"BootCurrent",\r
1790 &gEfiGlobalVariableGuid,\r
1791 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
1792 sizeof (UINT16),\r
1793 &Uint16\r
1794 );\r
1795\r
1796 //\r
1797 // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute\r
1798 // the boot option.\r
1799 //\r
7c69fbf2 1800 if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) {\r
067ed98a
RN
1801 DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n"));\r
1802 BmStopHotkeyService (NULL, NULL);\r
1803 } else {\r
1804 EfiSignalEventReadyToBoot();\r
1805 //\r
1806 // Report Status Code to indicate ReadyToBoot was signalled\r
1807 //\r
1808 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));\r
1809 //\r
1810 // 4. Repair system through DriverHealth protocol\r
1811 //\r
72208a9a 1812 BmRepairAllControllers (0);\r
067ed98a
RN
1813 }\r
1814\r
1815 PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1816\r
1817 //\r
3a986a35
RN
1818 // 5. Adjust the different type memory page number just before booting\r
1819 // and save the updated info into the variable for next boot to use\r
1820 //\r
1821 BmSetMemoryTypeInformationVariable (\r
1822 (BOOLEAN) ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT)\r
1823 );\r
1824\r
1825 //\r
1826 // 6. Load EFI boot option to ImageHandle\r
067ed98a 1827 //\r
3a986a35
RN
1828 DEBUG_CODE_BEGIN ();\r
1829 if (BootOption->Description == NULL) {\r
1830 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));\r
1831 } else {\r
1832 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));\r
1833 }\r
1834 DEBUG_CODE_END ();\r
1835\r
339ae051
RN
1836 ImageHandle = NULL;\r
1837 RamDiskDevicePath = NULL;\r
067ed98a 1838 if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {\r
08eff917
RN
1839 Status = EFI_NOT_FOUND;\r
1840 FilePath = NULL;\r
1841 EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);\r
1842 FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize);\r
339ae051
RN
1843 if (FileBuffer != NULL) {\r
1844 RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);\r
08eff917 1845\r
067ed98a
RN
1846 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));\r
1847 Status = gBS->LoadImage (\r
1848 TRUE,\r
1849 gImageHandle,\r
1850 FilePath,\r
1851 FileBuffer,\r
1852 FileSize,\r
1853 &ImageHandle\r
1854 );\r
1855 }\r
1856 if (FileBuffer != NULL) {\r
1857 FreePool (FileBuffer);\r
1858 }\r
1859 if (FilePath != NULL) {\r
1860 FreePool (FilePath);\r
1861 }\r
1862\r
1863 if (EFI_ERROR (Status)) {\r
1864 //\r
c2cf8720 1865 // Report Status Code with the failure status to indicate that the failure to load boot option\r
067ed98a 1866 //\r
5cc67962 1867 BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status);\r
067ed98a 1868 BootOption->Status = Status;\r
339ae051
RN
1869 //\r
1870 // Destroy the RAM disk\r
1871 //\r
1872 if (RamDiskDevicePath != NULL) {\r
1873 BmDestroyRamDisk (RamDiskDevicePath);\r
1874 FreePool (RamDiskDevicePath);\r
1875 }\r
067ed98a
RN
1876 return;\r
1877 }\r
1878 }\r
1879\r
067ed98a
RN
1880 //\r
1881 // Check to see if we should legacy BOOT. If yes then do the legacy boot\r
1882 // Write boot to OS performance data for Legacy boot\r
1883 //\r
1884 if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {\r
1885 if (mBmLegacyBoot != NULL) {\r
1886 //\r
1887 // Write boot to OS performance data for legacy boot.\r
1888 //\r
1889 PERF_CODE (\r
1890 //\r
1891 // Create an event to be signalled when Legacy Boot occurs to write performance data.\r
1892 //\r
1893 Status = EfiCreateEventLegacyBootEx(\r
1894 TPL_NOTIFY,\r
3a039a56 1895 BmEndOfBdsPerfCode,\r
d1102dba 1896 NULL,\r
067ed98a
RN
1897 &LegacyBootEvent\r
1898 );\r
1899 ASSERT_EFI_ERROR (Status);\r
1900 );\r
1901\r
1902 mBmLegacyBoot (BootOption);\r
1903 } else {\r
1904 BootOption->Status = EFI_UNSUPPORTED;\r
1905 }\r
1906\r
1907 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1908 return;\r
1909 }\r
d1102dba 1910\r
067ed98a
RN
1911 //\r
1912 // Provide the image with its load options\r
1913 //\r
1914 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
1915 ASSERT_EFI_ERROR (Status);\r
1916\r
404bd442
RN
1917 if (!BmIsAutoCreateBootOption (BootOption)) {\r
1918 ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;\r
1919 ImageInfo->LoadOptions = BootOption->OptionalData;\r
1920 }\r
067ed98a
RN
1921\r
1922 //\r
1923 // Clean to NULL because the image is loaded directly from the firmwares boot manager.\r
1924 //\r
1925 ImageInfo->ParentHandle = NULL;\r
1926\r
1927 //\r
1928 // Before calling the image, enable the Watchdog Timer for 5 minutes period\r
1929 //\r
1930 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);\r
1931\r
1932 //\r
1933 // Write boot to OS performance data for UEFI boot\r
1934 //\r
1935 PERF_CODE (\r
3a039a56 1936 BmEndOfBdsPerfCode (NULL, NULL);\r
067ed98a
RN
1937 );\r
1938\r
1939 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));\r
1940\r
1941 Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);\r
1942 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));\r
1943 BootOption->Status = Status;\r
1944 if (EFI_ERROR (Status)) {\r
1945 //\r
c2cf8720 1946 // Report Status Code with the failure status to indicate that boot failure\r
067ed98a 1947 //\r
5cc67962 1948 BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status);\r
067ed98a
RN
1949 }\r
1950 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1951\r
339ae051
RN
1952 //\r
1953 // Destroy the RAM disk\r
1954 //\r
1955 if (RamDiskDevicePath != NULL) {\r
1956 BmDestroyRamDisk (RamDiskDevicePath);\r
1957 FreePool (RamDiskDevicePath);\r
1958 }\r
1959\r
067ed98a
RN
1960 //\r
1961 // Clear the Watchdog Timer after the image returns\r
1962 //\r
1963 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);\r
1964\r
1965 //\r
1966 // Set Logo status invalid after trying one boot option\r
1967 //\r
1968 BootLogo = NULL;\r
1969 Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);\r
1970 if (!EFI_ERROR (Status) && (BootLogo != NULL)) {\r
1971 Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);\r
1972 ASSERT_EFI_ERROR (Status);\r
1973 }\r
1974\r
1975 //\r
1976 // Clear Boot Current\r
1977 //\r
1978 Status = gRT->SetVariable (\r
1979 L"BootCurrent",\r
1980 &gEfiGlobalVariableGuid,\r
1981 0,\r
1982 0,\r
1983 NULL\r
1984 );\r
1985 //\r
1986 // Deleting variable with current variable implementation shouldn't fail.\r
1987 // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,\r
1988 // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.\r
1989 //\r
1990 ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
1991}\r
1992\r
1993/**\r
1994 Check whether there is a instance in BlockIoDevicePath, which contain multi device path\r
1995 instances, has the same partition node with HardDriveDevicePath device path\r
1996\r
1997 @param BlockIoDevicePath Multi device path instances which need to check\r
1998 @param HardDriveDevicePath A device path which starts with a hard drive media\r
1999 device path.\r
2000\r
2001 @retval TRUE There is a matched device path instance.\r
2002 @retval FALSE There is no matched device path instance.\r
2003\r
2004**/\r
2005BOOLEAN\r
2006BmMatchPartitionDevicePathNode (\r
2007 IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,\r
2008 IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath\r
2009 )\r
2010{\r
2011 HARDDRIVE_DEVICE_PATH *Node;\r
2012\r
2013 if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {\r
2014 return FALSE;\r
2015 }\r
2016\r
2017 //\r
02021d2e 2018 // Match all the partition device path nodes including the nested partition nodes\r
067ed98a
RN
2019 //\r
2020 while (!IsDevicePathEnd (BlockIoDevicePath)) {\r
2021 if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&\r
2022 (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)\r
2023 ) {\r
02021d2e
GL
2024 //\r
2025 // See if the harddrive device path in blockio matches the orig Hard Drive Node\r
2026 //\r
2027 Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath;\r
2028\r
2029 //\r
2030 // Match Signature and PartitionNumber.\r
2031 // Unused bytes in Signature are initiaized with zeros.\r
2032 //\r
2033 if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&\r
2034 (Node->MBRType == HardDriveDevicePath->MBRType) &&\r
2035 (Node->SignatureType == HardDriveDevicePath->SignatureType) &&\r
2036 (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)) {\r
2037 return TRUE;\r
2038 }\r
067ed98a
RN
2039 }\r
2040\r
2041 BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);\r
2042 }\r
2043\r
02021d2e 2044 return FALSE;\r
067ed98a
RN
2045}\r
2046\r
2047/**\r
2048 Emuerate all possible bootable medias in the following order:\r
2049 1. Removable BlockIo - The boot option only points to the removable media\r
2050 device, like USB key, DVD, Floppy etc.\r
2051 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device,\r
2052 like HardDisk.\r
2053 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting\r
2054 SimpleFileSystem Protocol, but not supporting BlockIo\r
2055 protocol.\r
d1102dba 2056 4. LoadFile - The boot option points to the media supporting\r
067ed98a
RN
2057 LoadFile protocol.\r
2058 Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior\r
2059\r
2060 @param BootOptionCount Return the boot option count which has been found.\r
2061\r
2062 @retval Pointer to the boot option array.\r
2063**/\r
2064EFI_BOOT_MANAGER_LOAD_OPTION *\r
2065BmEnumerateBootOptions (\r
2066 UINTN *BootOptionCount\r
2067 )\r
2068{\r
2069 EFI_STATUS Status;\r
2070 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
067ed98a
RN
2071 UINTN HandleCount;\r
2072 EFI_HANDLE *Handles;\r
2073 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
2074 UINTN Removable;\r
2075 UINTN Index;\r
f41c71d2 2076 CHAR16 *Description;\r
067ed98a
RN
2077\r
2078 ASSERT (BootOptionCount != NULL);\r
2079\r
2080 *BootOptionCount = 0;\r
2081 BootOptions = NULL;\r
2082\r
2083 //\r
2084 // Parse removable block io followed by fixed block io\r
2085 //\r
2086 gBS->LocateHandleBuffer (\r
2087 ByProtocol,\r
2088 &gEfiBlockIoProtocolGuid,\r
2089 NULL,\r
2090 &HandleCount,\r
2091 &Handles\r
2092 );\r
2093\r
2094 for (Removable = 0; Removable < 2; Removable++) {\r
2095 for (Index = 0; Index < HandleCount; Index++) {\r
2096 Status = gBS->HandleProtocol (\r
2097 Handles[Index],\r
2098 &gEfiBlockIoProtocolGuid,\r
2099 (VOID **) &BlkIo\r
2100 );\r
2101 if (EFI_ERROR (Status)) {\r
2102 continue;\r
2103 }\r
2104\r
2105 //\r
2106 // Skip the logical partitions\r
2107 //\r
2108 if (BlkIo->Media->LogicalPartition) {\r
2109 continue;\r
2110 }\r
2111\r
2112 //\r
2113 // Skip the fixed block io then the removable block io\r
2114 //\r
2115 if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {\r
2116 continue;\r
2117 }\r
2118\r
f41c71d2 2119 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
2120 BootOptions = ReallocatePool (\r
2121 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
2122 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
2123 BootOptions\r
2124 );\r
2125 ASSERT (BootOptions != NULL);\r
2126\r
2127 Status = EfiBootManagerInitializeLoadOption (\r
2128 &BootOptions[(*BootOptionCount)++],\r
2129 LoadOptionNumberUnassigned,\r
2130 LoadOptionTypeBoot,\r
2131 LOAD_OPTION_ACTIVE,\r
f41c71d2 2132 Description,\r
067ed98a
RN
2133 DevicePathFromHandle (Handles[Index]),\r
2134 NULL,\r
2135 0\r
2136 );\r
2137 ASSERT_EFI_ERROR (Status);\r
2138\r
f41c71d2 2139 FreePool (Description);\r
067ed98a
RN
2140 }\r
2141 }\r
2142\r
2143 if (HandleCount != 0) {\r
2144 FreePool (Handles);\r
2145 }\r
2146\r
2147 //\r
2148 // Parse simple file system not based on block io\r
2149 //\r
067ed98a
RN
2150 gBS->LocateHandleBuffer (\r
2151 ByProtocol,\r
2152 &gEfiSimpleFileSystemProtocolGuid,\r
2153 NULL,\r
2154 &HandleCount,\r
2155 &Handles\r
2156 );\r
2157 for (Index = 0; Index < HandleCount; Index++) {\r
2158 Status = gBS->HandleProtocol (\r
2159 Handles[Index],\r
2160 &gEfiBlockIoProtocolGuid,\r
2161 (VOID **) &BlkIo\r
2162 );\r
2163 if (!EFI_ERROR (Status)) {\r
2164 //\r
2165 // Skip if the file system handle supports a BlkIo protocol, which we've handled in above\r
2166 //\r
2167 continue;\r
2168 }\r
f41c71d2 2169 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
2170 BootOptions = ReallocatePool (\r
2171 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
2172 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
2173 BootOptions\r
2174 );\r
2175 ASSERT (BootOptions != NULL);\r
2176\r
2177 Status = EfiBootManagerInitializeLoadOption (\r
2178 &BootOptions[(*BootOptionCount)++],\r
2179 LoadOptionNumberUnassigned,\r
2180 LoadOptionTypeBoot,\r
2181 LOAD_OPTION_ACTIVE,\r
2182 Description,\r
2183 DevicePathFromHandle (Handles[Index]),\r
2184 NULL,\r
2185 0\r
2186 );\r
2187 ASSERT_EFI_ERROR (Status);\r
f41c71d2 2188 FreePool (Description);\r
067ed98a
RN
2189 }\r
2190\r
2191 if (HandleCount != 0) {\r
2192 FreePool (Handles);\r
2193 }\r
2194\r
2195 //\r
2f014788 2196 // Parse load file protocol\r
067ed98a
RN
2197 //\r
2198 gBS->LocateHandleBuffer (\r
2199 ByProtocol,\r
2200 &gEfiLoadFileProtocolGuid,\r
2201 NULL,\r
2202 &HandleCount,\r
2203 &Handles\r
2204 );\r
2205 for (Index = 0; Index < HandleCount; Index++) {\r
8953d69a
LG
2206 //\r
2207 // Ignore BootManagerMenu. its boot option will be created by EfiBootManagerGetBootManagerMenu().\r
2208 //\r
2209 if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {\r
2210 continue;\r
2211 }\r
067ed98a 2212\r
f41c71d2 2213 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
2214 BootOptions = ReallocatePool (\r
2215 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
2216 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
2217 BootOptions\r
2218 );\r
2219 ASSERT (BootOptions != NULL);\r
2220\r
2221 Status = EfiBootManagerInitializeLoadOption (\r
2222 &BootOptions[(*BootOptionCount)++],\r
2223 LoadOptionNumberUnassigned,\r
2224 LoadOptionTypeBoot,\r
8953d69a 2225 LOAD_OPTION_ACTIVE,\r
067ed98a
RN
2226 Description,\r
2227 DevicePathFromHandle (Handles[Index]),\r
2228 NULL,\r
2229 0\r
2230 );\r
2231 ASSERT_EFI_ERROR (Status);\r
f41c71d2 2232 FreePool (Description);\r
067ed98a
RN
2233 }\r
2234\r
2235 if (HandleCount != 0) {\r
2236 FreePool (Handles);\r
2237 }\r
2238\r
d948fe96 2239 BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount);\r
067ed98a
RN
2240 return BootOptions;\r
2241}\r
2242\r
2243/**\r
2244 The function enumerates all boot options, creates them and registers them in the BootOrder variable.\r
2245**/\r
2246VOID\r
2247EFIAPI\r
2248EfiBootManagerRefreshAllBootOption (\r
2249 VOID\r
2250 )\r
2251{\r
2252 EFI_STATUS Status;\r
2253 EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;\r
2254 UINTN NvBootOptionCount;\r
2255 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2256 UINTN BootOptionCount;\r
2257 UINTN Index;\r
2258\r
2259 //\r
2260 // Optionally refresh the legacy boot option\r
2261 //\r
2262 if (mBmRefreshLegacyBootOption != NULL) {\r
2263 mBmRefreshLegacyBootOption ();\r
2264 }\r
2265\r
2266 BootOptions = BmEnumerateBootOptions (&BootOptionCount);\r
2267 NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);\r
2268\r
2269 //\r
2270 // Mark the boot option as added by BDS by setting OptionalData to a special GUID\r
2271 //\r
2272 for (Index = 0; Index < BootOptionCount; Index++) {\r
2273 BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);\r
2274 BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);\r
2275 }\r
2276\r
2277 //\r
2278 // Remove invalid EFI boot options from NV\r
2279 //\r
2280 for (Index = 0; Index < NvBootOptionCount; Index++) {\r
d1102dba 2281 if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) ||\r
067ed98a 2282 (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)\r
404bd442 2283 ) && BmIsAutoCreateBootOption (&NvBootOptions[Index])\r
067ed98a
RN
2284 ) {\r
2285 //\r
2286 // Only check those added by BDS\r
2287 // so that the boot options added by end-user or OS installer won't be deleted\r
2288 //\r
51dee9a4 2289 if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) {\r
067ed98a
RN
2290 Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
2291 //\r
2292 // Deleting variable with current variable implementation shouldn't fail.\r
2293 //\r
2294 ASSERT_EFI_ERROR (Status);\r
2295 }\r
2296 }\r
2297 }\r
2298\r
2299 //\r
2300 // Add new EFI boot options to NV\r
2301 //\r
2302 for (Index = 0; Index < BootOptionCount; Index++) {\r
51dee9a4 2303 if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) {\r
067ed98a
RN
2304 EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);\r
2305 //\r
2306 // Try best to add the boot options so continue upon failure.\r
2307 //\r
2308 }\r
2309 }\r
2310\r
2311 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2312 EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);\r
2313}\r
2314\r
2315/**\r
e58f1ae5 2316 This function is called to get or create the boot option for the Boot Manager Menu.\r
067ed98a
RN
2317\r
2318 The Boot Manager Menu is shown after successfully booting a boot option.\r
2319 Assume the BootManagerMenuFile is in the same FV as the module links to this library.\r
2320\r
2321 @param BootOption Return the boot option of the Boot Manager Menu\r
2322\r
2323 @retval EFI_SUCCESS Successfully register the Boot Manager Menu.\r
e58f1ae5
SW
2324 @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.\r
2325 @retval others Return status of gRT->SetVariable (). BootOption still points\r
2326 to the Boot Manager Menu even the Status is not EFI_SUCCESS\r
2327 and EFI_NOT_FOUND.\r
067ed98a
RN
2328**/\r
2329EFI_STATUS\r
2330BmRegisterBootManagerMenu (\r
2331 OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2332 )\r
2333{\r
2334 EFI_STATUS Status;\r
2335 CHAR16 *Description;\r
2336 UINTN DescriptionLength;\r
2337 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
2338 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
2339 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;\r
2f014788
LG
2340 UINTN HandleCount;\r
2341 EFI_HANDLE *Handles;\r
2342 UINTN Index;\r
e58f1ae5
SW
2343 VOID *Data;\r
2344 UINTN DataSize;\r
067ed98a 2345\r
2f014788 2346 DevicePath = NULL;\r
e9e44f65 2347 Description = NULL;\r
e58f1ae5 2348 //\r
7c69fbf2 2349 // Try to find BootManagerMenu from LoadFile protocol\r
e58f1ae5 2350 //\r
2f014788
LG
2351 gBS->LocateHandleBuffer (\r
2352 ByProtocol,\r
2353 &gEfiLoadFileProtocolGuid,\r
2354 NULL,\r
2355 &HandleCount,\r
2356 &Handles\r
2357 );\r
2358 for (Index = 0; Index < HandleCount; Index++) {\r
7c69fbf2 2359 if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {\r
2f014788
LG
2360 DevicePath = DuplicateDevicePath (DevicePathFromHandle (Handles[Index]));\r
2361 Description = BmGetBootDescription (Handles[Index]);\r
2362 break;\r
2363 }\r
2364 }\r
2365 if (HandleCount != 0) {\r
2366 FreePool (Handles);\r
067ed98a
RN
2367 }\r
2368\r
2f014788
LG
2369 if (DevicePath == NULL) {\r
2370 Data = NULL;\r
2371 Status = GetSectionFromFv (\r
2372 PcdGetPtr (PcdBootManagerMenuFile),\r
2373 EFI_SECTION_PE32,\r
2374 0,\r
2375 (VOID **) &Data,\r
2376 &DataSize\r
2377 );\r
2378 if (Data != NULL) {\r
2379 FreePool (Data);\r
2380 }\r
2381 if (EFI_ERROR (Status)) {\r
2382 DEBUG ((EFI_D_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n"));\r
2383 return EFI_NOT_FOUND;\r
2384 }\r
2385\r
2386 //\r
2387 // Get BootManagerMenu application's description from EFI User Interface Section.\r
2388 //\r
2389 Status = GetSectionFromFv (\r
2390 PcdGetPtr (PcdBootManagerMenuFile),\r
2391 EFI_SECTION_USER_INTERFACE,\r
2392 0,\r
2393 (VOID **) &Description,\r
2394 &DescriptionLength\r
2395 );\r
2396 if (EFI_ERROR (Status)) {\r
2397 Description = NULL;\r
2398 }\r
2399\r
2400 EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));\r
2401 Status = gBS->HandleProtocol (\r
2402 gImageHandle,\r
2403 &gEfiLoadedImageProtocolGuid,\r
2404 (VOID **) &LoadedImage\r
2405 );\r
2406 ASSERT_EFI_ERROR (Status);\r
2407 DevicePath = AppendDevicePathNode (\r
2408 DevicePathFromHandle (LoadedImage->DeviceHandle),\r
2409 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
2410 );\r
2411 ASSERT (DevicePath != NULL);\r
2412 }\r
067ed98a
RN
2413\r
2414 Status = EfiBootManagerInitializeLoadOption (\r
2415 BootOption,\r
2416 LoadOptionNumberUnassigned,\r
2417 LoadOptionTypeBoot,\r
2418 LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,\r
2419 (Description != NULL) ? Description : L"Boot Manager Menu",\r
2420 DevicePath,\r
2421 NULL,\r
2422 0\r
2423 );\r
2424 ASSERT_EFI_ERROR (Status);\r
2425 FreePool (DevicePath);\r
2426 if (Description != NULL) {\r
2427 FreePool (Description);\r
2428 }\r
2429\r
2430 DEBUG_CODE (\r
2431 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2432 UINTN BootOptionCount;\r
2433\r
2434 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
5d3a9896 2435 ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);\r
067ed98a
RN
2436 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2437 );\r
2438\r
2439 return EfiBootManagerAddLoadOptionVariable (BootOption, 0);\r
2440}\r
2441\r
2442/**\r
2443 Return the boot option corresponding to the Boot Manager Menu.\r
2444 It may automatically create one if the boot option hasn't been created yet.\r
e58f1ae5 2445\r
067ed98a
RN
2446 @param BootOption Return the Boot Manager Menu.\r
2447\r
2448 @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.\r
e58f1ae5
SW
2449 @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.\r
2450 @retval others Return status of gRT->SetVariable (). BootOption still points\r
2451 to the Boot Manager Menu even the Status is not EFI_SUCCESS\r
2452 and EFI_NOT_FOUND.\r
067ed98a
RN
2453**/\r
2454EFI_STATUS\r
2455EFIAPI\r
2456EfiBootManagerGetBootManagerMenu (\r
2457 EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2458 )\r
2459{\r
2460 EFI_STATUS Status;\r
2461 UINTN BootOptionCount;\r
2462 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2463 UINTN Index;\r
d1102dba 2464\r
067ed98a
RN
2465 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2466\r
2467 for (Index = 0; Index < BootOptionCount; Index++) {\r
7c69fbf2 2468 if (BmIsBootManagerMenuFilePath (BootOptions[Index].FilePath)) {\r
067ed98a
RN
2469 Status = EfiBootManagerInitializeLoadOption (\r
2470 BootOption,\r
2471 BootOptions[Index].OptionNumber,\r
2472 BootOptions[Index].OptionType,\r
2473 BootOptions[Index].Attributes,\r
2474 BootOptions[Index].Description,\r
2475 BootOptions[Index].FilePath,\r
2476 BootOptions[Index].OptionalData,\r
2477 BootOptions[Index].OptionalDataSize\r
2478 );\r
2479 ASSERT_EFI_ERROR (Status);\r
2480 break;\r
067ed98a
RN
2481 }\r
2482 }\r
2483\r
2484 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2485\r
2486 //\r
2487 // Automatically create the Boot#### for Boot Manager Menu when not found.\r
2488 //\r
2489 if (Index == BootOptionCount) {\r
2490 return BmRegisterBootManagerMenu (BootOption);\r
2491 } else {\r
2492 return EFI_SUCCESS;\r
2493 }\r
2494}\r
2495\r
b4e1ad87
CC
2496/**\r
2497 Get the next possible full path pointing to the load option.\r
2498 The routine doesn't guarantee the returned full path points to an existing\r
2499 file, and it also doesn't guarantee the existing file is a valid load option.\r
2500 BmGetNextLoadOptionBuffer() guarantees.\r
2501\r
2502 @param FilePath The device path pointing to a load option.\r
2503 It could be a short-form device path.\r
2504 @param FullPath The full path returned by the routine in last call.\r
2505 Set to NULL in first call.\r
2506\r
2507 @return The next possible full path pointing to the load option.\r
2508 Caller is responsible to free the memory.\r
2509**/\r
2510EFI_DEVICE_PATH_PROTOCOL *\r
68a4e15e
CC
2511EFIAPI\r
2512EfiBootManagerGetNextLoadOptionDevicePath (\r
b4e1ad87
CC
2513 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
2514 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
2515 )\r
2516{\r
2517 return BmGetNextLoadOptionDevicePath(FilePath, FullPath);\r
2518}\r