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