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