]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
Add Sample I2C Library for Baytrail I2C Controller.
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBoot.c
CommitLineData
067ed98a
RN
1/** @file\r
2 Library functions which relates with booting.\r
3\r
4Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>\r
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "InternalBm.h"\r
16\r
17#define VENDOR_IDENTIFICATION_OFFSET 3\r
18#define VENDOR_IDENTIFICATION_LENGTH 8\r
19#define PRODUCT_IDENTIFICATION_OFFSET 11\r
20#define PRODUCT_IDENTIFICATION_LENGTH 16\r
21\r
22CONST UINT16 mBmUsbLangId = 0x0409; // English\r
23CHAR16 mBmUefiPrefix[] = L"UEFI ";\r
24\r
25EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL;\r
26EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL;\r
27\r
28///\r
29/// This GUID is used for an EFI Variable that stores the front device pathes\r
30/// for a partial device path that starts with the HD node.\r
31///\r
32EFI_GUID mBmHardDriveBootVariableGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } };\r
33EFI_GUID mBmAutoCreateBootOptionGuid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } };\r
34\r
35/**\r
36 The function registers the legacy boot support capabilities.\r
37\r
38 @param RefreshLegacyBootOption The function pointer to create all the legacy boot options.\r
39 @param LegacyBoot The function pointer to boot the legacy boot option.\r
40**/\r
41VOID\r
42EFIAPI\r
43EfiBootManagerRegisterLegacyBootSupport (\r
44 EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption,\r
45 EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot\r
46 )\r
47{\r
48 mBmRefreshLegacyBootOption = RefreshLegacyBootOption;\r
49 mBmLegacyBoot = LegacyBoot;\r
50}\r
51\r
52/**\r
53 For a bootable Device path, return its boot type.\r
54\r
55 @param DevicePath The bootable device Path to check\r
56\r
57 @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node\r
58 which HID is floppy device.\r
59 @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
60 and its last device path node's subtype is MSG_ATAPI_DP.\r
61 @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
62 and its last device path node's subtype is MSG_SATA_DP.\r
63 @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
64 and its last device path node's subtype is MSG_SCSI_DP.\r
65 @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
66 and its last device path node's subtype is MSG_USB_DP.\r
67 @retval MessageNetworkBoot If given device path contains MESSAGING_DEVICE_PATH type device path node\r
68 and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP,\r
69 MSG_IPv4_DP or MSG_IPv6_DP.\r
70 @retval UnsupportedBoot If tiven device path doesn't match the above condition, it's not supported.\r
71\r
72**/\r
73BM_BOOT_TYPE\r
74BmDevicePathType (\r
75 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
76 )\r
77{\r
78 EFI_DEVICE_PATH_PROTOCOL *Node;\r
79 EFI_DEVICE_PATH_PROTOCOL *NextNode;\r
80\r
81 ASSERT (DevicePath != NULL);\r
82\r
83 for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {\r
84 switch (DevicePathType (Node)) {\r
85\r
86 case ACPI_DEVICE_PATH:\r
87 if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {\r
88 return BmAcpiFloppyBoot;\r
89 }\r
90 break;\r
91\r
92 case HARDWARE_DEVICE_PATH:\r
93 if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {\r
94 return BmHardwareDeviceBoot;\r
95 }\r
96 break;\r
97\r
98 case MESSAGING_DEVICE_PATH:\r
99 //\r
100 // Skip LUN device node\r
101 //\r
102 NextNode = Node;\r
103 do {\r
104 NextNode = NextDevicePathNode (NextNode);\r
105 } while (\r
106 (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&\r
107 (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)\r
108 );\r
109\r
110 //\r
111 // If the device path not only point to driver device, it is not a messaging device path,\r
112 //\r
113 if (!IsDevicePathEndType (NextNode)) {\r
114 break;\r
115 }\r
116\r
117 switch (DevicePathSubType (Node)) {\r
118 case MSG_ATAPI_DP:\r
119 return BmMessageAtapiBoot;\r
120 break;\r
121\r
122 case MSG_SATA_DP:\r
123 return BmMessageSataBoot;\r
124 break;\r
125\r
126 case MSG_USB_DP:\r
127 return BmMessageUsbBoot;\r
128 break;\r
129\r
130 case MSG_SCSI_DP:\r
131 return BmMessageScsiBoot;\r
132 break;\r
133\r
134 case MSG_MAC_ADDR_DP:\r
135 case MSG_VLAN_DP:\r
136 case MSG_IPv4_DP:\r
137 case MSG_IPv6_DP:\r
138 return BmMessageNetworkBoot;\r
139 break;\r
140 }\r
141 }\r
142 }\r
143\r
144 return BmMiscBoot;\r
145}\r
146\r
147/**\r
148 Find the boot option in the NV storage and return the option number.\r
149\r
150 @param OptionToFind Boot option to be checked.\r
151\r
152 @return The option number of the found boot option.\r
153\r
154**/\r
155UINTN\r
156BmFindBootOptionInVariable (\r
157 IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind\r
158 )\r
159{\r
160 EFI_STATUS Status;\r
161 EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
162 UINTN OptionNumber;\r
163 CHAR16 OptionName[BM_OPTION_NAME_LEN];\r
164 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
165 UINTN BootOptionCount;\r
166 UINTN Index;\r
167 \r
168 OptionNumber = LoadOptionNumberUnassigned;\r
169\r
170 //\r
171 // Try to match the variable exactly if the option number is assigned\r
172 //\r
173 if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) {\r
174 UnicodeSPrint (\r
175 OptionName, sizeof (OptionName), L"%s%04x",\r
176 mBmLoadOptionName[OptionToFind->OptionType], OptionToFind->OptionNumber\r
177 );\r
178 Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);\r
179\r
180 if (!EFI_ERROR (Status)) {\r
181 ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber);\r
182 if ((OptionToFind->Attributes == BootOption.Attributes) &&\r
183 (StrCmp (OptionToFind->Description, BootOption.Description) == 0) &&\r
184 (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) &&\r
185 (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) &&\r
186 (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0)\r
187 ) {\r
188 OptionNumber = OptionToFind->OptionNumber;\r
189 }\r
190 EfiBootManagerFreeLoadOption (&BootOption);\r
191 }\r
192 }\r
193\r
194 //\r
195 // The option number assigned is either incorrect or unassigned.\r
196 //\r
197 if (OptionNumber == LoadOptionNumberUnassigned) {\r
198 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
199\r
200 Index = BmFindLoadOption (OptionToFind, BootOptions, BootOptionCount);\r
201 if (Index != -1) {\r
202 OptionNumber = BootOptions[Index].OptionNumber;\r
203 }\r
204\r
205 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
206 }\r
207\r
208 return OptionNumber;\r
209}\r
210\r
211/**\r
212 Get the file buffer using a Memory Mapped Device Path.\r
213\r
214 FV address may change across reboot. This routine promises the FV file device path is right.\r
215\r
216 @param DevicePath The Memory Mapped Device Path to get the file buffer.\r
217 @param FullPath Receive the updated FV Device Path pointint to the file.\r
218 @param FileSize Receive the file buffer size.\r
219\r
220 @return The file buffer.\r
221**/\r
222VOID *\r
223BmGetFileBufferByMemmapFv (\r
224 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
225 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
226 OUT UINTN *FileSize\r
227 )\r
228{\r
229 EFI_STATUS Status;\r
230 UINTN Index;\r
231 EFI_DEVICE_PATH_PROTOCOL *FvFileNode;\r
232 EFI_HANDLE FvHandle;\r
233 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
234 UINT32 AuthenticationStatus;\r
235 UINTN FvHandleCount;\r
236 EFI_HANDLE *FvHandles;\r
237 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
238 VOID *FileBuffer;\r
239 \r
240 FvFileNode = DevicePath;\r
241 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);\r
242 if (!EFI_ERROR (Status)) {\r
243 FileBuffer = GetFileBufferByFilePath (TRUE, DevicePath, FileSize, &AuthenticationStatus);\r
244 if (FileBuffer != NULL) {\r
245 *FullPath = DuplicateDevicePath (DevicePath);\r
246 }\r
247 return FileBuffer;\r
248 }\r
249\r
250 FvFileNode = NextDevicePathNode (DevicePath);\r
251\r
252 //\r
253 // Firstly find the FV file in current FV\r
254 //\r
255 gBS->HandleProtocol (\r
256 gImageHandle,\r
257 &gEfiLoadedImageProtocolGuid,\r
258 (VOID **) &LoadedImage\r
259 );\r
260 NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);\r
261 FileBuffer = BmGetFileBufferByMemmapFv (NewDevicePath, FullPath, FileSize);\r
262 FreePool (NewDevicePath);\r
263\r
264 if (FileBuffer != NULL) {\r
265 return FileBuffer;\r
266 }\r
267\r
268 //\r
269 // Secondly find the FV file in all other FVs\r
270 //\r
271 gBS->LocateHandleBuffer (\r
272 ByProtocol,\r
273 &gEfiFirmwareVolume2ProtocolGuid,\r
274 NULL,\r
275 &FvHandleCount,\r
276 &FvHandles\r
277 );\r
278 for (Index = 0; (Index < FvHandleCount) && (FileBuffer == NULL); Index++) {\r
279 if (FvHandles[Index] == LoadedImage->DeviceHandle) {\r
280 //\r
281 // Skip current FV\r
282 //\r
283 continue;\r
284 }\r
285 NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);\r
286 FileBuffer = BmGetFileBufferByMemmapFv (NewDevicePath, FullPath, FileSize);\r
287 FreePool (NewDevicePath);\r
288 }\r
289 \r
290 if (FvHandles != NULL) {\r
291 FreePool (FvHandles);\r
292 }\r
293 return FileBuffer;\r
294}\r
295\r
296/**\r
297 Check if it's a Memory Mapped FV Device Path.\r
298 \r
299 The function doesn't garentee the device path points to existing FV file.\r
300\r
301 @param DevicePath Input device path.\r
302\r
303 @retval TRUE The device path is a Memory Mapped FV Device Path.\r
304 @retval FALSE The device path is NOT a Memory Mapped FV Device Path.\r
305**/\r
306BOOLEAN\r
307BmIsMemmapFvFilePath (\r
308 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
309 )\r
310{\r
311 EFI_DEVICE_PATH_PROTOCOL *FileNode;\r
312\r
313 if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {\r
314 FileNode = NextDevicePathNode (DevicePath);\r
315 if ((DevicePathType (FileNode) == MEDIA_DEVICE_PATH) && (DevicePathSubType (FileNode) == MEDIA_PIWG_FW_FILE_DP)) {\r
316 return IsDevicePathEnd (NextDevicePathNode (FileNode));\r
317 }\r
318 }\r
319\r
320 return FALSE;\r
321}\r
322\r
323/**\r
324 Check whether a USB device match the specified USB Class device path. This\r
325 function follows "Load Option Processing" behavior in UEFI specification.\r
326\r
327 @param UsbIo USB I/O protocol associated with the USB device.\r
328 @param UsbClass The USB Class device path to match.\r
329\r
330 @retval TRUE The USB device match the USB Class device path.\r
331 @retval FALSE The USB device does not match the USB Class device path.\r
332\r
333**/\r
334BOOLEAN\r
335BmMatchUsbClass (\r
336 IN EFI_USB_IO_PROTOCOL *UsbIo,\r
337 IN USB_CLASS_DEVICE_PATH *UsbClass\r
338 )\r
339{\r
340 EFI_STATUS Status;\r
341 EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
342 EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
343 UINT8 DeviceClass;\r
344 UINT8 DeviceSubClass;\r
345 UINT8 DeviceProtocol;\r
346\r
347 if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||\r
348 (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){\r
349 return FALSE;\r
350 }\r
351\r
352 //\r
353 // Check Vendor Id and Product Id.\r
354 //\r
355 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
356 if (EFI_ERROR (Status)) {\r
357 return FALSE;\r
358 }\r
359\r
360 if ((UsbClass->VendorId != 0xffff) &&\r
361 (UsbClass->VendorId != DevDesc.IdVendor)) {\r
362 return FALSE;\r
363 }\r
364\r
365 if ((UsbClass->ProductId != 0xffff) &&\r
366 (UsbClass->ProductId != DevDesc.IdProduct)) {\r
367 return FALSE;\r
368 }\r
369\r
370 DeviceClass = DevDesc.DeviceClass;\r
371 DeviceSubClass = DevDesc.DeviceSubClass;\r
372 DeviceProtocol = DevDesc.DeviceProtocol;\r
373 if (DeviceClass == 0) {\r
374 //\r
375 // If Class in Device Descriptor is set to 0, use the Class, SubClass and\r
376 // Protocol in Interface Descriptor instead.\r
377 //\r
378 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
379 if (EFI_ERROR (Status)) {\r
380 return FALSE;\r
381 }\r
382\r
383 DeviceClass = IfDesc.InterfaceClass;\r
384 DeviceSubClass = IfDesc.InterfaceSubClass;\r
385 DeviceProtocol = IfDesc.InterfaceProtocol;\r
386 }\r
387\r
388 //\r
389 // Check Class, SubClass and Protocol.\r
390 //\r
391 if ((UsbClass->DeviceClass != 0xff) &&\r
392 (UsbClass->DeviceClass != DeviceClass)) {\r
393 return FALSE;\r
394 }\r
395\r
396 if ((UsbClass->DeviceSubClass != 0xff) &&\r
397 (UsbClass->DeviceSubClass != DeviceSubClass)) {\r
398 return FALSE;\r
399 }\r
400\r
401 if ((UsbClass->DeviceProtocol != 0xff) &&\r
402 (UsbClass->DeviceProtocol != DeviceProtocol)) {\r
403 return FALSE;\r
404 }\r
405\r
406 return TRUE;\r
407}\r
408\r
409/**\r
410 Eliminate the extra spaces in the Str to one space.\r
411\r
412 @param Str Input string info.\r
413**/\r
414VOID\r
415BmEliminateExtraSpaces (\r
416 IN CHAR16 *Str\r
417 )\r
418{\r
419 UINTN Index;\r
420 UINTN ActualIndex;\r
421\r
422 for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {\r
423 if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {\r
424 Str[ActualIndex++] = Str[Index];\r
425 }\r
426 }\r
427 Str[ActualIndex] = L'\0';\r
428}\r
429\r
430/**\r
431 Try to get the controller's ATA/ATAPI description.\r
432\r
433 @param Handle Controller handle.\r
434\r
435 @return The description string.\r
436**/\r
437CHAR16 *\r
438BmGetDescriptionFromDiskInfo (\r
439 IN EFI_HANDLE Handle\r
440 )\r
441{\r
442 UINTN Index;\r
443 EFI_STATUS Status;\r
444 EFI_DISK_INFO_PROTOCOL *DiskInfo;\r
445 UINT32 BufferSize;\r
446 EFI_ATAPI_IDENTIFY_DATA IdentifyData;\r
447 EFI_SCSI_INQUIRY_DATA InquiryData;\r
448 CHAR16 *Description;\r
449 UINTN Length;\r
450 CONST UINTN ModelNameLength = 40;\r
451 CONST UINTN SerialNumberLength = 20;\r
452 CHAR8 *StrPtr;\r
453 UINT8 Temp;\r
454\r
455 Description = NULL;\r
456\r
457 Status = gBS->HandleProtocol (\r
458 Handle,\r
459 &gEfiDiskInfoProtocolGuid,\r
460 (VOID **) &DiskInfo\r
461 );\r
462 if (EFI_ERROR (Status)) {\r
463 return NULL;\r
464 }\r
465\r
466 if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) || \r
467 CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {\r
468 BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);\r
469 Status = DiskInfo->Identify (\r
470 DiskInfo,\r
471 &IdentifyData,\r
472 &BufferSize\r
473 );\r
474 if (!EFI_ERROR (Status)) {\r
475 Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));\r
476 ASSERT (Description != NULL);\r
477 for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {\r
478 Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1];\r
479 Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];\r
480 }\r
481\r
482 Length = Index;\r
483 Description[Length++] = L' ';\r
484\r
485 for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {\r
486 Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1];\r
487 Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];\r
488 }\r
489 Length += Index;\r
490 Description[Length++] = L'\0';\r
491 ASSERT (Length == ModelNameLength + SerialNumberLength + 2);\r
492\r
493 BmEliminateExtraSpaces (Description);\r
494 }\r
495 } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {\r
496 BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);\r
497 Status = DiskInfo->Inquiry (\r
498 DiskInfo,\r
499 &InquiryData,\r
500 &BufferSize\r
501 );\r
502 if (!EFI_ERROR (Status)) {\r
503 Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));\r
504 ASSERT (Description != NULL);\r
505\r
506 //\r
507 // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification\r
508 // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification, \r
509 // Here combine the vendor identification and product identification to the description.\r
510 //\r
511 StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);\r
512 Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];\r
513 StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';\r
514 AsciiStrToUnicodeStr (StrPtr, Description);\r
515 StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;\r
516\r
517 //\r
518 // Add one space at the middle of vendor information and product information.\r
519 //\r
520 Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';\r
521\r
522 StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);\r
523 StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';\r
524 AsciiStrToUnicodeStr (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1);\r
525\r
526 BmEliminateExtraSpaces (Description);\r
527 }\r
528 }\r
529\r
530 return Description;\r
531}\r
532\r
533/**\r
534 Try to get the controller's USB description.\r
535\r
536 @param Handle Controller handle.\r
537\r
538 @return The description string.\r
539**/\r
540CHAR16 *\r
541BmGetUsbDescription (\r
542 IN EFI_HANDLE Handle\r
543 )\r
544{\r
545 EFI_STATUS Status;\r
546 EFI_USB_IO_PROTOCOL *UsbIo;\r
547 CHAR16 NullChar;\r
548 CHAR16 *Manufacturer;\r
549 CHAR16 *Product;\r
550 CHAR16 *SerialNumber;\r
551 CHAR16 *Description;\r
552 EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
553\r
554 Status = gBS->HandleProtocol (\r
555 Handle,\r
556 &gEfiUsbIoProtocolGuid,\r
557 (VOID **) &UsbIo\r
558 );\r
559 if (EFI_ERROR (Status)) {\r
560 return NULL;\r
561 }\r
562\r
563 NullChar = L'\0';\r
564\r
565 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
566 if (EFI_ERROR (Status)) {\r
567 return NULL;\r
568 }\r
569\r
570 Status = UsbIo->UsbGetStringDescriptor (\r
571 UsbIo,\r
572 mBmUsbLangId,\r
573 DevDesc.StrManufacturer,\r
574 &Manufacturer\r
575 );\r
576 if (EFI_ERROR (Status)) {\r
577 Manufacturer = &NullChar;\r
578 }\r
579 \r
580 Status = UsbIo->UsbGetStringDescriptor (\r
581 UsbIo,\r
582 mBmUsbLangId,\r
583 DevDesc.StrProduct,\r
584 &Product\r
585 );\r
586 if (EFI_ERROR (Status)) {\r
587 Product = &NullChar;\r
588 }\r
589 \r
590 Status = UsbIo->UsbGetStringDescriptor (\r
591 UsbIo,\r
592 mBmUsbLangId,\r
593 DevDesc.StrSerialNumber,\r
594 &SerialNumber\r
595 );\r
596 if (EFI_ERROR (Status)) {\r
597 SerialNumber = &NullChar;\r
598 }\r
599\r
600 if ((Manufacturer == &NullChar) &&\r
601 (Product == &NullChar) &&\r
602 (SerialNumber == &NullChar)\r
603 ) {\r
604 return NULL;\r
605 }\r
606\r
607 Description = AllocateZeroPool (StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber));\r
608 ASSERT (Description != NULL);\r
609 StrCat (Description, Manufacturer);\r
610 StrCat (Description, L" ");\r
611\r
612 StrCat (Description, Product); \r
613 StrCat (Description, L" ");\r
614\r
615 StrCat (Description, SerialNumber);\r
616\r
617 if (Manufacturer != &NullChar) {\r
618 FreePool (Manufacturer);\r
619 }\r
620 if (Product != &NullChar) {\r
621 FreePool (Product);\r
622 }\r
623 if (SerialNumber != &NullChar) {\r
624 FreePool (SerialNumber);\r
625 }\r
626\r
627 BmEliminateExtraSpaces (Description);\r
628\r
629 return Description;\r
630}\r
631\r
632/**\r
633 Return the boot description for the controller based on the type.\r
634\r
635 @param Handle Controller handle.\r
636\r
637 @return The description string.\r
638**/\r
639CHAR16 *\r
640BmGetMiscDescription (\r
641 IN EFI_HANDLE Handle\r
642 )\r
643{\r
644 EFI_STATUS Status;\r
645 CHAR16 *Description;\r
646 EFI_BLOCK_IO_PROTOCOL *BlockIo;\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
680 default:\r
681 Description = L"Misc Device";\r
682 break;\r
683 }\r
684\r
685 return AllocateCopyPool (StrSize (Description), Description);\r
686}\r
687\r
688BM_GET_BOOT_DESCRIPTION mBmGetBootDescription[] = {\r
689 BmGetUsbDescription,\r
690 BmGetDescriptionFromDiskInfo,\r
691 BmGetMiscDescription\r
692};\r
693\r
694/**\r
695 Check whether a USB device match the specified USB WWID device path. This\r
696 function follows "Load Option Processing" behavior in UEFI specification.\r
697\r
698 @param UsbIo USB I/O protocol associated with the USB device.\r
699 @param UsbWwid The USB WWID device path to match.\r
700\r
701 @retval TRUE The USB device match the USB WWID device path.\r
702 @retval FALSE The USB device does not match the USB WWID device path.\r
703\r
704**/\r
705BOOLEAN\r
706BmMatchUsbWwid (\r
707 IN EFI_USB_IO_PROTOCOL *UsbIo,\r
708 IN USB_WWID_DEVICE_PATH *UsbWwid\r
709 )\r
710{\r
711 EFI_STATUS Status;\r
712 EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
713 EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
714 UINT16 *LangIdTable;\r
715 UINT16 TableSize;\r
716 UINT16 Index;\r
717 CHAR16 *CompareStr;\r
718 UINTN CompareLen;\r
719 CHAR16 *SerialNumberStr;\r
720 UINTN Length;\r
721\r
722 if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||\r
723 (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {\r
724 return FALSE;\r
725 }\r
726\r
727 //\r
728 // Check Vendor Id and Product Id.\r
729 //\r
730 Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
731 if (EFI_ERROR (Status)) {\r
732 return FALSE;\r
733 }\r
734 if ((DevDesc.IdVendor != UsbWwid->VendorId) ||\r
735 (DevDesc.IdProduct != UsbWwid->ProductId)) {\r
736 return FALSE;\r
737 }\r
738\r
739 //\r
740 // Check Interface Number.\r
741 //\r
742 Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
743 if (EFI_ERROR (Status)) {\r
744 return FALSE;\r
745 }\r
746 if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {\r
747 return FALSE;\r
748 }\r
749\r
750 //\r
751 // Check Serial Number.\r
752 //\r
753 if (DevDesc.StrSerialNumber == 0) {\r
754 return FALSE;\r
755 }\r
756\r
757 //\r
758 // Get all supported languages.\r
759 //\r
760 TableSize = 0;\r
761 LangIdTable = NULL;\r
762 Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);\r
763 if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {\r
764 return FALSE;\r
765 }\r
766\r
767 //\r
768 // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.\r
769 //\r
770 CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);\r
771 CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);\r
772 if (CompareStr[CompareLen - 1] == L'\0') {\r
773 CompareLen--;\r
774 }\r
775\r
776 //\r
777 // Compare serial number in each supported language.\r
778 //\r
779 for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {\r
780 SerialNumberStr = NULL;\r
781 Status = UsbIo->UsbGetStringDescriptor (\r
782 UsbIo,\r
783 LangIdTable[Index],\r
784 DevDesc.StrSerialNumber,\r
785 &SerialNumberStr\r
786 );\r
787 if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {\r
788 continue;\r
789 }\r
790\r
791 Length = StrLen (SerialNumberStr);\r
792 if ((Length >= CompareLen) &&\r
793 (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {\r
794 FreePool (SerialNumberStr);\r
795 return TRUE;\r
796 }\r
797\r
798 FreePool (SerialNumberStr);\r
799 }\r
800\r
801 return FALSE;\r
802}\r
803\r
804/**\r
805 Find a USB device which match the specified short-form device path start with \r
806 USB Class or USB WWID device path. If ParentDevicePath is NULL, this function\r
807 will search in all USB devices of the platform. If ParentDevicePath is not NULL,\r
808 this function will only search in its child devices.\r
809\r
810 @param DevicePath The device path that contains USB Class or USB WWID device path.\r
811 @param ParentDevicePathSize The length of the device path before the USB Class or \r
812 USB WWID device path.\r
813 @param UsbIoHandleCount A pointer to the count of the returned USB IO handles.\r
814\r
815 @retval NULL The matched USB IO handles cannot be found.\r
816 @retval other The matched USB IO handles.\r
817\r
818**/\r
819EFI_HANDLE *\r
820BmFindUsbDevice (\r
821 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
822 IN UINTN ParentDevicePathSize,\r
823 OUT UINTN *UsbIoHandleCount\r
824 )\r
825{\r
826 EFI_STATUS Status;\r
827 EFI_HANDLE *UsbIoHandles;\r
828 EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;\r
829 EFI_USB_IO_PROTOCOL *UsbIo;\r
830 UINTN Index;\r
831 UINTN UsbIoDevicePathSize;\r
832 BOOLEAN Matched;\r
833\r
834 ASSERT (UsbIoHandleCount != NULL); \r
835\r
836 //\r
837 // Get all UsbIo Handles.\r
838 //\r
839 Status = gBS->LocateHandleBuffer (\r
840 ByProtocol,\r
841 &gEfiUsbIoProtocolGuid,\r
842 NULL,\r
843 UsbIoHandleCount,\r
844 &UsbIoHandles\r
845 );\r
846 if (EFI_ERROR (Status)) {\r
847 *UsbIoHandleCount = 0;\r
848 UsbIoHandles = NULL;\r
849 }\r
850\r
851 for (Index = 0; Index < *UsbIoHandleCount; ) {\r
852 //\r
853 // Get the Usb IO interface.\r
854 //\r
855 Status = gBS->HandleProtocol(\r
856 UsbIoHandles[Index],\r
857 &gEfiUsbIoProtocolGuid,\r
858 (VOID **) &UsbIo\r
859 );\r
860 UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);\r
861 Matched = FALSE;\r
862 if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {\r
863 UsbIoDevicePathSize = GetDevicePathSize (UsbIoDevicePath) - END_DEVICE_PATH_LENGTH;\r
864\r
865 //\r
866 // Compare starting part of UsbIoHandle's device path with ParentDevicePath.\r
867 //\r
868 if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {\r
869 if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) ||\r
870 BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize))) {\r
871 Matched = TRUE;\r
872 }\r
873 }\r
874 }\r
875\r
876 if (!Matched) {\r
877 (*UsbIoHandleCount) --;\r
878 CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE));\r
879 } else {\r
880 Index++;\r
881 }\r
882 }\r
883\r
884 return UsbIoHandles;\r
885}\r
886\r
887/**\r
888 Expand USB Class or USB WWID device path node to be full device path of a USB\r
889 device in platform.\r
890\r
891 This function support following 4 cases:\r
892 1) Boot Option device path starts with a USB Class or USB WWID device path,\r
893 and there is no Media FilePath device path in the end.\r
894 In this case, it will follow Removable Media Boot Behavior.\r
895 2) Boot Option device path starts with a USB Class or USB WWID device path,\r
896 and ended with Media FilePath device path.\r
897 3) Boot Option device path starts with a full device path to a USB Host Controller,\r
898 contains a USB Class or USB WWID device path node, while not ended with Media\r
899 FilePath device path. In this case, it will follow Removable Media Boot Behavior.\r
900 4) Boot Option device path starts with a full device path to a USB Host Controller,\r
901 contains a USB Class or USB WWID device path node, and ended with Media\r
902 FilePath device path.\r
903\r
904 @param FilePath The device path pointing to a load option.\r
905 It could be a short-form device path.\r
906 @param FullPath Return the full device path of the load option after\r
907 short-form device path expanding.\r
908 Caller is responsible to free it.\r
909 @param FileSize Return the load option size.\r
910 @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.\r
911\r
912 @return The load option buffer. Caller is responsible to free the memory.\r
913**/\r
914VOID *\r
915BmExpandUsbDevicePath (\r
916 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
917 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
918 OUT UINTN *FileSize,\r
919 IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode\r
920 )\r
921{\r
922 UINTN ParentDevicePathSize;\r
923 EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;\r
924 EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;\r
925 EFI_HANDLE *Handles;\r
926 UINTN HandleCount;\r
927 UINTN Index;\r
928 VOID *FileBuffer;\r
929\r
930 ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;\r
931 RemainingDevicePath = NextDevicePathNode (ShortformNode);\r
932 FileBuffer = NULL;\r
933 Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);\r
934\r
935 for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) {\r
936 FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);\r
937 FileBuffer = BmGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize);\r
938 FreePool (FullDevicePath);\r
939 }\r
940\r
941 if (Handles != NULL) {\r
942 FreePool (Handles);\r
943 }\r
944\r
945 return FileBuffer;\r
946}\r
947\r
948/**\r
949 Save the partition DevicePath to the CachedDevicePath as the first instance.\r
950\r
951 @param CachedDevicePath The device path cache.\r
952 @param DevicePath The partition device path to be cached.\r
953**/\r
954VOID\r
955BmCachePartitionDevicePath (\r
956 IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath,\r
957 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
958 )\r
959{\r
960 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
961 UINTN Count;\r
962 \r
963 if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) {\r
964 TempDevicePath = *CachedDevicePath;\r
965 *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath);\r
966 FreePool (TempDevicePath);\r
967 }\r
968\r
969 if (*CachedDevicePath == NULL) {\r
970 *CachedDevicePath = DuplicateDevicePath (DevicePath);\r
971 return;\r
972 }\r
973\r
974 TempDevicePath = *CachedDevicePath;\r
975 *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath);\r
976 if (TempDevicePath != NULL) {\r
977 FreePool (TempDevicePath);\r
978 }\r
979\r
980 //\r
981 // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller\r
982 // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.\r
983 //\r
984 Count = 0;\r
985 TempDevicePath = *CachedDevicePath;\r
986 while (!IsDevicePathEnd (TempDevicePath)) {\r
987 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
988 //\r
989 // Parse one instance\r
990 //\r
991 while (!IsDevicePathEndType (TempDevicePath)) {\r
992 TempDevicePath = NextDevicePathNode (TempDevicePath);\r
993 }\r
994 Count++;\r
995 //\r
996 // If the CachedDevicePath variable contain too much instance, only remain 12 instances.\r
997 //\r
998 if (Count == 12) {\r
999 SetDevicePathEndNode (TempDevicePath);\r
1000 break;\r
1001 }\r
1002 }\r
1003}\r
1004\r
1005/**\r
1006 Expand a device path that starts with a hard drive media device path node to be a\r
1007 full device path that includes the full hardware path to the device. We need\r
1008 to do this so it can be booted. As an optimization the front match (the part point\r
1009 to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable\r
1010 so a connect all is not required on every boot. All successful history device path\r
1011 which point to partition node (the front part) will be saved.\r
1012\r
1013 @param FilePath The device path pointing to a load option.\r
1014 It could be a short-form device path.\r
1015 @param FullPath Return the full device path of the load option after\r
1016 short-form device path expanding.\r
1017 Caller is responsible to free it.\r
1018 @param FileSize Return the load option size.\r
1019\r
1020 @return The load option buffer. Caller is responsible to free the memory.\r
1021**/\r
1022VOID *\r
1023BmExpandPartitionDevicePath (\r
1024 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1025 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1026 OUT UINTN *FileSize\r
1027 )\r
1028{\r
1029 EFI_STATUS Status;\r
1030 UINTN BlockIoHandleCount;\r
1031 EFI_HANDLE *BlockIoBuffer;\r
1032 VOID *FileBuffer;\r
1033 EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;\r
1034 UINTN Index;\r
1035 EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;\r
1036 EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;\r
1037 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
1038 UINTN CachedDevicePathSize;\r
1039 BOOLEAN NeedAdjust;\r
1040 EFI_DEVICE_PATH_PROTOCOL *Instance;\r
1041 UINTN Size;\r
1042\r
1043 FileBuffer = NULL;\r
1044 //\r
1045 // Check if there is prestore 'HDDP' variable.\r
1046 // If exist, search the front path which point to partition node in the variable instants.\r
1047 // If fail to find or 'HDDP' not exist, reconnect all and search in all system\r
1048 //\r
1049 GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize);\r
1050\r
1051 //\r
1052 // Delete the invalid 'HDDP' variable.\r
1053 //\r
1054 if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {\r
1055 FreePool (CachedDevicePath);\r
1056 CachedDevicePath = NULL;\r
1057 Status = gRT->SetVariable (\r
1058 L"HDDP",\r
1059 &mBmHardDriveBootVariableGuid,\r
1060 0,\r
1061 0,\r
1062 NULL\r
1063 );\r
1064 ASSERT_EFI_ERROR (Status);\r
1065 }\r
1066\r
1067 if (CachedDevicePath != NULL) {\r
1068 TempNewDevicePath = CachedDevicePath;\r
1069 NeedAdjust = FALSE;\r
1070 do {\r
1071 //\r
1072 // Check every instance of the variable\r
1073 // First, check whether the instance contain the partition node, which is needed for distinguishing multi\r
1074 // partial partition boot option. Second, check whether the instance could be connected.\r
1075 //\r
1076 Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);\r
1077 if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
1078 //\r
1079 // Connect the device path instance, the device path point to hard drive media device path node\r
1080 // e.g. ACPI() /PCI()/ATA()/Partition()\r
1081 //\r
1082 Status = EfiBootManagerConnectDevicePath (Instance, NULL);\r
1083 if (!EFI_ERROR (Status)) {\r
1084 TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));\r
1085 FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);\r
1086 FreePool (TempDevicePath);\r
1087\r
1088 if (FileBuffer != NULL) {\r
1089 //\r
1090 // Adjust the 'HDDP' instances sequence if the matched one is not first one.\r
1091 //\r
1092 if (NeedAdjust) {\r
1093 BmCachePartitionDevicePath (&CachedDevicePath, Instance);\r
1094 //\r
1095 // Save the matching Device Path so we don't need to do a connect all next time\r
1096 // Failing to save only impacts performance next time expanding the short-form device path\r
1097 //\r
1098 Status = gRT->SetVariable (\r
1099 L"HDDP",\r
1100 &mBmHardDriveBootVariableGuid,\r
1101 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
1102 GetDevicePathSize (CachedDevicePath),\r
1103 CachedDevicePath\r
1104 );\r
1105 }\r
1106\r
1107 FreePool (Instance);\r
1108 FreePool (CachedDevicePath);\r
1109 return FileBuffer;\r
1110 }\r
1111 }\r
1112 }\r
1113 //\r
1114 // Come here means the first instance is not matched\r
1115 //\r
1116 NeedAdjust = TRUE;\r
1117 FreePool(Instance);\r
1118 } while (TempNewDevicePath != NULL);\r
1119 }\r
1120\r
1121 //\r
1122 // If we get here we fail to find or 'HDDP' not exist, and now we need\r
1123 // to search all devices in the system for a matched partition\r
1124 //\r
1125 EfiBootManagerConnectAll ();\r
1126 Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);\r
1127 if (EFI_ERROR (Status)) {\r
1128 BlockIoHandleCount = 0;\r
1129 BlockIoBuffer = NULL;\r
1130 }\r
1131 //\r
1132 // Loop through all the device handles that support the BLOCK_IO Protocol\r
1133 //\r
1134 for (Index = 0; Index < BlockIoHandleCount; Index++) {\r
1135 BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]);\r
1136 if (BlockIoDevicePath == NULL) {\r
1137 continue;\r
1138 }\r
1139\r
1140 if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
1141 //\r
1142 // Find the matched partition device path\r
1143 //\r
1144 TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));\r
1145 FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);\r
1146 FreePool (TempDevicePath);\r
1147\r
1148 if (FileBuffer != NULL) {\r
1149 BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);\r
1150\r
1151 //\r
1152 // Save the matching Device Path so we don't need to do a connect all next time\r
1153 // Failing to save only impacts performance next time expanding the short-form device path\r
1154 //\r
1155 Status = gRT->SetVariable (\r
1156 L"HDDP",\r
1157 &mBmHardDriveBootVariableGuid,\r
1158 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
1159 GetDevicePathSize (CachedDevicePath),\r
1160 CachedDevicePath\r
1161 );\r
1162\r
1163 break;\r
1164 }\r
1165 }\r
1166 }\r
1167\r
1168 if (CachedDevicePath != NULL) {\r
1169 FreePool (CachedDevicePath);\r
1170 }\r
1171 if (BlockIoBuffer != NULL) {\r
1172 FreePool (BlockIoBuffer);\r
1173 }\r
1174 return FileBuffer;\r
1175}\r
1176\r
1177/**\r
1178 Expand the media device path which points to a BlockIo or SimpleFileSystem instance\r
1179 by appending EFI_REMOVABLE_MEDIA_FILE_NAME.\r
1180\r
1181 @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.\r
1182 @param FullPath Return the full device path pointing to the load option.\r
1183 @param FileSize Return the size of the load option.\r
1184\r
1185 @return The load option buffer.\r
1186**/\r
1187VOID *\r
1188BmExpandMediaDevicePath (\r
1189 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
1190 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1191 OUT UINTN *FileSize\r
1192 )\r
1193{\r
1194 EFI_STATUS Status;\r
1195 EFI_HANDLE Handle;\r
1196 EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
1197 VOID *Buffer;\r
1198 EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
1199 UINTN Size;\r
1200 UINTN TempSize;\r
1201 EFI_HANDLE *SimpleFileSystemHandles;\r
1202 UINTN NumberSimpleFileSystemHandles;\r
1203 UINTN Index;\r
1204 VOID *FileBuffer;\r
1205 UINT32 AuthenticationStatus;\r
1206\r
1207 //\r
1208 // Check whether the device is connected\r
1209 //\r
1210 TempDevicePath = DevicePath;\r
1211 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);\r
1212 if (!EFI_ERROR (Status)) {\r
1213 ASSERT (IsDevicePathEnd (TempDevicePath));\r
1214\r
1215 TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
1216 FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);\r
1217 if (FileBuffer == NULL) {\r
1218 FreePool (TempDevicePath);\r
1219 TempDevicePath = NULL;\r
1220 }\r
1221 *FullPath = TempDevicePath;\r
1222 return FileBuffer;\r
1223 }\r
1224\r
1225 //\r
1226 // For device boot option only pointing to the removable device handle, \r
1227 // should make sure all its children handles (its child partion or media handles) are created and connected. \r
1228 //\r
1229 gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
1230\r
1231 //\r
1232 // Issue a dummy read to the device to check for media change.\r
1233 // When the removable media is changed, any Block IO read/write will\r
1234 // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is\r
1235 // returned. After the Block IO protocol is reinstalled, subsequent\r
1236 // Block IO read/write will success.\r
1237 //\r
1238 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);\r
1239 ASSERT_EFI_ERROR (Status);\r
1240 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
1241 ASSERT_EFI_ERROR (Status);\r
1242 Buffer = AllocatePool (BlockIo->Media->BlockSize);\r
1243 if (Buffer != NULL) {\r
1244 BlockIo->ReadBlocks (\r
1245 BlockIo,\r
1246 BlockIo->Media->MediaId,\r
1247 0,\r
1248 BlockIo->Media->BlockSize,\r
1249 Buffer\r
1250 );\r
1251 FreePool (Buffer);\r
1252 }\r
1253\r
1254 //\r
1255 // Detect the the default boot file from removable Media\r
1256 //\r
1257 FileBuffer = NULL;\r
1258 *FullPath = NULL;\r
1259 Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;\r
1260 gBS->LocateHandleBuffer (\r
1261 ByProtocol,\r
1262 &gEfiSimpleFileSystemProtocolGuid,\r
1263 NULL,\r
1264 &NumberSimpleFileSystemHandles,\r
1265 &SimpleFileSystemHandles\r
1266 );\r
1267 for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {\r
1268 //\r
1269 // Get the device path size of SimpleFileSystem handle\r
1270 //\r
1271 TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);\r
1272 TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH;\r
1273 //\r
1274 // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path\r
1275 //\r
1276 if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {\r
1277 TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);\r
1278 FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);\r
1279 if (FileBuffer != NULL) {\r
1280 *FullPath = TempDevicePath;\r
1281 break;\r
1282 }\r
1283 FreePool (TempDevicePath);\r
1284 }\r
1285 }\r
1286\r
1287 if (SimpleFileSystemHandles != NULL) {\r
1288 FreePool (SimpleFileSystemHandles);\r
1289 }\r
1290\r
1291 return FileBuffer;\r
1292}\r
1293\r
1294/**\r
1295 Get the load option by its device path.\r
1296\r
1297 @param FilePath The device path pointing to a load option.\r
1298 It could be a short-form device path.\r
1299 @param FullPath Return the full device path of the load option after\r
1300 short-form device path expanding.\r
1301 Caller is responsible to free it.\r
1302 @param FileSize Return the load option size.\r
1303\r
1304 @return The load option buffer. Caller is responsible to free the memory.\r
1305**/\r
1306VOID *\r
1307BmGetLoadOptionBuffer (\r
1308 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1309 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1310 OUT UINTN *FileSize\r
1311 )\r
1312{\r
1313 EFI_HANDLE Handle;\r
1314 VOID *FileBuffer;\r
1315 UINT32 AuthenticationStatus;\r
1316 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1317 EFI_STATUS Status;\r
1318\r
1319 ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL));\r
1320\r
1321 EfiBootManagerConnectDevicePath (FilePath, NULL);\r
1322\r
1323 *FullPath = NULL;\r
1324 *FileSize = 0;\r
1325 FileBuffer = NULL;\r
1326\r
1327 //\r
1328 // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI\r
1329 //\r
1330 Node = FilePath;\r
1331 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);\r
1332 if (EFI_ERROR (Status)) {\r
1333 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle);\r
1334 }\r
1335\r
1336 if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {\r
1337 return BmExpandMediaDevicePath (FilePath, FullPath, FileSize);\r
1338 }\r
1339\r
1340 //\r
1341 // Expand the short-form device path to full device path\r
1342 //\r
1343 if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&\r
1344 (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) {\r
1345 //\r
1346 // Expand the Harddrive device path\r
1347 //\r
1348 return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize);\r
1349 } else {\r
1350 for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {\r
1351 if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
1352 ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {\r
1353 break;\r
1354 }\r
1355 }\r
1356\r
1357 if (!IsDevicePathEnd (Node)) {\r
1358 //\r
1359 // Expand the USB WWID/Class device path\r
1360 //\r
1361 FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);\r
1362 if ((FileBuffer == NULL) && (FilePath == Node)) {\r
1363 //\r
1364 // Boot Option device path starts with USB Class or USB WWID device path.\r
1365 // For Boot Option device path which doesn't begin with the USB Class or\r
1366 // USB WWID device path, it's not needed to connect again here.\r
1367 //\r
1368 BmConnectUsbShortFormDevicePath (FilePath);\r
1369 FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);\r
1370 }\r
1371 return FileBuffer;\r
1372 }\r
1373 }\r
1374\r
1375 //\r
1376 // Fix up the boot option path if it points to a FV in memory map style of device path\r
1377 //\r
1378 if (BmIsMemmapFvFilePath (FilePath)) {\r
1379 return BmGetFileBufferByMemmapFv (FilePath, FullPath, FileSize);\r
1380 }\r
1381\r
1382 //\r
1383 // Directly reads the load option when it doesn't reside in simple file system instance (LoadFile/LoadFile2),\r
1384 // or it directly points to a file in simple file system instance.\r
1385 //\r
1386 FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);\r
1387 if (FileBuffer != NULL) {\r
1388 *FullPath = DuplicateDevicePath (FilePath);\r
1389 }\r
1390\r
1391 return FileBuffer;\r
1392}\r
1393\r
1394/**\r
1395 Attempt to boot the EFI boot option. This routine sets L"BootCurent" and\r
1396 also signals the EFI ready to boot event. If the device path for the option\r
1397 starts with a BBS device path a legacy boot is attempted via the registered \r
1398 gLegacyBoot function. Short form device paths are also supported via this \r
1399 rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,\r
1400 MSG_USB_CLASS_DP gets expaned out to find the first device that matches.\r
1401 If the BootOption Device Path fails the removable media boot algorithm \r
1402 is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type \r
1403 is tried per processor type)\r
1404\r
1405 @param BootOption Boot Option to try and boot.\r
1406 On return, BootOption->Status contains the boot status.\r
1407 EFI_SUCCESS BootOption was booted\r
1408 EFI_UNSUPPORTED A BBS device path was found with no valid callback\r
1409 registered via EfiBootManagerInitialize().\r
1410 EFI_NOT_FOUND The BootOption was not found on the system\r
1411 !EFI_SUCCESS BootOption failed with this error status\r
1412\r
1413**/\r
1414VOID\r
1415EFIAPI\r
1416EfiBootManagerBoot (\r
1417 IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
1418 )\r
1419{\r
1420 EFI_STATUS Status;\r
1421 EFI_HANDLE ImageHandle;\r
1422 EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;\r
1423 UINT16 Uint16;\r
1424 UINTN OptionNumber;\r
1425 UINTN OriginalOptionNumber;\r
1426 EFI_DEVICE_PATH_PROTOCOL *FilePath;\r
1427 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1428 EFI_HANDLE FvHandle;\r
1429 VOID *FileBuffer;\r
1430 UINTN FileSize;\r
1431 EFI_BOOT_LOGO_PROTOCOL *BootLogo;\r
1432 EFI_EVENT LegacyBootEvent;\r
1433\r
1434 if (BootOption == NULL) {\r
1435 return;\r
1436 }\r
1437\r
1438 if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) {\r
1439 BootOption->Status = EFI_INVALID_PARAMETER;\r
1440 return;\r
1441 }\r
1442\r
1443 //\r
1444 // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File")\r
1445 //\r
1446 OptionNumber = BmFindBootOptionInVariable (BootOption);\r
1447 if (OptionNumber == LoadOptionNumberUnassigned) {\r
1448 Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);\r
1449 if (!EFI_ERROR (Status)) {\r
1450 //\r
1451 // Save the BootOption->OptionNumber to restore later\r
1452 //\r
1453 OptionNumber = Uint16;\r
1454 OriginalOptionNumber = BootOption->OptionNumber;\r
1455 BootOption->OptionNumber = OptionNumber;\r
1456 Status = EfiBootManagerLoadOptionToVariable (BootOption);\r
1457 BootOption->OptionNumber = OriginalOptionNumber;\r
1458 }\r
1459\r
1460 if (EFI_ERROR (Status)) {\r
1461 DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));\r
1462 BootOption->Status = Status;\r
1463 return ;\r
1464 }\r
1465 }\r
1466\r
1467 //\r
1468 // 2. Set BootCurrent\r
1469 //\r
1470 Uint16 = (UINT16) OptionNumber;\r
1471 BmSetVariableAndReportStatusCodeOnError (\r
1472 L"BootCurrent",\r
1473 &gEfiGlobalVariableGuid,\r
1474 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
1475 sizeof (UINT16),\r
1476 &Uint16\r
1477 );\r
1478\r
1479 //\r
1480 // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute\r
1481 // the boot option.\r
1482 //\r
1483 Node = BootOption->FilePath;\r
1484 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);\r
1485 if (!EFI_ERROR (Status) && CompareGuid (\r
1486 EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),\r
1487 PcdGetPtr (PcdBootManagerMenuFile)\r
1488 )) {\r
1489 DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n"));\r
1490 BmStopHotkeyService (NULL, NULL);\r
1491 } else {\r
1492 EfiSignalEventReadyToBoot();\r
1493 //\r
1494 // Report Status Code to indicate ReadyToBoot was signalled\r
1495 //\r
1496 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));\r
1497 //\r
1498 // 4. Repair system through DriverHealth protocol\r
1499 //\r
1500 BmRepairAllControllers ();\r
1501 }\r
1502\r
1503 PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1504\r
1505 //\r
1506 // 5. Load EFI boot option to ImageHandle\r
1507 //\r
1508 ImageHandle = NULL;\r
1509 if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {\r
1510 Status = EFI_NOT_FOUND;\r
1511 FileBuffer = BmGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize);\r
1512 DEBUG_CODE (\r
1513 if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {\r
1514 DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));\r
1515 BmPrintDp (BootOption->FilePath);\r
1516 DEBUG ((EFI_D_INFO, " -> "));\r
1517 BmPrintDp (FilePath);\r
1518 DEBUG ((EFI_D_INFO, "\n"));\r
1519 }\r
1520 );\r
1521 if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) {\r
1522 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));\r
1523 Status = gBS->LoadImage (\r
1524 TRUE,\r
1525 gImageHandle,\r
1526 FilePath,\r
1527 FileBuffer,\r
1528 FileSize,\r
1529 &ImageHandle\r
1530 );\r
1531 }\r
1532 if (FileBuffer != NULL) {\r
1533 FreePool (FileBuffer);\r
1534 }\r
1535 if (FilePath != NULL) {\r
1536 FreePool (FilePath);\r
1537 }\r
1538\r
1539 if (EFI_ERROR (Status)) {\r
1540 //\r
1541 // Report Status Code to indicate that the failure to load boot option\r
1542 //\r
1543 REPORT_STATUS_CODE (\r
1544 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1545 (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)\r
1546 );\r
1547 BootOption->Status = Status;\r
1548 return;\r
1549 }\r
1550 }\r
1551\r
1552 //\r
1553 // 6. Adjust the different type memory page number just before booting\r
1554 // and save the updated info into the variable for next boot to use\r
1555 //\r
1556 if ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) {\r
1557 if (PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {\r
1558 BmSetMemoryTypeInformationVariable ();\r
1559 }\r
1560 }\r
1561\r
1562 DEBUG_CODE_BEGIN();\r
1563 if (BootOption->Description == NULL) {\r
1564 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));\r
1565 } else {\r
1566 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));\r
1567 }\r
1568 DEBUG_CODE_END();\r
1569\r
1570 //\r
1571 // Check to see if we should legacy BOOT. If yes then do the legacy boot\r
1572 // Write boot to OS performance data for Legacy boot\r
1573 //\r
1574 if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {\r
1575 if (mBmLegacyBoot != NULL) {\r
1576 //\r
1577 // Write boot to OS performance data for legacy boot.\r
1578 //\r
1579 PERF_CODE (\r
1580 //\r
1581 // Create an event to be signalled when Legacy Boot occurs to write performance data.\r
1582 //\r
1583 Status = EfiCreateEventLegacyBootEx(\r
1584 TPL_NOTIFY,\r
1585 BmWriteBootToOsPerformanceData,\r
1586 NULL, \r
1587 &LegacyBootEvent\r
1588 );\r
1589 ASSERT_EFI_ERROR (Status);\r
1590 );\r
1591\r
1592 mBmLegacyBoot (BootOption);\r
1593 } else {\r
1594 BootOption->Status = EFI_UNSUPPORTED;\r
1595 }\r
1596\r
1597 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1598 return;\r
1599 }\r
1600 \r
1601 //\r
1602 // Provide the image with its load options\r
1603 //\r
1604 Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
1605 ASSERT_EFI_ERROR (Status);\r
1606\r
1607 ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;\r
1608 ImageInfo->LoadOptions = BootOption->OptionalData;\r
1609\r
1610 //\r
1611 // Clean to NULL because the image is loaded directly from the firmwares boot manager.\r
1612 //\r
1613 ImageInfo->ParentHandle = NULL;\r
1614\r
1615 //\r
1616 // Before calling the image, enable the Watchdog Timer for 5 minutes period\r
1617 //\r
1618 gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);\r
1619\r
1620 //\r
1621 // Write boot to OS performance data for UEFI boot\r
1622 //\r
1623 PERF_CODE (\r
1624 BmWriteBootToOsPerformanceData (NULL, NULL);\r
1625 );\r
1626\r
1627 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));\r
1628\r
1629 Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);\r
1630 DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));\r
1631 BootOption->Status = Status;\r
1632 if (EFI_ERROR (Status)) {\r
1633 //\r
1634 // Report Status Code to indicate that boot failure\r
1635 //\r
1636 REPORT_STATUS_CODE (\r
1637 EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
1638 (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)\r
1639 );\r
1640 }\r
1641 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1642\r
1643 //\r
1644 // Clear the Watchdog Timer after the image returns\r
1645 //\r
1646 gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);\r
1647\r
1648 //\r
1649 // Set Logo status invalid after trying one boot option\r
1650 //\r
1651 BootLogo = NULL;\r
1652 Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);\r
1653 if (!EFI_ERROR (Status) && (BootLogo != NULL)) {\r
1654 Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);\r
1655 ASSERT_EFI_ERROR (Status);\r
1656 }\r
1657\r
1658 //\r
1659 // Clear Boot Current\r
1660 //\r
1661 Status = gRT->SetVariable (\r
1662 L"BootCurrent",\r
1663 &gEfiGlobalVariableGuid,\r
1664 0,\r
1665 0,\r
1666 NULL\r
1667 );\r
1668 //\r
1669 // Deleting variable with current variable implementation shouldn't fail.\r
1670 // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,\r
1671 // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.\r
1672 //\r
1673 ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
1674}\r
1675\r
1676/**\r
1677 Check whether there is a instance in BlockIoDevicePath, which contain multi device path\r
1678 instances, has the same partition node with HardDriveDevicePath device path\r
1679\r
1680 @param BlockIoDevicePath Multi device path instances which need to check\r
1681 @param HardDriveDevicePath A device path which starts with a hard drive media\r
1682 device path.\r
1683\r
1684 @retval TRUE There is a matched device path instance.\r
1685 @retval FALSE There is no matched device path instance.\r
1686\r
1687**/\r
1688BOOLEAN\r
1689BmMatchPartitionDevicePathNode (\r
1690 IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,\r
1691 IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath\r
1692 )\r
1693{\r
1694 HARDDRIVE_DEVICE_PATH *Node;\r
1695\r
1696 if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {\r
1697 return FALSE;\r
1698 }\r
1699\r
1700 //\r
1701 // find the partition device path node\r
1702 //\r
1703 while (!IsDevicePathEnd (BlockIoDevicePath)) {\r
1704 if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&\r
1705 (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)\r
1706 ) {\r
1707 break;\r
1708 }\r
1709\r
1710 BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);\r
1711 }\r
1712\r
1713 if (IsDevicePathEnd (BlockIoDevicePath)) {\r
1714 return FALSE;\r
1715 }\r
1716\r
1717 //\r
1718 // See if the harddrive device path in blockio matches the orig Hard Drive Node\r
1719 //\r
1720 Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath;\r
1721\r
1722 //\r
1723 // Match Signature and PartitionNumber.\r
1724 // Unused bytes in Signature are initiaized with zeros.\r
1725 //\r
1726 return (BOOLEAN) (\r
1727 (Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&\r
1728 (Node->MBRType == HardDriveDevicePath->MBRType) &&\r
1729 (Node->SignatureType == HardDriveDevicePath->SignatureType) &&\r
1730 (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)\r
1731 );\r
1732}\r
1733\r
1734/**\r
1735 Emuerate all possible bootable medias in the following order:\r
1736 1. Removable BlockIo - The boot option only points to the removable media\r
1737 device, like USB key, DVD, Floppy etc.\r
1738 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device,\r
1739 like HardDisk.\r
1740 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting\r
1741 SimpleFileSystem Protocol, but not supporting BlockIo\r
1742 protocol.\r
1743 4. LoadFile - The boot option points to the media supporting \r
1744 LoadFile protocol.\r
1745 Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior\r
1746\r
1747 @param BootOptionCount Return the boot option count which has been found.\r
1748\r
1749 @retval Pointer to the boot option array.\r
1750**/\r
1751EFI_BOOT_MANAGER_LOAD_OPTION *\r
1752BmEnumerateBootOptions (\r
1753 UINTN *BootOptionCount\r
1754 )\r
1755{\r
1756 EFI_STATUS Status;\r
1757 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
1758 UINT16 NonBlockNumber;\r
1759 UINTN HandleCount;\r
1760 EFI_HANDLE *Handles;\r
1761 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
1762 UINTN Removable;\r
1763 UINTN Index;\r
1764 UINTN FunctionIndex;\r
1765 CHAR16 *Temp;\r
1766 CHAR16 *DescriptionPtr;\r
1767 CHAR16 Description[30];\r
1768\r
1769 ASSERT (BootOptionCount != NULL);\r
1770\r
1771 *BootOptionCount = 0;\r
1772 BootOptions = NULL;\r
1773\r
1774 //\r
1775 // Parse removable block io followed by fixed block io\r
1776 //\r
1777 gBS->LocateHandleBuffer (\r
1778 ByProtocol,\r
1779 &gEfiBlockIoProtocolGuid,\r
1780 NULL,\r
1781 &HandleCount,\r
1782 &Handles\r
1783 );\r
1784\r
1785 for (Removable = 0; Removable < 2; Removable++) {\r
1786 for (Index = 0; Index < HandleCount; Index++) {\r
1787 Status = gBS->HandleProtocol (\r
1788 Handles[Index],\r
1789 &gEfiBlockIoProtocolGuid,\r
1790 (VOID **) &BlkIo\r
1791 );\r
1792 if (EFI_ERROR (Status)) {\r
1793 continue;\r
1794 }\r
1795\r
1796 //\r
1797 // Skip the logical partitions\r
1798 //\r
1799 if (BlkIo->Media->LogicalPartition) {\r
1800 continue;\r
1801 }\r
1802\r
1803 //\r
1804 // Skip the fixed block io then the removable block io\r
1805 //\r
1806 if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {\r
1807 continue;\r
1808 }\r
1809\r
1810 DescriptionPtr = NULL;\r
1811 for (FunctionIndex = 0; FunctionIndex < sizeof (mBmGetBootDescription) / sizeof (mBmGetBootDescription[0]); FunctionIndex++) {\r
1812 DescriptionPtr = mBmGetBootDescription[FunctionIndex] (Handles[Index]);\r
1813 if (DescriptionPtr != NULL) {\r
1814 break;\r
1815 }\r
1816 }\r
1817\r
1818 if (DescriptionPtr == NULL) {\r
1819 continue;\r
1820 }\r
1821\r
1822 //\r
1823 // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix\r
1824 //\r
1825 Temp = AllocatePool (StrSize (DescriptionPtr) + sizeof (mBmUefiPrefix)); \r
1826 ASSERT (Temp != NULL);\r
1827 StrCpy (Temp, mBmUefiPrefix);\r
1828 StrCat (Temp, DescriptionPtr);\r
1829 FreePool (DescriptionPtr);\r
1830 DescriptionPtr = Temp;\r
1831\r
1832 BootOptions = ReallocatePool (\r
1833 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
1834 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
1835 BootOptions\r
1836 );\r
1837 ASSERT (BootOptions != NULL);\r
1838\r
1839 Status = EfiBootManagerInitializeLoadOption (\r
1840 &BootOptions[(*BootOptionCount)++],\r
1841 LoadOptionNumberUnassigned,\r
1842 LoadOptionTypeBoot,\r
1843 LOAD_OPTION_ACTIVE,\r
1844 DescriptionPtr,\r
1845 DevicePathFromHandle (Handles[Index]),\r
1846 NULL,\r
1847 0\r
1848 );\r
1849 ASSERT_EFI_ERROR (Status);\r
1850\r
1851 FreePool (DescriptionPtr);\r
1852 }\r
1853 }\r
1854\r
1855 if (HandleCount != 0) {\r
1856 FreePool (Handles);\r
1857 }\r
1858\r
1859 //\r
1860 // Parse simple file system not based on block io\r
1861 //\r
1862 NonBlockNumber = 0;\r
1863 gBS->LocateHandleBuffer (\r
1864 ByProtocol,\r
1865 &gEfiSimpleFileSystemProtocolGuid,\r
1866 NULL,\r
1867 &HandleCount,\r
1868 &Handles\r
1869 );\r
1870 for (Index = 0; Index < HandleCount; Index++) {\r
1871 Status = gBS->HandleProtocol (\r
1872 Handles[Index],\r
1873 &gEfiBlockIoProtocolGuid,\r
1874 (VOID **) &BlkIo\r
1875 );\r
1876 if (!EFI_ERROR (Status)) {\r
1877 //\r
1878 // Skip if the file system handle supports a BlkIo protocol, which we've handled in above\r
1879 //\r
1880 continue;\r
1881 }\r
1882 UnicodeSPrint (Description, sizeof (Description), NonBlockNumber > 0 ? L"%s %d" : L"%s", L"UEFI Non-Block Boot Device", NonBlockNumber);\r
1883 \r
1884 BootOptions = ReallocatePool (\r
1885 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
1886 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
1887 BootOptions\r
1888 );\r
1889 ASSERT (BootOptions != NULL);\r
1890\r
1891 Status = EfiBootManagerInitializeLoadOption (\r
1892 &BootOptions[(*BootOptionCount)++],\r
1893 LoadOptionNumberUnassigned,\r
1894 LoadOptionTypeBoot,\r
1895 LOAD_OPTION_ACTIVE,\r
1896 Description,\r
1897 DevicePathFromHandle (Handles[Index]),\r
1898 NULL,\r
1899 0\r
1900 );\r
1901 ASSERT_EFI_ERROR (Status);\r
1902 }\r
1903\r
1904 if (HandleCount != 0) {\r
1905 FreePool (Handles);\r
1906 }\r
1907\r
1908 //\r
1909 // Parse load file, assuming UEFI Network boot option\r
1910 //\r
1911 gBS->LocateHandleBuffer (\r
1912 ByProtocol,\r
1913 &gEfiLoadFileProtocolGuid,\r
1914 NULL,\r
1915 &HandleCount,\r
1916 &Handles\r
1917 );\r
1918 for (Index = 0; Index < HandleCount; Index++) {\r
1919\r
1920 UnicodeSPrint (Description, sizeof (Description), Index > 0 ? L"%s %d" : L"%s", L"UEFI Network", Index);\r
1921\r
1922 BootOptions = ReallocatePool (\r
1923 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
1924 sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
1925 BootOptions\r
1926 );\r
1927 ASSERT (BootOptions != NULL);\r
1928\r
1929 Status = EfiBootManagerInitializeLoadOption (\r
1930 &BootOptions[(*BootOptionCount)++],\r
1931 LoadOptionNumberUnassigned,\r
1932 LoadOptionTypeBoot,\r
1933 LOAD_OPTION_ACTIVE,\r
1934 Description,\r
1935 DevicePathFromHandle (Handles[Index]),\r
1936 NULL,\r
1937 0\r
1938 );\r
1939 ASSERT_EFI_ERROR (Status);\r
1940 }\r
1941\r
1942 if (HandleCount != 0) {\r
1943 FreePool (Handles);\r
1944 }\r
1945\r
1946 return BootOptions;\r
1947}\r
1948\r
1949/**\r
1950 The function enumerates all boot options, creates them and registers them in the BootOrder variable.\r
1951**/\r
1952VOID\r
1953EFIAPI\r
1954EfiBootManagerRefreshAllBootOption (\r
1955 VOID\r
1956 )\r
1957{\r
1958 EFI_STATUS Status;\r
1959 EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;\r
1960 UINTN NvBootOptionCount;\r
1961 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
1962 UINTN BootOptionCount;\r
1963 UINTN Index;\r
1964\r
1965 //\r
1966 // Optionally refresh the legacy boot option\r
1967 //\r
1968 if (mBmRefreshLegacyBootOption != NULL) {\r
1969 mBmRefreshLegacyBootOption ();\r
1970 }\r
1971\r
1972 BootOptions = BmEnumerateBootOptions (&BootOptionCount);\r
1973 NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);\r
1974\r
1975 //\r
1976 // Mark the boot option as added by BDS by setting OptionalData to a special GUID\r
1977 //\r
1978 for (Index = 0; Index < BootOptionCount; Index++) {\r
1979 BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);\r
1980 BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);\r
1981 }\r
1982\r
1983 //\r
1984 // Remove invalid EFI boot options from NV\r
1985 //\r
1986 for (Index = 0; Index < NvBootOptionCount; Index++) {\r
1987 if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || \r
1988 (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)\r
1989 ) &&\r
1990 (NvBootOptions[Index].OptionalDataSize == sizeof (EFI_GUID)) &&\r
1991 CompareGuid ((EFI_GUID *) NvBootOptions[Index].OptionalData, &mBmAutoCreateBootOptionGuid)\r
1992 ) {\r
1993 //\r
1994 // Only check those added by BDS\r
1995 // so that the boot options added by end-user or OS installer won't be deleted\r
1996 //\r
1997 if (BmFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == (UINTN) -1) {\r
1998 Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
1999 //\r
2000 // Deleting variable with current variable implementation shouldn't fail.\r
2001 //\r
2002 ASSERT_EFI_ERROR (Status);\r
2003 }\r
2004 }\r
2005 }\r
2006\r
2007 //\r
2008 // Add new EFI boot options to NV\r
2009 //\r
2010 for (Index = 0; Index < BootOptionCount; Index++) {\r
2011 if (BmFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -1) {\r
2012 EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);\r
2013 //\r
2014 // Try best to add the boot options so continue upon failure.\r
2015 //\r
2016 }\r
2017 }\r
2018\r
2019 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2020 EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);\r
2021}\r
2022\r
2023/**\r
2024 This function is called to create the boot option for the Boot Manager Menu.\r
2025\r
2026 The Boot Manager Menu is shown after successfully booting a boot option.\r
2027 Assume the BootManagerMenuFile is in the same FV as the module links to this library.\r
2028\r
2029 @param BootOption Return the boot option of the Boot Manager Menu\r
2030\r
2031 @retval EFI_SUCCESS Successfully register the Boot Manager Menu.\r
2032 @retval Status Return status of gRT->SetVariable (). BootOption still points\r
2033 to the Boot Manager Menu even the Status is not EFI_SUCCESS.\r
2034**/\r
2035EFI_STATUS\r
2036BmRegisterBootManagerMenu (\r
2037 OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2038 )\r
2039{\r
2040 EFI_STATUS Status;\r
2041 CHAR16 *Description;\r
2042 UINTN DescriptionLength;\r
2043 EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
2044 EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
2045 MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;\r
2046\r
2047 Status = GetSectionFromFv (\r
2048 PcdGetPtr (PcdBootManagerMenuFile),\r
2049 EFI_SECTION_USER_INTERFACE,\r
2050 0,\r
2051 (VOID **) &Description,\r
2052 &DescriptionLength\r
2053 );\r
2054 if (EFI_ERROR (Status)) {\r
2055 Description = NULL;\r
2056 }\r
2057\r
2058 EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));\r
2059 Status = gBS->HandleProtocol (\r
2060 gImageHandle,\r
2061 &gEfiLoadedImageProtocolGuid,\r
2062 (VOID **) &LoadedImage\r
2063 );\r
2064 ASSERT_EFI_ERROR (Status);\r
2065 DevicePath = AppendDevicePathNode (\r
2066 DevicePathFromHandle (LoadedImage->DeviceHandle),\r
2067 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
2068 );\r
2069 ASSERT (DevicePath != NULL);\r
2070\r
2071 Status = EfiBootManagerInitializeLoadOption (\r
2072 BootOption,\r
2073 LoadOptionNumberUnassigned,\r
2074 LoadOptionTypeBoot,\r
2075 LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,\r
2076 (Description != NULL) ? Description : L"Boot Manager Menu",\r
2077 DevicePath,\r
2078 NULL,\r
2079 0\r
2080 );\r
2081 ASSERT_EFI_ERROR (Status);\r
2082 FreePool (DevicePath);\r
2083 if (Description != NULL) {\r
2084 FreePool (Description);\r
2085 }\r
2086\r
2087 DEBUG_CODE (\r
2088 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2089 UINTN BootOptionCount;\r
2090\r
2091 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2092 ASSERT (BmFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);\r
2093 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2094 );\r
2095\r
2096 return EfiBootManagerAddLoadOptionVariable (BootOption, 0);\r
2097}\r
2098\r
2099/**\r
2100 Return the boot option corresponding to the Boot Manager Menu.\r
2101 It may automatically create one if the boot option hasn't been created yet.\r
2102 \r
2103 @param BootOption Return the Boot Manager Menu.\r
2104\r
2105 @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.\r
2106 @retval Status Return status of gRT->SetVariable (). BootOption still points\r
2107 to the Boot Manager Menu even the Status is not EFI_SUCCESS.\r
2108**/\r
2109EFI_STATUS\r
2110EFIAPI\r
2111EfiBootManagerGetBootManagerMenu (\r
2112 EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
2113 )\r
2114{\r
2115 EFI_STATUS Status;\r
2116 UINTN BootOptionCount;\r
2117 EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
2118 UINTN Index;\r
2119 EFI_DEVICE_PATH_PROTOCOL *Node;\r
2120 EFI_HANDLE FvHandle;\r
2121 \r
2122 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2123\r
2124 for (Index = 0; Index < BootOptionCount; Index++) {\r
2125 Node = BootOptions[Index].FilePath;\r
2126 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);\r
2127 if (!EFI_ERROR (Status)) {\r
2128 if (CompareGuid (\r
2129 EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),\r
2130 PcdGetPtr (PcdBootManagerMenuFile)\r
2131 )\r
2132 ) { \r
2133 Status = EfiBootManagerInitializeLoadOption (\r
2134 BootOption,\r
2135 BootOptions[Index].OptionNumber,\r
2136 BootOptions[Index].OptionType,\r
2137 BootOptions[Index].Attributes,\r
2138 BootOptions[Index].Description,\r
2139 BootOptions[Index].FilePath,\r
2140 BootOptions[Index].OptionalData,\r
2141 BootOptions[Index].OptionalDataSize\r
2142 );\r
2143 ASSERT_EFI_ERROR (Status);\r
2144 break;\r
2145 }\r
2146 }\r
2147 }\r
2148\r
2149 EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
2150\r
2151 //\r
2152 // Automatically create the Boot#### for Boot Manager Menu when not found.\r
2153 //\r
2154 if (Index == BootOptionCount) {\r
2155 return BmRegisterBootManagerMenu (BootOption);\r
2156 } else {\r
2157 return EFI_SUCCESS;\r
2158 }\r
2159}\r
2160\r