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