MdeModulePkg/CapsuleLib: Free the buffer returned by GetVariable2 API
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBootDescription.c
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 }