]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
MdeModulePkg/UefiBootManagerLib: Separate boot description functions.
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBootDescription.c
1 /** @file
2 Library functions which relate with boot option description.
3
4 Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
5 (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions of the BSD License
8 which accompanies this distribution. The full text of the license may be found at
9 http://opensource.org/licenses/bsd-license.php
10
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13
14 **/
15
16 #include "InternalBm.h"
17
18 #define VENDOR_IDENTIFICATION_OFFSET 3
19 #define VENDOR_IDENTIFICATION_LENGTH 8
20 #define PRODUCT_IDENTIFICATION_OFFSET 11
21 #define PRODUCT_IDENTIFICATION_LENGTH 16
22
23 CONST UINT16 mBmUsbLangId = 0x0409; // English
24 CHAR16 mBmUefiPrefix[] = L"UEFI ";
25
26 LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers);
27
28 /**
29 For a bootable Device path, return its boot type.
30
31 @param DevicePath The bootable device Path to check
32
33 @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node
34 which HID is floppy device.
35 @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
36 and its last device path node's subtype is MSG_ATAPI_DP.
37 @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
38 and its last device path node's subtype is MSG_SATA_DP.
39 @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
40 and its last device path node's subtype is MSG_SCSI_DP.
41 @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
42 and its last device path node's subtype is MSG_USB_DP.
43 @retval MessageNetworkBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
44 and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP,
45 MSG_IPv4_DP or MSG_IPv6_DP.
46 @retval MessageHttpBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
47 and its last device path node's subtype is MSG_URI_DP.
48 @retval UnsupportedBoot If tiven device path doesn't match the above condition, it's not supported.
49
50 **/
51 BM_BOOT_TYPE
52 BmDevicePathType (
53 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
54 )
55 {
56 EFI_DEVICE_PATH_PROTOCOL *Node;
57 EFI_DEVICE_PATH_PROTOCOL *NextNode;
58
59 ASSERT (DevicePath != NULL);
60
61 for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
62 switch (DevicePathType (Node)) {
63
64 case ACPI_DEVICE_PATH:
65 if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {
66 return BmAcpiFloppyBoot;
67 }
68 break;
69
70 case HARDWARE_DEVICE_PATH:
71 if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {
72 return BmHardwareDeviceBoot;
73 }
74 break;
75
76 case MESSAGING_DEVICE_PATH:
77 //
78 // Skip LUN device node
79 //
80 NextNode = Node;
81 do {
82 NextNode = NextDevicePathNode (NextNode);
83 } while (
84 (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&
85 (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)
86 );
87
88 //
89 // If the device path not only point to driver device, it is not a messaging device path,
90 //
91 if (!IsDevicePathEndType (NextNode)) {
92 continue;
93 }
94
95 switch (DevicePathSubType (Node)) {
96 case MSG_ATAPI_DP:
97 return BmMessageAtapiBoot;
98 break;
99
100 case MSG_SATA_DP:
101 return BmMessageSataBoot;
102 break;
103
104 case MSG_USB_DP:
105 return BmMessageUsbBoot;
106 break;
107
108 case MSG_SCSI_DP:
109 return BmMessageScsiBoot;
110 break;
111
112 case MSG_MAC_ADDR_DP:
113 case MSG_VLAN_DP:
114 case MSG_IPv4_DP:
115 case MSG_IPv6_DP:
116 return BmMessageNetworkBoot;
117 break;
118
119 case MSG_URI_DP:
120 return BmMessageHttpBoot;
121 break;
122 }
123 }
124 }
125
126 return BmMiscBoot;
127 }
128
129 /**
130 Eliminate the extra spaces in the Str to one space.
131
132 @param Str Input string info.
133 **/
134 VOID
135 BmEliminateExtraSpaces (
136 IN CHAR16 *Str
137 )
138 {
139 UINTN Index;
140 UINTN ActualIndex;
141
142 for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {
143 if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {
144 Str[ActualIndex++] = Str[Index];
145 }
146 }
147 Str[ActualIndex] = L'\0';
148 }
149
150 /**
151 Try to get the controller's ATA/ATAPI description.
152
153 @param Handle Controller handle.
154
155 @return The description string.
156 **/
157 CHAR16 *
158 BmGetDescriptionFromDiskInfo (
159 IN EFI_HANDLE Handle
160 )
161 {
162 UINTN Index;
163 EFI_STATUS Status;
164 EFI_DISK_INFO_PROTOCOL *DiskInfo;
165 UINT32 BufferSize;
166 EFI_ATAPI_IDENTIFY_DATA IdentifyData;
167 EFI_SCSI_INQUIRY_DATA InquiryData;
168 CHAR16 *Description;
169 UINTN Length;
170 CONST UINTN ModelNameLength = 40;
171 CONST UINTN SerialNumberLength = 20;
172 CHAR8 *StrPtr;
173 UINT8 Temp;
174
175 Description = NULL;
176
177 Status = gBS->HandleProtocol (
178 Handle,
179 &gEfiDiskInfoProtocolGuid,
180 (VOID **) &DiskInfo
181 );
182 if (EFI_ERROR (Status)) {
183 return NULL;
184 }
185
186 if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||
187 CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
188 BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);
189 Status = DiskInfo->Identify (
190 DiskInfo,
191 &IdentifyData,
192 &BufferSize
193 );
194 if (!EFI_ERROR (Status)) {
195 Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
196 ASSERT (Description != NULL);
197 for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
198 Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1];
199 Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];
200 }
201
202 Length = Index;
203 Description[Length++] = L' ';
204
205 for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
206 Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1];
207 Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];
208 }
209 Length += Index;
210 Description[Length++] = L'\0';
211 ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
212
213 BmEliminateExtraSpaces (Description);
214 }
215 } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
216 BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);
217 Status = DiskInfo->Inquiry (
218 DiskInfo,
219 &InquiryData,
220 &BufferSize
221 );
222 if (!EFI_ERROR (Status)) {
223 Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
224 ASSERT (Description != NULL);
225
226 //
227 // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
228 // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
229 // Here combine the vendor identification and product identification to the description.
230 //
231 StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
232 Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
233 StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
234 AsciiStrToUnicodeStr (StrPtr, Description);
235 StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;
236
237 //
238 // Add one space at the middle of vendor information and product information.
239 //
240 Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';
241
242 StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);
243 StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';
244 AsciiStrToUnicodeStr (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1);
245
246 BmEliminateExtraSpaces (Description);
247 }
248 }
249
250 return Description;
251 }
252
253 /**
254 Try to get the controller's USB description.
255
256 @param Handle Controller handle.
257
258 @return The description string.
259 **/
260 CHAR16 *
261 BmGetUsbDescription (
262 IN EFI_HANDLE Handle
263 )
264 {
265 EFI_STATUS Status;
266 EFI_USB_IO_PROTOCOL *UsbIo;
267 CHAR16 NullChar;
268 CHAR16 *Manufacturer;
269 CHAR16 *Product;
270 CHAR16 *SerialNumber;
271 CHAR16 *Description;
272 EFI_USB_DEVICE_DESCRIPTOR DevDesc;
273 UINTN DescMaxSize;
274
275 Status = gBS->HandleProtocol (
276 Handle,
277 &gEfiUsbIoProtocolGuid,
278 (VOID **) &UsbIo
279 );
280 if (EFI_ERROR (Status)) {
281 return NULL;
282 }
283
284 NullChar = L'\0';
285
286 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
287 if (EFI_ERROR (Status)) {
288 return NULL;
289 }
290
291 Status = UsbIo->UsbGetStringDescriptor (
292 UsbIo,
293 mBmUsbLangId,
294 DevDesc.StrManufacturer,
295 &Manufacturer
296 );
297 if (EFI_ERROR (Status)) {
298 Manufacturer = &NullChar;
299 }
300
301 Status = UsbIo->UsbGetStringDescriptor (
302 UsbIo,
303 mBmUsbLangId,
304 DevDesc.StrProduct,
305 &Product
306 );
307 if (EFI_ERROR (Status)) {
308 Product = &NullChar;
309 }
310
311 Status = UsbIo->UsbGetStringDescriptor (
312 UsbIo,
313 mBmUsbLangId,
314 DevDesc.StrSerialNumber,
315 &SerialNumber
316 );
317 if (EFI_ERROR (Status)) {
318 SerialNumber = &NullChar;
319 }
320
321 if ((Manufacturer == &NullChar) &&
322 (Product == &NullChar) &&
323 (SerialNumber == &NullChar)
324 ) {
325 return NULL;
326 }
327
328 DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber);
329 Description = AllocateZeroPool (DescMaxSize);
330 ASSERT (Description != NULL);
331 StrCatS (Description, DescMaxSize/sizeof(CHAR16), Manufacturer);
332 StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
333
334 StrCatS (Description, DescMaxSize/sizeof(CHAR16), Product);
335 StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
336
337 StrCatS (Description, DescMaxSize/sizeof(CHAR16), SerialNumber);
338
339 if (Manufacturer != &NullChar) {
340 FreePool (Manufacturer);
341 }
342 if (Product != &NullChar) {
343 FreePool (Product);
344 }
345 if (SerialNumber != &NullChar) {
346 FreePool (SerialNumber);
347 }
348
349 BmEliminateExtraSpaces (Description);
350
351 return Description;
352 }
353
354 /**
355 Return the boot description for the controller based on the type.
356
357 @param Handle Controller handle.
358
359 @return The description string.
360 **/
361 CHAR16 *
362 BmGetMiscDescription (
363 IN EFI_HANDLE Handle
364 )
365 {
366 EFI_STATUS Status;
367 CHAR16 *Description;
368 EFI_BLOCK_IO_PROTOCOL *BlockIo;
369 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
370
371 switch (BmDevicePathType (DevicePathFromHandle (Handle))) {
372 case BmAcpiFloppyBoot:
373 Description = L"Floppy";
374 break;
375
376 case BmMessageAtapiBoot:
377 case BmMessageSataBoot:
378 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
379 ASSERT_EFI_ERROR (Status);
380 //
381 // Assume a removable SATA device should be the DVD/CD device
382 //
383 Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
384 break;
385
386 case BmMessageUsbBoot:
387 Description = L"USB Device";
388 break;
389
390 case BmMessageScsiBoot:
391 Description = L"SCSI Device";
392 break;
393
394 case BmHardwareDeviceBoot:
395 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
396 if (!EFI_ERROR (Status)) {
397 Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
398 } else {
399 Description = L"Misc Device";
400 }
401 break;
402
403 case BmMessageNetworkBoot:
404 Description = L"Network";
405 break;
406
407 case BmMessageHttpBoot:
408 Description = L"Http";
409 break;
410
411 default:
412 Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);
413 if (!EFI_ERROR (Status)) {
414 Description = L"Non-Block Boot Device";
415 } else {
416 Description = L"Misc Device";
417 }
418 break;
419 }
420
421 return AllocateCopyPool (StrSize (Description), Description);
422 }
423
424 /**
425 Register the platform provided boot description handler.
426
427 @param Handler The platform provided boot description handler
428
429 @retval EFI_SUCCESS The handler was registered successfully.
430 @retval EFI_ALREADY_STARTED The handler was already registered.
431 @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
432 **/
433 EFI_STATUS
434 EFIAPI
435 EfiBootManagerRegisterBootDescriptionHandler (
436 IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
437 )
438 {
439 LIST_ENTRY *Link;
440 BM_BOOT_DESCRIPTION_ENTRY *Entry;
441
442 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
443 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
444 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
445 ) {
446 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
447 if (Entry->Handler == Handler) {
448 return EFI_ALREADY_STARTED;
449 }
450 }
451
452 Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));
453 if (Entry == NULL) {
454 return EFI_OUT_OF_RESOURCES;
455 }
456
457 Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;
458 Entry->Handler = Handler;
459 InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);
460 return EFI_SUCCESS;
461 }
462
463 BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {
464 BmGetUsbDescription,
465 BmGetDescriptionFromDiskInfo,
466 BmGetMiscDescription
467 };
468
469 /**
470 Return the boot description for the controller.
471
472 @param Handle Controller handle.
473
474 @return The description string.
475 **/
476 CHAR16 *
477 BmGetBootDescription (
478 IN EFI_HANDLE Handle
479 )
480 {
481 LIST_ENTRY *Link;
482 BM_BOOT_DESCRIPTION_ENTRY *Entry;
483 CHAR16 *Description;
484 CHAR16 *DefaultDescription;
485 CHAR16 *Temp;
486 UINTN Index;
487
488 //
489 // Firstly get the default boot description
490 //
491 DefaultDescription = NULL;
492 for (Index = 0; Index < sizeof (mBmBootDescriptionHandlers) / sizeof (mBmBootDescriptionHandlers[0]); Index++) {
493 DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);
494 if (DefaultDescription != NULL) {
495 //
496 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
497 // ONLY for core provided boot description handler.
498 //
499 Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));
500 ASSERT (Temp != NULL);
501 StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);
502 StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);
503 FreePool (DefaultDescription);
504 DefaultDescription = Temp;
505 break;
506 }
507 }
508 ASSERT (DefaultDescription != NULL);
509
510 //
511 // Secondly query platform for the better boot description
512 //
513 for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
514 ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
515 ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
516 ) {
517 Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
518 Description = Entry->Handler (Handle, DefaultDescription);
519 if (Description != NULL) {
520 FreePool (DefaultDescription);
521 return Description;
522 }
523 }
524
525 return DefaultDescription;
526 }
527
528 /**
529 Enumerate all boot option descriptions and append " 2"/" 3"/... to make
530 unique description.
531
532 @param BootOptions Array of boot options.
533 @param BootOptionCount Count of boot options.
534 **/
535 VOID
536 BmMakeBootOptionDescriptionUnique (
537 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
538 UINTN BootOptionCount
539 )
540 {
541 UINTN Base;
542 UINTN Index;
543 UINTN DescriptionSize;
544 UINTN MaxSuffixSize;
545 BOOLEAN *Visited;
546 UINTN MatchCount;
547
548 if (BootOptionCount == 0) {
549 return;
550 }
551
552 //
553 // Calculate the maximum buffer size for the number suffix.
554 // The initial sizeof (CHAR16) is for the blank space before the number.
555 //
556 MaxSuffixSize = sizeof (CHAR16);
557 for (Index = BootOptionCount; Index != 0; Index = Index / 10) {
558 MaxSuffixSize += sizeof (CHAR16);
559 }
560
561 Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);
562 ASSERT (Visited != NULL);
563
564 for (Base = 0; Base < BootOptionCount; Base++) {
565 if (!Visited[Base]) {
566 MatchCount = 1;
567 Visited[Base] = TRUE;
568 DescriptionSize = StrSize (BootOptions[Base].Description);
569 for (Index = Base + 1; Index < BootOptionCount; Index++) {
570 if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {
571 Visited[Index] = TRUE;
572 MatchCount++;
573 FreePool (BootOptions[Index].Description);
574 BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);
575 UnicodeSPrint (
576 BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,
577 L"%s %d",
578 BootOptions[Base].Description, MatchCount
579 );
580 }
581 }
582 }
583 }
584
585 FreePool (Visited);
586 }