1/** @file
2  Library functions which relates with booting.
3
4Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
5(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
6This program and the accompanying materials
7are licensed and made available under the terms and conditions of the BSD License
8which accompanies this distribution.  The full text of the license may be found at
9http://opensource.org/licenses/bsd-license.php
10
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12WITHOUT 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
23CONST UINT16 mBmUsbLangId    = 0x0409; // English
24CHAR16       mBmUefiPrefix[] = L"UEFI ";
25
26EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION  mBmRefreshLegacyBootOption = NULL;
27EFI_BOOT_MANAGER_LEGACY_BOOT                 mBmLegacyBoot              = NULL;
28
29LIST_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///
35EFI_GUID mBmHardDriveBootVariableGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } };
36EFI_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**/
44VOID
45EFIAPI
46EfiBootManagerRegisterLegacyBootSupport (
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**/
63BOOLEAN
64BmIsAutoCreateBootOption (
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**/
100BM_BOOT_TYPE
101BmDevicePathType (
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**/
186UINTN
187BmFindBootOptionInVariable (
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**/
253VOID *
254BmGetFileBufferByMemmapFv (
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**/
337BOOLEAN
338BmIsMemmapFvFilePath (
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**/
365BOOLEAN
366BmMatchUsbClass (
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**/
445VOID
446BmEliminateExtraSpaces (
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**/
468CHAR16 *
469BmGetDescriptionFromDiskInfo (
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**/
571CHAR16 *
572BmGetUsbDescription (
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**/
672CHAR16 *
673BmGetMiscDescription (
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**/
744EFI_STATUS
745EFIAPI
746EfiBootManagerRegisterBootDescriptionHandler (
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
774BM_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**/
787CHAR16 *
788BmGetBootDescription (
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**/
856BOOLEAN
857BmMatchUsbWwid (
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**/
970EFI_HANDLE *
971BmFindUsbDevice (
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**/
1063VOID *
1064BmExpandUsbDevicePath (
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**/
1109VOID *
1110BmExpandFileDevicePath (
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**/
1173VOID
1174BmCachePartitionDevicePath (
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**/
1241VOID *
1242BmExpandPartitionDevicePath (
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**/
1406VOID *
1407BmExpandMediaDevicePath (
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**/
1525VOID *
1526BmGetLoadOptionBuffer (
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**/
1648VOID
1649EFIAPI
1650EfiBootManagerBoot (
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**/
1922BOOLEAN
1923BmMatchPartitionDevicePathNode (
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**/
1975VOID
1976BmMakeBootOptionDescriptionUnique (
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**/
2045EFI_BOOT_MANAGER_LOAD_OPTION *
2046BmEnumerateBootOptions (
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**/
2221VOID
2222EFIAPI
2223EfiBootManagerRefreshAllBootOption (
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**/
2302EFI_STATUS
2303BmRegisterBootManagerMenu (
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**/
2376EFI_STATUS
2377EFIAPI
2378EfiBootManagerGetBootManagerMenu (
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