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