2 Library functions which relate with boot option description.
4 Copyright (c) 2011 - 2017, 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
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.
16 #include "InternalBm.h"
18 #define VENDOR_IDENTIFICATION_OFFSET 3
19 #define VENDOR_IDENTIFICATION_LENGTH 8
20 #define PRODUCT_IDENTIFICATION_OFFSET 11
21 #define PRODUCT_IDENTIFICATION_LENGTH 16
23 CONST UINT16 mBmUsbLangId
= 0x0409; // English
24 CHAR16 mBmUefiPrefix
[] = L
"UEFI ";
26 LIST_ENTRY mPlatformBootDescriptionHandlers
= INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers
);
29 For a bootable Device path, return its boot type.
31 @param DevicePath The bootable device Path to check
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.
48 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
51 EFI_DEVICE_PATH_PROTOCOL
*Node
;
52 EFI_DEVICE_PATH_PROTOCOL
*NextNode
;
54 ASSERT (DevicePath
!= NULL
);
56 for (Node
= DevicePath
; !IsDevicePathEndType (Node
); Node
= NextDevicePathNode (Node
)) {
57 switch (DevicePathType (Node
)) {
59 case ACPI_DEVICE_PATH
:
60 if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH
*) Node
)->HID
) == 0x0604) {
61 return BmAcpiFloppyBoot
;
65 case HARDWARE_DEVICE_PATH
:
66 if (DevicePathSubType (Node
) == HW_CONTROLLER_DP
) {
67 return BmHardwareDeviceBoot
;
71 case MESSAGING_DEVICE_PATH
:
73 // Skip LUN device node
77 NextNode
= NextDevicePathNode (NextNode
);
79 (DevicePathType (NextNode
) == MESSAGING_DEVICE_PATH
) &&
80 (DevicePathSubType(NextNode
) == MSG_DEVICE_LOGICAL_UNIT_DP
)
84 // If the device path not only point to driver device, it is not a messaging device path,
86 if (!IsDevicePathEndType (NextNode
)) {
90 switch (DevicePathSubType (Node
)) {
92 return BmMessageAtapiBoot
;
96 return BmMessageSataBoot
;
100 return BmMessageUsbBoot
;
104 return BmMessageScsiBoot
;
114 Eliminate the extra spaces in the Str to one space.
116 @param Str Input string info.
119 BmEliminateExtraSpaces (
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
];
131 Str
[ActualIndex
] = L
'\0';
135 Try to get the controller's ATA/ATAPI description.
137 @param Handle Controller handle.
139 @return The description string.
142 BmGetDescriptionFromDiskInfo (
148 EFI_DISK_INFO_PROTOCOL
*DiskInfo
;
150 EFI_ATAPI_IDENTIFY_DATA IdentifyData
;
151 EFI_SCSI_INQUIRY_DATA InquiryData
;
154 CONST UINTN ModelNameLength
= 40;
155 CONST UINTN SerialNumberLength
= 20;
158 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
162 Status
= gBS
->HandleProtocol (
164 &gEfiDiskInfoProtocolGuid
,
167 if (EFI_ERROR (Status
)) {
171 if (CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoAhciInterfaceGuid
) ||
172 CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoIdeInterfaceGuid
)) {
173 BufferSize
= sizeof (EFI_ATAPI_IDENTIFY_DATA
);
174 Status
= DiskInfo
->Identify (
179 if (!EFI_ERROR (Status
)) {
180 Description
= AllocateZeroPool ((ModelNameLength
+ SerialNumberLength
+ 2) * sizeof (CHAR16
));
181 ASSERT (Description
!= NULL
);
182 for (Index
= 0; Index
+ 1 < ModelNameLength
; Index
+= 2) {
183 Description
[Index
] = (CHAR16
) IdentifyData
.ModelName
[Index
+ 1];
184 Description
[Index
+ 1] = (CHAR16
) IdentifyData
.ModelName
[Index
];
188 Description
[Length
++] = L
' ';
190 for (Index
= 0; Index
+ 1 < SerialNumberLength
; Index
+= 2) {
191 Description
[Length
+ Index
] = (CHAR16
) IdentifyData
.SerialNo
[Index
+ 1];
192 Description
[Length
+ Index
+ 1] = (CHAR16
) IdentifyData
.SerialNo
[Index
];
195 Description
[Length
++] = L
'\0';
196 ASSERT (Length
== ModelNameLength
+ SerialNumberLength
+ 2);
198 BmEliminateExtraSpaces (Description
);
200 } else if (CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoScsiInterfaceGuid
)) {
201 BufferSize
= sizeof (EFI_SCSI_INQUIRY_DATA
);
202 Status
= DiskInfo
->Inquiry (
207 if (!EFI_ERROR (Status
)) {
208 Description
= AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH
+ PRODUCT_IDENTIFICATION_LENGTH
+ 2) * sizeof (CHAR16
));
209 ASSERT (Description
!= NULL
);
212 // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
213 // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
214 // Here combine the vendor identification and product identification to the description.
216 StrPtr
= (CHAR8
*) (&InquiryData
.Reserved_5_95
[VENDOR_IDENTIFICATION_OFFSET
]);
217 Temp
= StrPtr
[VENDOR_IDENTIFICATION_LENGTH
];
218 StrPtr
[VENDOR_IDENTIFICATION_LENGTH
] = '\0';
219 AsciiStrToUnicodeStrS (StrPtr
, Description
, VENDOR_IDENTIFICATION_LENGTH
+ 1);
220 StrPtr
[VENDOR_IDENTIFICATION_LENGTH
] = Temp
;
223 // Add one space at the middle of vendor information and product information.
225 Description
[VENDOR_IDENTIFICATION_LENGTH
] = L
' ';
227 StrPtr
= (CHAR8
*) (&InquiryData
.Reserved_5_95
[PRODUCT_IDENTIFICATION_OFFSET
]);
228 StrPtr
[PRODUCT_IDENTIFICATION_LENGTH
] = '\0';
229 AsciiStrToUnicodeStrS (StrPtr
, Description
+ VENDOR_IDENTIFICATION_LENGTH
+ 1, PRODUCT_IDENTIFICATION_LENGTH
+ 1);
231 BmEliminateExtraSpaces (Description
);
233 } else if (CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoSdMmcInterfaceGuid
)) {
234 DevicePath
= DevicePathFromHandle (Handle
);
235 if (DevicePath
== NULL
) {
239 while (!IsDevicePathEnd (DevicePath
) && (DevicePathType (DevicePath
) != MESSAGING_DEVICE_PATH
)) {
240 DevicePath
= NextDevicePathNode (DevicePath
);
242 if (IsDevicePathEnd (DevicePath
)) {
246 if (DevicePathSubType (DevicePath
) == MSG_SD_DP
) {
247 Description
= L
"SD Device";
248 } else if (DevicePathSubType (DevicePath
) == MSG_EMMC_DP
) {
249 Description
= L
"eMMC Device";
254 Description
= AllocateCopyPool (StrSize (Description
), Description
);
261 Try to get the controller's USB description.
263 @param Handle Controller handle.
265 @return The description string.
268 BmGetUsbDescription (
273 EFI_USB_IO_PROTOCOL
*UsbIo
;
275 CHAR16
*Manufacturer
;
277 CHAR16
*SerialNumber
;
279 EFI_USB_DEVICE_DESCRIPTOR DevDesc
;
282 Status
= gBS
->HandleProtocol (
284 &gEfiUsbIoProtocolGuid
,
287 if (EFI_ERROR (Status
)) {
293 Status
= UsbIo
->UsbGetDeviceDescriptor (UsbIo
, &DevDesc
);
294 if (EFI_ERROR (Status
)) {
298 Status
= UsbIo
->UsbGetStringDescriptor (
301 DevDesc
.StrManufacturer
,
304 if (EFI_ERROR (Status
)) {
305 Manufacturer
= &NullChar
;
308 Status
= UsbIo
->UsbGetStringDescriptor (
314 if (EFI_ERROR (Status
)) {
318 Status
= UsbIo
->UsbGetStringDescriptor (
321 DevDesc
.StrSerialNumber
,
324 if (EFI_ERROR (Status
)) {
325 SerialNumber
= &NullChar
;
328 if ((Manufacturer
== &NullChar
) &&
329 (Product
== &NullChar
) &&
330 (SerialNumber
== &NullChar
)
335 DescMaxSize
= StrSize (Manufacturer
) + StrSize (Product
) + StrSize (SerialNumber
);
336 Description
= AllocateZeroPool (DescMaxSize
);
337 ASSERT (Description
!= NULL
);
338 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), Manufacturer
);
339 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), L
" ");
341 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), Product
);
342 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), L
" ");
344 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), SerialNumber
);
346 if (Manufacturer
!= &NullChar
) {
347 FreePool (Manufacturer
);
349 if (Product
!= &NullChar
) {
352 if (SerialNumber
!= &NullChar
) {
353 FreePool (SerialNumber
);
356 BmEliminateExtraSpaces (Description
);
362 Return the description for network boot device.
364 @param Handle Controller handle.
366 @return The description string.
369 BmGetNetworkDescription (
374 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
375 MAC_ADDR_DEVICE_PATH
*Mac
;
376 VLAN_DEVICE_PATH
*Vlan
;
377 EFI_DEVICE_PATH_PROTOCOL
*Ip
;
378 EFI_DEVICE_PATH_PROTOCOL
*Uri
;
380 UINTN DescriptionSize
;
382 Status
= gBS
->OpenProtocol (
384 &gEfiLoadFileProtocolGuid
,
388 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
390 if (EFI_ERROR (Status
)) {
394 Status
= gBS
->OpenProtocol (
396 &gEfiDevicePathProtocolGuid
,
397 (VOID
**) &DevicePath
,
400 EFI_OPEN_PROTOCOL_GET_PROTOCOL
402 if (EFI_ERROR (Status
) || (DevicePath
== NULL
)) {
407 // The PXE device path is like:
408 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]
409 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)
410 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)
412 // The HTTP device path is like:
413 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)
414 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...)
416 while (!IsDevicePathEnd (DevicePath
) &&
417 ((DevicePathType (DevicePath
) != MESSAGING_DEVICE_PATH
) ||
418 (DevicePathSubType (DevicePath
) != MSG_MAC_ADDR_DP
))
420 DevicePath
= NextDevicePathNode (DevicePath
);
423 if (IsDevicePathEnd (DevicePath
)) {
427 Mac
= (MAC_ADDR_DEVICE_PATH
*) DevicePath
;
428 DevicePath
= NextDevicePathNode (DevicePath
);
431 // Locate the optional Vlan node
433 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
434 (DevicePathSubType (DevicePath
) == MSG_VLAN_DP
)
436 Vlan
= (VLAN_DEVICE_PATH
*) DevicePath
;
437 DevicePath
= NextDevicePathNode (DevicePath
);
443 // Skip the optional Wi-Fi node
445 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
446 (DevicePathSubType (DevicePath
) == MSG_WIFI_DP
)
448 DevicePath
= NextDevicePathNode (DevicePath
);
452 // Locate the IP node
454 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
455 ((DevicePathSubType (DevicePath
) == MSG_IPv4_DP
) ||
456 (DevicePathSubType (DevicePath
) == MSG_IPv6_DP
))
459 DevicePath
= NextDevicePathNode (DevicePath
);
465 // Skip the optional DNS node
467 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
468 (DevicePathSubType (DevicePath
) == MSG_DNS_DP
)
470 DevicePath
= NextDevicePathNode (DevicePath
);
474 // Locate the URI node
476 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
477 (DevicePathSubType (DevicePath
) == MSG_URI_DP
)
480 DevicePath
= NextDevicePathNode (DevicePath
);
486 // Build description like below:
487 // "PXEv6 (MAC:112233445566 VLAN1)"
488 // "HTTPv4 (MAC:112233445566)"
490 DescriptionSize
= sizeof (L
"HTTPv6 (MAC:112233445566 VLAN65535)");
491 Description
= AllocatePool (DescriptionSize
);
492 ASSERT (Description
!= NULL
);
494 Description
, DescriptionSize
,
496 L
"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :
497 L
"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",
498 (Uri
== NULL
) ? L
"PXE" : L
"HTTP",
499 ((Ip
== NULL
) || (DevicePathSubType (Ip
) == MSG_IPv4_DP
)) ? 4 : 6,
500 Mac
->MacAddress
.Addr
[0], Mac
->MacAddress
.Addr
[1], Mac
->MacAddress
.Addr
[2],
501 Mac
->MacAddress
.Addr
[3], Mac
->MacAddress
.Addr
[4], Mac
->MacAddress
.Addr
[5],
502 (Vlan
== NULL
) ? 0 : Vlan
->VlanId
508 Return the boot description for LoadFile
510 @param Handle Controller handle.
512 @return The description string.
515 BmGetLoadFileDescription (
520 EFI_DEVICE_PATH_PROTOCOL
*FilePath
;
521 EFI_DEVICE_PATH_PROTOCOL
*DevicePathNode
;
523 EFI_LOAD_FILE_PROTOCOL
*LoadFile
;
525 Status
= gBS
->HandleProtocol (Handle
, &gEfiLoadFileProtocolGuid
, (VOID
**)&LoadFile
);
526 if (EFI_ERROR (Status
)) {
534 Status
= gBS
->HandleProtocol (Handle
, &gEfiDevicePathProtocolGuid
, (VOID
**)&FilePath
);
535 if (!EFI_ERROR (Status
)) {
536 DevicePathNode
= FilePath
;
537 while (!IsDevicePathEnd (DevicePathNode
)) {
538 if (DevicePathNode
->Type
== MEDIA_DEVICE_PATH
&& DevicePathNode
->SubType
== MEDIA_FILEPATH_DP
) {
539 Description
= (CHAR16
*)(DevicePathNode
+ 1);
542 DevicePathNode
= NextDevicePathNode (DevicePathNode
);
546 if (Description
!= NULL
) {
547 return AllocateCopyPool (StrSize (Description
), Description
);
554 Return the boot description for NVME boot device.
556 @param Handle Controller handle.
558 @return The description string.
561 BmGetNvmeDescription (
566 EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL
*NvmePassthru
;
567 EFI_DEV_PATH_PTR DevicePath
;
568 EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket
;
569 EFI_NVM_EXPRESS_COMMAND Command
;
570 EFI_NVM_EXPRESS_COMPLETION Completion
;
571 NVME_ADMIN_CONTROLLER_DATA ControllerData
;
576 Status
= gBS
->HandleProtocol (Handle
, &gEfiDevicePathProtocolGuid
, (VOID
**) &DevicePath
.DevPath
);
577 if (EFI_ERROR (Status
)) {
581 Status
= gBS
->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid
, &DevicePath
.DevPath
, &Handle
);
582 if (EFI_ERROR (Status
) ||
583 (DevicePathType (DevicePath
.DevPath
) != MESSAGING_DEVICE_PATH
) ||
584 (DevicePathSubType (DevicePath
.DevPath
) != MSG_NVME_NAMESPACE_DP
)) {
586 // Do not return description when the Handle is not a child of NVME controller.
592 // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number.
594 Status
= gBS
->HandleProtocol (Handle
, &gEfiNvmExpressPassThruProtocolGuid
, (VOID
**) &NvmePassthru
);
595 ASSERT_EFI_ERROR (Status
);
597 ZeroMem (&CommandPacket
, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET
));
598 ZeroMem (&Command
, sizeof(EFI_NVM_EXPRESS_COMMAND
));
599 ZeroMem (&Completion
, sizeof(EFI_NVM_EXPRESS_COMPLETION
));
601 Command
.Cdw0
.Opcode
= NVME_ADMIN_IDENTIFY_CMD
;
603 // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
604 // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
607 CommandPacket
.NvmeCmd
= &Command
;
608 CommandPacket
.NvmeCompletion
= &Completion
;
609 CommandPacket
.TransferBuffer
= &ControllerData
;
610 CommandPacket
.TransferLength
= sizeof (ControllerData
);
611 CommandPacket
.CommandTimeout
= EFI_TIMER_PERIOD_SECONDS (5);
612 CommandPacket
.QueueType
= NVME_ADMIN_QUEUE
;
614 // Set bit 0 (Cns bit) to 1 to identify a controller
617 Command
.Flags
= CDW10_VALID
;
619 Status
= NvmePassthru
->PassThru (
625 if (EFI_ERROR (Status
)) {
629 Description
= AllocateZeroPool (
630 (ARRAY_SIZE (ControllerData
.Mn
) + 1
631 + ARRAY_SIZE (ControllerData
.Sn
) + 1
632 + MAXIMUM_VALUE_CHARACTERS
+ 1
633 ) * sizeof (CHAR16
));
634 if (Description
!= NULL
) {
636 for (Index
= 0; Index
< ARRAY_SIZE (ControllerData
.Mn
); Index
++) {
637 *(Char
++) = (CHAR16
) ControllerData
.Mn
[Index
];
640 for (Index
= 0; Index
< ARRAY_SIZE (ControllerData
.Sn
); Index
++) {
641 *(Char
++) = (CHAR16
) ControllerData
.Sn
[Index
];
644 UnicodeValueToStringS (
645 Char
, sizeof (CHAR16
) * (MAXIMUM_VALUE_CHARACTERS
+ 1),
646 0, DevicePath
.NvmeNamespace
->NamespaceId
, 0
648 BmEliminateExtraSpaces (Description
);
655 Return the boot description for the controller based on the type.
657 @param Handle Controller handle.
659 @return The description string.
662 BmGetMiscDescription (
668 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
669 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
671 switch (BmDevicePathType (DevicePathFromHandle (Handle
))) {
672 case BmAcpiFloppyBoot
:
673 Description
= L
"Floppy";
676 case BmMessageAtapiBoot
:
677 case BmMessageSataBoot
:
678 Status
= gBS
->HandleProtocol (Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**) &BlockIo
);
679 ASSERT_EFI_ERROR (Status
);
681 // Assume a removable SATA device should be the DVD/CD device
683 Description
= BlockIo
->Media
->RemovableMedia
? L
"DVD/CDROM" : L
"Hard Drive";
686 case BmMessageUsbBoot
:
687 Description
= L
"USB Device";
690 case BmMessageScsiBoot
:
691 Description
= L
"SCSI Device";
694 case BmHardwareDeviceBoot
:
695 Status
= gBS
->HandleProtocol (Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**) &BlockIo
);
696 if (!EFI_ERROR (Status
)) {
697 Description
= BlockIo
->Media
->RemovableMedia
? L
"Removable Disk" : L
"Hard Drive";
699 Description
= L
"Misc Device";
704 Status
= gBS
->HandleProtocol (Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**) &Fs
);
705 if (!EFI_ERROR (Status
)) {
706 Description
= L
"Non-Block Boot Device";
708 Description
= L
"Misc Device";
713 return AllocateCopyPool (StrSize (Description
), Description
);
717 Register the platform provided boot description handler.
719 @param Handler The platform provided boot description handler
721 @retval EFI_SUCCESS The handler was registered successfully.
722 @retval EFI_ALREADY_STARTED The handler was already registered.
723 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
727 EfiBootManagerRegisterBootDescriptionHandler (
728 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
732 BM_BOOT_DESCRIPTION_ENTRY
*Entry
;
734 for ( Link
= GetFirstNode (&mPlatformBootDescriptionHandlers
)
735 ; !IsNull (&mPlatformBootDescriptionHandlers
, Link
)
736 ; Link
= GetNextNode (&mPlatformBootDescriptionHandlers
, Link
)
738 Entry
= CR (Link
, BM_BOOT_DESCRIPTION_ENTRY
, Link
, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
);
739 if (Entry
->Handler
== Handler
) {
740 return EFI_ALREADY_STARTED
;
744 Entry
= AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY
));
746 return EFI_OUT_OF_RESOURCES
;
749 Entry
->Signature
= BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
;
750 Entry
->Handler
= Handler
;
751 InsertTailList (&mPlatformBootDescriptionHandlers
, &Entry
->Link
);
755 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers
[] = {
757 BmGetDescriptionFromDiskInfo
,
758 BmGetNetworkDescription
,
759 BmGetLoadFileDescription
,
760 BmGetNvmeDescription
,
765 Return the boot description for the controller.
767 @param Handle Controller handle.
769 @return The description string.
772 BmGetBootDescription (
777 BM_BOOT_DESCRIPTION_ENTRY
*Entry
;
779 CHAR16
*DefaultDescription
;
784 // Firstly get the default boot description
786 DefaultDescription
= NULL
;
787 for (Index
= 0; Index
< ARRAY_SIZE (mBmBootDescriptionHandlers
); Index
++) {
788 DefaultDescription
= mBmBootDescriptionHandlers
[Index
] (Handle
);
789 if (DefaultDescription
!= NULL
) {
791 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
792 // ONLY for core provided boot description handler.
794 Temp
= AllocatePool (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
));
795 ASSERT (Temp
!= NULL
);
796 StrCpyS (Temp
, (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
)) / sizeof (CHAR16
), mBmUefiPrefix
);
797 StrCatS (Temp
, (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
)) / sizeof (CHAR16
), DefaultDescription
);
798 FreePool (DefaultDescription
);
799 DefaultDescription
= Temp
;
803 ASSERT (DefaultDescription
!= NULL
);
806 // Secondly query platform for the better boot description
808 for ( Link
= GetFirstNode (&mPlatformBootDescriptionHandlers
)
809 ; !IsNull (&mPlatformBootDescriptionHandlers
, Link
)
810 ; Link
= GetNextNode (&mPlatformBootDescriptionHandlers
, Link
)
812 Entry
= CR (Link
, BM_BOOT_DESCRIPTION_ENTRY
, Link
, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
);
813 Description
= Entry
->Handler (Handle
, DefaultDescription
);
814 if (Description
!= NULL
) {
815 FreePool (DefaultDescription
);
820 return DefaultDescription
;
824 Enumerate all boot option descriptions and append " 2"/" 3"/... to make
827 @param BootOptions Array of boot options.
828 @param BootOptionCount Count of boot options.
831 BmMakeBootOptionDescriptionUnique (
832 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
,
833 UINTN BootOptionCount
838 UINTN DescriptionSize
;
843 if (BootOptionCount
== 0) {
848 // Calculate the maximum buffer size for the number suffix.
849 // The initial sizeof (CHAR16) is for the blank space before the number.
851 MaxSuffixSize
= sizeof (CHAR16
);
852 for (Index
= BootOptionCount
; Index
!= 0; Index
= Index
/ 10) {
853 MaxSuffixSize
+= sizeof (CHAR16
);
856 Visited
= AllocateZeroPool (sizeof (BOOLEAN
) * BootOptionCount
);
857 ASSERT (Visited
!= NULL
);
859 for (Base
= 0; Base
< BootOptionCount
; Base
++) {
860 if (!Visited
[Base
]) {
862 Visited
[Base
] = TRUE
;
863 DescriptionSize
= StrSize (BootOptions
[Base
].Description
);
864 for (Index
= Base
+ 1; Index
< BootOptionCount
; Index
++) {
865 if (!Visited
[Index
] && StrCmp (BootOptions
[Base
].Description
, BootOptions
[Index
].Description
) == 0) {
866 Visited
[Index
] = TRUE
;
868 FreePool (BootOptions
[Index
].Description
);
869 BootOptions
[Index
].Description
= AllocatePool (DescriptionSize
+ MaxSuffixSize
);
871 BootOptions
[Index
].Description
, DescriptionSize
+ MaxSuffixSize
,
873 BootOptions
[Base
].Description
, MatchCount