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