2 Library functions which relate with boot option description.
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
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;
161 Status
= gBS
->HandleProtocol (
163 &gEfiDiskInfoProtocolGuid
,
166 if (EFI_ERROR (Status
)) {
170 if (CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoAhciInterfaceGuid
) ||
171 CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoIdeInterfaceGuid
)) {
172 BufferSize
= sizeof (EFI_ATAPI_IDENTIFY_DATA
);
173 Status
= DiskInfo
->Identify (
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
];
187 Description
[Length
++] = L
' ';
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
];
194 Description
[Length
++] = L
'\0';
195 ASSERT (Length
== ModelNameLength
+ SerialNumberLength
+ 2);
197 BmEliminateExtraSpaces (Description
);
199 } else if (CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoScsiInterfaceGuid
)) {
200 BufferSize
= sizeof (EFI_SCSI_INQUIRY_DATA
);
201 Status
= DiskInfo
->Inquiry (
206 if (!EFI_ERROR (Status
)) {
207 Description
= AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH
+ PRODUCT_IDENTIFICATION_LENGTH
+ 2) * sizeof (CHAR16
));
208 ASSERT (Description
!= NULL
);
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.
215 StrPtr
= (CHAR8
*) (&InquiryData
.Reserved_5_95
[VENDOR_IDENTIFICATION_OFFSET
]);
216 Temp
= StrPtr
[VENDOR_IDENTIFICATION_LENGTH
];
217 StrPtr
[VENDOR_IDENTIFICATION_LENGTH
] = '\0';
218 AsciiStrToUnicodeStr (StrPtr
, Description
);
219 StrPtr
[VENDOR_IDENTIFICATION_LENGTH
] = Temp
;
222 // Add one space at the middle of vendor information and product information.
224 Description
[VENDOR_IDENTIFICATION_LENGTH
] = L
' ';
226 StrPtr
= (CHAR8
*) (&InquiryData
.Reserved_5_95
[PRODUCT_IDENTIFICATION_OFFSET
]);
227 StrPtr
[PRODUCT_IDENTIFICATION_LENGTH
] = '\0';
228 AsciiStrToUnicodeStr (StrPtr
, Description
+ VENDOR_IDENTIFICATION_LENGTH
+ 1);
230 BmEliminateExtraSpaces (Description
);
238 Try to get the controller's USB description.
240 @param Handle Controller handle.
242 @return The description string.
245 BmGetUsbDescription (
250 EFI_USB_IO_PROTOCOL
*UsbIo
;
252 CHAR16
*Manufacturer
;
254 CHAR16
*SerialNumber
;
256 EFI_USB_DEVICE_DESCRIPTOR DevDesc
;
259 Status
= gBS
->HandleProtocol (
261 &gEfiUsbIoProtocolGuid
,
264 if (EFI_ERROR (Status
)) {
270 Status
= UsbIo
->UsbGetDeviceDescriptor (UsbIo
, &DevDesc
);
271 if (EFI_ERROR (Status
)) {
275 Status
= UsbIo
->UsbGetStringDescriptor (
278 DevDesc
.StrManufacturer
,
281 if (EFI_ERROR (Status
)) {
282 Manufacturer
= &NullChar
;
285 Status
= UsbIo
->UsbGetStringDescriptor (
291 if (EFI_ERROR (Status
)) {
295 Status
= UsbIo
->UsbGetStringDescriptor (
298 DevDesc
.StrSerialNumber
,
301 if (EFI_ERROR (Status
)) {
302 SerialNumber
= &NullChar
;
305 if ((Manufacturer
== &NullChar
) &&
306 (Product
== &NullChar
) &&
307 (SerialNumber
== &NullChar
)
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
" ");
318 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), Product
);
319 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), L
" ");
321 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), SerialNumber
);
323 if (Manufacturer
!= &NullChar
) {
324 FreePool (Manufacturer
);
326 if (Product
!= &NullChar
) {
329 if (SerialNumber
!= &NullChar
) {
330 FreePool (SerialNumber
);
333 BmEliminateExtraSpaces (Description
);
339 Return the description for network boot device.
341 @param Handle Controller handle.
343 @return The description string.
346 BmGetNetworkDescription (
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
;
357 UINTN DescriptionSize
;
359 Status
= gBS
->OpenProtocol (
361 &gEfiLoadFileProtocolGuid
,
365 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
367 if (EFI_ERROR (Status
)) {
371 Status
= gBS
->OpenProtocol (
373 &gEfiDevicePathProtocolGuid
,
374 (VOID
**) &DevicePath
,
377 EFI_OPEN_PROTOCOL_GET_PROTOCOL
379 if (EFI_ERROR (Status
) || (DevicePath
== NULL
)) {
384 // The PXE device path is like:
385 // ....../Mac(...)[/Vlan(...)]
386 // ....../Mac(...)[/Vlan(...)]/IPv4(...)
387 // ....../Mac(...)[/Vlan(...)]/IPv6(...)
389 // The HTTP device path is like:
390 // ....../Mac(...)[/Vlan(...)]/IPv4(...)/Uri(...)
391 // ....../Mac(...)[/Vlan(...)]/IPv6(...)/Uri(...)
393 while (!IsDevicePathEnd (DevicePath
) &&
394 ((DevicePathType (DevicePath
) != MESSAGING_DEVICE_PATH
) ||
395 (DevicePathSubType (DevicePath
) != MSG_MAC_ADDR_DP
))
397 DevicePath
= NextDevicePathNode (DevicePath
);
400 if (IsDevicePathEnd (DevicePath
)) {
404 Mac
= (MAC_ADDR_DEVICE_PATH
*) DevicePath
;
405 DevicePath
= NextDevicePathNode (DevicePath
);
407 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
408 (DevicePathSubType (DevicePath
) == MSG_VLAN_DP
)
410 Vlan
= (VLAN_DEVICE_PATH
*) DevicePath
;
411 DevicePath
= NextDevicePathNode (DevicePath
);
416 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
417 ((DevicePathSubType (DevicePath
) == MSG_IPv4_DP
) ||
418 (DevicePathSubType (DevicePath
) == MSG_IPv6_DP
))
421 DevicePath
= NextDevicePathNode (DevicePath
);
426 if ((DevicePathType (DevicePath
) == MESSAGING_DEVICE_PATH
) &&
427 (DevicePathSubType (DevicePath
) == MSG_URI_DP
)
430 DevicePath
= NextDevicePathNode (DevicePath
);
436 // Build description like below:
437 // "PXEv6 (MAC:112233445566 VLAN1)"
438 // "HTTPv4 (MAC:112233445566)"
440 DescriptionSize
= sizeof (L
"HTTPv6 (MAC:112233445566 VLAN65535)");
441 Description
= AllocatePool (DescriptionSize
);
442 ASSERT (Description
!= NULL
);
444 Description
, DescriptionSize
,
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
458 Return the boot description for the controller based on the type.
460 @param Handle Controller handle.
462 @return The description string.
465 BmGetMiscDescription (
471 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
472 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
474 switch (BmDevicePathType (DevicePathFromHandle (Handle
))) {
475 case BmAcpiFloppyBoot
:
476 Description
= L
"Floppy";
479 case BmMessageAtapiBoot
:
480 case BmMessageSataBoot
:
481 Status
= gBS
->HandleProtocol (Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**) &BlockIo
);
482 ASSERT_EFI_ERROR (Status
);
484 // Assume a removable SATA device should be the DVD/CD device
486 Description
= BlockIo
->Media
->RemovableMedia
? L
"DVD/CDROM" : L
"Hard Drive";
489 case BmMessageUsbBoot
:
490 Description
= L
"USB Device";
493 case BmMessageScsiBoot
:
494 Description
= L
"SCSI Device";
497 case BmHardwareDeviceBoot
:
498 Status
= gBS
->HandleProtocol (Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**) &BlockIo
);
499 if (!EFI_ERROR (Status
)) {
500 Description
= BlockIo
->Media
->RemovableMedia
? L
"Removable Disk" : L
"Hard Drive";
502 Description
= L
"Misc Device";
507 Status
= gBS
->HandleProtocol (Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**) &Fs
);
508 if (!EFI_ERROR (Status
)) {
509 Description
= L
"Non-Block Boot Device";
511 Description
= L
"Misc Device";
516 return AllocateCopyPool (StrSize (Description
), Description
);
520 Register the platform provided boot description handler.
522 @param Handler The platform provided boot description handler
524 @retval EFI_SUCCESS The handler was registered successfully.
525 @retval EFI_ALREADY_STARTED The handler was already registered.
526 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
530 EfiBootManagerRegisterBootDescriptionHandler (
531 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
535 BM_BOOT_DESCRIPTION_ENTRY
*Entry
;
537 for ( Link
= GetFirstNode (&mPlatformBootDescriptionHandlers
)
538 ; !IsNull (&mPlatformBootDescriptionHandlers
, Link
)
539 ; Link
= GetNextNode (&mPlatformBootDescriptionHandlers
, Link
)
541 Entry
= CR (Link
, BM_BOOT_DESCRIPTION_ENTRY
, Link
, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
);
542 if (Entry
->Handler
== Handler
) {
543 return EFI_ALREADY_STARTED
;
547 Entry
= AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY
));
549 return EFI_OUT_OF_RESOURCES
;
552 Entry
->Signature
= BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
;
553 Entry
->Handler
= Handler
;
554 InsertTailList (&mPlatformBootDescriptionHandlers
, &Entry
->Link
);
558 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers
[] = {
560 BmGetDescriptionFromDiskInfo
,
561 BmGetNetworkDescription
,
566 Return the boot description for the controller.
568 @param Handle Controller handle.
570 @return The description string.
573 BmGetBootDescription (
578 BM_BOOT_DESCRIPTION_ENTRY
*Entry
;
580 CHAR16
*DefaultDescription
;
585 // Firstly get the default boot description
587 DefaultDescription
= NULL
;
588 for (Index
= 0; Index
< sizeof (mBmBootDescriptionHandlers
) / sizeof (mBmBootDescriptionHandlers
[0]); Index
++) {
589 DefaultDescription
= mBmBootDescriptionHandlers
[Index
] (Handle
);
590 if (DefaultDescription
!= NULL
) {
592 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
593 // ONLY for core provided boot description handler.
595 Temp
= AllocatePool (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
));
596 ASSERT (Temp
!= NULL
);
597 StrCpyS (Temp
, (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
)) / sizeof (CHAR16
), mBmUefiPrefix
);
598 StrCatS (Temp
, (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
)) / sizeof (CHAR16
), DefaultDescription
);
599 FreePool (DefaultDescription
);
600 DefaultDescription
= Temp
;
604 ASSERT (DefaultDescription
!= NULL
);
607 // Secondly query platform for the better boot description
609 for ( Link
= GetFirstNode (&mPlatformBootDescriptionHandlers
)
610 ; !IsNull (&mPlatformBootDescriptionHandlers
, Link
)
611 ; Link
= GetNextNode (&mPlatformBootDescriptionHandlers
, Link
)
613 Entry
= CR (Link
, BM_BOOT_DESCRIPTION_ENTRY
, Link
, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
);
614 Description
= Entry
->Handler (Handle
, DefaultDescription
);
615 if (Description
!= NULL
) {
616 FreePool (DefaultDescription
);
621 return DefaultDescription
;
625 Enumerate all boot option descriptions and append " 2"/" 3"/... to make
628 @param BootOptions Array of boot options.
629 @param BootOptionCount Count of boot options.
632 BmMakeBootOptionDescriptionUnique (
633 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
,
634 UINTN BootOptionCount
639 UINTN DescriptionSize
;
644 if (BootOptionCount
== 0) {
649 // Calculate the maximum buffer size for the number suffix.
650 // The initial sizeof (CHAR16) is for the blank space before the number.
652 MaxSuffixSize
= sizeof (CHAR16
);
653 for (Index
= BootOptionCount
; Index
!= 0; Index
= Index
/ 10) {
654 MaxSuffixSize
+= sizeof (CHAR16
);
657 Visited
= AllocateZeroPool (sizeof (BOOLEAN
) * BootOptionCount
);
658 ASSERT (Visited
!= NULL
);
660 for (Base
= 0; Base
< BootOptionCount
; Base
++) {
661 if (!Visited
[Base
]) {
663 Visited
[Base
] = TRUE
;
664 DescriptionSize
= StrSize (BootOptions
[Base
].Description
);
665 for (Index
= Base
+ 1; Index
< BootOptionCount
; Index
++) {
666 if (!Visited
[Index
] && StrCmp (BootOptions
[Base
].Description
, BootOptions
[Index
].Description
) == 0) {
667 Visited
[Index
] = TRUE
;
669 FreePool (BootOptions
[Index
].Description
);
670 BootOptions
[Index
].Description
= AllocatePool (DescriptionSize
+ MaxSuffixSize
);
672 BootOptions
[Index
].Description
, DescriptionSize
+ MaxSuffixSize
,
674 BootOptions
[Base
].Description
, MatchCount