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