]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
MdeModulePkg/CapsuleLib: Free the buffer returned by GetVariable2 API
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBootDescription.c
... / ...
CommitLineData
1/** @file\r
2 Library functions which relate with boot option description.\r
3\r
4Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>\r
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
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
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
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
218 AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1);\r
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
228 AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1);\r
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
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
385 // ....../Mac(...)[/Vlan(...)]\r
386 // ....../Mac(...)[/Vlan(...)]/IPv4(...)\r
387 // ....../Mac(...)[/Vlan(...)]/IPv6(...)\r
388 //\r
389 // The HTTP device path is like:\r
390 // ....../Mac(...)[/Vlan(...)]/IPv4(...)/Uri(...)\r
391 // ....../Mac(...)[/Vlan(...)]/IPv6(...)/Uri(...)\r
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
407 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
408 (DevicePathSubType (DevicePath) == MSG_VLAN_DP)\r
409 ) {\r
410 Vlan = (VLAN_DEVICE_PATH *) DevicePath;\r
411 DevicePath = NextDevicePathNode (DevicePath);\r
412 } else {\r
413 Vlan = NULL;\r
414 }\r
415\r
416 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
417 ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||\r
418 (DevicePathSubType (DevicePath) == MSG_IPv6_DP))\r
419 ) {\r
420 Ip = DevicePath;\r
421 DevicePath = NextDevicePathNode (DevicePath);\r
422 } else {\r
423 Ip = NULL;\r
424 }\r
425\r
426 if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&\r
427 (DevicePathSubType (DevicePath) == MSG_URI_DP)\r
428 ) {\r
429 Uri = DevicePath;\r
430 DevicePath = NextDevicePathNode (DevicePath);\r
431 } else {\r
432 Uri = NULL;\r
433 }\r
434\r
435 //\r
436 // Build description like below:\r
437 // "PXEv6 (MAC:112233445566 VLAN1)"\r
438 // "HTTPv4 (MAC:112233445566)"\r
439 //\r
440 DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");\r
441 Description = AllocatePool (DescriptionSize);\r
442 ASSERT (Description != NULL);\r
443 UnicodeSPrint (\r
444 Description, DescriptionSize,\r
445 (Vlan == NULL) ?\r
446 L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :\r
447 L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",\r
448 (Uri == NULL) ? L"PXE" : L"HTTP",\r
449 ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,\r
450 Mac->MacAddress.Addr[0], Mac->MacAddress.Addr[1], Mac->MacAddress.Addr[2],\r
451 Mac->MacAddress.Addr[3], Mac->MacAddress.Addr[4], Mac->MacAddress.Addr[5],\r
452 (Vlan == NULL) ? 0 : Vlan->VlanId\r
453 );\r
454 return Description;\r
455}\r
456\r
457/**\r
458 Return the boot description for LoadFile\r
459\r
460 @param Handle Controller handle.\r
461\r
462 @return The description string.\r
463**/\r
464CHAR16 *\r
465BmGetLoadFileDescription (\r
466 IN EFI_HANDLE Handle\r
467 )\r
468{\r
469 EFI_STATUS Status;\r
470 EFI_DEVICE_PATH_PROTOCOL *FilePath;\r
471 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;\r
472 CHAR16 *Description;\r
473 EFI_LOAD_FILE_PROTOCOL *LoadFile;\r
474\r
475 Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);\r
476 if (EFI_ERROR (Status)) {\r
477 return NULL;\r
478 }\r
479\r
480 //\r
481 // Get the file name\r
482 //\r
483 Description = NULL;\r
484 Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath);\r
485 if (!EFI_ERROR (Status)) {\r
486 DevicePathNode = FilePath;\r
487 while (!IsDevicePathEnd (DevicePathNode)) {\r
488 if (DevicePathNode->Type == MEDIA_DEVICE_PATH && DevicePathNode->SubType == MEDIA_FILEPATH_DP) {\r
489 Description = (CHAR16 *)(DevicePathNode + 1);\r
490 break;\r
491 }\r
492 DevicePathNode = NextDevicePathNode (DevicePathNode);\r
493 }\r
494 }\r
495\r
496 if (Description != NULL) {\r
497 return AllocateCopyPool (StrSize (Description), Description);\r
498 }\r
499\r
500 return NULL;\r
501}\r
502\r
503/**\r
504 Return the boot description for the controller based on the type.\r
505\r
506 @param Handle Controller handle.\r
507\r
508 @return The description string.\r
509**/\r
510CHAR16 *\r
511BmGetMiscDescription (\r
512 IN EFI_HANDLE Handle\r
513 )\r
514{\r
515 EFI_STATUS Status;\r
516 CHAR16 *Description;\r
517 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
518 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
519\r
520 switch (BmDevicePathType (DevicePathFromHandle (Handle))) {\r
521 case BmAcpiFloppyBoot:\r
522 Description = L"Floppy";\r
523 break;\r
524\r
525 case BmMessageAtapiBoot:\r
526 case BmMessageSataBoot:\r
527 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
528 ASSERT_EFI_ERROR (Status);\r
529 //\r
530 // Assume a removable SATA device should be the DVD/CD device\r
531 //\r
532 Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";\r
533 break;\r
534\r
535 case BmMessageUsbBoot:\r
536 Description = L"USB Device";\r
537 break;\r
538\r
539 case BmMessageScsiBoot:\r
540 Description = L"SCSI Device";\r
541 break;\r
542\r
543 case BmHardwareDeviceBoot:\r
544 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
545 if (!EFI_ERROR (Status)) {\r
546 Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";\r
547 } else {\r
548 Description = L"Misc Device";\r
549 }\r
550 break;\r
551\r
552 default:\r
553 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);\r
554 if (!EFI_ERROR (Status)) {\r
555 Description = L"Non-Block Boot Device";\r
556 } else {\r
557 Description = L"Misc Device";\r
558 }\r
559 break;\r
560 }\r
561\r
562 return AllocateCopyPool (StrSize (Description), Description);\r
563}\r
564\r
565/**\r
566 Register the platform provided boot description handler.\r
567\r
568 @param Handler The platform provided boot description handler\r
569\r
570 @retval EFI_SUCCESS The handler was registered successfully.\r
571 @retval EFI_ALREADY_STARTED The handler was already registered.\r
572 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.\r
573**/\r
574EFI_STATUS\r
575EFIAPI\r
576EfiBootManagerRegisterBootDescriptionHandler (\r
577 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler\r
578 )\r
579{\r
580 LIST_ENTRY *Link;\r
581 BM_BOOT_DESCRIPTION_ENTRY *Entry;\r
582\r
583 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)\r
584 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)\r
585 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)\r
586 ) {\r
587 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);\r
588 if (Entry->Handler == Handler) {\r
589 return EFI_ALREADY_STARTED;\r
590 }\r
591 }\r
592\r
593 Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));\r
594 if (Entry == NULL) {\r
595 return EFI_OUT_OF_RESOURCES;\r
596 }\r
597\r
598 Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;\r
599 Entry->Handler = Handler;\r
600 InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);\r
601 return EFI_SUCCESS;\r
602}\r
603\r
604BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {\r
605 BmGetUsbDescription,\r
606 BmGetDescriptionFromDiskInfo,\r
607 BmGetNetworkDescription,\r
608 BmGetLoadFileDescription,\r
609 BmGetMiscDescription\r
610};\r
611\r
612/**\r
613 Return the boot description for the controller.\r
614\r
615 @param Handle Controller handle.\r
616\r
617 @return The description string.\r
618**/\r
619CHAR16 *\r
620BmGetBootDescription (\r
621 IN EFI_HANDLE Handle\r
622 )\r
623{\r
624 LIST_ENTRY *Link;\r
625 BM_BOOT_DESCRIPTION_ENTRY *Entry;\r
626 CHAR16 *Description;\r
627 CHAR16 *DefaultDescription;\r
628 CHAR16 *Temp;\r
629 UINTN Index;\r
630\r
631 //\r
632 // Firstly get the default boot description\r
633 //\r
634 DefaultDescription = NULL;\r
635 for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) {\r
636 DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);\r
637 if (DefaultDescription != NULL) {\r
638 //\r
639 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix\r
640 // ONLY for core provided boot description handler.\r
641 //\r
642 Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));\r
643 ASSERT (Temp != NULL);\r
644 StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);\r
645 StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);\r
646 FreePool (DefaultDescription);\r
647 DefaultDescription = Temp;\r
648 break;\r
649 }\r
650 }\r
651 ASSERT (DefaultDescription != NULL);\r
652\r
653 //\r
654 // Secondly query platform for the better boot description\r
655 //\r
656 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)\r
657 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)\r
658 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)\r
659 ) {\r
660 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);\r
661 Description = Entry->Handler (Handle, DefaultDescription);\r
662 if (Description != NULL) {\r
663 FreePool (DefaultDescription);\r
664 return Description;\r
665 }\r
666 }\r
667\r
668 return DefaultDescription;\r
669}\r
670\r
671/**\r
672 Enumerate all boot option descriptions and append " 2"/" 3"/... to make\r
673 unique description.\r
674\r
675 @param BootOptions Array of boot options.\r
676 @param BootOptionCount Count of boot options.\r
677**/\r
678VOID\r
679BmMakeBootOptionDescriptionUnique (\r
680 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,\r
681 UINTN BootOptionCount\r
682 )\r
683{\r
684 UINTN Base;\r
685 UINTN Index;\r
686 UINTN DescriptionSize;\r
687 UINTN MaxSuffixSize;\r
688 BOOLEAN *Visited;\r
689 UINTN MatchCount;\r
690\r
691 if (BootOptionCount == 0) {\r
692 return;\r
693 }\r
694\r
695 //\r
696 // Calculate the maximum buffer size for the number suffix.\r
697 // The initial sizeof (CHAR16) is for the blank space before the number.\r
698 //\r
699 MaxSuffixSize = sizeof (CHAR16);\r
700 for (Index = BootOptionCount; Index != 0; Index = Index / 10) {\r
701 MaxSuffixSize += sizeof (CHAR16);\r
702 }\r
703\r
704 Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);\r
705 ASSERT (Visited != NULL);\r
706\r
707 for (Base = 0; Base < BootOptionCount; Base++) {\r
708 if (!Visited[Base]) {\r
709 MatchCount = 1;\r
710 Visited[Base] = TRUE;\r
711 DescriptionSize = StrSize (BootOptions[Base].Description);\r
712 for (Index = Base + 1; Index < BootOptionCount; Index++) {\r
713 if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {\r
714 Visited[Index] = TRUE;\r
715 MatchCount++;\r
716 FreePool (BootOptions[Index].Description);\r
717 BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);\r
718 UnicodeSPrint (\r
719 BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,\r
720 L"%s %d",\r
721 BootOptions[Base].Description, MatchCount\r
722 );\r
723 }\r
724 }\r
725 }\r
726 }\r
727\r
728 FreePool (Visited);\r
729}\r