]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
MdeModulePkg/UefiBootManagerLib: Support DNS device path description
[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
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(...)][/Wi-Fi(...)]
386 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)
387 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)
388 //
389 // The HTTP device path is like:
390 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)
391 // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/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 //
408 // Locate the optional Vlan node
409 //
410 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
411 (DevicePathSubType (DevicePath) == MSG_VLAN_DP)
412 ) {
413 Vlan = (VLAN_DEVICE_PATH *) DevicePath;
414 DevicePath = NextDevicePathNode (DevicePath);
415 } else {
416 Vlan = NULL;
417 }
418
419 //
420 // Skip the optional Wi-Fi node
421 //
422 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
423 (DevicePathSubType (DevicePath) == MSG_WIFI_DP)
424 ) {
425 DevicePath = NextDevicePathNode (DevicePath);
426 }
427
428 //
429 // Locate the IP node
430 //
431 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
432 ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||
433 (DevicePathSubType (DevicePath) == MSG_IPv6_DP))
434 ) {
435 Ip = DevicePath;
436 DevicePath = NextDevicePathNode (DevicePath);
437 } else {
438 Ip = NULL;
439 }
440
441 //
442 // Skip the optional DNS node
443 //
444 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
445 (DevicePathSubType (DevicePath) == MSG_DNS_DP)
446 ) {
447 DevicePath = NextDevicePathNode (DevicePath);
448 }
449
450 //
451 // Locate the URI node
452 //
453 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
454 (DevicePathSubType (DevicePath) == MSG_URI_DP)
455 ) {
456 Uri = DevicePath;
457 DevicePath = NextDevicePathNode (DevicePath);
458 } else {
459 Uri = NULL;
460 }
461
462 //
463 // Build description like below:
464 // "PXEv6 (MAC:112233445566 VLAN1)"
465 // "HTTPv4 (MAC:112233445566)"
466 //
467 DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");
468 Description = AllocatePool (DescriptionSize);
469 ASSERT (Description != NULL);
470 UnicodeSPrint (
471 Description, DescriptionSize,
472 (Vlan == NULL) ?
473 L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :
474 L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",
475 (Uri == NULL) ? L"PXE" : L"HTTP",
476 ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,
477 Mac->MacAddress.Addr[0], Mac->MacAddress.Addr[1], Mac->MacAddress.Addr[2],
478 Mac->MacAddress.Addr[3], Mac->MacAddress.Addr[4], Mac->MacAddress.Addr[5],
479 (Vlan == NULL) ? 0 : Vlan->VlanId
480 );
481 return Description;
482 }
483
484 /**
485 Return the boot description for LoadFile
486
487 @param Handle Controller handle.
488
489 @return The description string.
490 **/
491 CHAR16 *
492 BmGetLoadFileDescription (
493 IN EFI_HANDLE Handle
494 )
495 {
496 EFI_STATUS Status;
497 EFI_DEVICE_PATH_PROTOCOL *FilePath;
498 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
499 CHAR16 *Description;
500 EFI_LOAD_FILE_PROTOCOL *LoadFile;
501
502 Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);
503 if (EFI_ERROR (Status)) {
504 return NULL;
505 }
506
507 //
508 // Get the file name
509 //
510 Description = NULL;
511 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath);
512 if (!EFI_ERROR (Status)) {
513 DevicePathNode = FilePath;
514 while (!IsDevicePathEnd (DevicePathNode)) {
515 if (DevicePathNode->Type == MEDIA_DEVICE_PATH && DevicePathNode->SubType == MEDIA_FILEPATH_DP) {
516 Description = (CHAR16 *)(DevicePathNode + 1);
517 break;
518 }
519 DevicePathNode = NextDevicePathNode (DevicePathNode);
520 }
521 }
522
523 if (Description != NULL) {
524 return AllocateCopyPool (StrSize (Description), Description);
525 }
526
527 return NULL;
528 }
529
530 /**
531 Return the boot description for NVME boot device.
532
533 @param Handle Controller handle.
534
535 @return The description string.
536 **/
537 CHAR16 *
538 BmGetNvmeDescription (
539 IN EFI_HANDLE Handle
540 )
541 {
542 EFI_STATUS Status;
543 EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru;
544 EFI_DEV_PATH_PTR DevicePath;
545 EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
546 EFI_NVM_EXPRESS_COMMAND Command;
547 EFI_NVM_EXPRESS_COMPLETION Completion;
548 NVME_ADMIN_CONTROLLER_DATA ControllerData;
549 CHAR16 *Description;
550 CHAR16 *Char;
551 UINTN Index;
552
553 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath.DevPath);
554 if (EFI_ERROR (Status)) {
555 return NULL;
556 }
557
558 Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle);
559 if (EFI_ERROR (Status) ||
560 (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) ||
561 (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP)) {
562 //
563 // Do not return description when the Handle is not a child of NVME controller.
564 //
565 return NULL;
566 }
567
568 //
569 // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number.
570 //
571 Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **) &NvmePassthru);
572 ASSERT_EFI_ERROR (Status);
573
574 ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
575 ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
576 ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
577
578 Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;
579 //
580 // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
581 // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
582 //
583 Command.Nsid = 0;
584 CommandPacket.NvmeCmd = &Command;
585 CommandPacket.NvmeCompletion = &Completion;
586 CommandPacket.TransferBuffer = &ControllerData;
587 CommandPacket.TransferLength = sizeof (ControllerData);
588 CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5);
589 CommandPacket.QueueType = NVME_ADMIN_QUEUE;
590 //
591 // Set bit 0 (Cns bit) to 1 to identify a controller
592 //
593 Command.Cdw10 = 1;
594 Command.Flags = CDW10_VALID;
595
596 Status = NvmePassthru->PassThru (
597 NvmePassthru,
598 0,
599 &CommandPacket,
600 NULL
601 );
602 if (EFI_ERROR (Status)) {
603 return NULL;
604 }
605
606 Description = AllocateZeroPool (
607 (ARRAY_SIZE (ControllerData.Mn) + 1
608 + ARRAY_SIZE (ControllerData.Sn) + 1
609 + MAXIMUM_VALUE_CHARACTERS + 1
610 ) * sizeof (CHAR16));
611 if (Description != NULL) {
612 Char = Description;
613 for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) {
614 *(Char++) = (CHAR16) ControllerData.Mn[Index];
615 }
616 *(Char++) = L' ';
617 for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) {
618 *(Char++) = (CHAR16) ControllerData.Sn[Index];
619 }
620 *(Char++) = L' ';
621 UnicodeValueToStringS (
622 Char, sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1),
623 0, DevicePath.NvmeNamespace->NamespaceId, 0
624 );
625 BmEliminateExtraSpaces (Description);
626 }
627
628 return Description;
629 }
630
631 /**
632 Return the boot description for the controller based on the type.
633
634 @param Handle Controller handle.
635
636 @return The description string.
637 **/
638 CHAR16 *
639 BmGetMiscDescription (
640 IN EFI_HANDLE Handle
641 )
642 {
643 EFI_STATUS Status;
644 CHAR16 *Description;
645 EFI_BLOCK_IO_PROTOCOL *BlockIo;
646 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
647
648 switch (BmDevicePathType (DevicePathFromHandle (Handle))) {
649 case BmAcpiFloppyBoot:
650 Description = L"Floppy";
651 break;
652
653 case BmMessageAtapiBoot:
654 case BmMessageSataBoot:
655 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
656 ASSERT_EFI_ERROR (Status);
657 //
658 // Assume a removable SATA device should be the DVD/CD device
659 //
660 Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
661 break;
662
663 case BmMessageUsbBoot:
664 Description = L"USB Device";
665 break;
666
667 case BmMessageScsiBoot:
668 Description = L"SCSI Device";
669 break;
670
671 case BmHardwareDeviceBoot:
672 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
673 if (!EFI_ERROR (Status)) {
674 Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
675 } else {
676 Description = L"Misc Device";
677 }
678 break;
679
680 default:
681 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);
682 if (!EFI_ERROR (Status)) {
683 Description = L"Non-Block Boot Device";
684 } else {
685 Description = L"Misc Device";
686 }
687 break;
688 }
689
690 return AllocateCopyPool (StrSize (Description), Description);
691 }
692
693 /**
694 Register the platform provided boot description handler.
695
696 @param Handler The platform provided boot description handler
697
698 @retval EFI_SUCCESS The handler was registered successfully.
699 @retval EFI_ALREADY_STARTED The handler was already registered.
700 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
701 **/
702 EFI_STATUS
703 EFIAPI
704 EfiBootManagerRegisterBootDescriptionHandler (
705 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
706 )
707 {
708 LIST_ENTRY *Link;
709 BM_BOOT_DESCRIPTION_ENTRY *Entry;
710
711 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
712 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
713 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
714 ) {
715 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
716 if (Entry->Handler == Handler) {
717 return EFI_ALREADY_STARTED;
718 }
719 }
720
721 Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));
722 if (Entry == NULL) {
723 return EFI_OUT_OF_RESOURCES;
724 }
725
726 Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;
727 Entry->Handler = Handler;
728 InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);
729 return EFI_SUCCESS;
730 }
731
732 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {
733 BmGetUsbDescription,
734 BmGetDescriptionFromDiskInfo,
735 BmGetNetworkDescription,
736 BmGetLoadFileDescription,
737 BmGetNvmeDescription,
738 BmGetMiscDescription
739 };
740
741 /**
742 Return the boot description for the controller.
743
744 @param Handle Controller handle.
745
746 @return The description string.
747 **/
748 CHAR16 *
749 BmGetBootDescription (
750 IN EFI_HANDLE Handle
751 )
752 {
753 LIST_ENTRY *Link;
754 BM_BOOT_DESCRIPTION_ENTRY *Entry;
755 CHAR16 *Description;
756 CHAR16 *DefaultDescription;
757 CHAR16 *Temp;
758 UINTN Index;
759
760 //
761 // Firstly get the default boot description
762 //
763 DefaultDescription = NULL;
764 for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) {
765 DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);
766 if (DefaultDescription != NULL) {
767 //
768 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
769 // ONLY for core provided boot description handler.
770 //
771 Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));
772 ASSERT (Temp != NULL);
773 StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);
774 StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);
775 FreePool (DefaultDescription);
776 DefaultDescription = Temp;
777 break;
778 }
779 }
780 ASSERT (DefaultDescription != NULL);
781
782 //
783 // Secondly query platform for the better boot description
784 //
785 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
786 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
787 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
788 ) {
789 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
790 Description = Entry->Handler (Handle, DefaultDescription);
791 if (Description != NULL) {
792 FreePool (DefaultDescription);
793 return Description;
794 }
795 }
796
797 return DefaultDescription;
798 }
799
800 /**
801 Enumerate all boot option descriptions and append " 2"/" 3"/... to make
802 unique description.
803
804 @param BootOptions Array of boot options.
805 @param BootOptionCount Count of boot options.
806 **/
807 VOID
808 BmMakeBootOptionDescriptionUnique (
809 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
810 UINTN BootOptionCount
811 )
812 {
813 UINTN Base;
814 UINTN Index;
815 UINTN DescriptionSize;
816 UINTN MaxSuffixSize;
817 BOOLEAN *Visited;
818 UINTN MatchCount;
819
820 if (BootOptionCount == 0) {
821 return;
822 }
823
824 //
825 // Calculate the maximum buffer size for the number suffix.
826 // The initial sizeof (CHAR16) is for the blank space before the number.
827 //
828 MaxSuffixSize = sizeof (CHAR16);
829 for (Index = BootOptionCount; Index != 0; Index = Index / 10) {
830 MaxSuffixSize += sizeof (CHAR16);
831 }
832
833 Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);
834 ASSERT (Visited != NULL);
835
836 for (Base = 0; Base < BootOptionCount; Base++) {
837 if (!Visited[Base]) {
838 MatchCount = 1;
839 Visited[Base] = TRUE;
840 DescriptionSize = StrSize (BootOptions[Base].Description);
841 for (Index = Base + 1; Index < BootOptionCount; Index++) {
842 if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {
843 Visited[Index] = TRUE;
844 MatchCount++;
845 FreePool (BootOptions[Index].Description);
846 BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);
847 UnicodeSPrint (
848 BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,
849 L"%s %d",
850 BootOptions[Base].Description, MatchCount
851 );
852 }
853 }
854 }
855 }
856
857 FreePool (Visited);
858 }