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