]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
MdeModulePkg: Make boot option description unique
[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
d948fe96
RN
1871/**\r
1872 Enumerate all boot option descriptions and append " 2"/" 3"/... to make\r
1873 unique description.\r
1874\r
1875 @param BootOptions Array of boot options.\r
1876 @param BootOptionCount Count of boot options.\r
1877**/\r
1878VOID\r
1879BmMakeBootOptionDescriptionUnique (\r
1880 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,\r
1881 UINTN BootOptionCount\r
1882 )\r
1883{\r
1884 UINTN Base;\r
1885 UINTN Index;\r
1886 UINTN DescriptionSize;\r
1887 UINTN MaxSuffixSize;\r
1888 BOOLEAN *Visited;\r
1889 UINTN MatchCount;\r
1890\r
1891 if (BootOptionCount == 0) {\r
1892 return;\r
1893 }\r
1894\r
1895 //\r
1896 // Calculate the maximum buffer size for the number suffix.\r
1897 // The initial sizeof (CHAR16) is for the blank space before the number.\r
1898 //\r
1899 MaxSuffixSize = sizeof (CHAR16);\r
1900 for (Index = BootOptionCount; Index != 0; Index = Index / 10) {\r
1901 MaxSuffixSize += sizeof (CHAR16);\r
1902 }\r
1903\r
1904 Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);\r
1905 ASSERT (Visited != NULL);\r
1906\r
1907 for (Base = 0; Base < BootOptionCount; Base++) {\r
1908 if (!Visited[Base]) {\r
1909 MatchCount = 1;\r
1910 Visited[Base] = TRUE;\r
1911 DescriptionSize = StrSize (BootOptions[Base].Description);\r
1912 for (Index = Base + 1; Index < BootOptionCount; Index++) {\r
1913 if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {\r
1914 Visited[Index] = TRUE;\r
1915 MatchCount++;\r
1916 FreePool (BootOptions[Index].Description);\r
1917 BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);\r
1918 UnicodeSPrint (\r
1919 BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,\r
1920 L"%s %d",\r
1921 BootOptions[Base].Description, MatchCount\r
1922 );\r
1923 }\r
1924 }\r
1925 }\r
1926 }\r
1927\r
1928 FreePool (Visited);\r
1929}\r
1930\r
067ed98a
RN
1931/**\r
1932 Emuerate all possible bootable medias in the following order:\r
1933 1. Removable BlockIo - The boot option only points to the removable media\r
1934 device, like USB key, DVD, Floppy etc.\r
1935 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device,\r
1936 like HardDisk.\r
1937 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting\r
1938 SimpleFileSystem Protocol, but not supporting BlockIo\r
1939 protocol.\r
1940 4. LoadFile - The boot option points to the media supporting \r
1941 LoadFile protocol.\r
1942 Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior\r
1943\r
1944 @param BootOptionCount Return the boot option count which has been found.\r
1945\r
1946 @retval Pointer to the boot option array.\r
1947**/\r
1948EFI_BOOT_MANAGER_LOAD_OPTION *\r
1949BmEnumerateBootOptions (\r
1950 UINTN *BootOptionCount\r
1951 )\r
1952{\r
1953 EFI_STATUS Status;\r
1954 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
067ed98a
RN
1955 UINTN HandleCount;\r
1956 EFI_HANDLE *Handles;\r
1957 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
1958 UINTN Removable;\r
1959 UINTN Index;\r
f41c71d2 1960 CHAR16 *Description;\r
067ed98a
RN
1961\r
1962 ASSERT (BootOptionCount != NULL);\r
1963\r
1964 *BootOptionCount = 0;\r
1965 BootOptions = NULL;\r
1966\r
1967 //\r
1968 // Parse removable block io followed by fixed block io\r
1969 //\r
1970 gBS->LocateHandleBuffer (\r
1971 ByProtocol,\r
1972 &gEfiBlockIoProtocolGuid,\r
1973 NULL,\r
1974 &HandleCount,\r
1975 &Handles\r
1976 );\r
1977\r
1978 for (Removable = 0; Removable < 2; Removable++) {\r
1979 for (Index = 0; Index < HandleCount; Index++) {\r
1980 Status = gBS->HandleProtocol (\r
1981 Handles[Index],\r
1982 &gEfiBlockIoProtocolGuid,\r
1983 (VOID **) &BlkIo\r
1984 );\r
1985 if (EFI_ERROR (Status)) {\r
1986 continue;\r
1987 }\r
1988\r
1989 //\r
1990 // Skip the logical partitions\r
1991 //\r
1992 if (BlkIo->Media->LogicalPartition) {\r
1993 continue;\r
1994 }\r
1995\r
1996 //\r
1997 // Skip the fixed block io then the removable block io\r
1998 //\r
1999 if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {\r
2000 continue;\r
2001 }\r
2002\r
f41c71d2 2003 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
2004 BootOptions = ReallocatePool (\r
2005 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
2006 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
2007 BootOptions\r
2008 );\r
2009 ASSERT (BootOptions != NULL);\r
2010\r
2011 Status = EfiBootManagerInitializeLoadOption (\r
2012 &BootOptions[(*BootOptionCount)++],\r
2013 LoadOptionNumberUnassigned,\r
2014 LoadOptionTypeBoot,\r
2015 LOAD_OPTION_ACTIVE,\r
f41c71d2 2016 Description,\r
067ed98a
RN
2017 DevicePathFromHandle (Handles[Index]),\r
2018 NULL,\r
2019 0\r
2020 );\r
2021 ASSERT_EFI_ERROR (Status);\r
2022\r
f41c71d2 2023 FreePool (Description);\r
067ed98a
RN
2024 }\r
2025 }\r
2026\r
2027 if (HandleCount != 0) {\r
2028 FreePool (Handles);\r
2029 }\r
2030\r
2031 //\r
2032 // Parse simple file system not based on block io\r
2033 //\r
067ed98a
RN
2034 gBS->LocateHandleBuffer (\r
2035 ByProtocol,\r
2036 &gEfiSimpleFileSystemProtocolGuid,\r
2037 NULL,\r
2038 &HandleCount,\r
2039 &Handles\r
2040 );\r
2041 for (Index = 0; Index < HandleCount; Index++) {\r
2042 Status = gBS->HandleProtocol (\r
2043 Handles[Index],\r
2044 &gEfiBlockIoProtocolGuid,\r
2045 (VOID **) &BlkIo\r
2046 );\r
2047 if (!EFI_ERROR (Status)) {\r
2048 //\r
2049 // Skip if the file system handle supports a BlkIo protocol, which we've handled in above\r
2050 //\r
2051 continue;\r
2052 }\r
f41c71d2 2053 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
2054 BootOptions = ReallocatePool (\r
2055 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
2056 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
2057 BootOptions\r
2058 );\r
2059 ASSERT (BootOptions != NULL);\r
2060\r
2061 Status = EfiBootManagerInitializeLoadOption (\r
2062 &BootOptions[(*BootOptionCount)++],\r
2063 LoadOptionNumberUnassigned,\r
2064 LoadOptionTypeBoot,\r
2065 LOAD_OPTION_ACTIVE,\r
2066 Description,\r
2067 DevicePathFromHandle (Handles[Index]),\r
2068 NULL,\r
2069 0\r
2070 );\r
2071 ASSERT_EFI_ERROR (Status);\r
f41c71d2 2072 FreePool (Description);\r
067ed98a
RN
2073 }\r
2074\r
2075 if (HandleCount != 0) {\r
2076 FreePool (Handles);\r
2077 }\r
2078\r
2079 //\r
2080 // Parse load file, assuming UEFI Network boot option\r
2081 //\r
2082 gBS->LocateHandleBuffer (\r
2083 ByProtocol,\r
2084 &gEfiLoadFileProtocolGuid,\r
2085 NULL,\r
2086 &HandleCount,\r
2087 &Handles\r
2088 );\r
2089 for (Index = 0; Index < HandleCount; Index++) {\r
2090\r
f41c71d2 2091 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
2092 BootOptions = ReallocatePool (\r
2093 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
2094 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
2095 BootOptions\r
2096 );\r
2097 ASSERT (BootOptions != NULL);\r
2098\r
2099 Status = EfiBootManagerInitializeLoadOption (\r
2100 &BootOptions[(*BootOptionCount)++],\r
2101 LoadOptionNumberUnassigned,\r
2102 LoadOptionTypeBoot,\r
2103 LOAD_OPTION_ACTIVE,\r
2104 Description,\r
2105 DevicePathFromHandle (Handles[Index]),\r
2106 NULL,\r
2107 0\r
2108 );\r
2109 ASSERT_EFI_ERROR (Status);\r
f41c71d2 2110 FreePool (Description);\r
067ed98a
RN
2111 }\r
2112\r
2113 if (HandleCount != 0) {\r
2114 FreePool (Handles);\r
2115 }\r
2116\r
d948fe96 2117 BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount);\r
067ed98a
RN
2118 return BootOptions;\r
2119}\r
2120\r
2121/**\r
2122 The function enumerates all boot options, creates them and registers them in the BootOrder variable.\r
2123**/\r
2124VOID\r
2125EFIAPI\r
2126EfiBootManagerRefreshAllBootOption (\r
2127 VOID\r
2128 )\r
2129{\r
2130 EFI_STATUS Status;\r
2131 EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;\r
2132 UINTN NvBootOptionCount;\r
2133 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2134 UINTN BootOptionCount;\r
2135 UINTN Index;\r
2136\r
2137 //\r
2138 // Optionally refresh the legacy boot option\r
2139 //\r
2140 if (mBmRefreshLegacyBootOption != NULL) {\r
2141 mBmRefreshLegacyBootOption ();\r
2142 }\r
2143\r
2144 BootOptions = BmEnumerateBootOptions (&BootOptionCount);\r
2145 NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);\r
2146\r
2147 //\r
2148 // Mark the boot option as added by BDS by setting OptionalData to a special GUID\r
2149 //\r
2150 for (Index = 0; Index < BootOptionCount; Index++) {\r
2151 BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);\r
2152 BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);\r
2153 }\r
2154\r
2155 //\r
2156 // Remove invalid EFI boot options from NV\r
2157 //\r
2158 for (Index = 0; Index < NvBootOptionCount; Index++) {\r
2159 if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || \r
2160 (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)\r
2161 ) &&\r
2162 (NvBootOptions[Index].OptionalDataSize == sizeof (EFI_GUID)) &&\r
2163 CompareGuid ((EFI_GUID *) NvBootOptions[Index].OptionalData, &mBmAutoCreateBootOptionGuid)\r
2164 ) {\r
2165 //\r
2166 // Only check those added by BDS\r
2167 // so that the boot options added by end-user or OS installer won't be deleted\r
2168 //\r
2169 if (BmFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == (UINTN) -1) {\r
2170 Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
2171 //\r
2172 // Deleting variable with current variable implementation shouldn't fail.\r
2173 //\r
2174 ASSERT_EFI_ERROR (Status);\r
2175 }\r
2176 }\r
2177 }\r
2178\r
2179 //\r
2180 // Add new EFI boot options to NV\r
2181 //\r
2182 for (Index = 0; Index < BootOptionCount; Index++) {\r
2183 if (BmFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -1) {\r
2184 EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);\r
2185 //\r
2186 // Try best to add the boot options so continue upon failure.\r
2187 //\r
2188 }\r
2189 }\r
2190\r
2191 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2192 EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);\r
2193}\r
2194\r
2195/**\r
2196 This function is called to create the boot option for the Boot Manager Menu.\r
2197\r
2198 The Boot Manager Menu is shown after successfully booting a boot option.\r
2199 Assume the BootManagerMenuFile is in the same FV as the module links to this library.\r
2200\r
2201 @param BootOption Return the boot option of the Boot Manager Menu\r
2202\r
2203 @retval EFI_SUCCESS Successfully register the Boot Manager Menu.\r
2204 @retval Status Return status of gRT->SetVariable (). BootOption still points\r
2205 to the Boot Manager Menu even the Status is not EFI_SUCCESS.\r
2206**/\r
2207EFI_STATUS\r
2208BmRegisterBootManagerMenu (\r
2209 OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2210 )\r
2211{\r
2212 EFI_STATUS Status;\r
2213 CHAR16 *Description;\r
2214 UINTN DescriptionLength;\r
2215 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
2216 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
2217 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;\r
2218\r
2219 Status = GetSectionFromFv (\r
2220 PcdGetPtr (PcdBootManagerMenuFile),\r
2221 EFI_SECTION_USER_INTERFACE,\r
2222 0,\r
2223 (VOID **) &Description,\r
2224 &DescriptionLength\r
2225 );\r
2226 if (EFI_ERROR (Status)) {\r
2227 Description = NULL;\r
2228 }\r
2229\r
2230 EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));\r
2231 Status = gBS->HandleProtocol (\r
2232 gImageHandle,\r
2233 &gEfiLoadedImageProtocolGuid,\r
2234 (VOID **) &LoadedImage\r
2235 );\r
2236 ASSERT_EFI_ERROR (Status);\r
2237 DevicePath = AppendDevicePathNode (\r
2238 DevicePathFromHandle (LoadedImage->DeviceHandle),\r
2239 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
2240 );\r
2241 ASSERT (DevicePath != NULL);\r
2242\r
2243 Status = EfiBootManagerInitializeLoadOption (\r
2244 BootOption,\r
2245 LoadOptionNumberUnassigned,\r
2246 LoadOptionTypeBoot,\r
2247 LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,\r
2248 (Description != NULL) ? Description : L"Boot Manager Menu",\r
2249 DevicePath,\r
2250 NULL,\r
2251 0\r
2252 );\r
2253 ASSERT_EFI_ERROR (Status);\r
2254 FreePool (DevicePath);\r
2255 if (Description != NULL) {\r
2256 FreePool (Description);\r
2257 }\r
2258\r
2259 DEBUG_CODE (\r
2260 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2261 UINTN BootOptionCount;\r
2262\r
2263 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2264 ASSERT (BmFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);\r
2265 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2266 );\r
2267\r
2268 return EfiBootManagerAddLoadOptionVariable (BootOption, 0);\r
2269}\r
2270\r
2271/**\r
2272 Return the boot option corresponding to the Boot Manager Menu.\r
2273 It may automatically create one if the boot option hasn't been created yet.\r
2274 \r
2275 @param BootOption Return the Boot Manager Menu.\r
2276\r
2277 @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.\r
2278 @retval Status Return status of gRT->SetVariable (). BootOption still points\r
2279 to the Boot Manager Menu even the Status is not EFI_SUCCESS.\r
2280**/\r
2281EFI_STATUS\r
2282EFIAPI\r
2283EfiBootManagerGetBootManagerMenu (\r
2284 EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2285 )\r
2286{\r
2287 EFI_STATUS Status;\r
2288 UINTN BootOptionCount;\r
2289 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2290 UINTN Index;\r
2291 EFI_DEVICE_PATH_PROTOCOL *Node;\r
2292 EFI_HANDLE FvHandle;\r
2293 \r
2294 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2295\r
2296 for (Index = 0; Index < BootOptionCount; Index++) {\r
2297 Node = BootOptions[Index].FilePath;\r
2298 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);\r
2299 if (!EFI_ERROR (Status)) {\r
2300 if (CompareGuid (\r
2301 EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),\r
2302 PcdGetPtr (PcdBootManagerMenuFile)\r
2303 )\r
2304 ) { \r
2305 Status = EfiBootManagerInitializeLoadOption (\r
2306 BootOption,\r
2307 BootOptions[Index].OptionNumber,\r
2308 BootOptions[Index].OptionType,\r
2309 BootOptions[Index].Attributes,\r
2310 BootOptions[Index].Description,\r
2311 BootOptions[Index].FilePath,\r
2312 BootOptions[Index].OptionalData,\r
2313 BootOptions[Index].OptionalDataSize\r
2314 );\r
2315 ASSERT_EFI_ERROR (Status);\r
2316 break;\r
2317 }\r
2318 }\r
2319 }\r
2320\r
2321 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2322\r
2323 //\r
2324 // Automatically create the Boot#### for Boot Manager Menu when not found.\r
2325 //\r
2326 if (Index == BootOptionCount) {\r
2327 return BmRegisterBootManagerMenu (BootOption);\r
2328 } else {\r
2329 return EFI_SUCCESS;\r
2330 }\r
2331}\r
2332\r