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