]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
MdeModulePkg: SecurityManagementLib to handle LoadFile DevicePath
[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
b6344b37 555 UINTN DescMaxSize;\r
067ed98a
RN
556\r
557 Status = gBS->HandleProtocol (\r
558 Handle,\r
559 &gEfiUsbIoProtocolGuid,\r
560 (VOID **) &UsbIo\r
561 );\r
562 if (EFI_ERROR (Status)) {\r
563 return NULL;\r
564 }\r
565\r
566 NullChar = L'\0';\r
567\r
568 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
569 if (EFI_ERROR (Status)) {\r
570 return NULL;\r
571 }\r
572\r
573 Status = UsbIo->UsbGetStringDescriptor (\r
574 UsbIo,\r
575 mBmUsbLangId,\r
576 DevDesc.StrManufacturer,\r
577 &Manufacturer\r
578 );\r
579 if (EFI_ERROR (Status)) {\r
580 Manufacturer = &NullChar;\r
581 }\r
582 \r
583 Status = UsbIo->UsbGetStringDescriptor (\r
584 UsbIo,\r
585 mBmUsbLangId,\r
586 DevDesc.StrProduct,\r
587 &Product\r
588 );\r
589 if (EFI_ERROR (Status)) {\r
590 Product = &NullChar;\r
591 }\r
592 \r
593 Status = UsbIo->UsbGetStringDescriptor (\r
594 UsbIo,\r
595 mBmUsbLangId,\r
596 DevDesc.StrSerialNumber,\r
597 &SerialNumber\r
598 );\r
599 if (EFI_ERROR (Status)) {\r
600 SerialNumber = &NullChar;\r
601 }\r
602\r
603 if ((Manufacturer == &NullChar) &&\r
604 (Product == &NullChar) &&\r
605 (SerialNumber == &NullChar)\r
606 ) {\r
607 return NULL;\r
608 }\r
609\r
b6344b37
QS
610 DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber);\r
611 Description = AllocateZeroPool (DescMaxSize);\r
067ed98a 612 ASSERT (Description != NULL);\r
b6344b37
QS
613 StrCatS (Description, DescMaxSize/sizeof(CHAR16), Manufacturer);\r
614 StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");\r
067ed98a 615\r
b6344b37
QS
616 StrCatS (Description, DescMaxSize/sizeof(CHAR16), Product); \r
617 StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");\r
067ed98a 618\r
b6344b37 619 StrCatS (Description, DescMaxSize/sizeof(CHAR16), SerialNumber);\r
067ed98a
RN
620\r
621 if (Manufacturer != &NullChar) {\r
622 FreePool (Manufacturer);\r
623 }\r
624 if (Product != &NullChar) {\r
625 FreePool (Product);\r
626 }\r
627 if (SerialNumber != &NullChar) {\r
628 FreePool (SerialNumber);\r
629 }\r
630\r
631 BmEliminateExtraSpaces (Description);\r
632\r
633 return Description;\r
634}\r
635\r
636/**\r
637 Return the boot description for the controller based on the type.\r
638\r
639 @param Handle Controller handle.\r
640\r
641 @return The description string.\r
642**/\r
643CHAR16 *\r
644BmGetMiscDescription (\r
645 IN EFI_HANDLE Handle\r
646 )\r
647{\r
f41c71d2
RN
648 EFI_STATUS Status;\r
649 CHAR16 *Description;\r
650 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
651 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
067ed98a
RN
652\r
653 switch (BmDevicePathType (DevicePathFromHandle (Handle))) {\r
654 case BmAcpiFloppyBoot:\r
655 Description = L"Floppy";\r
656 break;\r
657\r
658 case BmMessageAtapiBoot:\r
659 case BmMessageSataBoot:\r
660 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
661 ASSERT_EFI_ERROR (Status);\r
662 //\r
663 // Assume a removable SATA device should be the DVD/CD device\r
664 //\r
665 Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";\r
666 break;\r
667\r
668 case BmMessageUsbBoot:\r
669 Description = L"USB Device";\r
670 break;\r
671\r
672 case BmMessageScsiBoot:\r
673 Description = L"SCSI Device";\r
674 break;\r
675\r
676 case BmHardwareDeviceBoot:\r
677 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
678 if (!EFI_ERROR (Status)) {\r
679 Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";\r
680 } else {\r
681 Description = L"Misc Device";\r
682 }\r
683 break;\r
684\r
f41c71d2
RN
685 case BmMessageNetworkBoot:\r
686 Description = L"Network";\r
687 break;\r
688\r
067ed98a 689 default:\r
f41c71d2
RN
690 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);\r
691 if (!EFI_ERROR (Status)) {\r
692 Description = L"Non-Block Boot Device";\r
693 } else {\r
694 Description = L"Misc Device";\r
695 }\r
067ed98a
RN
696 break;\r
697 }\r
698\r
699 return AllocateCopyPool (StrSize (Description), Description);\r
700}\r
701\r
f41c71d2
RN
702/**\r
703 Register the platform provided boot description handler.\r
704\r
705 @param Handler The platform provided boot description handler\r
706\r
707 @retval EFI_SUCCESS The handler was registered successfully.\r
708 @retval EFI_ALREADY_STARTED The handler was already registered.\r
709 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.\r
710**/\r
711EFI_STATUS\r
712EFIAPI\r
713EfiBootManagerRegisterBootDescriptionHandler (\r
714 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler\r
715 )\r
716{\r
717 LIST_ENTRY *Link;\r
718 BM_BOOT_DESCRIPTION_ENTRY *Entry;\r
719\r
720 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)\r
721 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)\r
722 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)\r
723 ) {\r
724 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);\r
725 if (Entry->Handler == Handler) {\r
726 return EFI_ALREADY_STARTED;\r
727 }\r
728 }\r
729\r
730 Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));\r
731 if (Entry == NULL) {\r
732 return EFI_OUT_OF_RESOURCES;\r
733 }\r
734\r
735 Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;\r
736 Entry->Handler = Handler;\r
737 InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);\r
738 return EFI_SUCCESS;\r
739}\r
740\r
741BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {\r
067ed98a
RN
742 BmGetUsbDescription,\r
743 BmGetDescriptionFromDiskInfo,\r
744 BmGetMiscDescription\r
745};\r
746\r
f41c71d2
RN
747/**\r
748 Return the boot description for the controller.\r
749\r
750 @param Handle Controller handle.\r
751\r
752 @return The description string.\r
753**/\r
754CHAR16 *\r
755BmGetBootDescription (\r
756 IN EFI_HANDLE Handle\r
757 )\r
758{\r
759 LIST_ENTRY *Link;\r
760 BM_BOOT_DESCRIPTION_ENTRY *Entry;\r
761 CHAR16 *Description;\r
762 CHAR16 *DefaultDescription;\r
763 CHAR16 *Temp;\r
764 UINTN Index;\r
765\r
766 //\r
767 // Firstly get the default boot description\r
768 //\r
769 DefaultDescription = NULL;\r
770 for (Index = 0; Index < sizeof (mBmBootDescriptionHandlers) / sizeof (mBmBootDescriptionHandlers[0]); Index++) {\r
771 DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);\r
772 if (DefaultDescription != NULL) {\r
773 //\r
774 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix\r
775 // ONLY for core provided boot description handler.\r
776 //\r
777 Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)); \r
778 ASSERT (Temp != NULL);\r
b6344b37
QS
779 StrCpyS ( Temp, \r
780 (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix))/sizeof(CHAR16), \r
781 mBmUefiPrefix\r
782 );\r
783 StrCatS ( Temp, \r
784 (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix))/sizeof(CHAR16), \r
785 DefaultDescription\r
786 );\r
f41c71d2
RN
787 FreePool (DefaultDescription);\r
788 DefaultDescription = Temp;\r
789 break;\r
790 }\r
791 }\r
792 ASSERT (DefaultDescription != NULL);\r
793\r
794 //\r
795 // Secondly query platform for the better boot description\r
796 //\r
797 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)\r
798 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)\r
799 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)\r
800 ) {\r
801 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);\r
802 Description = Entry->Handler (Handle, DefaultDescription);\r
803 if (Description != NULL) {\r
804 FreePool (DefaultDescription);\r
805 return Description;\r
806 }\r
807 }\r
808\r
809 return DefaultDescription;\r
810}\r
811\r
067ed98a
RN
812/**\r
813 Check whether a USB device match the specified USB WWID device path. This\r
814 function follows "Load Option Processing" behavior in UEFI specification.\r
815\r
816 @param UsbIo USB I/O protocol associated with the USB device.\r
817 @param UsbWwid The USB WWID device path to match.\r
818\r
819 @retval TRUE The USB device match the USB WWID device path.\r
820 @retval FALSE The USB device does not match the USB WWID device path.\r
821\r
822**/\r
823BOOLEAN\r
824BmMatchUsbWwid (\r
825 IN EFI_USB_IO_PROTOCOL *UsbIo,\r
826 IN USB_WWID_DEVICE_PATH *UsbWwid\r
827 )\r
828{\r
829 EFI_STATUS Status;\r
830 EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
831 EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
832 UINT16 *LangIdTable;\r
833 UINT16 TableSize;\r
834 UINT16 Index;\r
835 CHAR16 *CompareStr;\r
836 UINTN CompareLen;\r
837 CHAR16 *SerialNumberStr;\r
838 UINTN Length;\r
839\r
840 if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||\r
841 (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {\r
842 return FALSE;\r
843 }\r
844\r
845 //\r
846 // Check Vendor Id and Product Id.\r
847 //\r
848 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
849 if (EFI_ERROR (Status)) {\r
850 return FALSE;\r
851 }\r
852 if ((DevDesc.IdVendor != UsbWwid->VendorId) ||\r
853 (DevDesc.IdProduct != UsbWwid->ProductId)) {\r
854 return FALSE;\r
855 }\r
856\r
857 //\r
858 // Check Interface Number.\r
859 //\r
860 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
861 if (EFI_ERROR (Status)) {\r
862 return FALSE;\r
863 }\r
864 if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {\r
865 return FALSE;\r
866 }\r
867\r
868 //\r
869 // Check Serial Number.\r
870 //\r
871 if (DevDesc.StrSerialNumber == 0) {\r
872 return FALSE;\r
873 }\r
874\r
875 //\r
876 // Get all supported languages.\r
877 //\r
878 TableSize = 0;\r
879 LangIdTable = NULL;\r
880 Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);\r
881 if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {\r
882 return FALSE;\r
883 }\r
884\r
885 //\r
886 // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.\r
887 //\r
888 CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);\r
889 CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);\r
890 if (CompareStr[CompareLen - 1] == L'\0') {\r
891 CompareLen--;\r
892 }\r
893\r
894 //\r
895 // Compare serial number in each supported language.\r
896 //\r
897 for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {\r
898 SerialNumberStr = NULL;\r
899 Status = UsbIo->UsbGetStringDescriptor (\r
900 UsbIo,\r
901 LangIdTable[Index],\r
902 DevDesc.StrSerialNumber,\r
903 &SerialNumberStr\r
904 );\r
905 if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {\r
906 continue;\r
907 }\r
908\r
909 Length = StrLen (SerialNumberStr);\r
910 if ((Length >= CompareLen) &&\r
911 (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {\r
912 FreePool (SerialNumberStr);\r
913 return TRUE;\r
914 }\r
915\r
916 FreePool (SerialNumberStr);\r
917 }\r
918\r
919 return FALSE;\r
920}\r
921\r
922/**\r
923 Find a USB device which match the specified short-form device path start with \r
924 USB Class or USB WWID device path. If ParentDevicePath is NULL, this function\r
925 will search in all USB devices of the platform. If ParentDevicePath is not NULL,\r
926 this function will only search in its child devices.\r
927\r
928 @param DevicePath The device path that contains USB Class or USB WWID device path.\r
929 @param ParentDevicePathSize The length of the device path before the USB Class or \r
930 USB WWID device path.\r
931 @param UsbIoHandleCount A pointer to the count of the returned USB IO handles.\r
932\r
933 @retval NULL The matched USB IO handles cannot be found.\r
934 @retval other The matched USB IO handles.\r
935\r
936**/\r
937EFI_HANDLE *\r
938BmFindUsbDevice (\r
939 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
940 IN UINTN ParentDevicePathSize,\r
941 OUT UINTN *UsbIoHandleCount\r
942 )\r
943{\r
944 EFI_STATUS Status;\r
945 EFI_HANDLE *UsbIoHandles;\r
946 EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;\r
947 EFI_USB_IO_PROTOCOL *UsbIo;\r
948 UINTN Index;\r
949 UINTN UsbIoDevicePathSize;\r
950 BOOLEAN Matched;\r
951\r
952 ASSERT (UsbIoHandleCount != NULL); \r
953\r
954 //\r
955 // Get all UsbIo Handles.\r
956 //\r
957 Status = gBS->LocateHandleBuffer (\r
958 ByProtocol,\r
959 &gEfiUsbIoProtocolGuid,\r
960 NULL,\r
961 UsbIoHandleCount,\r
962 &UsbIoHandles\r
963 );\r
964 if (EFI_ERROR (Status)) {\r
965 *UsbIoHandleCount = 0;\r
966 UsbIoHandles = NULL;\r
967 }\r
968\r
969 for (Index = 0; Index < *UsbIoHandleCount; ) {\r
970 //\r
971 // Get the Usb IO interface.\r
972 //\r
973 Status = gBS->HandleProtocol(\r
974 UsbIoHandles[Index],\r
975 &gEfiUsbIoProtocolGuid,\r
976 (VOID **) &UsbIo\r
977 );\r
978 UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);\r
979 Matched = FALSE;\r
980 if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {\r
981 UsbIoDevicePathSize = GetDevicePathSize (UsbIoDevicePath) - END_DEVICE_PATH_LENGTH;\r
982\r
983 //\r
984 // Compare starting part of UsbIoHandle's device path with ParentDevicePath.\r
985 //\r
986 if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {\r
987 if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) ||\r
988 BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize))) {\r
989 Matched = TRUE;\r
990 }\r
991 }\r
992 }\r
993\r
994 if (!Matched) {\r
995 (*UsbIoHandleCount) --;\r
996 CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE));\r
997 } else {\r
998 Index++;\r
999 }\r
1000 }\r
1001\r
1002 return UsbIoHandles;\r
1003}\r
1004\r
1005/**\r
1006 Expand USB Class or USB WWID device path node to be full device path of a USB\r
1007 device in platform.\r
1008\r
1009 This function support following 4 cases:\r
1010 1) Boot Option device path starts with a USB Class or USB WWID device path,\r
1011 and there is no Media FilePath device path in the end.\r
1012 In this case, it will follow Removable Media Boot Behavior.\r
1013 2) Boot Option device path starts with a USB Class or USB WWID device path,\r
1014 and ended with Media FilePath device path.\r
1015 3) Boot Option device path starts with a full device path to a USB Host Controller,\r
1016 contains a USB Class or USB WWID device path node, while not ended with Media\r
1017 FilePath device path. In this case, it will follow Removable Media Boot Behavior.\r
1018 4) Boot Option device path starts with a full device path to a USB Host Controller,\r
1019 contains a USB Class or USB WWID device path node, and ended with Media\r
1020 FilePath device path.\r
1021\r
1022 @param FilePath The device path pointing to a load option.\r
1023 It could be a short-form device path.\r
1024 @param FullPath Return the full device path of the load option after\r
1025 short-form device path expanding.\r
1026 Caller is responsible to free it.\r
1027 @param FileSize Return the load option size.\r
1028 @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.\r
1029\r
1030 @return The load option buffer. Caller is responsible to free the memory.\r
1031**/\r
1032VOID *\r
1033BmExpandUsbDevicePath (\r
1034 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1035 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1036 OUT UINTN *FileSize,\r
1037 IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode\r
1038 )\r
1039{\r
1040 UINTN ParentDevicePathSize;\r
1041 EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;\r
1042 EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;\r
1043 EFI_HANDLE *Handles;\r
1044 UINTN HandleCount;\r
1045 UINTN Index;\r
1046 VOID *FileBuffer;\r
1047\r
1048 ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;\r
1049 RemainingDevicePath = NextDevicePathNode (ShortformNode);\r
1050 FileBuffer = NULL;\r
1051 Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);\r
1052\r
1053 for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) {\r
1054 FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);\r
1055 FileBuffer = BmGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize);\r
1056 FreePool (FullDevicePath);\r
1057 }\r
1058\r
1059 if (Handles != NULL) {\r
1060 FreePool (Handles);\r
1061 }\r
1062\r
1063 return FileBuffer;\r
1064}\r
1065\r
1066/**\r
1067 Save the partition DevicePath to the CachedDevicePath as the first instance.\r
1068\r
1069 @param CachedDevicePath The device path cache.\r
1070 @param DevicePath The partition device path to be cached.\r
1071**/\r
1072VOID\r
1073BmCachePartitionDevicePath (\r
1074 IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath,\r
1075 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
1076 )\r
1077{\r
1078 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
1079 UINTN Count;\r
1080 \r
1081 if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) {\r
1082 TempDevicePath = *CachedDevicePath;\r
1083 *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath);\r
1084 FreePool (TempDevicePath);\r
1085 }\r
1086\r
1087 if (*CachedDevicePath == NULL) {\r
1088 *CachedDevicePath = DuplicateDevicePath (DevicePath);\r
1089 return;\r
1090 }\r
1091\r
1092 TempDevicePath = *CachedDevicePath;\r
1093 *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath);\r
1094 if (TempDevicePath != NULL) {\r
1095 FreePool (TempDevicePath);\r
1096 }\r
1097\r
1098 //\r
1099 // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller\r
1100 // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.\r
1101 //\r
1102 Count = 0;\r
1103 TempDevicePath = *CachedDevicePath;\r
1104 while (!IsDevicePathEnd (TempDevicePath)) {\r
1105 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
1106 //\r
1107 // Parse one instance\r
1108 //\r
1109 while (!IsDevicePathEndType (TempDevicePath)) {\r
1110 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
1111 }\r
1112 Count++;\r
1113 //\r
1114 // If the CachedDevicePath variable contain too much instance, only remain 12 instances.\r
1115 //\r
1116 if (Count == 12) {\r
1117 SetDevicePathEndNode (TempDevicePath);\r
1118 break;\r
1119 }\r
1120 }\r
1121}\r
1122\r
1123/**\r
1124 Expand a device path that starts with a hard drive media device path node to be a\r
1125 full device path that includes the full hardware path to the device. We need\r
1126 to do this so it can be booted. As an optimization the front match (the part point\r
1127 to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable\r
1128 so a connect all is not required on every boot. All successful history device path\r
1129 which point to partition node (the front part) will be saved.\r
1130\r
1131 @param FilePath The device path pointing to a load option.\r
1132 It could be a short-form device path.\r
1133 @param FullPath Return the full device path of the load option after\r
1134 short-form device path expanding.\r
1135 Caller is responsible to free it.\r
1136 @param FileSize Return the load option size.\r
1137\r
1138 @return The load option buffer. Caller is responsible to free the memory.\r
1139**/\r
1140VOID *\r
1141BmExpandPartitionDevicePath (\r
1142 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1143 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1144 OUT UINTN *FileSize\r
1145 )\r
1146{\r
1147 EFI_STATUS Status;\r
1148 UINTN BlockIoHandleCount;\r
1149 EFI_HANDLE *BlockIoBuffer;\r
1150 VOID *FileBuffer;\r
1151 EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;\r
1152 UINTN Index;\r
1153 EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;\r
1154 EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;\r
1155 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
1156 UINTN CachedDevicePathSize;\r
1157 BOOLEAN NeedAdjust;\r
1158 EFI_DEVICE_PATH_PROTOCOL *Instance;\r
1159 UINTN Size;\r
1160\r
1161 FileBuffer = NULL;\r
1162 //\r
1163 // Check if there is prestore 'HDDP' variable.\r
1164 // If exist, search the front path which point to partition node in the variable instants.\r
1165 // If fail to find or 'HDDP' not exist, reconnect all and search in all system\r
1166 //\r
1167 GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize);\r
1168\r
1169 //\r
1170 // Delete the invalid 'HDDP' variable.\r
1171 //\r
1172 if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {\r
1173 FreePool (CachedDevicePath);\r
1174 CachedDevicePath = NULL;\r
1175 Status = gRT->SetVariable (\r
1176 L"HDDP",\r
1177 &mBmHardDriveBootVariableGuid,\r
1178 0,\r
1179 0,\r
1180 NULL\r
1181 );\r
1182 ASSERT_EFI_ERROR (Status);\r
1183 }\r
1184\r
1185 if (CachedDevicePath != NULL) {\r
1186 TempNewDevicePath = CachedDevicePath;\r
1187 NeedAdjust = FALSE;\r
1188 do {\r
1189 //\r
1190 // Check every instance of the variable\r
1191 // First, check whether the instance contain the partition node, which is needed for distinguishing multi\r
1192 // partial partition boot option. Second, check whether the instance could be connected.\r
1193 //\r
1194 Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);\r
1195 if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
1196 //\r
1197 // Connect the device path instance, the device path point to hard drive media device path node\r
1198 // e.g. ACPI() /PCI()/ATA()/Partition()\r
1199 //\r
1200 Status = EfiBootManagerConnectDevicePath (Instance, NULL);\r
1201 if (!EFI_ERROR (Status)) {\r
1202 TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));\r
1203 FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);\r
1204 FreePool (TempDevicePath);\r
1205\r
1206 if (FileBuffer != NULL) {\r
1207 //\r
1208 // Adjust the 'HDDP' instances sequence if the matched one is not first one.\r
1209 //\r
1210 if (NeedAdjust) {\r
1211 BmCachePartitionDevicePath (&CachedDevicePath, Instance);\r
1212 //\r
1213 // Save the matching Device Path so we don't need to do a connect all next time\r
1214 // Failing to save only impacts performance next time expanding the short-form device path\r
1215 //\r
1216 Status = gRT->SetVariable (\r
1217 L"HDDP",\r
1218 &mBmHardDriveBootVariableGuid,\r
1219 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
1220 GetDevicePathSize (CachedDevicePath),\r
1221 CachedDevicePath\r
1222 );\r
1223 }\r
1224\r
1225 FreePool (Instance);\r
1226 FreePool (CachedDevicePath);\r
1227 return FileBuffer;\r
1228 }\r
1229 }\r
1230 }\r
1231 //\r
1232 // Come here means the first instance is not matched\r
1233 //\r
1234 NeedAdjust = TRUE;\r
1235 FreePool(Instance);\r
1236 } while (TempNewDevicePath != NULL);\r
1237 }\r
1238\r
1239 //\r
1240 // If we get here we fail to find or 'HDDP' not exist, and now we need\r
1241 // to search all devices in the system for a matched partition\r
1242 //\r
1243 EfiBootManagerConnectAll ();\r
1244 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);\r
1245 if (EFI_ERROR (Status)) {\r
1246 BlockIoHandleCount = 0;\r
1247 BlockIoBuffer = NULL;\r
1248 }\r
1249 //\r
1250 // Loop through all the device handles that support the BLOCK_IO Protocol\r
1251 //\r
1252 for (Index = 0; Index < BlockIoHandleCount; Index++) {\r
1253 BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]);\r
1254 if (BlockIoDevicePath == NULL) {\r
1255 continue;\r
1256 }\r
1257\r
1258 if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
1259 //\r
1260 // Find the matched partition device path\r
1261 //\r
1262 TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));\r
1263 FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);\r
1264 FreePool (TempDevicePath);\r
1265\r
1266 if (FileBuffer != NULL) {\r
1267 BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);\r
1268\r
1269 //\r
1270 // Save the matching Device Path so we don't need to do a connect all next time\r
1271 // Failing to save only impacts performance next time expanding the short-form device path\r
1272 //\r
1273 Status = gRT->SetVariable (\r
1274 L"HDDP",\r
1275 &mBmHardDriveBootVariableGuid,\r
1276 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
1277 GetDevicePathSize (CachedDevicePath),\r
1278 CachedDevicePath\r
1279 );\r
1280\r
1281 break;\r
1282 }\r
1283 }\r
1284 }\r
1285\r
1286 if (CachedDevicePath != NULL) {\r
1287 FreePool (CachedDevicePath);\r
1288 }\r
1289 if (BlockIoBuffer != NULL) {\r
1290 FreePool (BlockIoBuffer);\r
1291 }\r
1292 return FileBuffer;\r
1293}\r
1294\r
1295/**\r
1296 Expand the media device path which points to a BlockIo or SimpleFileSystem instance\r
1297 by appending EFI_REMOVABLE_MEDIA_FILE_NAME.\r
1298\r
1299 @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.\r
1300 @param FullPath Return the full device path pointing to the load option.\r
1301 @param FileSize Return the size of the load option.\r
1302\r
1303 @return The load option buffer.\r
1304**/\r
1305VOID *\r
1306BmExpandMediaDevicePath (\r
1307 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
1308 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1309 OUT UINTN *FileSize\r
1310 )\r
1311{\r
1312 EFI_STATUS Status;\r
1313 EFI_HANDLE Handle;\r
1314 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
1315 VOID *Buffer;\r
1316 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
1317 UINTN Size;\r
1318 UINTN TempSize;\r
1319 EFI_HANDLE *SimpleFileSystemHandles;\r
1320 UINTN NumberSimpleFileSystemHandles;\r
1321 UINTN Index;\r
1322 VOID *FileBuffer;\r
1323 UINT32 AuthenticationStatus;\r
1324\r
1325 //\r
1326 // Check whether the device is connected\r
1327 //\r
1328 TempDevicePath = DevicePath;\r
1329 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);\r
1330 if (!EFI_ERROR (Status)) {\r
1331 ASSERT (IsDevicePathEnd (TempDevicePath));\r
1332\r
1333 TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
1334 FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);\r
1335 if (FileBuffer == NULL) {\r
1336 FreePool (TempDevicePath);\r
1337 TempDevicePath = NULL;\r
1338 }\r
1339 *FullPath = TempDevicePath;\r
1340 return FileBuffer;\r
1341 }\r
1342\r
1343 //\r
1344 // For device boot option only pointing to the removable device handle, \r
1345 // should make sure all its children handles (its child partion or media handles) are created and connected. \r
1346 //\r
1347 gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
1348\r
1349 //\r
1350 // Issue a dummy read to the device to check for media change.\r
1351 // When the removable media is changed, any Block IO read/write will\r
1352 // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is\r
1353 // returned. After the Block IO protocol is reinstalled, subsequent\r
1354 // Block IO read/write will success.\r
1355 //\r
1356 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);\r
1357 ASSERT_EFI_ERROR (Status);\r
1358 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
1359 ASSERT_EFI_ERROR (Status);\r
1360 Buffer = AllocatePool (BlockIo->Media->BlockSize);\r
1361 if (Buffer != NULL) {\r
1362 BlockIo->ReadBlocks (\r
1363 BlockIo,\r
1364 BlockIo->Media->MediaId,\r
1365 0,\r
1366 BlockIo->Media->BlockSize,\r
1367 Buffer\r
1368 );\r
1369 FreePool (Buffer);\r
1370 }\r
1371\r
1372 //\r
1373 // Detect the the default boot file from removable Media\r
1374 //\r
1375 FileBuffer = NULL;\r
1376 *FullPath = NULL;\r
1377 Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;\r
1378 gBS->LocateHandleBuffer (\r
1379 ByProtocol,\r
1380 &gEfiSimpleFileSystemProtocolGuid,\r
1381 NULL,\r
1382 &NumberSimpleFileSystemHandles,\r
1383 &SimpleFileSystemHandles\r
1384 );\r
1385 for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {\r
1386 //\r
1387 // Get the device path size of SimpleFileSystem handle\r
1388 //\r
1389 TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);\r
1390 TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH;\r
1391 //\r
1392 // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path\r
1393 //\r
1394 if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {\r
1395 TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);\r
1396 FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);\r
1397 if (FileBuffer != NULL) {\r
1398 *FullPath = TempDevicePath;\r
1399 break;\r
1400 }\r
1401 FreePool (TempDevicePath);\r
1402 }\r
1403 }\r
1404\r
1405 if (SimpleFileSystemHandles != NULL) {\r
1406 FreePool (SimpleFileSystemHandles);\r
1407 }\r
1408\r
1409 return FileBuffer;\r
1410}\r
1411\r
1412/**\r
1413 Get the load option by its device path.\r
1414\r
1415 @param FilePath The device path pointing to a load option.\r
1416 It could be a short-form device path.\r
1417 @param FullPath Return the full device path of the load option after\r
1418 short-form device path expanding.\r
1419 Caller is responsible to free it.\r
1420 @param FileSize Return the load option size.\r
1421\r
1422 @return The load option buffer. Caller is responsible to free the memory.\r
1423**/\r
1424VOID *\r
1425BmGetLoadOptionBuffer (\r
1426 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1427 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1428 OUT UINTN *FileSize\r
1429 )\r
1430{\r
1431 EFI_HANDLE Handle;\r
1432 VOID *FileBuffer;\r
1433 UINT32 AuthenticationStatus;\r
1434 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1435 EFI_STATUS Status;\r
1436\r
1437 ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL));\r
1438\r
1439 EfiBootManagerConnectDevicePath (FilePath, NULL);\r
1440\r
1441 *FullPath = NULL;\r
1442 *FileSize = 0;\r
1443 FileBuffer = NULL;\r
1444\r
1445 //\r
1446 // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI\r
1447 //\r
1448 Node = FilePath;\r
1449 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);\r
1450 if (EFI_ERROR (Status)) {\r
1451 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle);\r
1452 }\r
1453\r
1454 if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {\r
1455 return BmExpandMediaDevicePath (FilePath, FullPath, FileSize);\r
1456 }\r
1457\r
1458 //\r
1459 // Expand the short-form device path to full device path\r
1460 //\r
1461 if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&\r
1462 (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) {\r
1463 //\r
1464 // Expand the Harddrive device path\r
1465 //\r
1466 return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize);\r
1467 } else {\r
1468 for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {\r
1469 if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
1470 ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {\r
1471 break;\r
1472 }\r
1473 }\r
1474\r
1475 if (!IsDevicePathEnd (Node)) {\r
1476 //\r
1477 // Expand the USB WWID/Class device path\r
1478 //\r
1479 FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);\r
1480 if ((FileBuffer == NULL) && (FilePath == Node)) {\r
1481 //\r
1482 // Boot Option device path starts with USB Class or USB WWID device path.\r
1483 // For Boot Option device path which doesn't begin with the USB Class or\r
1484 // USB WWID device path, it's not needed to connect again here.\r
1485 //\r
1486 BmConnectUsbShortFormDevicePath (FilePath);\r
1487 FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);\r
1488 }\r
1489 return FileBuffer;\r
1490 }\r
1491 }\r
1492\r
1493 //\r
1494 // Fix up the boot option path if it points to a FV in memory map style of device path\r
1495 //\r
1496 if (BmIsMemmapFvFilePath (FilePath)) {\r
1497 return BmGetFileBufferByMemmapFv (FilePath, FullPath, FileSize);\r
1498 }\r
1499\r
1500 //\r
1501 // Directly reads the load option when it doesn't reside in simple file system instance (LoadFile/LoadFile2),\r
1502 // or it directly points to a file in simple file system instance.\r
1503 //\r
1504 FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);\r
1505 if (FileBuffer != NULL) {\r
1506 *FullPath = DuplicateDevicePath (FilePath);\r
1507 }\r
1508\r
1509 return FileBuffer;\r
1510}\r
1511\r
1512/**\r
1513 Attempt to boot the EFI boot option. This routine sets L"BootCurent" and\r
1514 also signals the EFI ready to boot event. If the device path for the option\r
1515 starts with a BBS device path a legacy boot is attempted via the registered \r
1516 gLegacyBoot function. Short form device paths are also supported via this \r
1517 rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,\r
1518 MSG_USB_CLASS_DP gets expaned out to find the first device that matches.\r
1519 If the BootOption Device Path fails the removable media boot algorithm \r
1520 is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type \r
1521 is tried per processor type)\r
1522\r
1523 @param BootOption Boot Option to try and boot.\r
1524 On return, BootOption->Status contains the boot status.\r
1525 EFI_SUCCESS BootOption was booted\r
1526 EFI_UNSUPPORTED A BBS device path was found with no valid callback\r
1527 registered via EfiBootManagerInitialize().\r
1528 EFI_NOT_FOUND The BootOption was not found on the system\r
1529 !EFI_SUCCESS BootOption failed with this error status\r
1530\r
1531**/\r
1532VOID\r
1533EFIAPI\r
1534EfiBootManagerBoot (\r
1535 IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
1536 )\r
1537{\r
1538 EFI_STATUS Status;\r
1539 EFI_HANDLE ImageHandle;\r
1540 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;\r
1541 UINT16 Uint16;\r
1542 UINTN OptionNumber;\r
1543 UINTN OriginalOptionNumber;\r
1544 EFI_DEVICE_PATH_PROTOCOL *FilePath;\r
1545 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1546 EFI_HANDLE FvHandle;\r
1547 VOID *FileBuffer;\r
1548 UINTN FileSize;\r
1549 EFI_BOOT_LOGO_PROTOCOL *BootLogo;\r
1550 EFI_EVENT LegacyBootEvent;\r
1551\r
1552 if (BootOption == NULL) {\r
1553 return;\r
1554 }\r
1555\r
1556 if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) {\r
1557 BootOption->Status = EFI_INVALID_PARAMETER;\r
1558 return;\r
1559 }\r
1560\r
1561 //\r
1562 // 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
1563 //\r
1564 OptionNumber = BmFindBootOptionInVariable (BootOption);\r
1565 if (OptionNumber == LoadOptionNumberUnassigned) {\r
1566 Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);\r
1567 if (!EFI_ERROR (Status)) {\r
1568 //\r
1569 // Save the BootOption->OptionNumber to restore later\r
1570 //\r
1571 OptionNumber = Uint16;\r
1572 OriginalOptionNumber = BootOption->OptionNumber;\r
1573 BootOption->OptionNumber = OptionNumber;\r
1574 Status = EfiBootManagerLoadOptionToVariable (BootOption);\r
1575 BootOption->OptionNumber = OriginalOptionNumber;\r
1576 }\r
1577\r
1578 if (EFI_ERROR (Status)) {\r
1579 DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));\r
1580 BootOption->Status = Status;\r
1581 return ;\r
1582 }\r
1583 }\r
1584\r
1585 //\r
1586 // 2. Set BootCurrent\r
1587 //\r
1588 Uint16 = (UINT16) OptionNumber;\r
1589 BmSetVariableAndReportStatusCodeOnError (\r
1590 L"BootCurrent",\r
1591 &gEfiGlobalVariableGuid,\r
1592 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
1593 sizeof (UINT16),\r
1594 &Uint16\r
1595 );\r
1596\r
1597 //\r
1598 // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute\r
1599 // the boot option.\r
1600 //\r
1601 Node = BootOption->FilePath;\r
1602 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);\r
1603 if (!EFI_ERROR (Status) && CompareGuid (\r
1604 EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),\r
1605 PcdGetPtr (PcdBootManagerMenuFile)\r
1606 )) {\r
1607 DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n"));\r
1608 BmStopHotkeyService (NULL, NULL);\r
1609 } else {\r
1610 EfiSignalEventReadyToBoot();\r
1611 //\r
1612 // Report Status Code to indicate ReadyToBoot was signalled\r
1613 //\r
1614 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));\r
1615 //\r
1616 // 4. Repair system through DriverHealth protocol\r
1617 //\r
1618 BmRepairAllControllers ();\r
1619 }\r
1620\r
1621 PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1622\r
1623 //\r
1624 // 5. Load EFI boot option to ImageHandle\r
1625 //\r
1626 ImageHandle = NULL;\r
1627 if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {\r
1628 Status = EFI_NOT_FOUND;\r
1629 FileBuffer = BmGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize);\r
1630 DEBUG_CODE (\r
1631 if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {\r
1632 DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));\r
1633 BmPrintDp (BootOption->FilePath);\r
1634 DEBUG ((EFI_D_INFO, " -> "));\r
1635 BmPrintDp (FilePath);\r
1636 DEBUG ((EFI_D_INFO, "\n"));\r
1637 }\r
1638 );\r
1639 if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) {\r
1640 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));\r
1641 Status = gBS->LoadImage (\r
1642 TRUE,\r
1643 gImageHandle,\r
1644 FilePath,\r
1645 FileBuffer,\r
1646 FileSize,\r
1647 &ImageHandle\r
1648 );\r
1649 }\r
1650 if (FileBuffer != NULL) {\r
1651 FreePool (FileBuffer);\r
1652 }\r
1653 if (FilePath != NULL) {\r
1654 FreePool (FilePath);\r
1655 }\r
1656\r
1657 if (EFI_ERROR (Status)) {\r
1658 //\r
1659 // Report Status Code to indicate that the failure to load boot option\r
1660 //\r
1661 REPORT_STATUS_CODE (\r
1662 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1663 (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)\r
1664 );\r
1665 BootOption->Status = Status;\r
1666 return;\r
1667 }\r
1668 }\r
1669\r
1670 //\r
1671 // 6. Adjust the different type memory page number just before booting\r
1672 // and save the updated info into the variable for next boot to use\r
1673 //\r
1674 if ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) {\r
1675 if (PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {\r
1676 BmSetMemoryTypeInformationVariable ();\r
1677 }\r
1678 }\r
1679\r
1680 DEBUG_CODE_BEGIN();\r
1681 if (BootOption->Description == NULL) {\r
1682 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));\r
1683 } else {\r
1684 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));\r
1685 }\r
1686 DEBUG_CODE_END();\r
1687\r
1688 //\r
1689 // Check to see if we should legacy BOOT. If yes then do the legacy boot\r
1690 // Write boot to OS performance data for Legacy boot\r
1691 //\r
1692 if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {\r
1693 if (mBmLegacyBoot != NULL) {\r
1694 //\r
1695 // Write boot to OS performance data for legacy boot.\r
1696 //\r
1697 PERF_CODE (\r
1698 //\r
1699 // Create an event to be signalled when Legacy Boot occurs to write performance data.\r
1700 //\r
1701 Status = EfiCreateEventLegacyBootEx(\r
1702 TPL_NOTIFY,\r
1703 BmWriteBootToOsPerformanceData,\r
1704 NULL, \r
1705 &LegacyBootEvent\r
1706 );\r
1707 ASSERT_EFI_ERROR (Status);\r
1708 );\r
1709\r
1710 mBmLegacyBoot (BootOption);\r
1711 } else {\r
1712 BootOption->Status = EFI_UNSUPPORTED;\r
1713 }\r
1714\r
1715 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1716 return;\r
1717 }\r
1718 \r
1719 //\r
1720 // Provide the image with its load options\r
1721 //\r
1722 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
1723 ASSERT_EFI_ERROR (Status);\r
1724\r
1725 ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;\r
1726 ImageInfo->LoadOptions = BootOption->OptionalData;\r
1727\r
1728 //\r
1729 // Clean to NULL because the image is loaded directly from the firmwares boot manager.\r
1730 //\r
1731 ImageInfo->ParentHandle = NULL;\r
1732\r
1733 //\r
1734 // Before calling the image, enable the Watchdog Timer for 5 minutes period\r
1735 //\r
1736 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);\r
1737\r
1738 //\r
1739 // Write boot to OS performance data for UEFI boot\r
1740 //\r
1741 PERF_CODE (\r
1742 BmWriteBootToOsPerformanceData (NULL, NULL);\r
1743 );\r
1744\r
1745 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));\r
1746\r
1747 Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);\r
1748 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));\r
1749 BootOption->Status = Status;\r
1750 if (EFI_ERROR (Status)) {\r
1751 //\r
1752 // Report Status Code to indicate that boot failure\r
1753 //\r
1754 REPORT_STATUS_CODE (\r
1755 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1756 (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)\r
1757 );\r
1758 }\r
1759 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1760\r
1761 //\r
1762 // Clear the Watchdog Timer after the image returns\r
1763 //\r
1764 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);\r
1765\r
1766 //\r
1767 // Set Logo status invalid after trying one boot option\r
1768 //\r
1769 BootLogo = NULL;\r
1770 Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);\r
1771 if (!EFI_ERROR (Status) && (BootLogo != NULL)) {\r
1772 Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);\r
1773 ASSERT_EFI_ERROR (Status);\r
1774 }\r
1775\r
1776 //\r
1777 // Clear Boot Current\r
1778 //\r
1779 Status = gRT->SetVariable (\r
1780 L"BootCurrent",\r
1781 &gEfiGlobalVariableGuid,\r
1782 0,\r
1783 0,\r
1784 NULL\r
1785 );\r
1786 //\r
1787 // Deleting variable with current variable implementation shouldn't fail.\r
1788 // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,\r
1789 // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.\r
1790 //\r
1791 ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
1792}\r
1793\r
1794/**\r
1795 Check whether there is a instance in BlockIoDevicePath, which contain multi device path\r
1796 instances, has the same partition node with HardDriveDevicePath device path\r
1797\r
1798 @param BlockIoDevicePath Multi device path instances which need to check\r
1799 @param HardDriveDevicePath A device path which starts with a hard drive media\r
1800 device path.\r
1801\r
1802 @retval TRUE There is a matched device path instance.\r
1803 @retval FALSE There is no matched device path instance.\r
1804\r
1805**/\r
1806BOOLEAN\r
1807BmMatchPartitionDevicePathNode (\r
1808 IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,\r
1809 IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath\r
1810 )\r
1811{\r
1812 HARDDRIVE_DEVICE_PATH *Node;\r
1813\r
1814 if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {\r
1815 return FALSE;\r
1816 }\r
1817\r
1818 //\r
1819 // find the partition device path node\r
1820 //\r
1821 while (!IsDevicePathEnd (BlockIoDevicePath)) {\r
1822 if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&\r
1823 (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)\r
1824 ) {\r
1825 break;\r
1826 }\r
1827\r
1828 BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);\r
1829 }\r
1830\r
1831 if (IsDevicePathEnd (BlockIoDevicePath)) {\r
1832 return FALSE;\r
1833 }\r
1834\r
1835 //\r
1836 // See if the harddrive device path in blockio matches the orig Hard Drive Node\r
1837 //\r
1838 Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath;\r
1839\r
1840 //\r
1841 // Match Signature and PartitionNumber.\r
1842 // Unused bytes in Signature are initiaized with zeros.\r
1843 //\r
1844 return (BOOLEAN) (\r
1845 (Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&\r
1846 (Node->MBRType == HardDriveDevicePath->MBRType) &&\r
1847 (Node->SignatureType == HardDriveDevicePath->SignatureType) &&\r
1848 (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)\r
1849 );\r
1850}\r
1851\r
1852/**\r
1853 Emuerate all possible bootable medias in the following order:\r
1854 1. Removable BlockIo - The boot option only points to the removable media\r
1855 device, like USB key, DVD, Floppy etc.\r
1856 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device,\r
1857 like HardDisk.\r
1858 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting\r
1859 SimpleFileSystem Protocol, but not supporting BlockIo\r
1860 protocol.\r
1861 4. LoadFile - The boot option points to the media supporting \r
1862 LoadFile protocol.\r
1863 Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior\r
1864\r
1865 @param BootOptionCount Return the boot option count which has been found.\r
1866\r
1867 @retval Pointer to the boot option array.\r
1868**/\r
1869EFI_BOOT_MANAGER_LOAD_OPTION *\r
1870BmEnumerateBootOptions (\r
1871 UINTN *BootOptionCount\r
1872 )\r
1873{\r
1874 EFI_STATUS Status;\r
1875 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
067ed98a
RN
1876 UINTN HandleCount;\r
1877 EFI_HANDLE *Handles;\r
1878 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
1879 UINTN Removable;\r
1880 UINTN Index;\r
f41c71d2 1881 CHAR16 *Description;\r
067ed98a
RN
1882\r
1883 ASSERT (BootOptionCount != NULL);\r
1884\r
1885 *BootOptionCount = 0;\r
1886 BootOptions = NULL;\r
1887\r
1888 //\r
1889 // Parse removable block io followed by fixed block io\r
1890 //\r
1891 gBS->LocateHandleBuffer (\r
1892 ByProtocol,\r
1893 &gEfiBlockIoProtocolGuid,\r
1894 NULL,\r
1895 &HandleCount,\r
1896 &Handles\r
1897 );\r
1898\r
1899 for (Removable = 0; Removable < 2; Removable++) {\r
1900 for (Index = 0; Index < HandleCount; Index++) {\r
1901 Status = gBS->HandleProtocol (\r
1902 Handles[Index],\r
1903 &gEfiBlockIoProtocolGuid,\r
1904 (VOID **) &BlkIo\r
1905 );\r
1906 if (EFI_ERROR (Status)) {\r
1907 continue;\r
1908 }\r
1909\r
1910 //\r
1911 // Skip the logical partitions\r
1912 //\r
1913 if (BlkIo->Media->LogicalPartition) {\r
1914 continue;\r
1915 }\r
1916\r
1917 //\r
1918 // Skip the fixed block io then the removable block io\r
1919 //\r
1920 if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {\r
1921 continue;\r
1922 }\r
1923\r
f41c71d2 1924 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
1925 BootOptions = ReallocatePool (\r
1926 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
1927 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
1928 BootOptions\r
1929 );\r
1930 ASSERT (BootOptions != NULL);\r
1931\r
1932 Status = EfiBootManagerInitializeLoadOption (\r
1933 &BootOptions[(*BootOptionCount)++],\r
1934 LoadOptionNumberUnassigned,\r
1935 LoadOptionTypeBoot,\r
1936 LOAD_OPTION_ACTIVE,\r
f41c71d2 1937 Description,\r
067ed98a
RN
1938 DevicePathFromHandle (Handles[Index]),\r
1939 NULL,\r
1940 0\r
1941 );\r
1942 ASSERT_EFI_ERROR (Status);\r
1943\r
f41c71d2 1944 FreePool (Description);\r
067ed98a
RN
1945 }\r
1946 }\r
1947\r
1948 if (HandleCount != 0) {\r
1949 FreePool (Handles);\r
1950 }\r
1951\r
1952 //\r
1953 // Parse simple file system not based on block io\r
1954 //\r
067ed98a
RN
1955 gBS->LocateHandleBuffer (\r
1956 ByProtocol,\r
1957 &gEfiSimpleFileSystemProtocolGuid,\r
1958 NULL,\r
1959 &HandleCount,\r
1960 &Handles\r
1961 );\r
1962 for (Index = 0; Index < HandleCount; Index++) {\r
1963 Status = gBS->HandleProtocol (\r
1964 Handles[Index],\r
1965 &gEfiBlockIoProtocolGuid,\r
1966 (VOID **) &BlkIo\r
1967 );\r
1968 if (!EFI_ERROR (Status)) {\r
1969 //\r
1970 // Skip if the file system handle supports a BlkIo protocol, which we've handled in above\r
1971 //\r
1972 continue;\r
1973 }\r
f41c71d2 1974 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
1975 BootOptions = ReallocatePool (\r
1976 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
1977 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
1978 BootOptions\r
1979 );\r
1980 ASSERT (BootOptions != NULL);\r
1981\r
1982 Status = EfiBootManagerInitializeLoadOption (\r
1983 &BootOptions[(*BootOptionCount)++],\r
1984 LoadOptionNumberUnassigned,\r
1985 LoadOptionTypeBoot,\r
1986 LOAD_OPTION_ACTIVE,\r
1987 Description,\r
1988 DevicePathFromHandle (Handles[Index]),\r
1989 NULL,\r
1990 0\r
1991 );\r
1992 ASSERT_EFI_ERROR (Status);\r
f41c71d2 1993 FreePool (Description);\r
067ed98a
RN
1994 }\r
1995\r
1996 if (HandleCount != 0) {\r
1997 FreePool (Handles);\r
1998 }\r
1999\r
2000 //\r
2001 // Parse load file, assuming UEFI Network boot option\r
2002 //\r
2003 gBS->LocateHandleBuffer (\r
2004 ByProtocol,\r
2005 &gEfiLoadFileProtocolGuid,\r
2006 NULL,\r
2007 &HandleCount,\r
2008 &Handles\r
2009 );\r
2010 for (Index = 0; Index < HandleCount; Index++) {\r
2011\r
f41c71d2 2012 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
2013 BootOptions = ReallocatePool (\r
2014 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
2015 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
2016 BootOptions\r
2017 );\r
2018 ASSERT (BootOptions != NULL);\r
2019\r
2020 Status = EfiBootManagerInitializeLoadOption (\r
2021 &BootOptions[(*BootOptionCount)++],\r
2022 LoadOptionNumberUnassigned,\r
2023 LoadOptionTypeBoot,\r
2024 LOAD_OPTION_ACTIVE,\r
2025 Description,\r
2026 DevicePathFromHandle (Handles[Index]),\r
2027 NULL,\r
2028 0\r
2029 );\r
2030 ASSERT_EFI_ERROR (Status);\r
f41c71d2 2031 FreePool (Description);\r
067ed98a
RN
2032 }\r
2033\r
2034 if (HandleCount != 0) {\r
2035 FreePool (Handles);\r
2036 }\r
2037\r
2038 return BootOptions;\r
2039}\r
2040\r
2041/**\r
2042 The function enumerates all boot options, creates them and registers them in the BootOrder variable.\r
2043**/\r
2044VOID\r
2045EFIAPI\r
2046EfiBootManagerRefreshAllBootOption (\r
2047 VOID\r
2048 )\r
2049{\r
2050 EFI_STATUS Status;\r
2051 EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;\r
2052 UINTN NvBootOptionCount;\r
2053 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2054 UINTN BootOptionCount;\r
2055 UINTN Index;\r
2056\r
2057 //\r
2058 // Optionally refresh the legacy boot option\r
2059 //\r
2060 if (mBmRefreshLegacyBootOption != NULL) {\r
2061 mBmRefreshLegacyBootOption ();\r
2062 }\r
2063\r
2064 BootOptions = BmEnumerateBootOptions (&BootOptionCount);\r
2065 NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);\r
2066\r
2067 //\r
2068 // Mark the boot option as added by BDS by setting OptionalData to a special GUID\r
2069 //\r
2070 for (Index = 0; Index < BootOptionCount; Index++) {\r
2071 BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);\r
2072 BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);\r
2073 }\r
2074\r
2075 //\r
2076 // Remove invalid EFI boot options from NV\r
2077 //\r
2078 for (Index = 0; Index < NvBootOptionCount; Index++) {\r
2079 if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || \r
2080 (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)\r
2081 ) &&\r
2082 (NvBootOptions[Index].OptionalDataSize == sizeof (EFI_GUID)) &&\r
2083 CompareGuid ((EFI_GUID *) NvBootOptions[Index].OptionalData, &mBmAutoCreateBootOptionGuid)\r
2084 ) {\r
2085 //\r
2086 // Only check those added by BDS\r
2087 // so that the boot options added by end-user or OS installer won't be deleted\r
2088 //\r
2089 if (BmFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == (UINTN) -1) {\r
2090 Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
2091 //\r
2092 // Deleting variable with current variable implementation shouldn't fail.\r
2093 //\r
2094 ASSERT_EFI_ERROR (Status);\r
2095 }\r
2096 }\r
2097 }\r
2098\r
2099 //\r
2100 // Add new EFI boot options to NV\r
2101 //\r
2102 for (Index = 0; Index < BootOptionCount; Index++) {\r
2103 if (BmFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -1) {\r
2104 EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);\r
2105 //\r
2106 // Try best to add the boot options so continue upon failure.\r
2107 //\r
2108 }\r
2109 }\r
2110\r
2111 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2112 EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);\r
2113}\r
2114\r
2115/**\r
2116 This function is called to create the boot option for the Boot Manager Menu.\r
2117\r
2118 The Boot Manager Menu is shown after successfully booting a boot option.\r
2119 Assume the BootManagerMenuFile is in the same FV as the module links to this library.\r
2120\r
2121 @param BootOption Return the boot option of the Boot Manager Menu\r
2122\r
2123 @retval EFI_SUCCESS Successfully register the Boot Manager Menu.\r
2124 @retval Status Return status of gRT->SetVariable (). BootOption still points\r
2125 to the Boot Manager Menu even the Status is not EFI_SUCCESS.\r
2126**/\r
2127EFI_STATUS\r
2128BmRegisterBootManagerMenu (\r
2129 OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2130 )\r
2131{\r
2132 EFI_STATUS Status;\r
2133 CHAR16 *Description;\r
2134 UINTN DescriptionLength;\r
2135 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
2136 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
2137 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;\r
2138\r
2139 Status = GetSectionFromFv (\r
2140 PcdGetPtr (PcdBootManagerMenuFile),\r
2141 EFI_SECTION_USER_INTERFACE,\r
2142 0,\r
2143 (VOID **) &Description,\r
2144 &DescriptionLength\r
2145 );\r
2146 if (EFI_ERROR (Status)) {\r
2147 Description = NULL;\r
2148 }\r
2149\r
2150 EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));\r
2151 Status = gBS->HandleProtocol (\r
2152 gImageHandle,\r
2153 &gEfiLoadedImageProtocolGuid,\r
2154 (VOID **) &LoadedImage\r
2155 );\r
2156 ASSERT_EFI_ERROR (Status);\r
2157 DevicePath = AppendDevicePathNode (\r
2158 DevicePathFromHandle (LoadedImage->DeviceHandle),\r
2159 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
2160 );\r
2161 ASSERT (DevicePath != NULL);\r
2162\r
2163 Status = EfiBootManagerInitializeLoadOption (\r
2164 BootOption,\r
2165 LoadOptionNumberUnassigned,\r
2166 LoadOptionTypeBoot,\r
2167 LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,\r
2168 (Description != NULL) ? Description : L"Boot Manager Menu",\r
2169 DevicePath,\r
2170 NULL,\r
2171 0\r
2172 );\r
2173 ASSERT_EFI_ERROR (Status);\r
2174 FreePool (DevicePath);\r
2175 if (Description != NULL) {\r
2176 FreePool (Description);\r
2177 }\r
2178\r
2179 DEBUG_CODE (\r
2180 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2181 UINTN BootOptionCount;\r
2182\r
2183 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2184 ASSERT (BmFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);\r
2185 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2186 );\r
2187\r
2188 return EfiBootManagerAddLoadOptionVariable (BootOption, 0);\r
2189}\r
2190\r
2191/**\r
2192 Return the boot option corresponding to the Boot Manager Menu.\r
2193 It may automatically create one if the boot option hasn't been created yet.\r
2194 \r
2195 @param BootOption Return the Boot Manager Menu.\r
2196\r
2197 @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.\r
2198 @retval Status Return status of gRT->SetVariable (). BootOption still points\r
2199 to the Boot Manager Menu even the Status is not EFI_SUCCESS.\r
2200**/\r
2201EFI_STATUS\r
2202EFIAPI\r
2203EfiBootManagerGetBootManagerMenu (\r
2204 EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2205 )\r
2206{\r
2207 EFI_STATUS Status;\r
2208 UINTN BootOptionCount;\r
2209 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2210 UINTN Index;\r
2211 EFI_DEVICE_PATH_PROTOCOL *Node;\r
2212 EFI_HANDLE FvHandle;\r
2213 \r
2214 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2215\r
2216 for (Index = 0; Index < BootOptionCount; Index++) {\r
2217 Node = BootOptions[Index].FilePath;\r
2218 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);\r
2219 if (!EFI_ERROR (Status)) {\r
2220 if (CompareGuid (\r
2221 EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),\r
2222 PcdGetPtr (PcdBootManagerMenuFile)\r
2223 )\r
2224 ) { \r
2225 Status = EfiBootManagerInitializeLoadOption (\r
2226 BootOption,\r
2227 BootOptions[Index].OptionNumber,\r
2228 BootOptions[Index].OptionType,\r
2229 BootOptions[Index].Attributes,\r
2230 BootOptions[Index].Description,\r
2231 BootOptions[Index].FilePath,\r
2232 BootOptions[Index].OptionalData,\r
2233 BootOptions[Index].OptionalDataSize\r
2234 );\r
2235 ASSERT_EFI_ERROR (Status);\r
2236 break;\r
2237 }\r
2238 }\r
2239 }\r
2240\r
2241 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2242\r
2243 //\r
2244 // Automatically create the Boot#### for Boot Manager Menu when not found.\r
2245 //\r
2246 if (Index == BootOptionCount) {\r
2247 return BmRegisterBootManagerMenu (BootOption);\r
2248 } else {\r
2249 return EFI_SUCCESS;\r
2250 }\r
2251}\r
2252\r