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