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