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 MessageNetworkBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
44 and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP,
45 MSG_IPv4_DP or MSG_IPv6_DP.
46 @retval MessageHttpBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
47 and its last device path node's subtype is MSG_URI_DP.
48 @retval UnsupportedBoot If tiven device path doesn't match the above condition, it's not supported.
53 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
56 EFI_DEVICE_PATH_PROTOCOL
*Node
;
57 EFI_DEVICE_PATH_PROTOCOL
*NextNode
;
59 ASSERT (DevicePath
!= NULL
);
61 for (Node
= DevicePath
; !IsDevicePathEndType (Node
); Node
= NextDevicePathNode (Node
)) {
62 switch (DevicePathType (Node
)) {
64 case ACPI_DEVICE_PATH
:
65 if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH
*) Node
)->HID
) == 0x0604) {
66 return BmAcpiFloppyBoot
;
70 case HARDWARE_DEVICE_PATH
:
71 if (DevicePathSubType (Node
) == HW_CONTROLLER_DP
) {
72 return BmHardwareDeviceBoot
;
76 case MESSAGING_DEVICE_PATH
:
78 // Skip LUN device node
82 NextNode
= NextDevicePathNode (NextNode
);
84 (DevicePathType (NextNode
) == MESSAGING_DEVICE_PATH
) &&
85 (DevicePathSubType(NextNode
) == MSG_DEVICE_LOGICAL_UNIT_DP
)
89 // If the device path not only point to driver device, it is not a messaging device path,
91 if (!IsDevicePathEndType (NextNode
)) {
95 switch (DevicePathSubType (Node
)) {
97 return BmMessageAtapiBoot
;
101 return BmMessageSataBoot
;
105 return BmMessageUsbBoot
;
109 return BmMessageScsiBoot
;
112 case MSG_MAC_ADDR_DP
:
116 return BmMessageNetworkBoot
;
120 return BmMessageHttpBoot
;
130 Eliminate the extra spaces in the Str to one space.
132 @param Str Input string info.
135 BmEliminateExtraSpaces (
142 for (Index
= 0, ActualIndex
= 0; Str
[Index
] != L
'\0'; Index
++) {
143 if ((Str
[Index
] != L
' ') || ((ActualIndex
> 0) && (Str
[ActualIndex
- 1] != L
' '))) {
144 Str
[ActualIndex
++] = Str
[Index
];
147 Str
[ActualIndex
] = L
'\0';
151 Try to get the controller's ATA/ATAPI description.
153 @param Handle Controller handle.
155 @return The description string.
158 BmGetDescriptionFromDiskInfo (
164 EFI_DISK_INFO_PROTOCOL
*DiskInfo
;
166 EFI_ATAPI_IDENTIFY_DATA IdentifyData
;
167 EFI_SCSI_INQUIRY_DATA InquiryData
;
170 CONST UINTN ModelNameLength
= 40;
171 CONST UINTN SerialNumberLength
= 20;
177 Status
= gBS
->HandleProtocol (
179 &gEfiDiskInfoProtocolGuid
,
182 if (EFI_ERROR (Status
)) {
186 if (CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoAhciInterfaceGuid
) ||
187 CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoIdeInterfaceGuid
)) {
188 BufferSize
= sizeof (EFI_ATAPI_IDENTIFY_DATA
);
189 Status
= DiskInfo
->Identify (
194 if (!EFI_ERROR (Status
)) {
195 Description
= AllocateZeroPool ((ModelNameLength
+ SerialNumberLength
+ 2) * sizeof (CHAR16
));
196 ASSERT (Description
!= NULL
);
197 for (Index
= 0; Index
+ 1 < ModelNameLength
; Index
+= 2) {
198 Description
[Index
] = (CHAR16
) IdentifyData
.ModelName
[Index
+ 1];
199 Description
[Index
+ 1] = (CHAR16
) IdentifyData
.ModelName
[Index
];
203 Description
[Length
++] = L
' ';
205 for (Index
= 0; Index
+ 1 < SerialNumberLength
; Index
+= 2) {
206 Description
[Length
+ Index
] = (CHAR16
) IdentifyData
.SerialNo
[Index
+ 1];
207 Description
[Length
+ Index
+ 1] = (CHAR16
) IdentifyData
.SerialNo
[Index
];
210 Description
[Length
++] = L
'\0';
211 ASSERT (Length
== ModelNameLength
+ SerialNumberLength
+ 2);
213 BmEliminateExtraSpaces (Description
);
215 } else if (CompareGuid (&DiskInfo
->Interface
, &gEfiDiskInfoScsiInterfaceGuid
)) {
216 BufferSize
= sizeof (EFI_SCSI_INQUIRY_DATA
);
217 Status
= DiskInfo
->Inquiry (
222 if (!EFI_ERROR (Status
)) {
223 Description
= AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH
+ PRODUCT_IDENTIFICATION_LENGTH
+ 2) * sizeof (CHAR16
));
224 ASSERT (Description
!= NULL
);
227 // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
228 // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
229 // Here combine the vendor identification and product identification to the description.
231 StrPtr
= (CHAR8
*) (&InquiryData
.Reserved_5_95
[VENDOR_IDENTIFICATION_OFFSET
]);
232 Temp
= StrPtr
[VENDOR_IDENTIFICATION_LENGTH
];
233 StrPtr
[VENDOR_IDENTIFICATION_LENGTH
] = '\0';
234 AsciiStrToUnicodeStr (StrPtr
, Description
);
235 StrPtr
[VENDOR_IDENTIFICATION_LENGTH
] = Temp
;
238 // Add one space at the middle of vendor information and product information.
240 Description
[VENDOR_IDENTIFICATION_LENGTH
] = L
' ';
242 StrPtr
= (CHAR8
*) (&InquiryData
.Reserved_5_95
[PRODUCT_IDENTIFICATION_OFFSET
]);
243 StrPtr
[PRODUCT_IDENTIFICATION_LENGTH
] = '\0';
244 AsciiStrToUnicodeStr (StrPtr
, Description
+ VENDOR_IDENTIFICATION_LENGTH
+ 1);
246 BmEliminateExtraSpaces (Description
);
254 Try to get the controller's USB description.
256 @param Handle Controller handle.
258 @return The description string.
261 BmGetUsbDescription (
266 EFI_USB_IO_PROTOCOL
*UsbIo
;
268 CHAR16
*Manufacturer
;
270 CHAR16
*SerialNumber
;
272 EFI_USB_DEVICE_DESCRIPTOR DevDesc
;
275 Status
= gBS
->HandleProtocol (
277 &gEfiUsbIoProtocolGuid
,
280 if (EFI_ERROR (Status
)) {
286 Status
= UsbIo
->UsbGetDeviceDescriptor (UsbIo
, &DevDesc
);
287 if (EFI_ERROR (Status
)) {
291 Status
= UsbIo
->UsbGetStringDescriptor (
294 DevDesc
.StrManufacturer
,
297 if (EFI_ERROR (Status
)) {
298 Manufacturer
= &NullChar
;
301 Status
= UsbIo
->UsbGetStringDescriptor (
307 if (EFI_ERROR (Status
)) {
311 Status
= UsbIo
->UsbGetStringDescriptor (
314 DevDesc
.StrSerialNumber
,
317 if (EFI_ERROR (Status
)) {
318 SerialNumber
= &NullChar
;
321 if ((Manufacturer
== &NullChar
) &&
322 (Product
== &NullChar
) &&
323 (SerialNumber
== &NullChar
)
328 DescMaxSize
= StrSize (Manufacturer
) + StrSize (Product
) + StrSize (SerialNumber
);
329 Description
= AllocateZeroPool (DescMaxSize
);
330 ASSERT (Description
!= NULL
);
331 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), Manufacturer
);
332 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), L
" ");
334 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), Product
);
335 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), L
" ");
337 StrCatS (Description
, DescMaxSize
/sizeof(CHAR16
), SerialNumber
);
339 if (Manufacturer
!= &NullChar
) {
340 FreePool (Manufacturer
);
342 if (Product
!= &NullChar
) {
345 if (SerialNumber
!= &NullChar
) {
346 FreePool (SerialNumber
);
349 BmEliminateExtraSpaces (Description
);
355 Return the boot description for the controller based on the type.
357 @param Handle Controller handle.
359 @return The description string.
362 BmGetMiscDescription (
368 EFI_BLOCK_IO_PROTOCOL
*BlockIo
;
369 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*Fs
;
371 switch (BmDevicePathType (DevicePathFromHandle (Handle
))) {
372 case BmAcpiFloppyBoot
:
373 Description
= L
"Floppy";
376 case BmMessageAtapiBoot
:
377 case BmMessageSataBoot
:
378 Status
= gBS
->HandleProtocol (Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**) &BlockIo
);
379 ASSERT_EFI_ERROR (Status
);
381 // Assume a removable SATA device should be the DVD/CD device
383 Description
= BlockIo
->Media
->RemovableMedia
? L
"DVD/CDROM" : L
"Hard Drive";
386 case BmMessageUsbBoot
:
387 Description
= L
"USB Device";
390 case BmMessageScsiBoot
:
391 Description
= L
"SCSI Device";
394 case BmHardwareDeviceBoot
:
395 Status
= gBS
->HandleProtocol (Handle
, &gEfiBlockIoProtocolGuid
, (VOID
**) &BlockIo
);
396 if (!EFI_ERROR (Status
)) {
397 Description
= BlockIo
->Media
->RemovableMedia
? L
"Removable Disk" : L
"Hard Drive";
399 Description
= L
"Misc Device";
403 case BmMessageNetworkBoot
:
404 Description
= L
"Network";
407 case BmMessageHttpBoot
:
408 Description
= L
"Http";
412 Status
= gBS
->HandleProtocol (Handle
, &gEfiSimpleFileSystemProtocolGuid
, (VOID
**) &Fs
);
413 if (!EFI_ERROR (Status
)) {
414 Description
= L
"Non-Block Boot Device";
416 Description
= L
"Misc Device";
421 return AllocateCopyPool (StrSize (Description
), Description
);
425 Register the platform provided boot description handler.
427 @param Handler The platform provided boot description handler
429 @retval EFI_SUCCESS The handler was registered successfully.
430 @retval EFI_ALREADY_STARTED The handler was already registered.
431 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
435 EfiBootManagerRegisterBootDescriptionHandler (
436 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
440 BM_BOOT_DESCRIPTION_ENTRY
*Entry
;
442 for ( Link
= GetFirstNode (&mPlatformBootDescriptionHandlers
)
443 ; !IsNull (&mPlatformBootDescriptionHandlers
, Link
)
444 ; Link
= GetNextNode (&mPlatformBootDescriptionHandlers
, Link
)
446 Entry
= CR (Link
, BM_BOOT_DESCRIPTION_ENTRY
, Link
, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
);
447 if (Entry
->Handler
== Handler
) {
448 return EFI_ALREADY_STARTED
;
452 Entry
= AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY
));
454 return EFI_OUT_OF_RESOURCES
;
457 Entry
->Signature
= BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
;
458 Entry
->Handler
= Handler
;
459 InsertTailList (&mPlatformBootDescriptionHandlers
, &Entry
->Link
);
463 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers
[] = {
465 BmGetDescriptionFromDiskInfo
,
470 Return the boot description for the controller.
472 @param Handle Controller handle.
474 @return The description string.
477 BmGetBootDescription (
482 BM_BOOT_DESCRIPTION_ENTRY
*Entry
;
484 CHAR16
*DefaultDescription
;
489 // Firstly get the default boot description
491 DefaultDescription
= NULL
;
492 for (Index
= 0; Index
< sizeof (mBmBootDescriptionHandlers
) / sizeof (mBmBootDescriptionHandlers
[0]); Index
++) {
493 DefaultDescription
= mBmBootDescriptionHandlers
[Index
] (Handle
);
494 if (DefaultDescription
!= NULL
) {
496 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
497 // ONLY for core provided boot description handler.
499 Temp
= AllocatePool (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
));
500 ASSERT (Temp
!= NULL
);
501 StrCpyS (Temp
, (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
)) / sizeof (CHAR16
), mBmUefiPrefix
);
502 StrCatS (Temp
, (StrSize (DefaultDescription
) + sizeof (mBmUefiPrefix
)) / sizeof (CHAR16
), DefaultDescription
);
503 FreePool (DefaultDescription
);
504 DefaultDescription
= Temp
;
508 ASSERT (DefaultDescription
!= NULL
);
511 // Secondly query platform for the better boot description
513 for ( Link
= GetFirstNode (&mPlatformBootDescriptionHandlers
)
514 ; !IsNull (&mPlatformBootDescriptionHandlers
, Link
)
515 ; Link
= GetNextNode (&mPlatformBootDescriptionHandlers
, Link
)
517 Entry
= CR (Link
, BM_BOOT_DESCRIPTION_ENTRY
, Link
, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE
);
518 Description
= Entry
->Handler (Handle
, DefaultDescription
);
519 if (Description
!= NULL
) {
520 FreePool (DefaultDescription
);
525 return DefaultDescription
;
529 Enumerate all boot option descriptions and append " 2"/" 3"/... to make
532 @param BootOptions Array of boot options.
533 @param BootOptionCount Count of boot options.
536 BmMakeBootOptionDescriptionUnique (
537 EFI_BOOT_MANAGER_LOAD_OPTION
*BootOptions
,
538 UINTN BootOptionCount
543 UINTN DescriptionSize
;
548 if (BootOptionCount
== 0) {
553 // Calculate the maximum buffer size for the number suffix.
554 // The initial sizeof (CHAR16) is for the blank space before the number.
556 MaxSuffixSize
= sizeof (CHAR16
);
557 for (Index
= BootOptionCount
; Index
!= 0; Index
= Index
/ 10) {
558 MaxSuffixSize
+= sizeof (CHAR16
);
561 Visited
= AllocateZeroPool (sizeof (BOOLEAN
) * BootOptionCount
);
562 ASSERT (Visited
!= NULL
);
564 for (Base
= 0; Base
< BootOptionCount
; Base
++) {
565 if (!Visited
[Base
]) {
567 Visited
[Base
] = TRUE
;
568 DescriptionSize
= StrSize (BootOptions
[Base
].Description
);
569 for (Index
= Base
+ 1; Index
< BootOptionCount
; Index
++) {
570 if (!Visited
[Index
] && StrCmp (BootOptions
[Base
].Description
, BootOptions
[Index
].Description
) == 0) {
571 Visited
[Index
] = TRUE
;
573 FreePool (BootOptions
[Index
].Description
);
574 BootOptions
[Index
].Description
= AllocatePool (DescriptionSize
+ MaxSuffixSize
);
576 BootOptions
[Index
].Description
, DescriptionSize
+ MaxSuffixSize
,
578 BootOptions
[Base
].Description
, MatchCount