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