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