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