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