]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
MdeModulePkg/UefiBootManagerLib: log reserved mem allocation failure
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBoot.c
CommitLineData
067ed98a
RN
1/** @file\r
2 Library functions which relates with booting.\r
3\r
972d8872 4Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.\r
b4e1ad87 5Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>\r
e58f1ae5 6(C) Copyright 2015-2016 Hewlett Packard Enterprise Development LP<BR>\r
9d510e61 7SPDX-License-Identifier: BSD-2-Clause-Patent\r
067ed98a
RN
8\r
9**/\r
10\r
11#include "InternalBm.h"\r
12\r
339ae051
RN
13EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;\r
14\r
067ed98a
RN
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
3a039a56
DB
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
67e9ab84 43 PERF_CROSSMODULE_END("BDS");\r
3a039a56
DB
44\r
45 return ;\r
46}\r
47\r
067ed98a
RN
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
404bd442
RN
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
067ed98a
RN
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
d1102dba 107\r
067ed98a
RN
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
5d3a9896 140 Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount);\r
067ed98a
RN
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
08eff917 152 Return the correct FV file path.\r
067ed98a
RN
153 FV address may change across reboot. This routine promises the FV file device path is right.\r
154\r
525839ed 155 @param FilePath The Memory Mapped Device Path to get the file buffer.\r
067ed98a 156\r
08eff917 157 @return The updated FV Device Path pointint to the file.\r
067ed98a 158**/\r
08eff917
RN
159EFI_DEVICE_PATH_PROTOCOL *\r
160BmAdjustFvFilePath (\r
161 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
067ed98a
RN
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
067ed98a
RN
169 UINTN FvHandleCount;\r
170 EFI_HANDLE *FvHandles;\r
171 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
08eff917 172 EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
525839ed
RN
173\r
174 //\r
175 // Get the file buffer by using the exactly FilePath.\r
176 //\r
177 FvFileNode = FilePath;\r
067ed98a
RN
178 Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);\r
179 if (!EFI_ERROR (Status)) {\r
08eff917 180 return DuplicateDevicePath (FilePath);\r
067ed98a
RN
181 }\r
182\r
525839ed
RN
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
067ed98a
RN
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
08eff917 201 FullPath = BmAdjustFvFilePath (NewDevicePath);\r
067ed98a 202 FreePool (NewDevicePath);\r
08eff917
RN
203 if (FullPath != NULL) {\r
204 return FullPath;\r
067ed98a
RN
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
08eff917 217 for (Index = 0; Index < FvHandleCount; Index++) {\r
067ed98a
RN
218 if (FvHandles[Index] == LoadedImage->DeviceHandle) {\r
219 //\r
08eff917 220 // Skip current FV, it was handed in first step.\r
067ed98a
RN
221 //\r
222 continue;\r
223 }\r
224 NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);\r
08eff917 225 FullPath = BmAdjustFvFilePath (NewDevicePath);\r
067ed98a 226 FreePool (NewDevicePath);\r
08eff917
RN
227 if (FullPath != NULL) {\r
228 break;\r
229 }\r
067ed98a 230 }\r
d1102dba 231\r
067ed98a
RN
232 if (FvHandles != NULL) {\r
233 FreePool (FvHandles);\r
234 }\r
08eff917 235 return FullPath;\r
067ed98a
RN
236}\r
237\r
238/**\r
525839ed 239 Check if it's a Device Path pointing to FV file.\r
d1102dba 240\r
067ed98a
RN
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
525839ed
RN
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
067ed98a
RN
247**/\r
248BOOLEAN\r
525839ed 249BmIsFvFilePath (\r
067ed98a
RN
250 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
251 )\r
252{\r
525839ed
RN
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
067ed98a
RN
262\r
263 if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {\r
525839ed
RN
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
067ed98a
RN
267 }\r
268 }\r
067ed98a
RN
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
067ed98a
RN
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
d1102dba 469 Find a USB device which match the specified short-form device path start with\r
067ed98a
RN
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
d1102dba 475 @param ParentDevicePathSize The length of the device path before the USB Class or\r
067ed98a
RN
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
067ed98a
RN
495 BOOLEAN Matched;\r
496\r
d1102dba 497 ASSERT (UsbIoHandleCount != NULL);\r
067ed98a
RN
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
067ed98a
RN
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
08eff917
RN
568 @param FullPath The full path returned by the routine in last call.\r
569 Set to NULL in first call.\r
067ed98a
RN
570 @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.\r
571\r
08eff917
RN
572 @return The next possible full path pointing to the load option.\r
573 Caller is responsible to free the memory.\r
067ed98a 574**/\r
08eff917 575EFI_DEVICE_PATH_PROTOCOL *\r
067ed98a
RN
576BmExpandUsbDevicePath (\r
577 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
08eff917
RN
578 IN EFI_DEVICE_PATH_PROTOCOL *FullPath,\r
579 IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode\r
067ed98a
RN
580 )\r
581{\r
582 UINTN ParentDevicePathSize;\r
583 EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;\r
08eff917 584 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
067ed98a
RN
585 EFI_HANDLE *Handles;\r
586 UINTN HandleCount;\r
587 UINTN Index;\r
08eff917 588 BOOLEAN GetNext;\r
067ed98a 589\r
08eff917
RN
590 NextFullPath = NULL;\r
591 GetNext = (BOOLEAN)(FullPath == NULL);\r
067ed98a
RN
592 ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;\r
593 RemainingDevicePath = NextDevicePathNode (ShortformNode);\r
067ed98a
RN
594 Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);\r
595\r
08eff917
RN
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
067ed98a
RN
619 }\r
620\r
621 if (Handles != NULL) {\r
622 FreePool (Handles);\r
623 }\r
624\r
08eff917 625 return NextFullPath;\r
067ed98a
RN
626}\r
627\r
ccb66799
RN
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
08eff917
RN
633 @param FullPath The full path returned by the routine in last call.\r
634 Set to NULL in first call.\r
ccb66799 635\r
08eff917
RN
636 @return The next possible full path pointing to the load option.\r
637 Caller is responsible to free the memory.\r
ccb66799 638**/\r
08eff917 639EFI_DEVICE_PATH_PROTOCOL *\r
ccb66799
RN
640BmExpandFileDevicePath (\r
641 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
08eff917 642 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
ccb66799
RN
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
08eff917
RN
651 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
652 BOOLEAN GetNext;\r
d1102dba 653\r
ccb66799
RN
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
08eff917
RN
661 GetNext = (BOOLEAN)(FullPath == NULL);\r
662 NextFullPath = NULL;\r
ccb66799
RN
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
08eff917
RN
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
ccb66799 684 }\r
ccb66799
RN
685 }\r
686 }\r
08eff917
RN
687 if (NextFullPath != NULL) {\r
688 break;\r
689 }\r
ccb66799
RN
690 }\r
691\r
692 if (Handles != NULL) {\r
693 FreePool (Handles);\r
694 }\r
695\r
08eff917 696 return NextFullPath;\r
ccb66799
RN
697}\r
698\r
4a285ec1
RN
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
08eff917
RN
704 @param FullPath The full path returned by the routine in last call.\r
705 Set to NULL in first call.\r
4a285ec1 706\r
08eff917
RN
707 @return The next possible full path pointing to the load option.\r
708 Caller is responsible to free the memory.\r
4a285ec1 709**/\r
08eff917 710EFI_DEVICE_PATH_PROTOCOL *\r
4a285ec1
RN
711BmExpandUriDevicePath (\r
712 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
08eff917 713 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
4a285ec1
RN
714 )\r
715{\r
716 EFI_STATUS Status;\r
717 UINTN Index;\r
718 UINTN HandleCount;\r
719 EFI_HANDLE *Handles;\r
08eff917
RN
720 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
721 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
722 BOOLEAN GetNext;\r
4a285ec1
RN
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
08eff917
RN
731 NextFullPath = NULL;\r
732 GetNext = (BOOLEAN)(FullPath == NULL);\r
4a285ec1 733 for (Index = 0; Index < HandleCount; Index++) {\r
08eff917
RN
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
026ede49 741 break;\r
08eff917
RN
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
4a285ec1
RN
754 }\r
755 }\r
756\r
757 if (Handles != NULL) {\r
758 FreePool (Handles);\r
759 }\r
760\r
08eff917 761 return NextFullPath;\r
4a285ec1
RN
762}\r
763\r
067ed98a
RN
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
d1102dba 778\r
067ed98a
RN
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
067ed98a 831\r
08eff917 832 @return The full device path pointing to the load option.\r
067ed98a 833**/\r
08eff917 834EFI_DEVICE_PATH_PROTOCOL *\r
067ed98a 835BmExpandPartitionDevicePath (\r
08eff917 836 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
067ed98a
RN
837 )\r
838{\r
839 EFI_STATUS Status;\r
840 UINTN BlockIoHandleCount;\r
841 EFI_HANDLE *BlockIoBuffer;\r
067ed98a
RN
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
08eff917 847 EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
067ed98a
RN
848 UINTN CachedDevicePathSize;\r
849 BOOLEAN NeedAdjust;\r
850 EFI_DEVICE_PATH_PROTOCOL *Instance;\r
851 UINTN Size;\r
852\r
067ed98a
RN
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
08eff917 876 FullPath = NULL;\r
067ed98a
RN
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
08eff917
RN
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
067ed98a
RN
906 FreePool (TempDevicePath);\r
907\r
08eff917 908 if (FullPath != NULL) {\r
067ed98a
RN
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
08eff917 929 return FullPath;\r
067ed98a
RN
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
08eff917 965 FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);\r
067ed98a
RN
966 FreePool (TempDevicePath);\r
967\r
08eff917 968 if (FullPath != NULL) {\r
067ed98a
RN
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
08eff917 994 return FullPath;\r
067ed98a
RN
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
08eff917
RN
1002 @param FullPath The full path returned by the routine in last call.\r
1003 Set to NULL in first call.\r
067ed98a 1004\r
08eff917
RN
1005 @return The next possible full path pointing to the load option.\r
1006 Caller is responsible to free the memory.\r
067ed98a 1007**/\r
08eff917 1008EFI_DEVICE_PATH_PROTOCOL *\r
067ed98a
RN
1009BmExpandMediaDevicePath (\r
1010 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
08eff917 1011 IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
067ed98a
RN
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
08eff917 1019 EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
067ed98a
RN
1020 UINTN Size;\r
1021 UINTN TempSize;\r
1022 EFI_HANDLE *SimpleFileSystemHandles;\r
1023 UINTN NumberSimpleFileSystemHandles;\r
1024 UINTN Index;\r
08eff917 1025 BOOLEAN GetNext;\r
067ed98a 1026\r
08eff917 1027 GetNext = (BOOLEAN)(FullPath == NULL);\r
067ed98a
RN
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
08eff917
RN
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
067ed98a 1045 }\r
067ed98a
RN
1046 }\r
1047\r
00e39b09
RN
1048 Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);\r
1049 ASSERT_EFI_ERROR (Status);\r
1050\r
067ed98a 1051 //\r
00e39b09
RN
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
067ed98a
RN
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
067ed98a
RN
1065 Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
1066 ASSERT_EFI_ERROR (Status);\r
6c27a4d3
MH
1067 if (EFI_ERROR (Status)) {\r
1068 return NULL;\r
1069 }\r
067ed98a
RN
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
08eff917 1085 NextFullPath = NULL;\r
067ed98a
RN
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
08eff917
RN
1104 NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);\r
1105 if (GetNext) {\r
067ed98a 1106 break;\r
08eff917
RN
1107 } else {\r
1108 GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
1109 FreePool (NextFullPath);\r
1110 NextFullPath = NULL;\r
067ed98a 1111 }\r
067ed98a
RN
1112 }\r
1113 }\r
1114\r
1115 if (SimpleFileSystemHandles != NULL) {\r
1116 FreePool (SimpleFileSystemHandles);\r
1117 }\r
1118\r
08eff917 1119 return NextFullPath;\r
067ed98a
RN
1120}\r
1121\r
bf5328f0
RN
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
6945837e
JW
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
bf5328f0
RN
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
f5c12172
RN
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
339ae051 1166 @param RamDiskHandle Return the RAM Disk handle.\r
f5c12172 1167\r
08eff917
RN
1168 @return The next possible full path pointing to the load option.\r
1169 Caller is responsible to free the memory.\r
f5c12172 1170**/\r
08eff917
RN
1171EFI_DEVICE_PATH_PROTOCOL *\r
1172BmExpandNetworkFileSystem (\r
f5c12172 1173 IN EFI_HANDLE LoadFileHandle,\r
339ae051 1174 OUT EFI_HANDLE *RamDiskHandle\r
f5c12172
RN
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
eeab16c8
RN
1195\r
1196 Handle = NULL;\r
f5c12172
RN
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
08eff917
RN
1203 //\r
1204 // Find the BlockIo instance populated from the LoadFile.\r
1205 //\r
f5c12172
RN
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
339ae051
RN
1215 if (Index == HandleCount) {\r
1216 Handle = NULL;\r
1217 }\r
1218\r
1219 *RamDiskHandle = Handle;\r
1220\r
1221 if (Handle != NULL) {\r
08eff917
RN
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
f5c12172
RN
1227 } else {\r
1228 return NULL;\r
1229 }\r
1230}\r
1231\r
339ae051
RN
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
026ede49
RN
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
026ede49 1345\r
08eff917 1346 @return The full device path pointing to the load option buffer.\r
026ede49 1347**/\r
08eff917
RN
1348EFI_DEVICE_PATH_PROTOCOL *\r
1349BmExpandLoadFile (\r
339ae051 1350 IN EFI_HANDLE LoadFileHandle,\r
08eff917 1351 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
026ede49
RN
1352 )\r
1353{\r
1354 EFI_STATUS Status;\r
1355 EFI_LOAD_FILE_PROTOCOL *LoadFile;\r
1356 VOID *FileBuffer;\r
339ae051 1357 EFI_HANDLE RamDiskHandle;\r
026ede49 1358 UINTN BufferSize;\r
08eff917 1359 EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
026ede49
RN
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
08eff917
RN
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
026ede49 1389 if (FileBuffer == NULL) {\r
a56af23f
LE
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
026ede49
RN
1421 return NULL;\r
1422 }\r
1423\r
1424 Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
1425 if (EFI_ERROR (Status)) {\r
08eff917 1426 FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));\r
026ede49
RN
1427 return NULL;\r
1428 }\r
1429\r
08eff917
RN
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
026ede49
RN
1436 }\r
1437\r
08eff917 1438 return FullPath;\r
026ede49
RN
1439}\r
1440\r
bf5328f0 1441/**\r
08eff917
RN
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
bf5328f0
RN
1455\r
1456 @param FilePath The media device path pointing to a LoadFile instance.\r
bf5328f0
RN
1457\r
1458 @return The load option buffer.\r
1459**/\r
08eff917
RN
1460EFI_DEVICE_PATH_PROTOCOL *\r
1461BmExpandLoadFiles (\r
1462 IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
bf5328f0
RN
1463 )\r
1464{\r
1465 EFI_STATUS Status;\r
1466 EFI_HANDLE Handle;\r
bf5328f0
RN
1467 EFI_HANDLE *Handles;\r
1468 UINTN HandleCount;\r
1469 UINTN Index;\r
1470 EFI_DEVICE_PATH_PROTOCOL *Node;\r
bf5328f0
RN
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
08eff917 1515 return BmExpandLoadFile (Handle, FilePath);\r
bf5328f0
RN
1516}\r
1517\r
067ed98a
RN
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
4ed2440d
RN
1531EFIAPI\r
1532EfiBootManagerGetLoadOptionBuffer (\r
067ed98a
RN
1533 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
1534 OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
1535 OUT UINTN *FileSize\r
1536 )\r
08eff917
RN
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
067ed98a
RN
1563{\r
1564 EFI_HANDLE Handle;\r
067ed98a
RN
1565 EFI_DEVICE_PATH_PROTOCOL *Node;\r
1566 EFI_STATUS Status;\r
1567\r
08eff917 1568 ASSERT (FilePath != NULL);\r
067ed98a
RN
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
08eff917 1580 return BmExpandMediaDevicePath (FilePath, FullPath);\r
067ed98a
RN
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
08eff917
RN
1591 if (FullPath == NULL) {\r
1592 return BmExpandPartitionDevicePath (FilePath);\r
1593 } else {\r
1594 return NULL;\r
1595 }\r
ccb66799
RN
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
08eff917 1601 return BmExpandFileDevicePath (FilePath, FullPath);\r
4a285ec1
RN
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
08eff917 1607 return BmExpandUriDevicePath (FilePath, FullPath);\r
067ed98a 1608 } else {\r
21e359dc
RN
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
067ed98a 1622 }\r
067ed98a 1623\r
21e359dc
RN
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
067ed98a 1637 }\r
067ed98a
RN
1638 }\r
1639 }\r
1640\r
1641 //\r
08eff917
RN
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
067ed98a 1651 //\r
525839ed 1652 if (BmIsFvFilePath (FilePath)) {\r
08eff917 1653 return BmAdjustFvFilePath (FilePath);\r
067ed98a
RN
1654 }\r
1655\r
1656 //\r
08eff917 1657 // Load option resides in Simple File System.\r
067ed98a 1658 //\r
de6c0eff 1659 Node = FilePath;\r
bf5328f0
RN
1660 Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);\r
1661 if (!EFI_ERROR (Status)) {\r
08eff917 1662 return DuplicateDevicePath (FilePath);\r
067ed98a
RN
1663 }\r
1664\r
08eff917
RN
1665 //\r
1666 // Last chance to try: Load option may be loaded through LoadFile.\r
1667 //\r
1668 return BmExpandLoadFiles (FilePath);\r
067ed98a
RN
1669}\r
1670\r
0aa09042 1671/**\r
7c69fbf2 1672 Check if it's a Device Path pointing to BootManagerMenu.\r
0aa09042
LG
1673\r
1674 @param DevicePath Input device path.\r
1675\r
7c69fbf2
LG
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
0aa09042
LG
1678**/\r
1679BOOLEAN\r
7c69fbf2 1680BmIsBootManagerMenuFilePath (\r
0aa09042
LG
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
5cc67962
LE
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
067ed98a
RN
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
d1102dba
LG
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
067ed98a
RN
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
d1102dba
LG
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
067ed98a
RN
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
339ae051 1777 EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
067ed98a
RN
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
7c69fbf2 1832 if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) {\r
067ed98a
RN
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
72208a9a 1844 BmRepairAllControllers (0);\r
067ed98a
RN
1845 }\r
1846\r
1847 PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1848\r
1849 //\r
3a986a35
RN
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
067ed98a 1859 //\r
3a986a35
RN
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
339ae051
RN
1868 ImageHandle = NULL;\r
1869 RamDiskDevicePath = NULL;\r
067ed98a 1870 if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {\r
08eff917
RN
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
339ae051
RN
1875 if (FileBuffer != NULL) {\r
1876 RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);\r
08eff917 1877\r
067ed98a
RN
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
f7fdd620
DB
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
067ed98a 1905 //\r
c2cf8720 1906 // Report Status Code with the failure status to indicate that the failure to load boot option\r
067ed98a 1907 //\r
5cc67962 1908 BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status);\r
067ed98a 1909 BootOption->Status = Status;\r
339ae051
RN
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
067ed98a
RN
1917 return;\r
1918 }\r
1919 }\r
1920\r
067ed98a
RN
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
3a039a56 1936 BmEndOfBdsPerfCode,\r
d1102dba 1937 NULL,\r
067ed98a
RN
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
d1102dba 1951\r
067ed98a
RN
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
404bd442
RN
1958 if (!BmIsAutoCreateBootOption (BootOption)) {\r
1959 ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;\r
1960 ImageInfo->LoadOptions = BootOption->OptionalData;\r
1961 }\r
067ed98a
RN
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
3a039a56 1977 BmEndOfBdsPerfCode (NULL, NULL);\r
067ed98a
RN
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
c2cf8720 1987 // Report Status Code with the failure status to indicate that boot failure\r
067ed98a 1988 //\r
5cc67962 1989 BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status);\r
067ed98a
RN
1990 }\r
1991 PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
1992\r
339ae051
RN
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
067ed98a
RN
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
02021d2e 2059 // Match all the partition device path nodes including the nested partition nodes\r
067ed98a
RN
2060 //\r
2061 while (!IsDevicePathEnd (BlockIoDevicePath)) {\r
2062 if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&\r
2063 (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)\r
2064 ) {\r
02021d2e
GL
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
067ed98a
RN
2080 }\r
2081\r
2082 BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);\r
2083 }\r
2084\r
02021d2e 2085 return FALSE;\r
067ed98a
RN
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
d1102dba 2097 4. LoadFile - The boot option points to the media supporting\r
067ed98a
RN
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
067ed98a
RN
2112 UINTN HandleCount;\r
2113 EFI_HANDLE *Handles;\r
2114 EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
2115 UINTN Removable;\r
2116 UINTN Index;\r
f41c71d2 2117 CHAR16 *Description;\r
067ed98a
RN
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
f41c71d2 2160 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
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
f41c71d2 2173 Description,\r
067ed98a
RN
2174 DevicePathFromHandle (Handles[Index]),\r
2175 NULL,\r
2176 0\r
2177 );\r
2178 ASSERT_EFI_ERROR (Status);\r
2179\r
f41c71d2 2180 FreePool (Description);\r
067ed98a
RN
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
067ed98a
RN
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
f41c71d2 2210 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
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
f41c71d2 2229 FreePool (Description);\r
067ed98a
RN
2230 }\r
2231\r
2232 if (HandleCount != 0) {\r
2233 FreePool (Handles);\r
2234 }\r
2235\r
2236 //\r
2f014788 2237 // Parse load file protocol\r
067ed98a
RN
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
8953d69a
LG
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
067ed98a 2253\r
f41c71d2 2254 Description = BmGetBootDescription (Handles[Index]);\r
067ed98a
RN
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
8953d69a 2266 LOAD_OPTION_ACTIVE,\r
067ed98a
RN
2267 Description,\r
2268 DevicePathFromHandle (Handles[Index]),\r
2269 NULL,\r
2270 0\r
2271 );\r
2272 ASSERT_EFI_ERROR (Status);\r
f41c71d2 2273 FreePool (Description);\r
067ed98a
RN
2274 }\r
2275\r
2276 if (HandleCount != 0) {\r
2277 FreePool (Handles);\r
2278 }\r
2279\r
d948fe96 2280 BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount);\r
067ed98a
RN
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
972d8872
AS
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
067ed98a
RN
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
067ed98a
RN
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
972d8872
AS
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
067ed98a
RN
2344 //\r
2345 // Remove invalid EFI boot options from NV\r
2346 //\r
2347 for (Index = 0; Index < NvBootOptionCount; Index++) {\r
d1102dba 2348 if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) ||\r
067ed98a 2349 (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)\r
404bd442 2350 ) && BmIsAutoCreateBootOption (&NvBootOptions[Index])\r
067ed98a
RN
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
51dee9a4 2356 if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) {\r
067ed98a
RN
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
51dee9a4 2370 if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) {\r
067ed98a
RN
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
e58f1ae5 2383 This function is called to get or create the boot option for the Boot Manager Menu.\r
067ed98a
RN
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
e58f1ae5
SW
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
067ed98a
RN
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
2f014788
LG
2407 UINTN HandleCount;\r
2408 EFI_HANDLE *Handles;\r
2409 UINTN Index;\r
e58f1ae5
SW
2410 VOID *Data;\r
2411 UINTN DataSize;\r
067ed98a 2412\r
2f014788 2413 DevicePath = NULL;\r
e9e44f65 2414 Description = NULL;\r
e58f1ae5 2415 //\r
7c69fbf2 2416 // Try to find BootManagerMenu from LoadFile protocol\r
e58f1ae5 2417 //\r
2f014788
LG
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
7c69fbf2 2426 if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {\r
2f014788
LG
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
067ed98a
RN
2434 }\r
2435\r
2f014788
LG
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
067ed98a
RN
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
5d3a9896 2502 ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);\r
067ed98a
RN
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
e58f1ae5 2512\r
067ed98a
RN
2513 @param BootOption Return the Boot Manager Menu.\r
2514\r
2515 @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.\r
e58f1ae5
SW
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
067ed98a
RN
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
d1102dba 2531\r
067ed98a
RN
2532 BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
2533\r
2534 for (Index = 0; Index < BootOptionCount; Index++) {\r
7c69fbf2 2535 if (BmIsBootManagerMenuFilePath (BootOptions[Index].FilePath)) {\r
067ed98a
RN
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
067ed98a
RN
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
b4e1ad87
CC
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
68a4e15e
CC
2578EFIAPI\r
2579EfiBootManagerGetNextLoadOptionDevicePath (\r
b4e1ad87
CC
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