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