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