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