]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBootDescription.c
1 /** @file
2 Library functions which relate with boot option description.
3
4 Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include "InternalBm.h"
11
12 #define VENDOR_IDENTIFICATION_OFFSET 3
13 #define VENDOR_IDENTIFICATION_LENGTH 8
14 #define PRODUCT_IDENTIFICATION_OFFSET 11
15 #define PRODUCT_IDENTIFICATION_LENGTH 16
16
17 CONST UINT16 mBmUsbLangId = 0x0409; // English
18 CHAR16 mBmUefiPrefix[] = L"UEFI ";
19
20 LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers);
21
22 /**
23 For a bootable Device path, return its boot type.
24
25 @param DevicePath The bootable device Path to check
26
27 @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node
28 which HID is floppy device.
29 @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
30 and its last device path node's subtype is MSG_ATAPI_DP.
31 @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
32 and its last device path node's subtype is MSG_SATA_DP.
33 @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
34 and its last device path node's subtype is MSG_SCSI_DP.
35 @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
36 and its last device path node's subtype is MSG_USB_DP.
37 @retval BmMiscBoot If tiven device path doesn't match the above condition.
38
39 **/
40 BM_BOOT_TYPE
41 BmDevicePathType (
42 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
43 )
44 {
45 EFI_DEVICE_PATH_PROTOCOL *Node;
46 EFI_DEVICE_PATH_PROTOCOL *NextNode;
47
48 ASSERT (DevicePath != NULL);
49
50 for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
51 switch (DevicePathType (Node)) {
52
53 case ACPI_DEVICE_PATH:
54 if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {
55 return BmAcpiFloppyBoot;
56 }
57 break;
58
59 case HARDWARE_DEVICE_PATH:
60 if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {
61 return BmHardwareDeviceBoot;
62 }
63 break;
64
65 case MESSAGING_DEVICE_PATH:
66 //
67 // Skip LUN device node
68 //
69 NextNode = Node;
70 do {
71 NextNode = NextDevicePathNode (NextNode);
72 } while (
73 (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&
74 (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)
75 );
76
77 //
78 // If the device path not only point to driver device, it is not a messaging device path,
79 //
80 if (!IsDevicePathEndType (NextNode)) {
81 continue;
82 }
83
84 switch (DevicePathSubType (Node)) {
85 case MSG_ATAPI_DP:
86 return BmMessageAtapiBoot;
87 break;
88
89 case MSG_SATA_DP:
90 return BmMessageSataBoot;
91 break;
92
93 case MSG_USB_DP:
94 return BmMessageUsbBoot;
95 break;
96
97 case MSG_SCSI_DP:
98 return BmMessageScsiBoot;
99 break;
100 }
101 }
102 }
103
104 return BmMiscBoot;
105 }
106
107 /**
108 Eliminate the extra spaces in the Str to one space.
109
110 @param Str Input string info.
111 **/
112 VOID
113 BmEliminateExtraSpaces (
114 IN CHAR16 *Str
115 )
116 {
117 UINTN Index;
118 UINTN ActualIndex;
119
120 for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {
121 if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {
122 Str[ActualIndex++] = Str[Index];
123 }
124 }
125 Str[ActualIndex] = L'\0';
126 }
127
128 /**
129 Try to get the controller's ATA/ATAPI description.
130
131 @param Handle Controller handle.
132
133 @return The description string.
134 **/
135 CHAR16 *
136 BmGetDescriptionFromDiskInfo (
137 IN EFI_HANDLE Handle
138 )
139 {
140 UINTN Index;
141 EFI_STATUS Status;
142 EFI_DISK_INFO_PROTOCOL *DiskInfo;
143 UINT32 BufferSize;
144 EFI_ATAPI_IDENTIFY_DATA IdentifyData;
145 EFI_SCSI_INQUIRY_DATA InquiryData;
146 CHAR16 *Description;
147 UINTN Length;
148 CONST UINTN ModelNameLength = 40;
149 CONST UINTN SerialNumberLength = 20;
150 CHAR8 *StrPtr;
151 UINT8 Temp;
152 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
153
154 Description = NULL;
155
156 Status = gBS->HandleProtocol (
157 Handle,
158 &gEfiDiskInfoProtocolGuid,
159 (VOID **) &DiskInfo
160 );
161 if (EFI_ERROR (Status)) {
162 return NULL;
163 }
164
165 if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||
166 CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
167 BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);
168 Status = DiskInfo->Identify (
169 DiskInfo,
170 &IdentifyData,
171 &BufferSize
172 );
173 if (!EFI_ERROR (Status)) {
174 Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
175 ASSERT (Description != NULL);
176 for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
177 Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1];
178 Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];
179 }
180
181 Length = Index;
182 Description[Length++] = L' ';
183
184 for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
185 Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1];
186 Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];
187 }
188 Length += Index;
189 Description[Length++] = L'\0';
190 ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
191
192 BmEliminateExtraSpaces (Description);
193 }
194 } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
195 BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);
196 Status = DiskInfo->Inquiry (
197 DiskInfo,
198 &InquiryData,
199 &BufferSize
200 );
201 if (!EFI_ERROR (Status)) {
202 Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
203 ASSERT (Description != NULL);
204
205 //
206 // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
207 // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
208 // Here combine the vendor identification and product identification to the description.
209 //
210 StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
211 Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
212 StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
213 AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1);
214 StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;
215
216 //
217 // Add one space at the middle of vendor information and product information.
218 //
219 Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';
220
221 StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);
222 StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';
223 AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1);
224
225 BmEliminateExtraSpaces (Description);
226 }
227 } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoSdMmcInterfaceGuid)) {
228 DevicePath = DevicePathFromHandle (Handle);
229 if (DevicePath == NULL) {
230 return NULL;
231 }
232
233 while (!IsDevicePathEnd (DevicePath) && (DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH)) {
234 DevicePath = NextDevicePathNode (DevicePath);
235 }
236 if (IsDevicePathEnd (DevicePath)) {
237 return NULL;
238 }
239
240 if (DevicePathSubType (DevicePath) == MSG_SD_DP) {
241 Description = L"SD Device";
242 } else if (DevicePathSubType (DevicePath) == MSG_EMMC_DP) {
243 Description = L"eMMC Device";
244 } else {
245 return NULL;
246 }
247
248 Description = AllocateCopyPool (StrSize (Description), Description);
249 }
250
251 return Description;
252 }
253
254 /**
255 Try to get the controller's USB description.
256
257 @param Handle Controller handle.
258
259 @return The description string.
260 **/
261 CHAR16 *
262 BmGetUsbDescription (
263 IN EFI_HANDLE Handle
264 )
265 {
266 EFI_STATUS Status;
267 EFI_USB_IO_PROTOCOL *UsbIo;
268 CHAR16 NullChar;
269 CHAR16 *Manufacturer;
270 CHAR16 *Product;
271 CHAR16 *SerialNumber;
272 CHAR16 *Description;
273 EFI_USB_DEVICE_DESCRIPTOR DevDesc;
274 UINTN DescMaxSize;
275
276 Status = gBS->HandleProtocol (
277 Handle,
278 &gEfiUsbIoProtocolGuid,
279 (VOID **) &UsbIo
280 );
281 if (EFI_ERROR (Status)) {
282 return NULL;
283 }
284
285 NullChar = L'\0';
286
287 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
288 if (EFI_ERROR (Status)) {
289 return NULL;
290 }
291
292 Status = UsbIo->UsbGetStringDescriptor (
293 UsbIo,
294 mBmUsbLangId,
295 DevDesc.StrManufacturer,
296 &Manufacturer
297 );
298 if (EFI_ERROR (Status)) {
299 Manufacturer = &NullChar;
300 }
301
302 Status = UsbIo->UsbGetStringDescriptor (
303 UsbIo,
304 mBmUsbLangId,
305 DevDesc.StrProduct,
306 &Product
307 );
308 if (EFI_ERROR (Status)) {
309 Product = &NullChar;
310 }
311
312 Status = UsbIo->UsbGetStringDescriptor (
313 UsbIo,
314 mBmUsbLangId,
315 DevDesc.StrSerialNumber,
316 &SerialNumber
317 );
318 if (EFI_ERROR (Status)) {
319 SerialNumber = &NullChar;
320 }
321
322 if ((Manufacturer == &NullChar) &&
323 (Product == &NullChar) &&
324 (SerialNumber == &NullChar)
325 ) {
326 return NULL;
327 }
328
329 DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber);
330 Description = AllocateZeroPool (DescMaxSize);
331 ASSERT (Description != NULL);
332 StrCatS (Description, DescMaxSize/sizeof(CHAR16), Manufacturer);
333 StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
334
335 StrCatS (Description, DescMaxSize/sizeof(CHAR16), Product);
336 StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
337
338 StrCatS (Description, DescMaxSize/sizeof(CHAR16), SerialNumber);
339
340 if (Manufacturer != &NullChar) {
341 FreePool (Manufacturer);
342 }
343 if (Product != &NullChar) {
344 FreePool (Product);
345 }
346 if (SerialNumber != &NullChar) {
347 FreePool (SerialNumber);
348 }
349
350 BmEliminateExtraSpaces (Description);
351
352 return Description;
353 }
354
355 /**
356 Return the description for network boot device.
357
358 @param Handle Controller handle.
359
360 @return The description string.
361 **/
362 CHAR16 *
363 BmGetNetworkDescription (
364 IN EFI_HANDLE Handle
365 )
366 {
367 EFI_STATUS Status;
368 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
369 MAC_ADDR_DEVICE_PATH *Mac;
370 VLAN_DEVICE_PATH *Vlan;
371 EFI_DEVICE_PATH_PROTOCOL *Ip;
372 EFI_DEVICE_PATH_PROTOCOL *Uri;
373 CHAR16 *Description;
374 UINTN DescriptionSize;
375
376 Status = gBS->OpenProtocol (
377 Handle,
378 &gEfiLoadFileProtocolGuid,
379 NULL,
380 gImageHandle,
381 Handle,
382 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
383 );
384 if (EFI_ERROR (Status)) {
385 return NULL;
386 }
387
388 Status = gBS->OpenProtocol (
389 Handle,
390 &gEfiDevicePathProtocolGuid,
391 (VOID **) &DevicePath,
392 gImageHandle,
393 Handle,
394 EFI_OPEN_PROTOCOL_GET_PROTOCOL
395 );
396 if (EFI_ERROR (Status) || (DevicePath == NULL)) {
397 return NULL;
398 }
399
400 //
401 // The PXE device path is like:
402 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]
403 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)
404 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)
405 //
406 // The HTTP device path is like:
407 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)
408 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...)
409 //
410 while (!IsDevicePathEnd (DevicePath) &&
411 ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||
412 (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP))
413 ) {
414 DevicePath = NextDevicePathNode (DevicePath);
415 }
416
417 if (IsDevicePathEnd (DevicePath)) {
418 return NULL;
419 }
420
421 Mac = (MAC_ADDR_DEVICE_PATH *) DevicePath;
422 DevicePath = NextDevicePathNode (DevicePath);
423
424 //
425 // Locate the optional Vlan node
426 //
427 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
428 (DevicePathSubType (DevicePath) == MSG_VLAN_DP)
429 ) {
430 Vlan = (VLAN_DEVICE_PATH *) DevicePath;
431 DevicePath = NextDevicePathNode (DevicePath);
432 } else {
433 Vlan = NULL;
434 }
435
436 //
437 // Skip the optional Wi-Fi node
438 //
439 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
440 (DevicePathSubType (DevicePath) == MSG_WIFI_DP)
441 ) {
442 DevicePath = NextDevicePathNode (DevicePath);
443 }
444
445 //
446 // Locate the IP node
447 //
448 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
449 ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||
450 (DevicePathSubType (DevicePath) == MSG_IPv6_DP))
451 ) {
452 Ip = DevicePath;
453 DevicePath = NextDevicePathNode (DevicePath);
454 } else {
455 Ip = NULL;
456 }
457
458 //
459 // Skip the optional DNS node
460 //
461 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
462 (DevicePathSubType (DevicePath) == MSG_DNS_DP)
463 ) {
464 DevicePath = NextDevicePathNode (DevicePath);
465 }
466
467 //
468 // Locate the URI node
469 //
470 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
471 (DevicePathSubType (DevicePath) == MSG_URI_DP)
472 ) {
473 Uri = DevicePath;
474 DevicePath = NextDevicePathNode (DevicePath);
475 } else {
476 Uri = NULL;
477 }
478
479 //
480 // Build description like below:
481 // "PXEv6 (MAC:112233445566 VLAN1)"
482 // "HTTPv4 (MAC:112233445566)"
483 //
484 DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");
485 Description = AllocatePool (DescriptionSize);
486 ASSERT (Description != NULL);
487 UnicodeSPrint (
488 Description, DescriptionSize,
489 (Vlan == NULL) ?
490 L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :
491 L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",
492 (Uri == NULL) ? L"PXE" : L"HTTP",
493 ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,
494 Mac->MacAddress.Addr[0], Mac->MacAddress.Addr[1], Mac->MacAddress.Addr[2],
495 Mac->MacAddress.Addr[3], Mac->MacAddress.Addr[4], Mac->MacAddress.Addr[5],
496 (Vlan == NULL) ? 0 : Vlan->VlanId
497 );
498 return Description;
499 }
500
501 /**
502 Return the boot description for LoadFile
503
504 @param Handle Controller handle.
505
506 @return The description string.
507 **/
508 CHAR16 *
509 BmGetLoadFileDescription (
510 IN EFI_HANDLE Handle
511 )
512 {
513 EFI_STATUS Status;
514 EFI_DEVICE_PATH_PROTOCOL *FilePath;
515 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
516 CHAR16 *Description;
517 EFI_LOAD_FILE_PROTOCOL *LoadFile;
518
519 Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);
520 if (EFI_ERROR (Status)) {
521 return NULL;
522 }
523
524 //
525 // Get the file name
526 //
527 Description = NULL;
528 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath);
529 if (!EFI_ERROR (Status)) {
530 DevicePathNode = FilePath;
531 while (!IsDevicePathEnd (DevicePathNode)) {
532 if (DevicePathNode->Type == MEDIA_DEVICE_PATH && DevicePathNode->SubType == MEDIA_FILEPATH_DP) {
533 Description = (CHAR16 *)(DevicePathNode + 1);
534 break;
535 }
536 DevicePathNode = NextDevicePathNode (DevicePathNode);
537 }
538 }
539
540 if (Description != NULL) {
541 return AllocateCopyPool (StrSize (Description), Description);
542 }
543
544 return NULL;
545 }
546
547 /**
548 Return the boot description for NVME boot device.
549
550 @param Handle Controller handle.
551
552 @return The description string.
553 **/
554 CHAR16 *
555 BmGetNvmeDescription (
556 IN EFI_HANDLE Handle
557 )
558 {
559 EFI_STATUS Status;
560 EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru;
561 EFI_DEV_PATH_PTR DevicePath;
562 EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
563 EFI_NVM_EXPRESS_COMMAND Command;
564 EFI_NVM_EXPRESS_COMPLETION Completion;
565 NVME_ADMIN_CONTROLLER_DATA ControllerData;
566 CHAR16 *Description;
567 CHAR16 *Char;
568 UINTN Index;
569
570 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath.DevPath);
571 if (EFI_ERROR (Status)) {
572 return NULL;
573 }
574
575 Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle);
576 if (EFI_ERROR (Status) ||
577 (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) ||
578 (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP)) {
579 //
580 // Do not return description when the Handle is not a child of NVME controller.
581 //
582 return NULL;
583 }
584
585 //
586 // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number.
587 //
588 Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **) &NvmePassthru);
589 ASSERT_EFI_ERROR (Status);
590
591 ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
592 ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
593 ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
594
595 Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;
596 //
597 // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
598 // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
599 //
600 Command.Nsid = 0;
601 CommandPacket.NvmeCmd = &Command;
602 CommandPacket.NvmeCompletion = &Completion;
603 CommandPacket.TransferBuffer = &ControllerData;
604 CommandPacket.TransferLength = sizeof (ControllerData);
605 CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5);
606 CommandPacket.QueueType = NVME_ADMIN_QUEUE;
607 //
608 // Set bit 0 (Cns bit) to 1 to identify a controller
609 //
610 Command.Cdw10 = 1;
611 Command.Flags = CDW10_VALID;
612
613 Status = NvmePassthru->PassThru (
614 NvmePassthru,
615 0,
616 &CommandPacket,
617 NULL
618 );
619 if (EFI_ERROR (Status)) {
620 return NULL;
621 }
622
623 Description = AllocateZeroPool (
624 (ARRAY_SIZE (ControllerData.Mn) + 1
625 + ARRAY_SIZE (ControllerData.Sn) + 1
626 + MAXIMUM_VALUE_CHARACTERS + 1
627 ) * sizeof (CHAR16));
628 if (Description != NULL) {
629 Char = Description;
630 for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) {
631 *(Char++) = (CHAR16) ControllerData.Mn[Index];
632 }
633 *(Char++) = L' ';
634 for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) {
635 *(Char++) = (CHAR16) ControllerData.Sn[Index];
636 }
637 *(Char++) = L' ';
638 UnicodeValueToStringS (
639 Char, sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1),
640 0, DevicePath.NvmeNamespace->NamespaceId, 0
641 );
642 BmEliminateExtraSpaces (Description);
643 }
644
645 return Description;
646 }
647
648 /**
649 Return the boot description for the controller based on the type.
650
651 @param Handle Controller handle.
652
653 @return The description string.
654 **/
655 CHAR16 *
656 BmGetMiscDescription (
657 IN EFI_HANDLE Handle
658 )
659 {
660 EFI_STATUS Status;
661 CHAR16 *Description;
662 EFI_BLOCK_IO_PROTOCOL *BlockIo;
663 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
664
665 switch (BmDevicePathType (DevicePathFromHandle (Handle))) {
666 case BmAcpiFloppyBoot:
667 Description = L"Floppy";
668 break;
669
670 case BmMessageAtapiBoot:
671 case BmMessageSataBoot:
672 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
673 ASSERT_EFI_ERROR (Status);
674 //
675 // Assume a removable SATA device should be the DVD/CD device
676 //
677 Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
678 break;
679
680 case BmMessageUsbBoot:
681 Description = L"USB Device";
682 break;
683
684 case BmMessageScsiBoot:
685 Description = L"SCSI Device";
686 break;
687
688 case BmHardwareDeviceBoot:
689 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
690 if (!EFI_ERROR (Status)) {
691 Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
692 } else {
693 Description = L"Misc Device";
694 }
695 break;
696
697 default:
698 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);
699 if (!EFI_ERROR (Status)) {
700 Description = L"Non-Block Boot Device";
701 } else {
702 Description = L"Misc Device";
703 }
704 break;
705 }
706
707 return AllocateCopyPool (StrSize (Description), Description);
708 }
709
710 /**
711 Register the platform provided boot description handler.
712
713 @param Handler The platform provided boot description handler
714
715 @retval EFI_SUCCESS The handler was registered successfully.
716 @retval EFI_ALREADY_STARTED The handler was already registered.
717 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
718 **/
719 EFI_STATUS
720 EFIAPI
721 EfiBootManagerRegisterBootDescriptionHandler (
722 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
723 )
724 {
725 LIST_ENTRY *Link;
726 BM_BOOT_DESCRIPTION_ENTRY *Entry;
727
728 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
729 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
730 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
731 ) {
732 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
733 if (Entry->Handler == Handler) {
734 return EFI_ALREADY_STARTED;
735 }
736 }
737
738 Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));
739 if (Entry == NULL) {
740 return EFI_OUT_OF_RESOURCES;
741 }
742
743 Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;
744 Entry->Handler = Handler;
745 InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);
746 return EFI_SUCCESS;
747 }
748
749 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {
750 BmGetUsbDescription,
751 BmGetDescriptionFromDiskInfo,
752 BmGetNetworkDescription,
753 BmGetLoadFileDescription,
754 BmGetNvmeDescription,
755 BmGetMiscDescription
756 };
757
758 /**
759 Return the boot description for the controller.
760
761 @param Handle Controller handle.
762
763 @return The description string.
764 **/
765 CHAR16 *
766 BmGetBootDescription (
767 IN EFI_HANDLE Handle
768 )
769 {
770 LIST_ENTRY *Link;
771 BM_BOOT_DESCRIPTION_ENTRY *Entry;
772 CHAR16 *Description;
773 CHAR16 *DefaultDescription;
774 CHAR16 *Temp;
775 UINTN Index;
776
777 //
778 // Firstly get the default boot description
779 //
780 DefaultDescription = NULL;
781 for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) {
782 DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);
783 if (DefaultDescription != NULL) {
784 //
785 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
786 // ONLY for core provided boot description handler.
787 //
788 Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));
789 ASSERT (Temp != NULL);
790 StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);
791 StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);
792 FreePool (DefaultDescription);
793 DefaultDescription = Temp;
794 break;
795 }
796 }
797 ASSERT (DefaultDescription != NULL);
798
799 //
800 // Secondly query platform for the better boot description
801 //
802 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
803 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
804 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
805 ) {
806 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
807 Description = Entry->Handler (Handle, DefaultDescription);
808 if (Description != NULL) {
809 FreePool (DefaultDescription);
810 return Description;
811 }
812 }
813
814 return DefaultDescription;
815 }
816
817 /**
818 Enumerate all boot option descriptions and append " 2"/" 3"/... to make
819 unique description.
820
821 @param BootOptions Array of boot options.
822 @param BootOptionCount Count of boot options.
823 **/
824 VOID
825 BmMakeBootOptionDescriptionUnique (
826 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
827 UINTN BootOptionCount
828 )
829 {
830 UINTN Base;
831 UINTN Index;
832 UINTN DescriptionSize;
833 UINTN MaxSuffixSize;
834 BOOLEAN *Visited;
835 UINTN MatchCount;
836
837 if (BootOptionCount == 0) {
838 return;
839 }
840
841 //
842 // Calculate the maximum buffer size for the number suffix.
843 // The initial sizeof (CHAR16) is for the blank space before the number.
844 //
845 MaxSuffixSize = sizeof (CHAR16);
846 for (Index = BootOptionCount; Index != 0; Index = Index / 10) {
847 MaxSuffixSize += sizeof (CHAR16);
848 }
849
850 Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);
851 ASSERT (Visited != NULL);
852
853 for (Base = 0; Base < BootOptionCount; Base++) {
854 if (!Visited[Base]) {
855 MatchCount = 1;
856 Visited[Base] = TRUE;
857 DescriptionSize = StrSize (BootOptions[Base].Description);
858 for (Index = Base + 1; Index < BootOptionCount; Index++) {
859 if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {
860 Visited[Index] = TRUE;
861 MatchCount++;
862 FreePool (BootOptions[Index].Description);
863 BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);
864 UnicodeSPrint (
865 BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,
866 L"%s %d",
867 BootOptions[Base].Description, MatchCount
868 );
869 }
870 }
871 }
872 }
873
874 FreePool (Visited);
875 }