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