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