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