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