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