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