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
BmDevicePathType(IN EFI_DEVICE_PATH_PROTOCOL * DevicePath)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
BmEliminateExtraSpaces(IN CHAR16 * Str)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 *
BmGetDescriptionFromDiskInfo(IN EFI_HANDLE Handle)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 *
BmGetUsbDescription(IN EFI_HANDLE Handle)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 *
BmGetNetworkDescription(IN EFI_HANDLE Handle)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 *
BmGetLoadFileDescription(IN EFI_HANDLE Handle)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 *
BmGetMiscDescription(IN EFI_HANDLE Handle)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
EfiBootManagerRegisterBootDescriptionHandler(IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler)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 *
BmGetBootDescription(IN EFI_HANDLE Handle)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
BmMakeBootOptionDescriptionUnique(EFI_BOOT_MANAGER_LOAD_OPTION * BootOptions,UINTN BootOptionCount)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