Home | History | Annotate | Download | only in UefiBootManagerLib
      1 /** @file
      2   Library functions which relate with boot option description.
      3 
      4 Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
      5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "InternalBm.h"
     17 
     18 #define VENDOR_IDENTIFICATION_OFFSET     3
     19 #define VENDOR_IDENTIFICATION_LENGTH     8
     20 #define PRODUCT_IDENTIFICATION_OFFSET    11
     21 #define PRODUCT_IDENTIFICATION_LENGTH    16
     22 
     23 CONST UINT16 mBmUsbLangId    = 0x0409; // English
     24 CHAR16       mBmUefiPrefix[] = L"UEFI ";
     25 
     26 LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers);
     27 
     28 /**
     29   For a bootable Device path, return its boot type.
     30 
     31   @param  DevicePath        The bootable device Path to check
     32 
     33   @retval AcpiFloppyBoot    If given device path contains ACPI_DEVICE_PATH type device path node
     34                             which HID is floppy device.
     35   @retval MessageAtapiBoot  If given device path contains MESSAGING_DEVICE_PATH type device path node
     36                             and its last device path node's subtype is MSG_ATAPI_DP.
     37   @retval MessageSataBoot   If given device path contains MESSAGING_DEVICE_PATH type device path node
     38                             and its last device path node's subtype is MSG_SATA_DP.
     39   @retval MessageScsiBoot   If given device path contains MESSAGING_DEVICE_PATH type device path node
     40                             and its last device path node's subtype is MSG_SCSI_DP.
     41   @retval MessageUsbBoot    If given device path contains MESSAGING_DEVICE_PATH type device path node
     42                             and its last device path node's subtype is MSG_USB_DP.
     43   @retval BmMiscBoot        If tiven device path doesn't match the above condition.
     44 
     45 **/
     46 BM_BOOT_TYPE
     47 BmDevicePathType (
     48   IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
     49   )
     50 {
     51   EFI_DEVICE_PATH_PROTOCOL      *Node;
     52   EFI_DEVICE_PATH_PROTOCOL      *NextNode;
     53 
     54   ASSERT (DevicePath != NULL);
     55 
     56   for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
     57     switch (DevicePathType (Node)) {
     58 
     59       case ACPI_DEVICE_PATH:
     60         if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {
     61           return BmAcpiFloppyBoot;
     62         }
     63         break;
     64 
     65       case HARDWARE_DEVICE_PATH:
     66         if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {
     67           return BmHardwareDeviceBoot;
     68         }
     69         break;
     70 
     71       case MESSAGING_DEVICE_PATH:
     72         //
     73         // Skip LUN device node
     74         //
     75         NextNode = Node;
     76         do {
     77           NextNode = NextDevicePathNode (NextNode);
     78         } while (
     79             (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&
     80             (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)
     81             );
     82 
     83         //
     84         // If the device path not only point to driver device, it is not a messaging device path,
     85         //
     86         if (!IsDevicePathEndType (NextNode)) {
     87           continue;
     88         }
     89 
     90         switch (DevicePathSubType (Node)) {
     91         case MSG_ATAPI_DP:
     92           return BmMessageAtapiBoot;
     93           break;
     94 
     95         case MSG_SATA_DP:
     96           return BmMessageSataBoot;
     97           break;
     98 
     99         case MSG_USB_DP:
    100           return BmMessageUsbBoot;
    101           break;
    102 
    103         case MSG_SCSI_DP:
    104           return BmMessageScsiBoot;
    105           break;
    106         }
    107     }
    108   }
    109 
    110   return BmMiscBoot;
    111 }
    112 
    113 /**
    114   Eliminate the extra spaces in the Str to one space.
    115 
    116   @param    Str     Input string info.
    117 **/
    118 VOID
    119 BmEliminateExtraSpaces (
    120   IN CHAR16                    *Str
    121   )
    122 {
    123   UINTN                        Index;
    124   UINTN                        ActualIndex;
    125 
    126   for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {
    127     if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {
    128       Str[ActualIndex++] = Str[Index];
    129     }
    130   }
    131   Str[ActualIndex] = L'\0';
    132 }
    133 
    134 /**
    135   Try to get the controller's ATA/ATAPI description.
    136 
    137   @param Handle                Controller handle.
    138 
    139   @return  The description string.
    140 **/
    141 CHAR16 *
    142 BmGetDescriptionFromDiskInfo (
    143   IN EFI_HANDLE                Handle
    144   )
    145 {
    146   UINTN                        Index;
    147   EFI_STATUS                   Status;
    148   EFI_DISK_INFO_PROTOCOL       *DiskInfo;
    149   UINT32                       BufferSize;
    150   EFI_ATAPI_IDENTIFY_DATA      IdentifyData;
    151   EFI_SCSI_INQUIRY_DATA        InquiryData;
    152   CHAR16                       *Description;
    153   UINTN                        Length;
    154   CONST UINTN                  ModelNameLength    = 40;
    155   CONST UINTN                  SerialNumberLength = 20;
    156   CHAR8                        *StrPtr;
    157   UINT8                        Temp;
    158 
    159   Description  = NULL;
    160 
    161   Status = gBS->HandleProtocol (
    162                   Handle,
    163                   &gEfiDiskInfoProtocolGuid,
    164                   (VOID **) &DiskInfo
    165                   );
    166   if (EFI_ERROR (Status)) {
    167     return NULL;
    168   }
    169 
    170   if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||
    171       CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
    172     BufferSize   = sizeof (EFI_ATAPI_IDENTIFY_DATA);
    173     Status = DiskInfo->Identify (
    174                          DiskInfo,
    175                          &IdentifyData,
    176                          &BufferSize
    177                          );
    178     if (!EFI_ERROR (Status)) {
    179       Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
    180       ASSERT (Description != NULL);
    181       for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
    182         Description[Index]     = (CHAR16) IdentifyData.ModelName[Index + 1];
    183         Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];
    184       }
    185 
    186       Length = Index;
    187       Description[Length++] = L' ';
    188 
    189       for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
    190         Description[Length + Index]     = (CHAR16) IdentifyData.SerialNo[Index + 1];
    191         Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];
    192       }
    193       Length += Index;
    194       Description[Length++] = L'\0';
    195       ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
    196 
    197       BmEliminateExtraSpaces (Description);
    198     }
    199   } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
    200     BufferSize   = sizeof (EFI_SCSI_INQUIRY_DATA);
    201     Status = DiskInfo->Inquiry (
    202                          DiskInfo,
    203                          &InquiryData,
    204                          &BufferSize
    205                          );
    206     if (!EFI_ERROR (Status)) {
    207       Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
    208       ASSERT (Description != NULL);
    209 
    210       //
    211       // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
    212       // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
    213       // Here combine the vendor identification and product identification to the description.
    214       //
    215       StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
    216       Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
    217       StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
    218       AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1);
    219       StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;
    220 
    221       //
    222       // Add one space at the middle of vendor information and product information.
    223       //
    224       Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';
    225 
    226       StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);
    227       StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';
    228       AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1);
    229 
    230       BmEliminateExtraSpaces (Description);
    231     }
    232   }
    233 
    234   return Description;
    235 }
    236 
    237 /**
    238   Try to get the controller's USB description.
    239 
    240   @param Handle                Controller handle.
    241 
    242   @return  The description string.
    243 **/
    244 CHAR16 *
    245 BmGetUsbDescription (
    246   IN EFI_HANDLE                Handle
    247   )
    248 {
    249   EFI_STATUS                   Status;
    250   EFI_USB_IO_PROTOCOL          *UsbIo;
    251   CHAR16                       NullChar;
    252   CHAR16                       *Manufacturer;
    253   CHAR16                       *Product;
    254   CHAR16                       *SerialNumber;
    255   CHAR16                       *Description;
    256   EFI_USB_DEVICE_DESCRIPTOR    DevDesc;
    257   UINTN                        DescMaxSize;
    258 
    259   Status = gBS->HandleProtocol (
    260                   Handle,
    261                   &gEfiUsbIoProtocolGuid,
    262                   (VOID **) &UsbIo
    263                   );
    264   if (EFI_ERROR (Status)) {
    265     return NULL;
    266   }
    267 
    268   NullChar = L'\0';
    269 
    270   Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
    271   if (EFI_ERROR (Status)) {
    272     return NULL;
    273   }
    274 
    275   Status = UsbIo->UsbGetStringDescriptor (
    276                     UsbIo,
    277                     mBmUsbLangId,
    278                     DevDesc.StrManufacturer,
    279                     &Manufacturer
    280                     );
    281   if (EFI_ERROR (Status)) {
    282     Manufacturer = &NullChar;
    283   }
    284 
    285   Status = UsbIo->UsbGetStringDescriptor (
    286                     UsbIo,
    287                     mBmUsbLangId,
    288                     DevDesc.StrProduct,
    289                     &Product
    290                     );
    291   if (EFI_ERROR (Status)) {
    292     Product = &NullChar;
    293   }
    294 
    295   Status = UsbIo->UsbGetStringDescriptor (
    296                     UsbIo,
    297                     mBmUsbLangId,
    298                     DevDesc.StrSerialNumber,
    299                     &SerialNumber
    300                     );
    301   if (EFI_ERROR (Status)) {
    302     SerialNumber = &NullChar;
    303   }
    304 
    305   if ((Manufacturer == &NullChar) &&
    306       (Product == &NullChar) &&
    307       (SerialNumber == &NullChar)
    308       ) {
    309     return NULL;
    310   }
    311 
    312   DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber);
    313   Description = AllocateZeroPool (DescMaxSize);
    314   ASSERT (Description != NULL);
    315   StrCatS (Description, DescMaxSize/sizeof(CHAR16), Manufacturer);
    316   StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
    317 
    318   StrCatS (Description, DescMaxSize/sizeof(CHAR16), Product);
    319   StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
    320 
    321   StrCatS (Description, DescMaxSize/sizeof(CHAR16), SerialNumber);
    322 
    323   if (Manufacturer != &NullChar) {
    324     FreePool (Manufacturer);
    325   }
    326   if (Product != &NullChar) {
    327     FreePool (Product);
    328   }
    329   if (SerialNumber != &NullChar) {
    330     FreePool (SerialNumber);
    331   }
    332 
    333   BmEliminateExtraSpaces (Description);
    334 
    335   return Description;
    336 }
    337 
    338 /**
    339   Return the description for network boot device.
    340 
    341   @param Handle                Controller handle.
    342 
    343   @return  The description string.
    344 **/
    345 CHAR16 *
    346 BmGetNetworkDescription (
    347   IN EFI_HANDLE                  Handle
    348   )
    349 {
    350   EFI_STATUS                     Status;
    351   EFI_DEVICE_PATH_PROTOCOL       *DevicePath;
    352   MAC_ADDR_DEVICE_PATH           *Mac;
    353   VLAN_DEVICE_PATH               *Vlan;
    354   EFI_DEVICE_PATH_PROTOCOL       *Ip;
    355   EFI_DEVICE_PATH_PROTOCOL       *Uri;
    356   CHAR16                         *Description;
    357   UINTN                          DescriptionSize;
    358 
    359   Status = gBS->OpenProtocol (
    360                   Handle,
    361                   &gEfiLoadFileProtocolGuid,
    362                   NULL,
    363                   gImageHandle,
    364                   Handle,
    365                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
    366                   );
    367   if (EFI_ERROR (Status)) {
    368     return NULL;
    369   }
    370 
    371   Status = gBS->OpenProtocol (
    372                   Handle,
    373                   &gEfiDevicePathProtocolGuid,
    374                   (VOID **) &DevicePath,
    375                   gImageHandle,
    376                   Handle,
    377                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
    378                   );
    379   if (EFI_ERROR (Status) || (DevicePath == NULL)) {
    380     return NULL;
    381   }
    382 
    383   //
    384   // The PXE device path is like:
    385   //   ....../Mac(...)[/Vlan(...)]
    386   //   ....../Mac(...)[/Vlan(...)]/IPv4(...)
    387   //   ....../Mac(...)[/Vlan(...)]/IPv6(...)
    388   //
    389   // The HTTP device path is like:
    390   //   ....../Mac(...)[/Vlan(...)]/IPv4(...)/Uri(...)
    391   //   ....../Mac(...)[/Vlan(...)]/IPv6(...)/Uri(...)
    392   //
    393   while (!IsDevicePathEnd (DevicePath) &&
    394          ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||
    395           (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP))
    396          ) {
    397     DevicePath = NextDevicePathNode (DevicePath);
    398   }
    399 
    400   if (IsDevicePathEnd (DevicePath)) {
    401     return NULL;
    402   }
    403 
    404   Mac = (MAC_ADDR_DEVICE_PATH *) DevicePath;
    405   DevicePath = NextDevicePathNode (DevicePath);
    406 
    407   if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
    408       (DevicePathSubType (DevicePath) == MSG_VLAN_DP)
    409       ) {
    410     Vlan = (VLAN_DEVICE_PATH *) DevicePath;
    411     DevicePath = NextDevicePathNode (DevicePath);
    412   } else {
    413     Vlan = NULL;
    414   }
    415 
    416   if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
    417       ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||
    418        (DevicePathSubType (DevicePath) == MSG_IPv6_DP))
    419       ) {
    420     Ip = DevicePath;
    421     DevicePath = NextDevicePathNode (DevicePath);
    422   } else {
    423     Ip = NULL;
    424   }
    425 
    426   if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
    427       (DevicePathSubType (DevicePath) == MSG_URI_DP)
    428       ) {
    429     Uri = DevicePath;
    430     DevicePath = NextDevicePathNode (DevicePath);
    431   } else {
    432     Uri = NULL;
    433   }
    434 
    435   //
    436   // Build description like below:
    437   //   "PXEv6 (MAC:112233445566 VLAN1)"
    438   //   "HTTPv4 (MAC:112233445566)"
    439   //
    440   DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");
    441   Description     = AllocatePool (DescriptionSize);
    442   ASSERT (Description != NULL);
    443   UnicodeSPrint (
    444     Description, DescriptionSize,
    445     (Vlan == NULL) ?
    446     L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :
    447     L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",
    448     (Uri == NULL) ? L"PXE" : L"HTTP",
    449     ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,
    450     Mac->MacAddress.Addr[0], Mac->MacAddress.Addr[1], Mac->MacAddress.Addr[2],
    451     Mac->MacAddress.Addr[3], Mac->MacAddress.Addr[4], Mac->MacAddress.Addr[5],
    452     (Vlan == NULL) ? 0 : Vlan->VlanId
    453     );
    454   return Description;
    455 }
    456 
    457 /**
    458   Return the boot description for LoadFile
    459 
    460   @param Handle                Controller handle.
    461 
    462   @return  The description string.
    463 **/
    464 CHAR16 *
    465 BmGetLoadFileDescription (
    466   IN EFI_HANDLE                  Handle
    467   )
    468 {
    469   EFI_STATUS                            Status;
    470   EFI_DEVICE_PATH_PROTOCOL              *FilePath;
    471   EFI_DEVICE_PATH_PROTOCOL              *DevicePathNode;
    472   CHAR16                                *Description;
    473   EFI_LOAD_FILE_PROTOCOL                *LoadFile;
    474 
    475   Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);
    476   if (EFI_ERROR (Status)) {
    477     return NULL;
    478   }
    479 
    480   //
    481   // Get the file name
    482   //
    483   Description = NULL;
    484   Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath);
    485   if (!EFI_ERROR (Status)) {
    486     DevicePathNode = FilePath;
    487     while (!IsDevicePathEnd (DevicePathNode)) {
    488       if (DevicePathNode->Type == MEDIA_DEVICE_PATH && DevicePathNode->SubType == MEDIA_FILEPATH_DP) {
    489         Description = (CHAR16 *)(DevicePathNode + 1);
    490         break;
    491       }
    492       DevicePathNode = NextDevicePathNode (DevicePathNode);
    493     }
    494   }
    495 
    496   if (Description != NULL) {
    497     return AllocateCopyPool (StrSize (Description), Description);
    498   }
    499 
    500   return NULL;
    501 }
    502 
    503 /**
    504   Return the boot description for the controller based on the type.
    505 
    506   @param Handle                Controller handle.
    507 
    508   @return  The description string.
    509 **/
    510 CHAR16 *
    511 BmGetMiscDescription (
    512   IN EFI_HANDLE                  Handle
    513   )
    514 {
    515   EFI_STATUS                      Status;
    516   CHAR16                          *Description;
    517   EFI_BLOCK_IO_PROTOCOL           *BlockIo;
    518   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
    519 
    520   switch (BmDevicePathType (DevicePathFromHandle (Handle))) {
    521   case BmAcpiFloppyBoot:
    522     Description = L"Floppy";
    523     break;
    524 
    525   case BmMessageAtapiBoot:
    526   case BmMessageSataBoot:
    527     Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
    528     ASSERT_EFI_ERROR (Status);
    529     //
    530     // Assume a removable SATA device should be the DVD/CD device
    531     //
    532     Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
    533     break;
    534 
    535   case BmMessageUsbBoot:
    536     Description = L"USB Device";
    537     break;
    538 
    539   case BmMessageScsiBoot:
    540     Description = L"SCSI Device";
    541     break;
    542 
    543   case BmHardwareDeviceBoot:
    544     Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
    545     if (!EFI_ERROR (Status)) {
    546       Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
    547     } else {
    548       Description = L"Misc Device";
    549     }
    550     break;
    551 
    552   default:
    553     Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);
    554     if (!EFI_ERROR (Status)) {
    555       Description = L"Non-Block Boot Device";
    556     } else {
    557       Description = L"Misc Device";
    558     }
    559     break;
    560   }
    561 
    562   return AllocateCopyPool (StrSize (Description), Description);
    563 }
    564 
    565 /**
    566   Register the platform provided boot description handler.
    567 
    568   @param Handler  The platform provided boot description handler
    569 
    570   @retval EFI_SUCCESS          The handler was registered successfully.
    571   @retval EFI_ALREADY_STARTED  The handler was already registered.
    572   @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
    573 **/
    574 EFI_STATUS
    575 EFIAPI
    576 EfiBootManagerRegisterBootDescriptionHandler (
    577   IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER  Handler
    578   )
    579 {
    580   LIST_ENTRY                                    *Link;
    581   BM_BOOT_DESCRIPTION_ENTRY                    *Entry;
    582 
    583   for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
    584       ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
    585       ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
    586       ) {
    587     Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
    588     if (Entry->Handler == Handler) {
    589       return EFI_ALREADY_STARTED;
    590     }
    591   }
    592 
    593   Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));
    594   if (Entry == NULL) {
    595     return EFI_OUT_OF_RESOURCES;
    596   }
    597 
    598   Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;
    599   Entry->Handler   = Handler;
    600   InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);
    601   return EFI_SUCCESS;
    602 }
    603 
    604 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {
    605   BmGetUsbDescription,
    606   BmGetDescriptionFromDiskInfo,
    607   BmGetNetworkDescription,
    608   BmGetLoadFileDescription,
    609   BmGetMiscDescription
    610 };
    611 
    612 /**
    613   Return the boot description for the controller.
    614 
    615   @param Handle                Controller handle.
    616 
    617   @return  The description string.
    618 **/
    619 CHAR16 *
    620 BmGetBootDescription (
    621   IN EFI_HANDLE                  Handle
    622   )
    623 {
    624   LIST_ENTRY                     *Link;
    625   BM_BOOT_DESCRIPTION_ENTRY      *Entry;
    626   CHAR16                         *Description;
    627   CHAR16                         *DefaultDescription;
    628   CHAR16                         *Temp;
    629   UINTN                          Index;
    630 
    631   //
    632   // Firstly get the default boot description
    633   //
    634   DefaultDescription = NULL;
    635   for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) {
    636     DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);
    637     if (DefaultDescription != NULL) {
    638       //
    639       // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
    640       // ONLY for core provided boot description handler.
    641       //
    642       Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));
    643       ASSERT (Temp != NULL);
    644       StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);
    645       StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);
    646       FreePool (DefaultDescription);
    647       DefaultDescription = Temp;
    648       break;
    649     }
    650   }
    651   ASSERT (DefaultDescription != NULL);
    652 
    653   //
    654   // Secondly query platform for the better boot description
    655   //
    656   for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
    657       ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
    658       ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
    659       ) {
    660     Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
    661     Description = Entry->Handler (Handle, DefaultDescription);
    662     if (Description != NULL) {
    663       FreePool (DefaultDescription);
    664       return Description;
    665     }
    666   }
    667 
    668   return DefaultDescription;
    669 }
    670 
    671 /**
    672   Enumerate all boot option descriptions and append " 2"/" 3"/... to make
    673   unique description.
    674 
    675   @param BootOptions            Array of boot options.
    676   @param BootOptionCount        Count of boot options.
    677 **/
    678 VOID
    679 BmMakeBootOptionDescriptionUnique (
    680   EFI_BOOT_MANAGER_LOAD_OPTION         *BootOptions,
    681   UINTN                                BootOptionCount
    682   )
    683 {
    684   UINTN                                Base;
    685   UINTN                                Index;
    686   UINTN                                DescriptionSize;
    687   UINTN                                MaxSuffixSize;
    688   BOOLEAN                              *Visited;
    689   UINTN                                MatchCount;
    690 
    691   if (BootOptionCount == 0) {
    692     return;
    693   }
    694 
    695   //
    696   // Calculate the maximum buffer size for the number suffix.
    697   // The initial sizeof (CHAR16) is for the blank space before the number.
    698   //
    699   MaxSuffixSize = sizeof (CHAR16);
    700   for (Index = BootOptionCount; Index != 0; Index = Index / 10) {
    701     MaxSuffixSize += sizeof (CHAR16);
    702   }
    703 
    704   Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);
    705   ASSERT (Visited != NULL);
    706 
    707   for (Base = 0; Base < BootOptionCount; Base++) {
    708     if (!Visited[Base]) {
    709       MatchCount      = 1;
    710       Visited[Base]   = TRUE;
    711       DescriptionSize = StrSize (BootOptions[Base].Description);
    712       for (Index = Base + 1; Index < BootOptionCount; Index++) {
    713         if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {
    714           Visited[Index] = TRUE;
    715           MatchCount++;
    716           FreePool (BootOptions[Index].Description);
    717           BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);
    718           UnicodeSPrint (
    719             BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,
    720             L"%s %d",
    721             BootOptions[Base].Description, MatchCount
    722             );
    723         }
    724       }
    725     }
    726   }
    727 
    728   FreePool (Visited);
    729 }
    730