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