]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg: Add UefiBootManagerLib
authorRuiyu Ni <ruiyu.ni@intel.com>
Wed, 6 May 2015 04:47:02 +0000 (04:47 +0000)
committerniruiyu <niruiyu@Edk2>
Wed, 6 May 2015 04:47:02 +0000 (04:47 +0000)
UefiBootManagerLib provides:
 load option library functions;
 hot key library functions;
 boot library functions;
 connect and disconnect library functions;
 driver health library functions.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17327 6f19259b-4bc3-4df7-8a09-765794883524

14 files changed:
MdeModulePkg/Include/Library/UefiBootManagerLib.h [new file with mode: 0644]
MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf
MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/BmPerformance.c [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h [new file with mode: 0644]
MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf [new file with mode: 0644]
MdeModulePkg/MdeModulePkg.dec
MdeModulePkg/MdeModulePkg.dsc

diff --git a/MdeModulePkg/Include/Library/UefiBootManagerLib.h b/MdeModulePkg/Include/Library/UefiBootManagerLib.h
new file mode 100644 (file)
index 0000000..9b08364
--- /dev/null
@@ -0,0 +1,657 @@
+/** @file
+  Provide Boot Manager related library APIs.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#ifndef _UEFI_BOOT_MANAGER_LIB_H_
+#define _UEFI_BOOT_MANAGER_LIB_H_
+
+#include <Protocol/DriverHealth.h>
+#include <Library/SortLib.h>
+
+//
+// Boot Manager load option library functions.
+//
+
+//
+// Load Option Type
+//
+typedef enum {
+  LoadOptionTypeBoot,
+  LoadOptionTypeDriver,
+  LoadOptionTypeMax
+} EFI_BOOT_MANAGER_LOAD_OPTION_TYPE;
+
+typedef enum {
+  LoadOptionNumberMax = 0x10000,
+  LoadOptionNumberUnassigned = LoadOptionNumberMax
+} EFI_BOOT_MANAGER_LOAD_OPTION_NUMBER;
+
+//
+// Common structure definition for DriverOption and BootOption
+//
+typedef struct {
+  //
+  // Data read from UEFI NV variables
+  //
+  UINTN                             OptionNumber;       // #### numerical value, could be LoadOptionNumberUnassigned
+  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;         // LoadOptionTypeBoot or LoadOptionTypeDriver
+  UINT32                            Attributes;         // Load Option Attributes
+  CHAR16                            *Description;       // Load Option Description
+  EFI_DEVICE_PATH_PROTOCOL          *FilePath;          // Load Option Device Path
+  UINT8                             *OptionalData;      // Load Option optional data to pass into image
+  UINT32                            OptionalDataSize;   // Load Option size of OptionalData
+
+  //
+  // Used at runtime
+  //
+  EFI_STATUS                        Status;             // Status returned from boot attempt gBS->StartImage ()
+  CHAR16                            *ExitData;          // Exit data returned from gBS->StartImage () 
+  UINTN                             ExitDataSize;       // Size of ExitData
+} EFI_BOOT_MANAGER_LOAD_OPTION;
+
+/**
+  Returns an array of load options based on the EFI variable
+  L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it.
+  #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry. 
+
+  @param  LoadOptionCount   Returns number of entries in the array.
+  @param  LoadOptionType    The type of the load option.
+
+  @retval NULL  No load options exist.
+  @retval !NULL Array of load option entries.
+
+**/
+EFI_BOOT_MANAGER_LOAD_OPTION *
+EFIAPI
+EfiBootManagerGetLoadOptions (
+  OUT UINTN                            *LoadOptionCount,
+  IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType
+  );
+
+/**
+  Free an array of load options returned from EfiBootManagerGetLoadOptions().
+
+  @param  LoadOptions      Pointer to the array of load options to free.
+  @param  LoadOptionCount  Number of array entries in LoadOptions.
+
+  @return EFI_SUCCESS           LoadOptions was freed.
+  @return EFI_INVALID_PARAMETER LoadOptions is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeLoadOptions (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION  *LoadOptions,
+  IN  UINTN                         LoadOptionCount
+  );
+
+/**
+  Initialize a load option.
+
+  @param Option           Pointer to the load option to be initialized.
+  @param OptionNumber     Option number of the load option.
+  @param OptionType       Type of the load option.
+  @param Attributes       Attributes of the load option.
+  @param Description      Description of the load option.
+  @param FilePath         Device path of the load option.
+  @param OptionalData     Optional data of the load option.
+  @param OptionalDataSize Size of the optional data of the load option.
+
+  @retval EFI_SUCCESS           The load option was initialized successfully.
+  @retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerInitializeLoadOption (
+  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION   *Option,
+  IN  UINTN                             OptionNumber,
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,
+  IN  UINT32                            Attributes,
+  IN  CHAR16                            *Description,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  UINT8                             *OptionalData,
+  IN  UINT32                            OptionalDataSize
+  );
+
+/**
+  Free a load option created by EfiBootManagerInitializeLoadOption()
+  or EfiBootManagerVariableToLoadOption().
+
+  @param  LoadOption   Pointer to the load option to free.
+  CONCERN: Check Boot#### instead of BootOrder, optimize, spec clarify
+  @return EFI_SUCCESS           LoadOption was freed.
+  @return EFI_INVALID_PARAMETER LoadOption is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeLoadOption (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION  *LoadOption
+  );
+
+/**
+  Initialize the load option from the VariableName.
+
+  @param  VariableName          EFI Variable name which could be Boot#### or
+                                Driver####
+  @param  LoadOption            Pointer to the load option to be initialized
+
+  @retval EFI_SUCCESS           The option was created
+  @retval EFI_INVALID_PARAMETER VariableName or LoadOption is NULL.
+  @retval EFI_NOT_FOUND         The variable specified by VariableName cannot be found.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerVariableToLoadOption (
+  IN CHAR16                           *VariableName,
+  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption
+  );
+
+/**
+  Create the Boot#### or Driver#### variable from the load option.
+  
+  @param  LoadOption      Pointer to the load option.
+
+  @retval EFI_SUCCESS     The variable was created.
+  @retval Others          Error status returned by RT->SetVariable.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerLoadOptionToVariable (
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION     *LoadOption
+  );
+
+/**
+  This function will update the Boot####/Driver#### and the BootOrder/DriverOrder
+  to add a new load option.
+
+  @param  Option        Pointer to load option to add.
+  @param  Position      Position of the new load option to put in the BootOrder/DriverOrder.
+
+  @retval EFI_SUCCESS   The load option has been successfully added.
+  @retval Others        Error status returned by RT->SetVariable.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerAddLoadOptionVariable (
+  IN EFI_BOOT_MANAGER_LOAD_OPTION  *Option,
+  IN UINTN                         Position
+  );
+
+/**
+  Delete the load option according to the OptionNumber and OptionType.
+  
+  Only the BootOrder/DriverOrder is updated to remove the reference of the OptionNumber.
+  
+  @param  OptionNumber        Option number of the load option.
+  @param  OptionType          Type of the load option.
+
+  @retval EFI_NOT_FOUND       The load option cannot be found.
+  @retval EFI_SUCCESS         The load option was deleted.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerDeleteLoadOptionVariable (
+  IN UINTN                              OptionNumber,
+  IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  OptionType
+  );
+
+/**
+  Sort the load options. The DriverOrder/BootOrder variables will be re-created to 
+  reflect the new order.
+
+  @param OptionType        The type of the load option.
+  @param Comparator        The comparator function pointer.
+**/
+VOID
+EFIAPI
+EfiBootManagerSortLoadOptionVariable (
+  IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,
+  IN SORT_COMPARE                      CompareFunction
+  );
+
+//
+// Boot Manager hot key library functions.
+//
+
+#pragma pack(1)
+///
+/// EFI Key Option.
+///
+typedef struct {
+  ///
+  /// Specifies options about how the key will be processed.
+  ///
+  EFI_BOOT_KEY_DATA  KeyData;
+  ///
+  /// The CRC-32 which should match the CRC-32 of the entire EFI_LOAD_OPTION to
+  /// which BootOption refers. If the CRC-32s do not match this value, then this key
+  /// option is ignored.
+  ///
+  UINT32             BootOptionCrc;
+  ///
+  /// The Boot#### option which will be invoked if this key is pressed and the boot option
+  /// is active (LOAD_OPTION_ACTIVE is set).
+  ///
+  UINT16             BootOption;
+  ///
+  /// The key codes to compare against those returned by the
+  /// EFI_SIMPLE_TEXT_INPUT and EFI_SIMPLE_TEXT_INPUT_EX protocols.
+  /// The number of key codes (0-3) is specified by the EFI_KEY_CODE_COUNT field in KeyOptions.
+  ///
+  EFI_INPUT_KEY      Keys[3];
+  UINT16             OptionNumber;
+} EFI_BOOT_MANAGER_KEY_OPTION;
+#pragma pack()
+
+/**
+  Start the hot key service so that the key press can trigger the boot option.
+
+  @param HotkeyTriggered  Return the waitable event and it will be signaled 
+                          when a valid hot key is pressed.
+
+  @retval EFI_SUCCESS     The hot key service is started.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerStartHotkeyService (
+  IN EFI_EVENT      *HotkeyTriggered
+  );
+
+//
+// Modifier for EfiBootManagerAddKeyOptionVariable and EfiBootManagerDeleteKeyOptionVariable
+//
+#define EFI_BOOT_MANAGER_SHIFT_PRESSED    0x00000001
+#define EFI_BOOT_MANAGER_CONTROL_PRESSED  0x00000002
+#define EFI_BOOT_MANAGER_ALT_PRESSED      0x00000004
+#define EFI_BOOT_MANAGER_LOGO_PRESSED     0x00000008
+#define EFI_BOOT_MANAGER_MENU_KEY_PRESSED 0x00000010
+#define EFI_BOOT_MANAGER_SYS_REQ_PRESSED  0x00000020
+
+/**
+  Add the key option.
+  It adds the key option variable and the key option takes affect immediately.
+
+  @param AddedOption      Return the added key option.
+  @param BootOptionNumber The boot option number for the key option.
+  @param Modifier         Key shift state.
+  @param ...              Parameter list of pointer of EFI_INPUT_KEY.
+
+  @retval EFI_SUCCESS         The key option is added.
+  @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerAddKeyOptionVariable (
+  OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption,   OPTIONAL
+  IN UINT16                       BootOptionNumber,
+  IN UINT32                       Modifier,
+  ...
+  );
+
+/**
+  Delete the Key Option variable and unregister the hot key
+
+  @param DeletedOption  Return the deleted key options.
+  @param Modifier       Key shift state.
+  @param ...            Parameter list of pointer of EFI_INPUT_KEY.
+
+  @retval EFI_SUCCESS   The key option is deleted.
+  @retval EFI_NOT_FOUND The key option cannot be found.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerDeleteKeyOptionVariable (
+  IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL
+  IN UINT32                      Modifier,
+  ...
+  );
+
+/**
+  Register the key option to exit the waiting of the Boot Manager timeout.
+  Platform should ensure that the continue key option isn't conflict with
+  other boot key options.
+
+  @param Modifier     Key shift state.
+  @param  ...         Parameter list of pointer of EFI_INPUT_KEY.
+
+  @retval EFI_SUCCESS         Successfully register the continue key option.
+  @retval EFI_ALREADY_STARTED The continue key option is already registered.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerRegisterContinueKeyOption (
+  IN UINT32           Modifier,
+  ...
+  );
+
+/**
+  Try to boot the boot option triggered by hot key.
+**/
+VOID
+EFIAPI
+EfiBootManagerHotkeyBoot (
+  VOID
+  );
+//
+// Boot Manager boot library functions.
+//
+
+/**
+  The function creates boot options for all possible bootable medias in the following order:
+  1. Removable BlockIo            - The boot option only points to the removable media
+                                    device, like USB key, DVD, Floppy etc.
+  2. Fixed BlockIo                - The boot option only points to a Fixed blockIo device,
+                                    like HardDisk.
+  3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting
+                                    SimpleFileSystem Protocol, but not supporting BlockIo
+                                    protocol.
+  4. LoadFile                     - The boot option points to the media supporting 
+                                    LoadFile protocol.
+  Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior
+
+  The function won't delete the boot option not added by itself.
+**/
+VOID
+EFIAPI
+EfiBootManagerRefreshAllBootOption (
+  VOID
+  );
+
+/**
+  Attempt to boot the EFI boot option. This routine sets L"BootCurent" and
+  signals the EFI ready to boot event. If the device path for the option starts
+  with a BBS device path a legacy boot is attempted. Short form device paths are
+  also supported via this rountine. A device path starting with 
+  MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP, MSG_USB_CLASS_DP gets expaned out
+  to find the first device that matches. If the BootOption Device Path 
+  fails the removable media boot algorithm is attempted (\EFI\BOOTIA32.EFI,
+  \EFI\BOOTX64.EFI,... only one file type is tried per processor type)
+
+  @param  BootOption    Boot Option to try and boot.
+                        On return, BootOption->Status contains the boot status:
+                        EFI_SUCCESS     BootOption was booted
+                        EFI_UNSUPPORTED BootOption isn't supported.
+                        EFI_NOT_FOUND   The BootOption was not found on the system
+                        Others          BootOption failed with this error status
+
+**/
+VOID
+EFIAPI
+EfiBootManagerBoot (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption
+  );
+
+/**
+  Return the Boot Manager Menu.
+  @param BootOption    Return the Boot Manager Menu.
+
+  @retval EFI_SUCCESS   The Boot Manager Menu is successfully returned.
+  @retval EFI_NOT_FOUND The Boot Manager Menu is not found.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerGetBootManagerMenu (
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
+  );
+
+/**
+  The function enumerates all the legacy boot options, creates them and 
+  registers them in the BootOrder variable.
+**/
+typedef
+VOID
+(EFIAPI *EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION) (
+  VOID
+  );
+
+/**
+  The function boots a legacy boot option.
+**/
+typedef
+VOID
+(EFIAPI *EFI_BOOT_MANAGER_LEGACY_BOOT) (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOption
+  );
+
+/**
+  The function registers the legacy boot support capabilities.
+
+  @param RefreshLegacyBootOption The function pointer to create all the legacy boot options.
+  @param LegacyBoot              The function pointer to boot the legacy boot option.
+**/
+VOID
+EFIAPI
+EfiBootManagerRegisterLegacyBootSupport (
+  EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION   RefreshLegacyBootOption,
+  EFI_BOOT_MANAGER_LEGACY_BOOT                  LegacyBoot
+  );
+
+
+//
+// Boot Manager connect and disconnect library functions
+//
+
+/**
+  This function will connect all the system driver to controller
+  first, and then special connect the default console, this make
+  sure all the system controller available and the platform default
+  console connected.
+**/
+VOID
+EFIAPI
+EfiBootManagerConnectAll (
+  VOID
+  );
+
+/**
+  This function will create all handles associate with every device
+  path node. If the handle associate with one device path node can not
+  be created successfully, then still give one chance to do the dispatch,
+  which load the missing drivers if possible.
+
+  @param  DevicePathToConnect   The device path which will be connected, it CANNOT be
+                                a multi-instance device path
+  @param  MatchingHandle        Return the controller handle closest to the DevicePathToConnect
+
+  @retval EFI_INVALID_PARAMETER DevicePathToConnect is NULL.
+  @retval EFI_NOT_FOUND         Failed to create all handles associate with every device path node.
+  @retval EFI_SUCCESS           Successful to create all handles associate with every device path node.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL  *DevicePathToConnect,
+  OUT EFI_HANDLE                *MatchingHandle          OPTIONAL
+  );
+
+/**
+  This function will disconnect all current system handles. 
+  
+  gBS->DisconnectController() is invoked for each handle exists in system handle buffer.
+  If handle is a bus type handle, all childrens also are disconnected recursively by
+  gBS->DisconnectController().
+**/
+VOID
+EFIAPI
+EfiBootManagerDisconnectAll (
+  VOID
+  );
+
+
+//
+// Boot Manager console library functions
+//
+
+typedef enum {
+  ConIn,
+  ConOut,
+  ErrOut,
+  ConInDev,
+  ConOutDev,
+  ErrOutDev,
+  ConsoleTypeMax
+} CONSOLE_TYPE;
+
+/**
+  This function will connect all the console devices base on the console
+  device variable ConIn, ConOut and ErrOut.
+**/
+VOID
+EFIAPI
+EfiBootManagerConnectAllDefaultConsoles (
+  VOID
+  );
+
+/**
+  This function updates the console variable based on ConVarName. It can
+  add or remove one specific console device path from the variable
+
+  @param  ConsoleType              ConIn, ConOut, ErrOut, ConInDev, ConOutDev or ErrOutDev.
+  @param  CustomizedConDevicePath  The console device path to be added to
+                                   the console variable. Cannot be multi-instance.
+  @param  ExclusiveDevicePath      The console device path to be removed
+                                   from the console variable. Cannot be multi-instance.
+
+  @retval EFI_UNSUPPORTED          The added device path is the same as a removed one.
+  @retval EFI_SUCCESS              Successfully added or removed the device path from the
+                                   console variable.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerUpdateConsoleVariable (
+  IN  CONSOLE_TYPE              ConsoleType,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *CustomizedConDevicePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *ExclusiveDevicePath
+  );
+
+/**
+  Connect the console device base on the variable ConVarName, if
+  device path of the ConVarName is multi-instance device path, if
+  anyone of the instances is connected success, then this function
+  will return success.
+
+  @param  ConsoleType              ConIn, ConOut or ErrOut.
+
+  @retval EFI_NOT_FOUND            There is not any console devices connected
+                                   success
+  @retval EFI_SUCCESS              Success connect any one instance of the console
+                                   device path base on the variable ConVarName.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectConsoleVariable (
+  IN  CONSOLE_TYPE              ConsoleType
+  );
+
+/**
+  Query all the children of VideoController and return the device paths of all the 
+  children that support GraphicsOutput protocol.
+
+  @param VideoController       PCI handle of video controller.
+
+  @return  Device paths of all the children that support GraphicsOutput protocol.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiBootManagerGetGopDevicePath (
+  IN  EFI_HANDLE               VideoController
+  );
+
+/**
+  Connect the platform active active video controller.
+
+  @param VideoController       PCI handle of video controller.
+
+  @retval EFI_NOT_FOUND There is no active video controller.
+  @retval EFI_SUCCESS   The video controller is connected.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectVideoController (
+  EFI_HANDLE                 VideoController  OPTIONAL
+  );
+
+//
+// Boot Manager driver health library functions.
+//
+
+typedef struct {
+  EFI_DRIVER_HEALTH_PROTOCOL      *DriverHealth;
+
+  ///
+  /// Driver relative handles
+  ///
+  EFI_HANDLE                      DriverHealthHandle;
+  EFI_HANDLE                      ControllerHandle;
+  EFI_HANDLE                      ChildHandle;
+
+  ///
+  /// Driver health messages of the specify Driver 
+  ///
+  EFI_DRIVER_HEALTH_HII_MESSAGE   *MessageList;
+
+  ///
+  /// HII relative handles
+  ///
+  EFI_HII_HANDLE                  HiiHandle;
+
+  ///
+  /// Driver Health status
+  ///
+  EFI_DRIVER_HEALTH_STATUS        HealthStatus;
+} EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO;
+
+/**
+  Return all the Driver Health information.
+
+  When the cumulative health status of all the controllers managed by the
+  driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such
+  EFI_DRIVER_HEALTH_PROTOCOL instance.
+  Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
+  entry. Additionally every child controller creates one
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver.
+
+  @param Count      Return the count of the Driver Health information.
+
+  @retval NULL      No Driver Health information is returned.
+  @retval !NULL     Pointer to the Driver Health information array.
+**/
+EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *
+EFIAPI
+EfiBootManagerGetDriverHealthInfo (
+  UINTN    *Count
+  );
+
+/**
+  Free the Driver Health information array.
+
+  @param DriverHealthInfo       Pointer to array of the Driver Health information.
+  @param Count                  Count of the array.
+
+  @retval EFI_SUCCESS           The array is freed.
+  @retval EFI_INVALID_PARAMETER The array is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeDriverHealthInfo (
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO  *DriverHealthInfo,
+  UINTN                                Count
+  );
+
+#endif
index e372248fef527f6a9393255daeaae5685fd3f0fd..4aade32cf25dec71e9e3339f5427c85923bb4777 100644 (file)
@@ -1,7 +1,7 @@
 ##  @file\r
 #   Library used for sorting routines.\r
 #\r
-#  Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved. <BR>\r
+#  Copyright (c) 2009 - 2015, Intel Corporation. All rights reserved. <BR>
 #\r
 #  This program and the accompanying materials\r
 #  are licensed and made available under the terms and conditions of the BSD License\r
@@ -19,7 +19,7 @@
   FILE_GUID                      = 03F3331B-F12D-494f-BF37-E55A657F2497\r
   MODULE_TYPE                    = UEFI_DRIVER\r
   VERSION_STRING                 = 1.0\r
-  LIBRARY_CLASS                  = SortLib|UEFI_APPLICATION UEFI_DRIVER\r
+  LIBRARY_CLASS                  = SortLib|DXE_DRIVER UEFI_APPLICATION UEFI_DRIVER
 \r
 #\r
 #  VALID_ARCHITECTURES           = IA32 X64 IPF EBC\r
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
new file mode 100644 (file)
index 0000000..ddda9ae
--- /dev/null
@@ -0,0 +1,2278 @@
+/** @file
+  Library functions which relates with booting.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+#define VENDOR_IDENTIFICATION_OFFSET     3
+#define VENDOR_IDENTIFICATION_LENGTH     8
+#define PRODUCT_IDENTIFICATION_OFFSET    11
+#define PRODUCT_IDENTIFICATION_LENGTH    16
+
+CONST UINT16 mBmUsbLangId    = 0x0409; // English
+CHAR16       mBmUefiPrefix[] = L"UEFI ";
+
+EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION  mBmRefreshLegacyBootOption = NULL;
+EFI_BOOT_MANAGER_LEGACY_BOOT                 mBmLegacyBoot              = NULL;
+
+///
+/// This GUID is used for an EFI Variable that stores the front device pathes
+/// for a partial device path that starts with the HD node.
+///
+EFI_GUID mBmHardDriveBootVariableGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } };
+EFI_GUID mBmAutoCreateBootOptionGuid  = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } };
+
+/**
+  The function registers the legacy boot support capabilities.
+
+  @param RefreshLegacyBootOption The function pointer to create all the legacy boot options.
+  @param LegacyBoot              The function pointer to boot the legacy boot option.
+**/
+VOID
+EFIAPI
+EfiBootManagerRegisterLegacyBootSupport (
+  EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION   RefreshLegacyBootOption,
+  EFI_BOOT_MANAGER_LEGACY_BOOT                  LegacyBoot
+  )
+{
+  mBmRefreshLegacyBootOption = RefreshLegacyBootOption;
+  mBmLegacyBoot              = LegacyBoot;
+}
+
+/**
+  For a bootable Device path, return its boot type.
+
+  @param  DevicePath                   The bootable device Path to check
+
+  @retval AcpiFloppyBoot               If given device path contains ACPI_DEVICE_PATH type device path node
+                                       which HID is floppy device.
+  @retval MessageAtapiBoot             If given device path contains MESSAGING_DEVICE_PATH type device path node
+                                       and its last device path node's subtype is MSG_ATAPI_DP.
+  @retval MessageSataBoot              If given device path contains MESSAGING_DEVICE_PATH type device path node
+                                       and its last device path node's subtype is MSG_SATA_DP.
+  @retval MessageScsiBoot              If given device path contains MESSAGING_DEVICE_PATH type device path node
+                                       and its last device path node's subtype is MSG_SCSI_DP.
+  @retval MessageUsbBoot               If given device path contains MESSAGING_DEVICE_PATH type device path node
+                                       and its last device path node's subtype is MSG_USB_DP.
+  @retval MessageNetworkBoot           If given device path contains MESSAGING_DEVICE_PATH type device path node
+                                       and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP,
+                                       MSG_IPv4_DP or MSG_IPv6_DP.
+  @retval UnsupportedBoot              If tiven device path doesn't match the above condition, it's not supported.
+
+**/
+BM_BOOT_TYPE
+BmBootTypeFromDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL     *DevicePath
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL      *Node;
+  EFI_DEVICE_PATH_PROTOCOL      *NextNode;
+
+  ASSERT (DevicePath != NULL);
+
+  for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
+    switch (DevicePathType (Node)) {
+
+      case ACPI_DEVICE_PATH:
+        if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {
+          return BmAcpiFloppyBoot;
+        }
+        break;
+
+      case HARDWARE_DEVICE_PATH:
+        if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {
+          return BmHardwareDeviceBoot;
+        }
+        break;
+
+      case MESSAGING_DEVICE_PATH:
+        //
+        // Skip LUN device node
+        //
+        NextNode = Node;
+        do {
+          NextNode = NextDevicePathNode (NextNode);
+        } while (
+            (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&
+            (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)
+            );
+
+        //
+        // If the device path not only point to driver device, it is not a messaging device path,
+        //
+        if (!IsDevicePathEndType (NextNode)) {
+          break;
+        }
+
+        switch (DevicePathSubType (Node)) {
+        case MSG_ATAPI_DP:
+          return BmMessageAtapiBoot;
+          break;
+
+        case MSG_SATA_DP:
+          return BmMessageSataBoot;
+          break;
+
+        case MSG_USB_DP:
+          return BmMessageUsbBoot;
+          break;
+
+        case MSG_SCSI_DP:
+          return BmMessageScsiBoot;
+          break;
+
+        case MSG_MAC_ADDR_DP:
+        case MSG_VLAN_DP:
+        case MSG_IPv4_DP:
+        case MSG_IPv6_DP:
+          return BmMessageNetworkBoot;
+          break;
+        }
+    }
+  }
+
+  return BmMiscBoot;
+}
+
+/**
+  Free old buffer and reuse the pointer to return new buffer.
+
+  @param Orig  Pointer to the old buffer.
+  @param New   Pointer to the new buffer.
+**/
+VOID
+BmFreeAndSet (
+  VOID   **Orig,
+  VOID   *New
+  )
+{
+  FreePool (*Orig);
+  *Orig = New;
+}
+
+/**
+  Find the boot option in the NV storage and return the option number.
+
+  @param OptionToFind  Boot option to be checked.
+
+  @return   The option number of the found boot option.
+
+**/
+UINTN
+BmFindBootOptionInVariable (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION             *OptionToFind
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
+  UINTN                        OptionNumber;
+  CHAR16                       OptionName[sizeof ("Boot####")];
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+  UINTN                        BootOptionCount;
+  UINTN                        Index;
+  
+  OptionNumber = LoadOptionNumberUnassigned;
+
+  //
+  // Try to match the variable exactly if the option number is assigned
+  //
+  if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) {
+    UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionToFind->OptionNumber);
+    Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);
+
+    if (!EFI_ERROR (Status)) {
+      ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber);
+      if ((OptionToFind->Attributes == BootOption.Attributes) &&
+          (StrCmp (OptionToFind->Description, BootOption.Description) == 0) &&
+          (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) &&
+          (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) &&
+          (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0)
+         ) {
+        OptionNumber = OptionToFind->OptionNumber;
+      }
+      EfiBootManagerFreeLoadOption (&BootOption);
+    }
+  }
+
+  //
+  // The option number assigned is either incorrect or unassigned.
+  //
+  if (OptionNumber == LoadOptionNumberUnassigned) {
+    BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+
+    Index = BmFindLoadOption (OptionToFind, BootOptions, BootOptionCount);
+    if (Index != -1) {
+      OptionNumber = BootOptions[Index].OptionNumber;
+    }
+
+    EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+  }
+
+  return OptionNumber;
+}
+
+/**
+  According to a file guild, check a Fv file device path is valid. If it is invalid,
+  try to return the valid device path.
+  FV address maybe changes for memory layout adjust from time to time, use this function
+  could promise the Fv file device path is right.
+
+  @param  DevicePath   The Fv file device path to be fixed up.
+
+**/
+VOID
+BmFixupMemmapFvFilePath (
+  IN OUT EFI_DEVICE_PATH_PROTOCOL      **DevicePath
+  )
+{
+  EFI_STATUS                    Status;
+  UINTN                         Index;
+  EFI_DEVICE_PATH_PROTOCOL      *Node;
+  EFI_HANDLE                    FvHandle;
+  EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
+  EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
+  UINTN                         Size;
+  EFI_FV_FILETYPE               Type;
+  EFI_FV_FILE_ATTRIBUTES        Attributes;
+  UINT32                        AuthenticationStatus;
+  UINTN                         FvHandleCount;
+  EFI_HANDLE                    *FvHandleBuffer;
+  EFI_DEVICE_PATH_PROTOCOL      *NewDevicePath;
+  
+  Node = *DevicePath;
+  Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
+  if (!EFI_ERROR (Status)) {
+    Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **) &Fv);
+    ASSERT_EFI_ERROR (Status);
+
+    Status = Fv->ReadFile (
+                   Fv,
+                   EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),
+                   NULL,
+                   &Size,
+                   &Type,
+                   &Attributes,
+                   &AuthenticationStatus
+                   );
+    if (EFI_ERROR (Status)) {
+      BmFreeAndSet ((VOID **) DevicePath, NULL);
+    }
+    return;
+  }
+
+    
+  Node = NextDevicePathNode (DevicePath);
+
+  //
+  // Firstly find the FV file in current FV
+  //
+  gBS->HandleProtocol (
+         gImageHandle,
+         &gEfiLoadedImageProtocolGuid,
+         (VOID **) &LoadedImage
+         );
+  NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), Node);
+  BmFixupMemmapFvFilePath (&NewDevicePath);
+
+  if (NewDevicePath != NULL) {
+    BmFreeAndSet ((VOID **) DevicePath, NewDevicePath);
+    return;
+  }
+
+  //
+  // Secondly find the FV file in all other FVs
+  //
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiFirmwareVolume2ProtocolGuid,
+         NULL,
+         &FvHandleCount,
+         &FvHandleBuffer
+         );
+  for (Index = 0; Index < FvHandleCount; Index++) {
+    if (FvHandleBuffer[Index] == LoadedImage->DeviceHandle) {
+      //
+      // Skip current FV
+      //
+      continue;
+    }
+    NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandleBuffer[Index]), Node);
+    BmFixupMemmapFvFilePath (&NewDevicePath);
+
+    if (NewDevicePath != NULL) {
+      BmFreeAndSet ((VOID **) DevicePath, NewDevicePath);
+      return;
+    }
+  }
+}
+
+/**
+  Check if it's of Fv file device path type.
+  
+  The function doesn't garentee the device path points to existing Fv file.
+
+  @param  DevicePath     Input device path info.
+
+  @retval TRUE   The device path is of Fv file device path type.
+  @retval FALSE  The device path isn't of Fv file device path type.
+**/
+BOOLEAN
+BmIsMemmapFvFilePath (
+  IN EFI_DEVICE_PATH_PROTOCOL    *DevicePath
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL   *FileNode;
+
+  if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {
+    FileNode = NextDevicePathNode (DevicePath);
+    if ((DevicePathType (FileNode) == MEDIA_DEVICE_PATH) && (DevicePathSubType (FileNode) == MEDIA_PIWG_FW_FILE_DP)) {
+      return IsDevicePathEnd (NextDevicePathNode (FileNode));
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+  Check whether a USB device match the specified USB Class device path. This
+  function follows "Load Option Processing" behavior in UEFI specification.
+
+  @param UsbIo       USB I/O protocol associated with the USB device.
+  @param UsbClass    The USB Class device path to match.
+
+  @retval TRUE       The USB device match the USB Class device path.
+  @retval FALSE      The USB device does not match the USB Class device path.
+
+**/
+BOOLEAN
+BmMatchUsbClass (
+  IN EFI_USB_IO_PROTOCOL        *UsbIo,
+  IN USB_CLASS_DEVICE_PATH      *UsbClass
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_USB_DEVICE_DESCRIPTOR     DevDesc;
+  EFI_USB_INTERFACE_DESCRIPTOR  IfDesc;
+  UINT8                         DeviceClass;
+  UINT8                         DeviceSubClass;
+  UINT8                         DeviceProtocol;
+
+  if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||
+      (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){
+    return FALSE;
+  }
+
+  //
+  // Check Vendor Id and Product Id.
+  //
+  Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+
+  if ((UsbClass->VendorId != 0xffff) &&
+      (UsbClass->VendorId != DevDesc.IdVendor)) {
+    return FALSE;
+  }
+
+  if ((UsbClass->ProductId != 0xffff) &&
+      (UsbClass->ProductId != DevDesc.IdProduct)) {
+    return FALSE;
+  }
+
+  DeviceClass    = DevDesc.DeviceClass;
+  DeviceSubClass = DevDesc.DeviceSubClass;
+  DeviceProtocol = DevDesc.DeviceProtocol;
+  if (DeviceClass == 0) {
+    //
+    // If Class in Device Descriptor is set to 0, use the Class, SubClass and
+    // Protocol in Interface Descriptor instead.
+    //
+    Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
+    if (EFI_ERROR (Status)) {
+      return FALSE;
+    }
+
+    DeviceClass    = IfDesc.InterfaceClass;
+    DeviceSubClass = IfDesc.InterfaceSubClass;
+    DeviceProtocol = IfDesc.InterfaceProtocol;
+  }
+
+  //
+  // Check Class, SubClass and Protocol.
+  //
+  if ((UsbClass->DeviceClass != 0xff) &&
+      (UsbClass->DeviceClass != DeviceClass)) {
+    return FALSE;
+  }
+
+  if ((UsbClass->DeviceSubClass != 0xff) &&
+      (UsbClass->DeviceSubClass != DeviceSubClass)) {
+    return FALSE;
+  }
+
+  if ((UsbClass->DeviceProtocol != 0xff) &&
+      (UsbClass->DeviceProtocol != DeviceProtocol)) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+  Eliminate the extra spaces in the Str to one space.
+
+  @param    Str     Input string info.
+**/
+VOID
+BmEliminateExtraSpaces (
+  IN CHAR16                    *Str
+  )
+{
+  UINTN                        Index;
+  UINTN                        ActualIndex;
+
+  for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {
+    if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {
+      Str[ActualIndex++] = Str[Index];
+    }
+  }
+  Str[ActualIndex] = L'\0';
+}
+
+/**
+  Try to get the controller's ATA/ATAPI description.
+
+  @param Handle                Controller handle.
+
+  @return  The description string.
+**/
+CHAR16 *
+BmGetDescriptionFromDiskInfo (
+  IN EFI_HANDLE                Handle
+  )
+{
+  UINTN                        Index;
+  EFI_STATUS                   Status;
+  EFI_DISK_INFO_PROTOCOL       *DiskInfo;
+  UINT32                       BufferSize;
+  EFI_ATAPI_IDENTIFY_DATA      IdentifyData;
+  EFI_SCSI_INQUIRY_DATA        InquiryData;
+  CHAR16                       *Description;
+  UINTN                        Length;
+  CONST UINTN                  ModelNameLength    = 40;
+  CONST UINTN                  SerialNumberLength = 20;
+  CHAR8                        *StrPtr;
+  UINT8                        Temp;
+
+  Description  = NULL;
+
+  Status = gBS->HandleProtocol (
+                  Handle,
+                  &gEfiDiskInfoProtocolGuid,
+                  (VOID **) &DiskInfo
+                  );
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) || 
+      CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
+    BufferSize   = sizeof (EFI_ATAPI_IDENTIFY_DATA);
+    Status = DiskInfo->Identify (
+                         DiskInfo,
+                         &IdentifyData,
+                         &BufferSize
+                         );
+    if (!EFI_ERROR (Status)) {
+      Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
+      ASSERT (Description != NULL);
+      for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
+        Description[Index]     = (CHAR16) IdentifyData.ModelName[Index + 1];
+        Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];
+      }
+
+      Length = Index;
+      Description[Length++] = L' ';
+
+      for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
+        Description[Length + Index]     = (CHAR16) IdentifyData.SerialNo[Index + 1];
+        Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];
+      }
+      Length += Index;
+      Description[Length++] = L'\0';
+      ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
+
+      BmEliminateExtraSpaces (Description);
+    }
+  } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
+    BufferSize   = sizeof (EFI_SCSI_INQUIRY_DATA);
+    Status = DiskInfo->Inquiry (
+                         DiskInfo,
+                         &InquiryData,
+                         &BufferSize
+                         );
+    if (!EFI_ERROR (Status)) {
+      Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
+      ASSERT (Description != NULL);
+
+      //
+      // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
+      // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification, 
+      // Here combine the vendor identification and product identification to the description.
+      //
+      StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
+      Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
+      StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
+      AsciiStrToUnicodeStr (StrPtr, Description);
+      StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;
+
+      //
+      // Add one space at the middle of vendor information and product information.
+      //
+      Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';
+
+      StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);
+      StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';
+      AsciiStrToUnicodeStr (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1);
+
+      BmEliminateExtraSpaces (Description);
+    }
+  }
+
+  return Description;
+}
+
+/**
+  Try to get the controller's USB description.
+
+  @param Handle                Controller handle.
+
+  @return  The description string.
+**/
+CHAR16 *
+BmGetUsbDescription (
+  IN EFI_HANDLE                Handle
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_USB_IO_PROTOCOL          *UsbIo;
+  CHAR16                       NullChar;
+  CHAR16                       *Manufacturer;
+  CHAR16                       *Product;
+  CHAR16                       *SerialNumber;
+  CHAR16                       *Description;
+  EFI_USB_DEVICE_DESCRIPTOR    DevDesc;
+
+  Status = gBS->HandleProtocol (
+                  Handle,
+                  &gEfiUsbIoProtocolGuid,
+                  (VOID **) &UsbIo
+                  );
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  NullChar = L'\0';
+
+  Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  Status = UsbIo->UsbGetStringDescriptor (
+                    UsbIo,
+                    mBmUsbLangId,
+                    DevDesc.StrManufacturer,
+                    &Manufacturer
+                    );
+  if (EFI_ERROR (Status)) {
+    Manufacturer = &NullChar;
+  }
+  
+  Status = UsbIo->UsbGetStringDescriptor (
+                    UsbIo,
+                    mBmUsbLangId,
+                    DevDesc.StrProduct,
+                    &Product
+                    );
+  if (EFI_ERROR (Status)) {
+    Product = &NullChar;
+  }
+  
+  Status = UsbIo->UsbGetStringDescriptor (
+                    UsbIo,
+                    mBmUsbLangId,
+                    DevDesc.StrSerialNumber,
+                    &SerialNumber
+                    );
+  if (EFI_ERROR (Status)) {
+    SerialNumber = &NullChar;
+  }
+
+  if ((Manufacturer == &NullChar) &&
+      (Product == &NullChar) &&
+      (SerialNumber == &NullChar)
+      ) {
+    return NULL;
+  }
+
+  Description = AllocateZeroPool (StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber));
+  ASSERT (Description != NULL);
+  StrCat (Description, Manufacturer);
+  StrCat (Description, L" ");
+
+  StrCat (Description, Product);  
+  StrCat (Description, L" ");
+
+  StrCat (Description, SerialNumber);
+
+  if (Manufacturer != &NullChar) {
+    FreePool (Manufacturer);
+  }
+  if (Product != &NullChar) {
+    FreePool (Product);
+  }
+  if (SerialNumber != &NullChar) {
+    FreePool (SerialNumber);
+  }
+
+  BmEliminateExtraSpaces (Description);
+
+  return Description;
+}
+
+/**
+  Return the boot description for the controller based on the type.
+
+  @param Handle                Controller handle.
+
+  @return  The description string.
+**/
+CHAR16 *
+BmGetMiscDescription (
+  IN EFI_HANDLE                  Handle
+  )
+{
+  EFI_STATUS                     Status;
+  CHAR16                         *Description;
+  EFI_BLOCK_IO_PROTOCOL          *BlockIo;
+
+  switch (BmBootTypeFromDevicePath (DevicePathFromHandle (Handle))) {
+  case BmAcpiFloppyBoot:
+    Description = L"Floppy";
+    break;
+
+  case BmMessageAtapiBoot:
+  case BmMessageSataBoot:
+    Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
+    ASSERT_EFI_ERROR (Status);
+    //
+    // Assume a removable SATA device should be the DVD/CD device
+    //
+    Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
+    break;
+
+  case BmMessageUsbBoot:
+    Description = L"USB Device";
+    break;
+
+  case BmMessageScsiBoot:
+    Description = L"SCSI Device";
+    break;
+
+  case BmHardwareDeviceBoot:
+    Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
+    if (!EFI_ERROR (Status)) {
+      Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
+    } else {
+      Description = L"Misc Device";
+    }
+    break;
+
+  default:
+    Description = L"Misc Device";
+    break;
+  }
+
+  return AllocateCopyPool (StrSize (Description), Description);
+}
+
+BM_GET_BOOT_DESCRIPTION mBmGetBootDescription[] = {
+  BmGetUsbDescription,
+  BmGetDescriptionFromDiskInfo,
+  BmGetMiscDescription
+};
+
+/**
+  Check whether a USB device match the specified USB WWID device path. This
+  function follows "Load Option Processing" behavior in UEFI specification.
+
+  @param UsbIo       USB I/O protocol associated with the USB device.
+  @param UsbWwid     The USB WWID device path to match.
+
+  @retval TRUE       The USB device match the USB WWID device path.
+  @retval FALSE      The USB device does not match the USB WWID device path.
+
+**/
+BOOLEAN
+BmMatchUsbWwid (
+  IN EFI_USB_IO_PROTOCOL        *UsbIo,
+  IN USB_WWID_DEVICE_PATH       *UsbWwid
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_USB_DEVICE_DESCRIPTOR    DevDesc;
+  EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
+  UINT16                       *LangIdTable;
+  UINT16                       TableSize;
+  UINT16                       Index;
+  CHAR16                       *CompareStr;
+  UINTN                        CompareLen;
+  CHAR16                       *SerialNumberStr;
+  UINTN                        Length;
+
+  if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||
+      (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {
+    return FALSE;
+  }
+
+  //
+  // Check Vendor Id and Product Id.
+  //
+  Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+  if ((DevDesc.IdVendor != UsbWwid->VendorId) ||
+      (DevDesc.IdProduct != UsbWwid->ProductId)) {
+    return FALSE;
+  }
+
+  //
+  // Check Interface Number.
+  //
+  Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
+  if (EFI_ERROR (Status)) {
+    return FALSE;
+  }
+  if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {
+    return FALSE;
+  }
+
+  //
+  // Check Serial Number.
+  //
+  if (DevDesc.StrSerialNumber == 0) {
+    return FALSE;
+  }
+
+  //
+  // Get all supported languages.
+  //
+  TableSize = 0;
+  LangIdTable = NULL;
+  Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
+  if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {
+    return FALSE;
+  }
+
+  //
+  // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.
+  //
+  CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);
+  CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);
+  if (CompareStr[CompareLen - 1] == L'\0') {
+    CompareLen--;
+  }
+
+  //
+  // Compare serial number in each supported language.
+  //
+  for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
+    SerialNumberStr = NULL;
+    Status = UsbIo->UsbGetStringDescriptor (
+                      UsbIo,
+                      LangIdTable[Index],
+                      DevDesc.StrSerialNumber,
+                      &SerialNumberStr
+                      );
+    if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {
+      continue;
+    }
+
+    Length = StrLen (SerialNumberStr);
+    if ((Length >= CompareLen) &&
+        (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {
+      FreePool (SerialNumberStr);
+      return TRUE;
+    }
+
+    FreePool (SerialNumberStr);
+  }
+
+  return FALSE;
+}
+
+/**
+  Print the device path info.
+
+  @param DevicePath           The device path need to print.
+
+**/
+VOID
+BmPrintDp (
+  EFI_DEVICE_PATH_PROTOCOL            *DevicePath
+  )
+{
+  CHAR16                              *Str;
+
+  Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+  DEBUG ((EFI_D_INFO, "%s", Str));
+  if (Str != NULL) {
+    FreePool (Str);
+  }
+}
+
+/**
+  Find a USB device which match the specified short-form device path start with 
+  USB Class or USB WWID device path. If ParentDevicePath is NULL, this function
+  will search in all USB devices of the platform. If ParentDevicePath is not NULL,
+  this function will only search in its child devices.
+
+  @param DevicePath           The device path that contains USB Class or USB WWID device path.
+  @param ParentDevicePathSize The length of the device path before the USB Class or 
+                              USB WWID device path.
+  @param UsbIoHandleCount     A pointer to the count of the returned USB IO handles.
+
+  @retval NULL       The matched USB IO handles cannot be found.
+  @retval other      The matched USB IO handles.
+
+**/
+EFI_HANDLE *
+BmFindUsbDevice (
+  IN  EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
+  IN  UINTN                     ParentDevicePathSize,
+  OUT UINTN                     *UsbIoHandleCount
+  )
+{
+  EFI_STATUS                Status;
+  EFI_HANDLE                *UsbIoHandles;
+  EFI_DEVICE_PATH_PROTOCOL  *UsbIoDevicePath;
+  EFI_USB_IO_PROTOCOL       *UsbIo;
+  UINTN                     Index;
+  UINTN                     UsbIoDevicePathSize;
+  BOOLEAN                   Matched;
+
+  ASSERT (UsbIoHandleCount != NULL);  
+
+  //
+  // Get all UsbIo Handles.
+  //
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiUsbIoProtocolGuid,
+                  NULL,
+                  UsbIoHandleCount,
+                  &UsbIoHandles
+                  );
+  if (EFI_ERROR (Status)) {
+    *UsbIoHandleCount = 0;
+    UsbIoHandles      = NULL;
+  }
+
+  for (Index = 0; Index < *UsbIoHandleCount; ) {
+    //
+    // Get the Usb IO interface.
+    //
+    Status = gBS->HandleProtocol(
+                    UsbIoHandles[Index],
+                    &gEfiUsbIoProtocolGuid,
+                    (VOID **) &UsbIo
+                    );
+    UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);
+    Matched         = FALSE;
+    if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {
+      UsbIoDevicePathSize = GetDevicePathSize (UsbIoDevicePath) - END_DEVICE_PATH_LENGTH;
+
+      //
+      // Compare starting part of UsbIoHandle's device path with ParentDevicePath.
+      //
+      if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {
+        if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) ||
+            BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize))) {
+          Matched = TRUE;
+        }
+      }
+    }
+
+    if (!Matched) {
+      (*UsbIoHandleCount) --;
+      CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE));
+    } else {
+      Index++;
+    }
+  }
+
+  return UsbIoHandles;
+}
+
+/**
+  Expand USB Class or USB WWID device path node to be full device path of a USB
+  device in platform.
+
+  This function support following 4 cases:
+  1) Boot Option device path starts with a USB Class or USB WWID device path,
+     and there is no Media FilePath device path in the end.
+     In this case, it will follow Removable Media Boot Behavior.
+  2) Boot Option device path starts with a USB Class or USB WWID device path,
+     and ended with Media FilePath device path.
+  3) Boot Option device path starts with a full device path to a USB Host Controller,
+     contains a USB Class or USB WWID device path node, while not ended with Media
+     FilePath device path. In this case, it will follow Removable Media Boot Behavior.
+  4) Boot Option device path starts with a full device path to a USB Host Controller,
+     contains a USB Class or USB WWID device path node, and ended with Media
+     FilePath device path.
+
+  @param  DevicePath    On input, a pointer to an allocated buffer that contains the 
+                        file device path.
+                        On output, a pointer to an reallocated buffer that contains 
+                        the expanded device path. It would point to NULL if the file
+                        cannot be read.
+
+  @param  FileSize      A pointer to the file size.
+
+  @retval !NULL  The file buffer.
+  @retval NULL   The input device path doesn't point to a valid file.
+**/
+VOID *
+BmExpandUsbShortFormDevicePath (
+  IN OUT EFI_DEVICE_PATH_PROTOCOL  **DevicePath,
+  OUT UINTN                        *FileSize
+  )
+{
+  UINTN                             ParentDevicePathSize;
+  EFI_DEVICE_PATH_PROTOCOL          *ShortformNode;
+  EFI_DEVICE_PATH_PROTOCOL          *RemainingDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL          *FullDevicePath;
+  EFI_HANDLE                        *UsbIoHandles;
+  UINTN                             UsbIoHandleCount;
+  UINTN                             Index;
+  VOID                              *FileBuffer;
+  
+  //
+  // Search for USB Class or USB WWID device path node.
+  //
+  for ( ShortformNode = *DevicePath
+      ; !IsDevicePathEnd (ShortformNode)
+      ; ShortformNode = NextDevicePathNode (ShortformNode)
+      ) {
+    if ((DevicePathType (ShortformNode) == MESSAGING_DEVICE_PATH) &&
+        ((DevicePathSubType (ShortformNode) == MSG_USB_CLASS_DP) ||
+         (DevicePathSubType (ShortformNode) == MSG_USB_WWID_DP))) {
+      break;
+    }
+  }
+  ASSERT (!IsDevicePathEnd (ShortformNode));
+
+  FullDevicePath       = NULL;
+  ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) *DevicePath;
+  RemainingDevicePath  = NextDevicePathNode (ShortformNode);
+  FileBuffer           = NULL;
+  UsbIoHandles         = BmFindUsbDevice (*DevicePath, ParentDevicePathSize, &UsbIoHandleCount);
+
+  for (Index = 0; Index < UsbIoHandleCount; Index++) {
+    FullDevicePath = AppendDevicePath (DevicePathFromHandle (UsbIoHandles[Index]), RemainingDevicePath);
+    DEBUG ((EFI_D_INFO, "[Bds] FullDp1[%d]:", Index)); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, "\n"));
+    FileBuffer = BmLoadEfiBootOption (&FullDevicePath, FileSize);
+    if (FileBuffer != NULL) {
+      DEBUG ((EFI_D_INFO, "-->")); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, FileBuffer != NULL ? " - Found\n" : "\n"));
+      break;
+    }
+  }
+
+  if (UsbIoHandles != NULL) {
+    FreePool (UsbIoHandles);
+  }
+
+  if (FileBuffer == NULL) {
+    //
+    // Boot Option device path starts with USB Class or USB WWID device path.
+    // For Boot Option device path which doesn't begin with the USB Class or
+    // USB WWID device path, it's not needed to connect again here.
+    //
+    if ((DevicePathType (*DevicePath) == MESSAGING_DEVICE_PATH) &&
+        ((DevicePathSubType (*DevicePath) == MSG_USB_CLASS_DP) ||
+         (DevicePathSubType (*DevicePath) == MSG_USB_WWID_DP))) {
+      BmConnectUsbShortFormDevicePath (*DevicePath);
+
+      UsbIoHandles = BmFindUsbDevice (*DevicePath, ParentDevicePathSize, &UsbIoHandleCount);
+      for (Index = 0; Index < UsbIoHandleCount; Index++) {
+        FullDevicePath = AppendDevicePath (DevicePathFromHandle (UsbIoHandles[Index]), RemainingDevicePath);
+        DEBUG ((EFI_D_INFO, "[Bds] FullDp2[%d]:", Index)); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, "\n"));
+        FileBuffer = BmLoadEfiBootOption (&FullDevicePath, FileSize);
+        if (FileBuffer != NULL) {
+          DEBUG ((EFI_D_INFO, "-->")); DEBUG_CODE (BmPrintDp (FullDevicePath); ); DEBUG ((EFI_D_INFO, FileBuffer != NULL ? " - Found\n" : "\n"));
+          break;
+        }
+      }
+
+      if (UsbIoHandles != NULL) {
+        FreePool (UsbIoHandles);
+      }
+    }
+  }
+
+  BmFreeAndSet ((VOID **) DevicePath, FullDevicePath);
+  return FileBuffer;
+}
+
+/**
+  Expand a device path that starts with a hard drive media device path node to be a
+  full device path that includes the full hardware path to the device. We need
+  to do this so it can be booted. As an optimization the front match (the part point
+  to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable
+  so a connect all is not required on every boot. All successful history device path
+  which point to partition node (the front part) will be saved.
+
+  @param  DevicePath    On input, a pointer to an allocated buffer that contains the 
+                        file device path.
+                        On output, a pointer to an reallocated buffer that contains 
+                        the expanded device path. It would point to NULL if the file
+                        cannot be read.
+
+**/
+VOID
+BmExpandPartitionShortFormDevicePath (
+  IN OUT EFI_DEVICE_PATH_PROTOCOL      **DevicePath
+  )
+{
+  EFI_STATUS                Status;
+  UINTN                     BlockIoHandleCount;
+  EFI_HANDLE                *BlockIoBuffer;
+  EFI_DEVICE_PATH_PROTOCOL  *FullDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *BlockIoDevicePath;
+  UINTN                     Index;
+  UINTN                     InstanceNum;
+  EFI_DEVICE_PATH_PROTOCOL  *CachedDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;
+  UINTN                     CachedDevicePathSize;
+  BOOLEAN                   DeviceExist;
+  BOOLEAN                   NeedAdjust;
+  EFI_DEVICE_PATH_PROTOCOL  *Instance;
+  UINTN                     Size;
+
+  FullDevicePath      = NULL;
+  //
+  // Check if there is prestore 'HDDP' variable.
+  // If exist, search the front path which point to partition node in the variable instants.
+  // If fail to find or 'HDDP' not exist, reconnect all and search in all system
+  //
+  GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize);
+
+  //
+  // Delete the invalid 'HDDP' variable.
+  //
+  if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {
+    FreePool (CachedDevicePath);
+    CachedDevicePath = NULL;
+    Status = gRT->SetVariable (
+                    L"HDDP",
+                    &mBmHardDriveBootVariableGuid,
+                    0,
+                    0,
+                    NULL
+                    );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  if (CachedDevicePath != NULL) {
+    TempNewDevicePath = CachedDevicePath;
+    DeviceExist = FALSE;
+    NeedAdjust = FALSE;
+    do {
+      //
+      // Check every instance of the variable
+      // First, check whether the instance contain the partition node, which is needed for distinguishing  multi
+      // partial partition boot option. Second, check whether the instance could be connected.
+      //
+      Instance  = GetNextDevicePathInstance (&TempNewDevicePath, &Size);
+      if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) {
+        //
+        // Connect the device path instance, the device path point to hard drive media device path node
+        // e.g. ACPI() /PCI()/ATA()/Partition()
+        //
+        Status = EfiBootManagerConnectDevicePath (Instance, NULL);
+        if (!EFI_ERROR (Status)) {
+          DeviceExist = TRUE;
+          break;
+        }
+      }
+      //
+      // Come here means the first instance is not matched
+      //
+      NeedAdjust = TRUE;
+      FreePool(Instance);
+    } while (TempNewDevicePath != NULL);
+
+    if (DeviceExist) {
+      //
+      // Find the matched device path.
+      // Append the file path information from the boot option and return the fully expanded device path.
+      //
+      FullDevicePath = AppendDevicePath (Instance, NextDevicePathNode (*DevicePath));
+
+      //
+      // Adjust the 'HDDP' instances sequence if the matched one is not first one.
+      //
+      if (NeedAdjust) {
+        //
+        // First delete the matched instance.
+        //
+        TempNewDevicePath = CachedDevicePath;
+        CachedDevicePath  = BmDelPartMatchInstance (CachedDevicePath, Instance);
+        FreePool (TempNewDevicePath);
+
+        //
+        // Second, append the remaining path after the matched instance
+        //
+        TempNewDevicePath = CachedDevicePath;
+        CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath );
+        FreePool (TempNewDevicePath);
+        //
+        // Save the matching Device Path so we don't need to do a connect all next time
+        // Failing to save only impacts performance next time expanding the short-form device path
+        //
+        Status = gRT->SetVariable (
+                        L"HDDP",
+                        &mBmHardDriveBootVariableGuid,
+                        EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                        GetDevicePathSize (CachedDevicePath),
+                        CachedDevicePath
+                        );
+      }
+
+      FreePool (Instance);
+      FreePool (CachedDevicePath);
+      FreePool (*DevicePath);
+      *DevicePath = FullDevicePath;
+      return;
+    }
+  }
+
+  //
+  // If we get here we fail to find or 'HDDP' not exist, and now we need
+  // to search all devices in the system for a matched partition
+  //
+  EfiBootManagerConnectAll ();
+  Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);
+  if (EFI_ERROR (Status)) {
+    BlockIoHandleCount = 0;
+    BlockIoBuffer      = NULL;
+  }
+  //
+  // Loop through all the device handles that support the BLOCK_IO Protocol
+  //
+  for (Index = 0; Index < BlockIoHandleCount; Index++) {
+
+    Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath);
+    if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) {
+      continue;
+    }
+
+    if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) {
+      //
+      // Find the matched partition device path
+      //
+      FullDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (*DevicePath));
+
+      //
+      // Save the matched partition device path in 'HDDP' variable
+      //
+      if (CachedDevicePath != NULL) {
+        //
+        // Save the matched partition device path as first instance of 'HDDP' variable
+        //
+        if (BmMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) {
+          TempNewDevicePath = CachedDevicePath;
+          CachedDevicePath = BmDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath);
+          FreePool(TempNewDevicePath);
+        }
+
+        if (CachedDevicePath != NULL) {
+          TempNewDevicePath = CachedDevicePath;
+          CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
+          FreePool(TempNewDevicePath);
+        } else {
+          CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
+        }
+
+        //
+        // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller
+        // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.
+        //
+        InstanceNum = 0;
+        ASSERT (CachedDevicePath != NULL);
+        TempNewDevicePath = CachedDevicePath;
+        while (!IsDevicePathEnd (TempNewDevicePath)) {
+          TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
+          //
+          // Parse one instance
+          //
+          while (!IsDevicePathEndType (TempNewDevicePath)) {
+            TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
+          }
+          InstanceNum++;
+          //
+          // If the CachedDevicePath variable contain too much instance, only remain 12 instances.
+          //
+          if (InstanceNum >= 12) {
+            SetDevicePathEndNode (TempNewDevicePath);
+            break;
+          }
+        }
+      } else {
+        CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
+      }
+
+      //
+      // Save the matching Device Path so we don't need to do a connect all next time
+      // Failing to save only impacts performance next time expanding the short-form device path
+      //
+      Status = gRT->SetVariable (
+                      L"HDDP",
+                      &mBmHardDriveBootVariableGuid,
+                      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                      GetDevicePathSize (CachedDevicePath),
+                      CachedDevicePath
+                      );
+
+      break;
+    }
+  }
+
+  if (CachedDevicePath != NULL) {
+    FreePool (CachedDevicePath);
+  }
+  if (BlockIoBuffer != NULL) {
+    FreePool (BlockIoBuffer);
+  }
+  BmFreeAndSet ((VOID **) DevicePath, FullDevicePath);
+}
+
+/**
+  Algorithm follows the UEFI Spec chapter 3.4 Boot Mechanisms.
+
+  @param  DevicePath  Device Path to a  bootable device
+
+  @return  The bootable media handle. If the media on the DevicePath is not bootable, NULL will return.
+
+**/
+EFI_HANDLE
+BmGetBootableDeviceHandle (
+  IN  EFI_DEVICE_PATH_PROTOCOL        *DevicePath
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_DEVICE_PATH_PROTOCOL            *UpdatedDevicePath;
+  EFI_HANDLE                          Handle;
+  EFI_BLOCK_IO_PROTOCOL               *BlockIo;
+  VOID                                *Buffer;
+  EFI_DEVICE_PATH_PROTOCOL            *TempDevicePath;
+  UINTN                               Size;
+  UINTN                               TempSize;
+  EFI_HANDLE                          ReturnHandle;
+  EFI_HANDLE                          *SimpleFileSystemHandles;
+  UINTN                               NumberSimpleFileSystemHandles;
+  UINTN                               Index;
+  EFI_IMAGE_DOS_HEADER                DosHeader;
+  EFI_IMAGE_OPTIONAL_HEADER_UNION     HdrData;
+  EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
+
+  ReturnHandle      = NULL;
+  UpdatedDevicePath = DevicePath;
+
+  //
+  // Check whether the device is connected
+  //
+  Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle);
+  if (EFI_ERROR (Status)) {
+    //
+    // Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol,
+    //
+    Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle);
+    if (EFI_ERROR (Status)) {
+      //
+      // Fail to find the proper BlockIo and simple file protocol, maybe because device not present,  we need to connect it firstly
+      //
+      UpdatedDevicePath = DevicePath;
+      Status            = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
+      gBS->ConnectController (Handle, NULL, NULL, TRUE);
+    }
+  } else {
+    //
+    // For removable device boot option, its contained device path only point to the removable device handle, 
+    // should make sure all its children handles (its child partion or media handles) are created and connected. 
+    //
+    gBS->ConnectController (Handle, NULL, NULL, TRUE); 
+    //
+    // Get BlockIo protocol and check removable attribute
+    //
+    Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
+    //
+    // Issue a dummy read to the device to check for media change.
+    // When the removable media is changed, any Block IO read/write will
+    // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is
+    // returned. After the Block IO protocol is reinstalled, subsequent
+    // Block IO read/write will success.
+    //
+    Buffer = AllocatePool (BlockIo->Media->BlockSize);
+    if (Buffer != NULL) {
+      BlockIo->ReadBlocks (
+               BlockIo,
+               BlockIo->Media->MediaId,
+               0,
+               BlockIo->Media->BlockSize,
+               Buffer
+               );
+      FreePool(Buffer);
+    }
+  }
+
+  //
+  // Detect the the default boot file from removable Media
+  //
+  Size = GetDevicePathSize(DevicePath) - END_DEVICE_PATH_LENGTH;
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiSimpleFileSystemProtocolGuid,
+         NULL,
+         &NumberSimpleFileSystemHandles,
+         &SimpleFileSystemHandles
+         );
+  for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
+    //
+    // Get the device path size of SimpleFileSystem handle
+    //
+    TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
+    TempSize       = GetDevicePathSize (TempDevicePath)- END_DEVICE_PATH_LENGTH;
+    //
+    // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
+    //
+    if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {
+      //
+      // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
+      //  machinename is ia32, ia64, x64, ...
+      //
+      Hdr.Union = &HdrData;
+      Status = BmGetImageHeader (
+                 SimpleFileSystemHandles[Index],
+                 EFI_REMOVABLE_MEDIA_FILE_NAME,
+                 &DosHeader,
+                 Hdr
+                 );
+      if (!EFI_ERROR (Status) && EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&
+          (Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)
+         ) {
+        ReturnHandle = SimpleFileSystemHandles[Index];
+        break;
+      }
+    }
+  }
+
+  if (SimpleFileSystemHandles != NULL) {
+    FreePool(SimpleFileSystemHandles);
+  }
+
+  return ReturnHandle;
+}
+
+/**
+  Get the image file buffer data and buffer size by its device path. 
+
+  @param FilePath  On input, a pointer to an allocated buffer that contains the 
+                   file device path.
+                   On output the device path pointer could be modified to point to
+                   a new allocated buffer that contains the full device path.
+                   It could be caused by either short-form device path expanding,
+                   or default boot file path appending.
+  @param FileSize  A pointer to the size of the file buffer.
+
+  @retval NULL   The file can't be found.
+  @retval other  The file buffer. The caller is responsible to free memory.
+**/
+VOID *
+BmLoadEfiBootOption (
+  IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
+  OUT    UINTN                    *FileSize
+  )
+{
+  EFI_HANDLE                      Handle;
+  VOID                            *FileBuffer;
+  UINT32                          AuthenticationStatus;
+  EFI_DEVICE_PATH_PROTOCOL        *Node;
+
+  ASSERT ((FilePath != NULL) && (*FilePath != NULL) && (FileSize != NULL));
+
+  EfiBootManagerConnectDevicePath (*FilePath, NULL);
+
+  *FileSize  = 0;
+  FileBuffer = NULL;
+  //
+  // Expand the short-form device path to full device path
+  //
+  if ((DevicePathType (*FilePath) == MEDIA_DEVICE_PATH) &&
+      (DevicePathSubType (*FilePath) == MEDIA_HARDDRIVE_DP)) {
+    //
+    // Expand the Harddrive device path
+    //
+    BmExpandPartitionShortFormDevicePath (FilePath);
+    if (*FilePath == NULL) {
+      return NULL;
+    }
+
+  } else {
+    for (Node = *FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {
+      if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
+          ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||
+           (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {
+        break;
+      }
+    }
+
+    if (!IsDevicePathEnd (Node)) {
+      //
+      // Expand the USB WWID/Class device path
+      //
+      FileBuffer = BmExpandUsbShortFormDevicePath (FilePath, FileSize);
+      if (FileBuffer == NULL) {
+        return NULL;
+      }
+    }
+  }
+
+  //
+  // Fix up the boot option path if it points to a FV in memory map style of device path
+  //
+  if (BmIsMemmapFvFilePath (*FilePath)) {
+    BmFixupMemmapFvFilePath (FilePath);
+    if (*FilePath == NULL) {
+      return NULL;
+    }
+  }
+
+  if (FileBuffer == NULL) {
+    FileBuffer = GetFileBufferByFilePath (TRUE, *FilePath, FileSize, &AuthenticationStatus);
+  }
+
+  //
+  // If we didn't find an image directly, we need to try as if it is a removable device boot option
+  // and load the image according to the default boot behavior.
+  //
+  if (FileBuffer == NULL) {
+    //
+    // check if there is a bootable media could be found in this device path,
+    // and get the bootable media handle
+    //
+    Handle = BmGetBootableDeviceHandle (*FilePath);
+    if (Handle != NULL) {
+      //
+      // Load the default boot file \EFI\BOOT\boot{machinename}.EFI from the media
+      //  machinename is ia32, ia64, x64, ...
+      //
+      BmFreeAndSet ((VOID **) FilePath, FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME));
+      ASSERT (*FilePath != NULL);
+      FileBuffer = GetFileBufferByFilePath (TRUE, *FilePath, FileSize, &AuthenticationStatus);
+    }
+  }
+
+  if (FileBuffer == NULL) {
+    BmFreeAndSet ((VOID **) FilePath, NULL);
+  }
+
+  return FileBuffer;
+}
+
+/**
+  Attempt to boot the EFI boot option. This routine sets L"BootCurent" and
+  also signals the EFI ready to boot event. If the device path for the option
+  starts with a BBS device path a legacy boot is attempted via the registered 
+  gLegacyBoot function. Short form device paths are also supported via this 
+  rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,
+  MSG_USB_CLASS_DP gets expaned out to find the first device that matches.
+  If the BootOption Device Path fails the removable media boot algorithm 
+  is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type 
+  is tried per processor type)
+
+  @param  BootOption    Boot Option to try and boot.
+                        On return, BootOption->Status contains the boot status.
+                        EFI_SUCCESS     BootOption was booted
+                        EFI_UNSUPPORTED A BBS device path was found with no valid callback
+                                        registered via EfiBootManagerInitialize().
+                        EFI_NOT_FOUND   The BootOption was not found on the system
+                        !EFI_SUCCESS    BootOption failed with this error status
+
+**/
+VOID
+EFIAPI
+EfiBootManagerBoot (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION             *BootOption
+  )
+{
+  EFI_STATUS                Status;
+  EFI_HANDLE                ImageHandle;
+  EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
+  UINT16                    Uint16;
+  UINTN                     OptionNumber;
+  UINTN                     OriginalOptionNumber;
+  EFI_DEVICE_PATH_PROTOCOL  *FilePath;
+  EFI_DEVICE_PATH_PROTOCOL  *Node;
+  EFI_HANDLE                FvHandle;
+  VOID                      *FileBuffer;
+  UINTN                     FileSize;
+  EFI_BOOT_LOGO_PROTOCOL    *BootLogo;
+  EFI_EVENT                 LegacyBootEvent;
+
+  if (BootOption == NULL) {
+    return;
+  }
+
+  if (BootOption->FilePath == NULL) {
+    BootOption->Status = EFI_INVALID_PARAMETER;
+    return;
+  }
+
+  //
+  // 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")
+  //
+  OptionNumber = BmFindBootOptionInVariable (BootOption);
+  if (OptionNumber == LoadOptionNumberUnassigned) {
+    Status = BmGetFreeOptionNumber (L"BootOrder", &Uint16);
+    if (!EFI_ERROR (Status)) {
+      //
+      // Save the BootOption->OptionNumber to restore later
+      //
+      OptionNumber             = Uint16;
+      OriginalOptionNumber     = BootOption->OptionNumber;
+      BootOption->OptionNumber = OptionNumber;
+      Status = EfiBootManagerLoadOptionToVariable (BootOption);
+      BootOption->OptionNumber = OriginalOptionNumber;
+    }
+
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));
+      BootOption->Status = Status;
+      return ;
+    }
+  }
+
+  //
+  // 2. Set BootCurrent
+  //
+  Uint16 = (UINT16) OptionNumber;
+  BmSetVariableAndReportStatusCodeOnError (
+    L"BootCurrent",
+    &gEfiGlobalVariableGuid,
+    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+    sizeof (UINT16),
+    &Uint16
+    );
+
+  //
+  // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute
+  //    the boot option.
+  //
+  Node   = BootOption->FilePath;
+  Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
+  if (!EFI_ERROR (Status) && CompareGuid (
+        EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),
+        PcdGetPtr (PcdBootManagerMenuFile)
+        )) {
+    DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n"));
+    BmStopHotkeyService (NULL, NULL);
+  } else {
+    REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
+    EfiSignalEventReadyToBoot();
+    //
+    // 4. Repair system through DriverHealth protocol
+    //
+    BmRepairAllControllers ();
+  }
+
+  PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
+
+  //
+  // 5. Load EFI boot option to ImageHandle
+  //
+  ImageHandle = NULL;
+  if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
+    Status     = EFI_NOT_FOUND;
+    FilePath   = DuplicateDevicePath (BootOption->FilePath);
+    FileBuffer = BmLoadEfiBootOption (&FilePath, &FileSize);
+    if (FileBuffer != NULL) {
+
+      REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
+
+      Status = gBS->LoadImage (
+                      TRUE,
+                      gImageHandle,
+                      FilePath,
+                      FileBuffer,
+                      FileSize,
+                      &ImageHandle
+                      );
+      FreePool (FileBuffer);
+      FreePool (FilePath);
+    }
+
+    if (EFI_ERROR (Status)) {
+      //
+      // Report Status Code to indicate that the failure to load boot option
+      //
+      REPORT_STATUS_CODE (
+        EFI_ERROR_CODE | EFI_ERROR_MINOR,
+        (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)
+        );
+      BootOption->Status = Status;
+      return;
+    }
+  }
+
+  //
+  // 6. Adjust the different type memory page number just before booting
+  //    and save the updated info into the variable for next boot to use
+  //
+  if ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) {
+    if (PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {
+      BmSetMemoryTypeInformationVariable ();
+    }
+  }
+
+  DEBUG_CODE_BEGIN();
+    if (BootOption->Description == NULL) {
+      DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));
+    } else {
+      DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));
+    }
+  DEBUG_CODE_END();
+
+  //
+  // Check to see if we should legacy BOOT. If yes then do the legacy boot
+  // Write boot to OS performance data for Legacy boot
+  //
+  if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {
+    if (mBmLegacyBoot != NULL) {
+      //
+      // Write boot to OS performance data for legacy boot.
+      //
+      PERF_CODE (
+        //
+        // Create an event to be signalled when Legacy Boot occurs to write performance data.
+        //
+        Status = EfiCreateEventLegacyBootEx(
+                   TPL_NOTIFY,
+                   BmWriteBootToOsPerformanceData,
+                   NULL, 
+                   &LegacyBootEvent
+                   );
+        ASSERT_EFI_ERROR (Status);
+      );
+
+      mBmLegacyBoot (BootOption);
+    } else {
+      BootOption->Status = EFI_UNSUPPORTED;
+    }
+
+    PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
+    return;
+  }
+  //
+  // Provide the image with its load options
+  //
+  Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
+  ASSERT_EFI_ERROR (Status);
+
+  ImageInfo->LoadOptionsSize  = BootOption->OptionalDataSize;
+  ImageInfo->LoadOptions      = BootOption->OptionalData;
+
+  //
+  // Clean to NULL because the image is loaded directly from the firmwares boot manager.
+  //
+  ImageInfo->ParentHandle = NULL;
+
+  //
+  // Before calling the image, enable the Watchdog Timer for 5 minutes period
+  //
+  gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
+
+  //
+  // Write boot to OS performance data for UEFI boot
+  //
+  PERF_CODE (
+    BmWriteBootToOsPerformanceData (NULL, NULL);
+  );
+
+  REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));
+
+  Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);
+  DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));
+  BootOption->Status = Status;
+  if (EFI_ERROR (Status)) {
+    //
+    // Report Status Code to indicate that boot failure
+    //
+    REPORT_STATUS_CODE (
+      EFI_ERROR_CODE | EFI_ERROR_MINOR,
+      (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)
+      );
+  }
+  PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
+
+  //
+  // Clear the Watchdog Timer after the image returns
+  //
+  gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
+
+  //
+  // Set Logo status invalid after trying one boot option
+  //
+  BootLogo = NULL;
+  Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
+  if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
+    Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  //
+  // Clear Boot Current
+  //
+  Status = gRT->SetVariable (
+                  L"BootCurrent",
+                  &gEfiGlobalVariableGuid,
+                  0,
+                  0,
+                  NULL
+                  );
+  //
+  // Deleting variable with current variable implementation shouldn't fail.
+  // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,
+  // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.
+  //
+  ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
+}
+
+/**
+  Check whether there is a instance in BlockIoDevicePath, which contain multi device path
+  instances, has the same partition node with HardDriveDevicePath device path
+
+  @param  BlockIoDevicePath      Multi device path instances which need to check
+  @param  HardDriveDevicePath    A device path which starts with a hard drive media
+                                 device path.
+
+  @retval TRUE                   There is a matched device path instance.
+  @retval FALSE                  There is no matched device path instance.
+
+**/
+BOOLEAN
+BmMatchPartitionDevicePathNode (
+  IN  EFI_DEVICE_PATH_PROTOCOL   *BlockIoDevicePath,
+  IN  HARDDRIVE_DEVICE_PATH      *HardDriveDevicePath
+  )
+{
+  HARDDRIVE_DEVICE_PATH     *TmpHdPath;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+  BOOLEAN                   Match;
+  EFI_DEVICE_PATH_PROTOCOL  *BlockIoHdDevicePathNode;
+
+  if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
+    return FALSE;
+  }
+
+  //
+  // Make PreviousDevicePath == the device path node before the end node
+  //
+  DevicePath              = BlockIoDevicePath;
+  BlockIoHdDevicePathNode = NULL;
+
+  //
+  // find the partition device path node
+  //
+  while (!IsDevicePathEnd (DevicePath)) {
+    if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
+        (DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)
+        ) {
+      BlockIoHdDevicePathNode = DevicePath;
+      break;
+    }
+
+    DevicePath = NextDevicePathNode (DevicePath);
+  }
+
+  if (BlockIoHdDevicePathNode == NULL) {
+    return FALSE;
+  }
+  //
+  // See if the harddrive device path in blockio matches the orig Hard Drive Node
+  //
+  TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode;
+  Match = FALSE;
+
+  //
+  // Check for the match
+  //
+  if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) &&
+      (TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) {
+    switch (TmpHdPath->SignatureType) {
+    case SIGNATURE_TYPE_GUID:
+      Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature);
+      break;
+    case SIGNATURE_TYPE_MBR:
+      Match = (BOOLEAN) (*((UINT32 *) (&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0]))));
+      break;
+    default:
+      Match = FALSE;
+      break;
+    }
+  }
+
+  return Match;
+}
+
+/**
+  Emuerate all possible bootable medias in the following order:
+  1. Removable BlockIo            - The boot option only points to the removable media
+                                    device, like USB key, DVD, Floppy etc.
+  2. Fixed BlockIo                - The boot option only points to a Fixed blockIo device,
+                                    like HardDisk.
+  3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting
+                                    SimpleFileSystem Protocol, but not supporting BlockIo
+                                    protocol.
+  4. LoadFile                     - The boot option points to the media supporting 
+                                    LoadFile protocol.
+  Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior
+
+  @param BootOptionCount   Return the boot option count which has been found.
+
+  @retval   Pointer to the boot option array.
+**/
+EFI_BOOT_MANAGER_LOAD_OPTION *
+BmEnumerateBootOptions (
+  UINTN                                 *BootOptionCount
+  )
+{
+  EFI_STATUS                            Status;
+  EFI_BOOT_MANAGER_LOAD_OPTION          *BootOptions;
+  UINT16                                NonBlockNumber;
+  UINTN                                 HandleCount;
+  EFI_HANDLE                            *Handles;
+  EFI_BLOCK_IO_PROTOCOL                 *BlkIo;
+  UINTN                                 Removable;
+  UINTN                                 Index;
+  UINTN                                 FunctionIndex;
+  CHAR16                                *Temp;
+  CHAR16                                *DescriptionPtr;
+  CHAR16                                Description[30];
+
+  ASSERT (BootOptionCount != NULL);
+
+  *BootOptionCount = 0;
+  BootOptions      = NULL;
+
+  //
+  // Parse removable block io followed by fixed block io
+  //
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiBlockIoProtocolGuid,
+         NULL,
+         &HandleCount,
+         &Handles
+         );
+
+  for (Removable = 0; Removable < 2; Removable++) {
+    for (Index = 0; Index < HandleCount; Index++) {
+      Status = gBS->HandleProtocol (
+                      Handles[Index],
+                      &gEfiBlockIoProtocolGuid,
+                      (VOID **) &BlkIo
+                      );
+      if (EFI_ERROR (Status)) {
+        continue;
+      }
+
+      //
+      // Skip the logical partitions
+      //
+      if (BlkIo->Media->LogicalPartition) {
+        continue;
+      }
+
+      //
+      // Skip the fixed block io then the removable block io
+      //
+      if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {
+        continue;
+      }
+
+      DescriptionPtr = NULL;
+      for (FunctionIndex = 0; FunctionIndex < sizeof (mBmGetBootDescription) / sizeof (mBmGetBootDescription[0]); FunctionIndex++) {
+        DescriptionPtr = mBmGetBootDescription[FunctionIndex] (Handles[Index]);
+        if (DescriptionPtr != NULL) {
+          break;
+        }
+      }
+
+      if (DescriptionPtr == NULL) {
+        continue;
+      }
+
+      //
+      // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
+      //
+      Temp = AllocatePool (StrSize (DescriptionPtr) + sizeof (mBmUefiPrefix)); 
+      ASSERT (Temp != NULL);
+      StrCpy (Temp, mBmUefiPrefix);
+      StrCat (Temp, DescriptionPtr);
+      FreePool (DescriptionPtr);
+      DescriptionPtr = Temp;
+
+      BootOptions = ReallocatePool (
+                      sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
+                      sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
+                      BootOptions
+                      );
+      ASSERT (BootOptions != NULL);
+
+      Status = EfiBootManagerInitializeLoadOption (
+                 &BootOptions[(*BootOptionCount)++],
+                 LoadOptionNumberUnassigned,
+                 LoadOptionTypeBoot,
+                 LOAD_OPTION_ACTIVE,
+                 DescriptionPtr,
+                 DevicePathFromHandle (Handles[Index]),
+                 NULL,
+                 0
+                 );
+      ASSERT_EFI_ERROR (Status);
+
+      FreePool (DescriptionPtr);
+    }
+  }
+
+  if (HandleCount != 0) {
+    FreePool (Handles);
+  }
+
+  //
+  // Parse simple file system not based on block io
+  //
+  NonBlockNumber = 0;
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiSimpleFileSystemProtocolGuid,
+         NULL,
+         &HandleCount,
+         &Handles
+         );
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (
+                    Handles[Index],
+                    &gEfiBlockIoProtocolGuid,
+                    (VOID **) &BlkIo
+                    );
+     if (!EFI_ERROR (Status)) {
+      //
+      //  Skip if the file system handle supports a BlkIo protocol, which we've handled in above
+      //
+      continue;
+    }
+    UnicodeSPrint (Description, sizeof (Description), NonBlockNumber > 0 ? L"%s %d" : L"%s", L"UEFI Non-Block Boot Device", NonBlockNumber);
+    
+    BootOptions = ReallocatePool (
+                    sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
+                    sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
+                    BootOptions
+                    );
+    ASSERT (BootOptions != NULL);
+
+    Status = EfiBootManagerInitializeLoadOption (
+               &BootOptions[(*BootOptionCount)++],
+               LoadOptionNumberUnassigned,
+               LoadOptionTypeBoot,
+               LOAD_OPTION_ACTIVE,
+               Description,
+               DevicePathFromHandle (Handles[Index]),
+               NULL,
+               0
+               );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  if (HandleCount != 0) {
+    FreePool (Handles);
+  }
+
+  //
+  // Parse load file, assuming UEFI Network boot option
+  //
+  gBS->LocateHandleBuffer (
+         ByProtocol,
+         &gEfiLoadFileProtocolGuid,
+         NULL,
+         &HandleCount,
+         &Handles
+         );
+  for (Index = 0; Index < HandleCount; Index++) {
+
+    UnicodeSPrint (Description, sizeof (Description), Index > 0 ? L"%s %d" : L"%s", L"UEFI Network", Index);
+
+    BootOptions = ReallocatePool (
+                    sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
+                    sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
+                    BootOptions
+                    );
+    ASSERT (BootOptions != NULL);
+
+    Status = EfiBootManagerInitializeLoadOption (
+               &BootOptions[(*BootOptionCount)++],
+               LoadOptionNumberUnassigned,
+               LoadOptionTypeBoot,
+               LOAD_OPTION_ACTIVE,
+               Description,
+               DevicePathFromHandle (Handles[Index]),
+               NULL,
+               0
+               );
+    ASSERT_EFI_ERROR (Status);
+  }
+
+  if (HandleCount != 0) {
+    FreePool (Handles);
+  }
+
+  return BootOptions;
+}
+
+/**
+  The function enumerates all boot options, creates them and registers them in the BootOrder variable.
+**/
+VOID
+EFIAPI
+EfiBootManagerRefreshAllBootOption (
+  VOID
+  )
+{
+  EFI_STATUS                    Status;
+  EFI_BOOT_MANAGER_LOAD_OPTION  *NvBootOptions;
+  UINTN                         NvBootOptionCount;
+  EFI_BOOT_MANAGER_LOAD_OPTION  *BootOptions;
+  UINTN                         BootOptionCount;
+  UINTN                         Index;
+
+  //
+  // Optionally refresh the legacy boot option
+  //
+  if (mBmRefreshLegacyBootOption != NULL) {
+    mBmRefreshLegacyBootOption ();
+  }
+
+  BootOptions   = BmEnumerateBootOptions (&BootOptionCount);
+  NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);
+
+  //
+  // Mark the boot option as added by BDS by setting OptionalData to a special GUID
+  //
+  for (Index = 0; Index < BootOptionCount; Index++) {
+    BootOptions[Index].OptionalData     = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);
+    BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);
+  }
+
+  //
+  // Remove invalid EFI boot options from NV
+  //
+  for (Index = 0; Index < NvBootOptionCount; Index++) {
+    if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || 
+         (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)
+        ) &&
+        (NvBootOptions[Index].OptionalDataSize == sizeof (EFI_GUID)) &&
+        CompareGuid ((EFI_GUID *) NvBootOptions[Index].OptionalData, &mBmAutoCreateBootOptionGuid)
+       ) {
+      //
+      // Only check those added by BDS
+      // so that the boot options added by end-user or OS installer won't be deleted
+      //
+      if (BmFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == (UINTN) -1) {
+        Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);
+        //
+        // Deleting variable with current variable implementation shouldn't fail.
+        //
+        ASSERT_EFI_ERROR (Status);
+      }
+    }
+  }
+
+  //
+  // Add new EFI boot options to NV
+  //
+  for (Index = 0; Index < BootOptionCount; Index++) {
+    if (BmFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -1) {
+      EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);
+      //
+      // Try best to add the boot options so continue upon failure.
+      //
+    }
+  }
+
+  EfiBootManagerFreeLoadOptions (BootOptions,   BootOptionCount);
+  EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);
+}
+
+/**
+  This function is called to create the boot option for the Boot Manager Menu.
+
+  The Boot Manager Menu is shown after successfully booting a boot option.
+  Assume the BootManagerMenuFile is in the same FV as the module links to this library.
+
+  @param  BootOption    Return the boot option of the Boot Manager Menu
+
+  @retval EFI_SUCCESS   Successfully register the Boot Manager Menu.
+  @retval Status        Return status of gRT->SetVariable (). BootOption still points
+                        to the Boot Manager Menu even the Status is not EFI_SUCCESS.
+**/
+EFI_STATUS
+BmRegisterBootManagerMenu (
+  OUT EFI_BOOT_MANAGER_LOAD_OPTION   *BootOption
+  )
+{
+  EFI_STATUS                         Status;
+  CHAR16                             *Description;
+  UINTN                              DescriptionLength;
+  EFI_DEVICE_PATH_PROTOCOL           *DevicePath;
+  EFI_LOADED_IMAGE_PROTOCOL          *LoadedImage;
+  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH  FileNode;
+
+  Status = GetSectionFromFv (
+             PcdGetPtr (PcdBootManagerMenuFile),
+             EFI_SECTION_USER_INTERFACE,
+             0,
+             (VOID **) &Description,
+             &DescriptionLength
+             );
+  if (EFI_ERROR (Status)) {
+    Description = NULL;
+  }
+
+  EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));
+  Status = gBS->HandleProtocol (
+                  gImageHandle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **) &LoadedImage
+                  );
+  ASSERT_EFI_ERROR (Status);
+  DevicePath = AppendDevicePathNode (
+                 DevicePathFromHandle (LoadedImage->DeviceHandle),
+                 (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
+                 );
+  ASSERT (DevicePath != NULL);
+
+  Status = EfiBootManagerInitializeLoadOption (
+             BootOption,
+             LoadOptionNumberUnassigned,
+             LoadOptionTypeBoot,
+             LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,
+             (Description != NULL) ? Description : L"Boot Manager Menu",
+             DevicePath,
+             NULL,
+             0
+             );
+  ASSERT_EFI_ERROR (Status);
+  FreePool (DevicePath);
+  if (Description != NULL) {
+    FreePool (Description);
+  }
+
+  DEBUG_CODE (
+    EFI_BOOT_MANAGER_LOAD_OPTION    *BootOptions;
+    UINTN                           BootOptionCount;
+
+    BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+    ASSERT (BmFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);
+    EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+    );
+
+  return EfiBootManagerAddLoadOptionVariable (BootOption, 0);
+}
+
+/**
+  Return the boot option corresponding to the Boot Manager Menu.
+  It may automatically create one if the boot option hasn't been created yet.
+  
+  @param BootOption    Return the Boot Manager Menu.
+
+  @retval EFI_SUCCESS   The Boot Manager Menu is successfully returned.
+  @retval Status        Return status of gRT->SetVariable (). BootOption still points
+                        to the Boot Manager Menu even the Status is not EFI_SUCCESS.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerGetBootManagerMenu (
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
+  )
+{
+  EFI_STATUS                   Status;
+  UINTN                        BootOptionCount;
+  EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+  UINTN                        Index;
+  EFI_DEVICE_PATH_PROTOCOL     *Node;
+  EFI_HANDLE                   FvHandle;
+  
+  BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+
+  for (Index = 0; Index < BootOptionCount; Index++) {
+    Node   = BootOptions[Index].FilePath;
+    Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
+    if (!EFI_ERROR (Status)) {
+      if (CompareGuid (
+            EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),
+            PcdGetPtr (PcdBootManagerMenuFile)
+            )
+          ) {        
+        Status = EfiBootManagerInitializeLoadOption (
+                   BootOption,
+                   BootOptions[Index].OptionNumber,
+                   BootOptions[Index].OptionType,
+                   BootOptions[Index].Attributes,
+                   BootOptions[Index].Description,
+                   BootOptions[Index].FilePath,
+                   BootOptions[Index].OptionalData,
+                   BootOptions[Index].OptionalDataSize
+                   );
+        ASSERT_EFI_ERROR (Status);
+        break;
+      }
+    }
+  }
+
+  EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+
+  //
+  // Automatically create the Boot#### for Boot Manager Menu when not found.
+  //
+  if (Index == BootOptionCount) {
+    return BmRegisterBootManagerMenu (BootOption);
+  } else {
+    return EFI_SUCCESS;
+  }
+}
+
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c
new file mode 100644 (file)
index 0000000..0172f95
--- /dev/null
@@ -0,0 +1,318 @@
+/** @file
+  Library functions which relate with connecting the device.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+/**
+  Connect all the drivers to all the controllers.
+
+  This function makes sure all the current system drivers manage the correspoinding
+  controllers if have. And at the same time, makes sure all the system controllers
+  have driver to manage it if have.
+**/
+VOID
+BmConnectAllDriversToAllControllers (
+  VOID
+  )
+{
+  EFI_STATUS  Status;
+  UINTN       HandleCount;
+  EFI_HANDLE  *HandleBuffer;
+  UINTN       Index;
+
+  do {
+    //
+    // Connect All EFI 1.10 drivers following EFI 1.10 algorithm
+    //
+    gBS->LocateHandleBuffer (
+           AllHandles,
+           NULL,
+           NULL,
+           &HandleCount,
+           &HandleBuffer
+           );
+
+    for (Index = 0; Index < HandleCount; Index++) {
+      gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
+    }
+
+    if (HandleBuffer != NULL) {
+      FreePool (HandleBuffer);
+    }
+
+    //
+    // Check to see if it's possible to dispatch an more DXE drivers.
+    // The above code may have made new DXE drivers show up.
+    // If any new driver is dispatched (Status == EFI_SUCCESS) and we will try
+    // the connect again.
+    //
+    Status = gDS->Dispatch ();
+
+  } while (!EFI_ERROR (Status));
+}
+
+/**
+  This function will connect all the system driver to controller
+  first, and then special connect the default console, this make
+  sure all the system controller available and the platform default
+  console connected.
+
+**/
+VOID
+EFIAPI
+EfiBootManagerConnectAll (
+  VOID
+  )
+{
+  //
+  // Connect the platform console first
+  //
+  EfiBootManagerConnectAllDefaultConsoles ();
+
+  //
+  // Generic way to connect all the drivers
+  //
+  BmConnectAllDriversToAllControllers ();
+
+  //
+  // Here we have the assumption that we have already had
+  // platform default console
+  //
+  EfiBootManagerConnectAllDefaultConsoles ();
+}
+
+/**
+  This function will create all handles associate with every device
+  path node. If the handle associate with one device path node can not
+  be created successfully, then still give chance to do the dispatch,
+  which load the missing drivers if possible.
+
+  @param  DevicePathToConnect   The device path which will be connected, it can be
+                                a multi-instance device path
+  @param  MatchingHandle        Return the controller handle closest to the DevicePathToConnect
+
+  @retval EFI_SUCCESS           All handles associate with every device path  node
+                                have been created
+  @retval EFI_OUT_OF_RESOURCES  There is no resource to create new handles
+  @retval EFI_NOT_FOUND         Create the handle associate with one device  path
+                                node failed
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL  *DevicePathToConnect,
+  OUT EFI_HANDLE                *MatchingHandle          OPTIONAL
+  )
+{
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  *RemainingDevicePath;
+  EFI_HANDLE                Handle;
+  EFI_HANDLE                PreviousHandle;
+  EFI_TPL                   CurrentTpl;
+
+  if (DevicePathToConnect == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  CurrentTpl = EfiGetCurrentTpl ();
+  //
+  // Start the real work of connect with RemainingDevicePath
+  //
+  PreviousHandle = NULL;
+  do {
+    //
+    // Find the handle that best matches the Device Path. If it is only a
+    // partial match the remaining part of the device path is returned in
+    // RemainingDevicePath.
+    //
+    RemainingDevicePath = DevicePathToConnect;
+    Status              = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &Handle);
+    if (!EFI_ERROR (Status)) {
+      if (Handle == PreviousHandle) {
+        //
+        // If no forward progress is made try invoking the Dispatcher.
+        // A new FV may have been added to the system an new drivers
+        // may now be found.
+        // Status == EFI_SUCCESS means a driver was dispatched
+        // Status == EFI_NOT_FOUND means no new drivers were dispatched
+        //
+        if (CurrentTpl == TPL_APPLICATION) {
+          Status = gDS->Dispatch ();
+        } else {
+          //
+          // Always return EFI_NOT_FOUND here
+          // to prevent dead loop when control handle is found but connection failded case
+          //
+          Status = EFI_NOT_FOUND;
+        }
+      }
+
+
+      if (!EFI_ERROR (Status)) {
+        PreviousHandle = Handle;
+        //
+        // Connect all drivers that apply to Handle and RemainingDevicePath,
+        // the Recursive flag is FALSE so only one level will be expanded.
+        //
+        // Do not check the connect status here, if the connect controller fail,
+        // then still give the chance to do dispatch, because partial
+        // RemainingDevicepath may be in the new FV
+        //
+        // 1. If the connect fail, RemainingDevicepath and handle will not
+        //    change, so next time will do the dispatch, then dispatch's status
+        //    will take effect
+        // 2. If the connect success, the RemainingDevicepath and handle will
+        //    change, then avoid the dispatch, we have chance to continue the
+        //    next connection
+        //
+        gBS->ConnectController (Handle, NULL, RemainingDevicePath, FALSE);
+        if (MatchingHandle != NULL) {
+          *MatchingHandle = Handle;
+        }
+      }
+    }
+    //
+    // Loop until RemainingDevicePath is an empty device path
+    //
+  } while (!EFI_ERROR (Status) && !IsDevicePathEnd (RemainingDevicePath));
+
+  ASSERT (EFI_ERROR (Status) || IsDevicePathEnd (RemainingDevicePath));
+
+  return Status;
+}
+
+/**
+  This function will disconnect all current system handles. 
+  
+  gBS->DisconnectController() is invoked for each handle exists in system handle buffer.
+  If handle is a bus type handle, all childrens also are disconnected recursively by
+  gBS->DisconnectController().
+**/
+VOID
+EFIAPI
+EfiBootManagerDisconnectAll (
+  VOID
+  )
+{
+  UINTN       HandleCount;
+  EFI_HANDLE  *HandleBuffer;
+  UINTN       Index;
+
+  //
+  // Disconnect all
+  //
+  gBS->LocateHandleBuffer (
+         AllHandles,
+         NULL,
+         NULL,
+         &HandleCount,
+         &HandleBuffer
+         );
+  for (Index = 0; Index < HandleCount; Index++) {
+    gBS->DisconnectController (HandleBuffer[Index], NULL, NULL);
+  }
+
+  if (HandleBuffer != NULL) {
+    FreePool (HandleBuffer);
+  }
+}
+
+/**
+  Connect the specific Usb device which match the short form device path,
+  and whose bus is determined by Host Controller (Uhci or Ehci).
+
+  @param  DevicePath             A short-form device path that starts with the first
+                                 element being a USB WWID or a USB Class device
+                                 path
+
+  @return EFI_INVALID_PARAMETER  DevicePath is NULL pointer.
+                                 DevicePath is not a USB device path.
+
+  @return EFI_SUCCESS            Success to connect USB device
+  @return EFI_NOT_FOUND          Fail to find handle for USB controller to connect.
+
+**/
+EFI_STATUS
+BmConnectUsbShortFormDevicePath (
+  IN EFI_DEVICE_PATH_PROTOCOL   *DevicePath
+  )
+{
+  EFI_STATUS                            Status;
+  EFI_HANDLE                            *Handles;
+  UINTN                                 HandleCount;
+  UINTN                                 Index;
+  EFI_PCI_IO_PROTOCOL                   *PciIo;
+  UINT8                                 Class[3];
+  BOOLEAN                               AtLeastOneConnected;
+
+  //
+  // Check the passed in parameters
+  //
+  if (DevicePath == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||
+      ((DevicePathSubType (DevicePath) != MSG_USB_CLASS_DP) && (DevicePathSubType (DevicePath) != MSG_USB_WWID_DP))
+     ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Find the usb host controller firstly, then connect with the remaining device path
+  //
+  AtLeastOneConnected = FALSE;
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciIoProtocolGuid,
+                  NULL,
+                  &HandleCount,
+                  &Handles
+                  );
+  if (!EFI_ERROR (Status)) {
+    for (Index = 0; Index < HandleCount; Index++) {
+      Status = gBS->HandleProtocol (
+                      Handles[Index],
+                      &gEfiPciIoProtocolGuid,
+                      (VOID **) &PciIo
+                      );
+      if (!EFI_ERROR (Status)) {
+        //
+        // Check whether the Pci device is the wanted usb host controller
+        //
+        Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x09, 3, &Class);
+        if (!EFI_ERROR (Status) &&
+            ((PCI_CLASS_SERIAL == Class[2]) && (PCI_CLASS_SERIAL_USB == Class[1]))
+           ) {
+          Status = gBS->ConnectController (
+                          Handles[Index],
+                          NULL,
+                          DevicePath,
+                          FALSE
+                          );
+          if (!EFI_ERROR(Status)) {
+            AtLeastOneConnected = TRUE;
+          }
+        }
+      }
+    }
+
+    if (Handles != NULL) {
+      FreePool (Handles);
+    }
+  }
+
+  return AtLeastOneConnected ? EFI_SUCCESS : EFI_NOT_FOUND;
+}
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c
new file mode 100644 (file)
index 0000000..49b9995
--- /dev/null
@@ -0,0 +1,748 @@
+/** @file
+  Library functions which contain all the code to connect console device.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+CHAR16       *mConVarName[] = {
+  L"ConIn",
+  L"ConOut",
+  L"ErrOut",
+  L"ConInDev",
+  L"ConOutDev",
+  L"ErrOutDev"
+};
+
+/**
+  Search out the video controller.
+
+  @return  PCI device path of the video controller.
+**/
+EFI_HANDLE
+BmGetVideoController (
+  VOID
+  )
+{
+  EFI_STATUS                Status;
+  UINTN                     RootBridgeHandleCount;
+  EFI_HANDLE                *RootBridgeHandleBuffer;
+  UINTN                     HandleCount;
+  EFI_HANDLE                *HandleBuffer;
+  UINTN                     RootBridgeIndex;
+  UINTN                     Index;
+  EFI_HANDLE                VideoController;
+  EFI_PCI_IO_PROTOCOL       *PciIo;
+  PCI_TYPE00                Pci;
+
+  //
+  // Make all the PCI_IO protocols show up
+  //
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiPciRootBridgeIoProtocolGuid,
+                  NULL,
+                  &RootBridgeHandleCount,
+                  &RootBridgeHandleBuffer
+                  );
+  if (EFI_ERROR (Status) || (RootBridgeHandleCount == 0)) {
+    return NULL;
+  }
+
+  VideoController = NULL;
+  for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) {
+    gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE);
+
+    //
+    // Start to check all the pci io to find the first video controller
+    //
+    Status = gBS->LocateHandleBuffer (
+                    ByProtocol,
+                    &gEfiPciIoProtocolGuid,
+                    NULL,
+                    &HandleCount,
+                    &HandleBuffer
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    for (Index = 0; Index < HandleCount; Index++) {
+      Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
+      if (!EFI_ERROR (Status)) {
+        //
+        // Check for all video controller
+        //
+        Status = PciIo->Pci.Read (
+                          PciIo,
+                          EfiPciIoWidthUint32,
+                          0,
+                          sizeof (Pci) / sizeof (UINT32),
+                          &Pci
+                          );
+        if (!EFI_ERROR (Status) && IS_PCI_VGA (&Pci)) {
+          // TODO: use IS_PCI_DISPLAY??
+          VideoController = HandleBuffer[Index];
+          break;
+        }
+      }
+    }
+    FreePool (HandleBuffer);
+
+    if (VideoController != NULL) {
+      break;
+    }
+  }
+  FreePool (RootBridgeHandleBuffer);
+  
+  return VideoController;
+}
+
+/**
+  Query all the children of VideoController and return the device paths of all the 
+  children that support GraphicsOutput protocol.
+
+  @param VideoController       PCI handle of video controller.
+
+  @return  Device paths of all the children that support GraphicsOutput protocol.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiBootManagerGetGopDevicePath (
+  IN  EFI_HANDLE                       VideoController
+  )
+{
+  UINTN                                Index;
+  EFI_STATUS                           Status;
+  EFI_GUID                             **ProtocolBuffer;
+  UINTN                                ProtocolBufferCount;
+  UINTN                                ProtocolIndex;
+  EFI_OPEN_PROTOCOL_INFORMATION_ENTRY  *OpenInfoBuffer;
+  UINTN                                EntryCount;
+  EFI_DEVICE_PATH_PROTOCOL             *DevicePath;
+  EFI_DEVICE_PATH_PROTOCOL             *Next;
+  EFI_DEVICE_PATH_PROTOCOL             *Previous;
+  EFI_DEVICE_PATH_PROTOCOL             *TempDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL             *GopPool;
+  EFI_DEVICE_PATH_PROTOCOL             *ReturnDevicePath;
+
+
+  Status = gBS->ProtocolsPerHandle (
+                  VideoController,
+                  &ProtocolBuffer,
+                  &ProtocolBufferCount
+                  );
+  if (EFI_ERROR (Status)) {
+    return NULL;
+  }
+
+  GopPool = NULL;
+
+  for (ProtocolIndex = 0; ProtocolIndex < ProtocolBufferCount; ProtocolIndex++) {
+    Status = gBS->OpenProtocolInformation (
+                    VideoController,
+                    ProtocolBuffer[ProtocolIndex],
+                    &OpenInfoBuffer,
+                    &EntryCount
+                    );
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    for (Index = 0; Index < EntryCount; Index++) {
+      //
+      // Query all the children
+      //
+      if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
+        Status = gBS->OpenProtocol (
+                        OpenInfoBuffer[Index].ControllerHandle,
+                        &gEfiDevicePathProtocolGuid,
+                        (VOID **) &DevicePath,
+                        NULL,
+                        NULL,
+                        EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                        );
+        if (EFI_ERROR (Status)) {
+          continue;
+        }
+
+        Previous = NULL;
+        for (Next = DevicePath; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) {
+          Previous = Next;
+        }
+        ASSERT (Previous != NULL);
+
+        if (DevicePathType (Previous) == ACPI_DEVICE_PATH && DevicePathSubType (Previous) == ACPI_ADR_DP) {
+          Status = gBS->OpenProtocol (
+                          OpenInfoBuffer[Index].ControllerHandle,
+                          &gEfiGraphicsOutputProtocolGuid,
+                          NULL,
+                          NULL,
+                          NULL,
+                          EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                          );
+          if (!EFI_ERROR (Status)) {
+            //
+            // Append the device path to GOP pool when there is GOP protocol installed.
+            //
+            TempDevicePath = GopPool;
+            GopPool = AppendDevicePathInstance (GopPool, DevicePath);
+            gBS->FreePool (TempDevicePath);
+          }
+        }
+
+        if (DevicePathType (Previous) == HARDWARE_DEVICE_PATH && DevicePathSubType (Previous) == HW_CONTROLLER_DP) {
+          //
+          // Recursively look for GOP child in this frame buffer handle
+          //
+          DEBUG ((EFI_D_INFO, "[Bds] Looking for GOP child deeper ... \n"));
+          TempDevicePath = GopPool;
+          ReturnDevicePath = EfiBootManagerGetGopDevicePath (OpenInfoBuffer[Index].ControllerHandle);
+          GopPool = AppendDevicePathInstance (GopPool, ReturnDevicePath);
+          gBS->FreePool (ReturnDevicePath);
+          gBS->FreePool (TempDevicePath);
+        }
+      }
+    }
+
+    FreePool (OpenInfoBuffer);
+  }
+
+  FreePool (ProtocolBuffer);
+
+  return GopPool;
+}
+
+/**
+  Connect the platform active active video controller.
+
+  @param VideoController       PCI handle of video controller.
+
+  @retval EFI_NOT_FOUND There is no active video controller.
+  @retval EFI_SUCCESS   The video controller is connected.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectVideoController (
+  EFI_HANDLE                 VideoController  OPTIONAL
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL   *Gop;
+  
+  if (VideoController == NULL) {
+    //
+    // Get the platform vga device
+    //
+    VideoController = BmGetVideoController ();
+  }
+  if (VideoController == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Try to connect the PCI device path, so that GOP dirver could start on this 
+  // device and create child handles with GraphicsOutput Protocol installed
+  // on them, then we get device paths of these child handles and select 
+  // them as possible console device.
+  //
+  gBS->ConnectController (VideoController, NULL, NULL, FALSE);
+
+  Gop = EfiBootManagerGetGopDevicePath (VideoController);
+  if (Gop == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  EfiBootManagerUpdateConsoleVariable (ConOut, Gop, NULL);
+  FreePool (Gop);
+
+  //
+  // Necessary for ConPlatform and ConSplitter driver to start up again after ConOut is updated.
+  //
+  return gBS->ConnectController (VideoController, NULL, NULL, TRUE);
+}
+
+/**
+  Fill console handle in System Table if there are no valid console handle in.
+
+  Firstly, check the validation of console handle in System Table. If it is invalid,
+  update it by the first console device handle from EFI console variable. 
+
+  @param  VarName            The name of the EFI console variable.
+  @param  ConsoleGuid        Specified Console protocol GUID.
+  @param  ConsoleHandle      On IN,  console handle in System Table to be checked. 
+                             On OUT, new console handle in system table.
+  @param  ProtocolInterface  On IN,  console protocol on console handle in System Table to be checked. 
+                             On OUT, new console protocol on new console handle in system table.
+
+  @retval TRUE               System Table has been updated.
+  @retval FALSE              System Table hasn't been updated.
+
+**/
+BOOLEAN 
+BmUpdateSystemTableConsole (
+  IN     CHAR16                   *VarName,
+  IN     EFI_GUID                 *ConsoleGuid,
+  IN OUT EFI_HANDLE               *ConsoleHandle,
+  IN OUT VOID                     **ProtocolInterface
+  )
+{
+  EFI_STATUS                      Status;
+  UINTN                           DevicePathSize;
+  EFI_DEVICE_PATH_PROTOCOL        *FullDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL        *VarConsole;
+  EFI_DEVICE_PATH_PROTOCOL        *Instance;
+  VOID                            *Interface;
+  EFI_HANDLE                      NewHandle;
+  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut;
+
+  ASSERT (VarName != NULL);
+  ASSERT (ConsoleHandle != NULL);
+  ASSERT (ConsoleGuid != NULL);
+  ASSERT (ProtocolInterface != NULL);
+
+  if (*ConsoleHandle != NULL) {
+    Status = gBS->HandleProtocol (
+                   *ConsoleHandle,
+                   ConsoleGuid,
+                   &Interface
+                   );
+    if (Status == EFI_SUCCESS && Interface == *ProtocolInterface) {
+      //
+      // If ConsoleHandle is valid and console protocol on this handle also
+      // also matched, just return.
+      //
+      return FALSE;
+    }
+  }
+  
+  //
+  // Get all possible consoles device path from EFI variable
+  //
+  GetEfiGlobalVariable2 (VarName, (VOID **) &VarConsole, NULL);
+  if (VarConsole == NULL) {
+    //
+    // If there is no any console device, just return.
+    //
+    return FALSE;
+  }
+
+  FullDevicePath = VarConsole;
+
+  do {
+    //
+    // Check every instance of the console variable
+    //
+    Instance  = GetNextDevicePathInstance (&VarConsole, &DevicePathSize);
+    if (Instance == NULL) {
+      DEBUG ((EFI_D_ERROR, "[Bds] No valid console instance is found for %s!\n", VarName));
+      // We should not ASSERT when all the console devices are removed.
+      // ASSERT_EFI_ERROR (EFI_NOT_FOUND);
+      FreePool (FullDevicePath);
+      return FALSE;
+    }
+    
+    //
+    // Find console device handle by device path instance
+    //
+    Status = gBS->LocateDevicePath (
+                    ConsoleGuid,
+                    &Instance,
+                    &NewHandle
+                    );
+    if (!EFI_ERROR (Status)) {
+      //
+      // Get the console protocol on this console device handle
+      //
+      Status = gBS->HandleProtocol (
+                      NewHandle,
+                      ConsoleGuid,
+                      &Interface
+                      );
+      if (!EFI_ERROR (Status)) {
+        //
+        // Update new console handle in System Table.
+        //
+        *ConsoleHandle     = NewHandle;
+        *ProtocolInterface = Interface;
+        if (CompareGuid (ConsoleGuid, &gEfiSimpleTextOutProtocolGuid)) {
+          //
+          // If it is console out device, set console mode 80x25 if current mode is invalid.
+          //
+          TextOut = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *) Interface;
+          if (TextOut->Mode->Mode == -1) {
+            TextOut->SetMode (TextOut, 0);
+          }
+        }
+        return TRUE;
+      }
+    }
+
+  } while (Instance != NULL);
+
+  //
+  // No any available console devcie found.
+  //
+  return FALSE;
+}
+
+/**
+  This function updates the console variable based on ConVarName. It can
+  add or remove one specific console device path from the variable
+
+  @param  ConsoleType              ConIn, ConOut, ErrOut, ConInDev, ConOutDev or ErrOutDev.
+  @param  CustomizedConDevicePath  The console device path to be added to
+                                   the console variable. Cannot be multi-instance.
+  @param  ExclusiveDevicePath      The console device path to be removed
+                                   from the console variable. Cannot be multi-instance.
+
+  @retval EFI_UNSUPPORTED          The added device path is the same as a removed one.
+  @retval EFI_SUCCESS              Successfully added or removed the device path from the
+                                   console variable.
+  @retval others                   Return status of RT->SetVariable().
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerUpdateConsoleVariable (
+  IN  CONSOLE_TYPE              ConsoleType,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *CustomizedConDevicePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *ExclusiveDevicePath
+  )
+{
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  *VarConsole;
+  EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;
+
+  if (ConsoleType >= sizeof (mConVarName) / sizeof (mConVarName[0])) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Notes: check the device path point, here should check
+  // with compare memory
+  //
+  if (CustomizedConDevicePath == ExclusiveDevicePath) {
+    return EFI_UNSUPPORTED;
+  }
+  //
+  // Delete the ExclusiveDevicePath from current default console
+  //
+  GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &VarConsole, NULL);
+  //
+  // Initialize NewDevicePath
+  //
+  NewDevicePath = VarConsole;
+
+  //
+  // If ExclusiveDevicePath is even the part of the instance in VarConsole, delete it.
+  // In the end, NewDevicePath is the final device path.
+  //
+  if (ExclusiveDevicePath != NULL && VarConsole != NULL) {
+      NewDevicePath = BmDelPartMatchInstance (VarConsole, ExclusiveDevicePath);
+  }
+  //
+  // Try to append customized device path to NewDevicePath.
+  //
+  if (CustomizedConDevicePath != NULL) {
+    if (!BmMatchDevicePaths (NewDevicePath, CustomizedConDevicePath)) {
+      //
+      // Check if there is part of CustomizedConDevicePath in NewDevicePath, delete it.
+      //
+      NewDevicePath = BmDelPartMatchInstance (NewDevicePath, CustomizedConDevicePath);
+      //
+      // In the first check, the default console variable will be _ModuleEntryPoint,
+      // just append current customized device path
+      //
+      TempNewDevicePath = NewDevicePath;
+      NewDevicePath = AppendDevicePathInstance (NewDevicePath, CustomizedConDevicePath);
+      if (TempNewDevicePath != NULL) {
+        FreePool(TempNewDevicePath);
+      }
+    }
+  }
+
+  //
+  // Finally, Update the variable of the default console by NewDevicePath
+  //
+  Status = gRT->SetVariable (
+                  mConVarName[ConsoleType],
+                  &gEfiGlobalVariableGuid,
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
+                                                  | ((ConsoleType < ConInDev) ? EFI_VARIABLE_NON_VOLATILE : 0),
+                  GetDevicePathSize (NewDevicePath),
+                  NewDevicePath
+                  );
+
+  if (VarConsole == NewDevicePath) {
+    if (VarConsole != NULL) {
+      FreePool(VarConsole);
+    }
+  } else {
+    if (VarConsole != NULL) {
+      FreePool(VarConsole);
+    }
+    if (NewDevicePath != NULL) {
+      FreePool(NewDevicePath);
+    }
+  }
+
+  return Status;
+}
+
+
+/**
+  Connect the console device base on the variable ConsoleType.
+
+  @param  ConsoleType              ConIn, ConOut or ErrOut.
+
+  @retval EFI_NOT_FOUND            There is not any console devices connected
+                                   success
+  @retval EFI_SUCCESS              Success connect any one instance of the console
+                                   device path base on the variable ConVarName.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectConsoleVariable (
+  IN  CONSOLE_TYPE              ConsoleType
+  )
+{
+  EFI_STATUS                Status;
+  EFI_DEVICE_PATH_PROTOCOL  *StartDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *Instance;
+  EFI_DEVICE_PATH_PROTOCOL  *Next;
+  EFI_DEVICE_PATH_PROTOCOL  *CopyOfDevicePath;
+  UINTN                     Size;
+  BOOLEAN                   DeviceExist;
+  EFI_HANDLE                Handle;
+
+  if ((ConsoleType != ConIn) && (ConsoleType != ConOut) && (ConsoleType != ErrOut)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status      = EFI_SUCCESS;
+  DeviceExist = FALSE;
+  Handle      = NULL;
+
+  //
+  // Check if the console variable exist
+  //
+  GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &StartDevicePath, NULL);
+  if (StartDevicePath == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  CopyOfDevicePath = StartDevicePath;
+  do {
+    //
+    // Check every instance of the console variable
+    //
+    Instance  = GetNextDevicePathInstance (&CopyOfDevicePath, &Size);
+    if (Instance == NULL) {
+      FreePool (StartDevicePath);
+      return EFI_UNSUPPORTED;
+    }
+    
+    Next      = Instance;
+    while (!IsDevicePathEndType (Next)) {
+      Next = NextDevicePathNode (Next);
+    }
+
+    SetDevicePathEndNode (Next);
+    //
+    // Connect the USB console
+    // USB console device path is a short-form device path that 
+    //  starts with the first element being a USB WWID
+    //  or a USB Class device path
+    //
+    if ((DevicePathType (Instance) == MESSAGING_DEVICE_PATH) &&
+        ((DevicePathSubType (Instance) == MSG_USB_CLASS_DP) || (DevicePathSubType (Instance) == MSG_USB_WWID_DP))
+       ) {
+      Status = BmConnectUsbShortFormDevicePath (Instance);
+      if (!EFI_ERROR (Status)) {
+        DeviceExist = TRUE;
+      }
+    } else {
+      for (Next = Instance; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) {
+        if (DevicePathType (Next) == ACPI_DEVICE_PATH && DevicePathSubType (Next) == ACPI_ADR_DP) {
+          break;
+        } else if (DevicePathType (Next) == HARDWARE_DEVICE_PATH && 
+                   DevicePathSubType (Next) == HW_CONTROLLER_DP &&
+                   DevicePathType (NextDevicePathNode (Next)) == ACPI_DEVICE_PATH &&
+                   DevicePathSubType (NextDevicePathNode (Next)) == ACPI_ADR_DP
+                   ) {
+          break;
+        }
+      }
+      if (!IsDevicePathEnd (Next)) {
+        //
+        // For GOP device path, start the video driver with NULL remaining device path
+        //
+        SetDevicePathEndNode (Next);
+        Status = EfiBootManagerConnectDevicePath (Instance, &Handle);
+        if (!EFI_ERROR (Status)) {
+          gBS->ConnectController (Handle, NULL, NULL, TRUE);
+        }
+      } else {
+        Status = EfiBootManagerConnectDevicePath (Instance, NULL);
+      }
+      if (EFI_ERROR (Status)) {
+        //
+        // Delete the instance from the console varialbe
+        //
+        EfiBootManagerUpdateConsoleVariable (ConsoleType, NULL, Instance);
+      } else {
+        DeviceExist = TRUE;
+      }
+    }
+    FreePool(Instance);
+  } while (CopyOfDevicePath != NULL);
+
+  FreePool (StartDevicePath);
+
+  if (!DeviceExist) {
+    return EFI_NOT_FOUND;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  This function will search every input/output device in current system,
+  and make every input/output device as potential console device.
+**/
+VOID
+EFIAPI
+EfiBootManagerConnectAllConsoles (
+  VOID
+  )
+{
+  UINTN                     Index;
+  EFI_DEVICE_PATH_PROTOCOL  *ConDevicePath;
+  UINTN                     HandleCount;
+  EFI_HANDLE                *HandleBuffer;
+
+  Index         = 0;
+  HandleCount   = 0;
+  HandleBuffer  = NULL;
+  ConDevicePath = NULL;
+
+  //
+  // Update all the console variables
+  //
+  gBS->LocateHandleBuffer (
+          ByProtocol,
+          &gEfiSimpleTextInProtocolGuid,
+          NULL,
+          &HandleCount,
+          &HandleBuffer
+          );
+
+  for (Index = 0; Index < HandleCount; Index++) {
+    gBS->HandleProtocol (
+            HandleBuffer[Index],
+            &gEfiDevicePathProtocolGuid,
+            (VOID **) &ConDevicePath
+            );
+    EfiBootManagerUpdateConsoleVariable (ConIn, ConDevicePath, NULL);
+  }
+
+  if (HandleBuffer != NULL) {
+    FreePool(HandleBuffer);
+    HandleBuffer = NULL;
+  }
+
+  gBS->LocateHandleBuffer (
+          ByProtocol,
+          &gEfiSimpleTextOutProtocolGuid,
+          NULL,
+          &HandleCount,
+          &HandleBuffer
+          );
+  for (Index = 0; Index < HandleCount; Index++) {
+    gBS->HandleProtocol (
+            HandleBuffer[Index],
+            &gEfiDevicePathProtocolGuid,
+            (VOID **) &ConDevicePath
+            );
+    EfiBootManagerUpdateConsoleVariable (ConOut, ConDevicePath, NULL);
+    EfiBootManagerUpdateConsoleVariable (ErrOut, ConDevicePath, NULL);
+  }
+
+  if (HandleBuffer != NULL) {
+    FreePool(HandleBuffer);
+  }
+
+  //
+  // Connect all console variables
+  //
+  EfiBootManagerConnectAllDefaultConsoles ();
+}
+
+
+/**
+  This function will connect all the console devices base on the console
+  device variable ConIn, ConOut and ErrOut.
+**/
+VOID
+EFIAPI
+EfiBootManagerConnectAllDefaultConsoles (
+  VOID
+  )
+{
+  BOOLEAN                   SystemTableUpdated;
+
+  EfiBootManagerConnectConsoleVariable (ConOut);
+  PERF_START (NULL, "ConOutReady", "BDS", 1);
+  PERF_END   (NULL, "ConOutReady", "BDS", 0);
+
+  
+  EfiBootManagerConnectConsoleVariable (ConIn);
+  PERF_START (NULL, "ConInReady", "BDS", 1);
+  PERF_END   (NULL, "ConInReady", "BDS", 0);
+
+  //
+  // The _ModuleEntryPoint err out var is legal.
+  //
+  EfiBootManagerConnectConsoleVariable (ErrOut);
+  PERF_START (NULL, "ErrOutReady", "BDS", 1);
+  PERF_END   (NULL, "ErrOutReady", "BDS", 0);
+
+  SystemTableUpdated = FALSE;
+  //
+  // Fill console handles in System Table if no console device assignd.
+  //
+  if (BmUpdateSystemTableConsole (L"ConIn", &gEfiSimpleTextInProtocolGuid, &gST->ConsoleInHandle, (VOID **) &gST->ConIn)) {
+    SystemTableUpdated = TRUE;
+  }
+  if (BmUpdateSystemTableConsole (L"ConOut", &gEfiSimpleTextOutProtocolGuid, &gST->ConsoleOutHandle, (VOID **) &gST->ConOut)) {
+    SystemTableUpdated = TRUE;
+  }
+  if (BmUpdateSystemTableConsole (L"ErrOut", &gEfiSimpleTextOutProtocolGuid, &gST->StandardErrorHandle, (VOID **) &gST->StdErr)) {
+    SystemTableUpdated = TRUE;
+  }
+
+  if (SystemTableUpdated) {
+    //
+    // Update the CRC32 in the EFI System Table header
+    //
+    gST->Hdr.CRC32 = 0;
+    gBS->CalculateCrc32 (
+          (UINT8 *) &gST->Hdr,
+          gST->Hdr.HeaderSize,
+          &gST->Hdr.CRC32
+          );
+  }
+}
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c b/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c
new file mode 100644 (file)
index 0000000..2b52573
--- /dev/null
@@ -0,0 +1,578 @@
+/** @file
+  Library functions which relates with driver health.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED
+  CHAR16 *mBmHealthStatusText[] = {
+    L"Healthy",
+    L"Repair Required",
+    L"Configuration Required",
+    L"Failed",
+    L"Reconnect Required",
+    L"Reboot Required"
+    };
+
+/**
+  Return the controller name.
+
+  @param DriverHealthHandle  The handle on which the Driver Health protocol instance is retrieved.
+  @param ControllerHandle    The handle of a controller that the driver specified by DriverBindingHandle is managing.
+                             This handle specifies the controller whose name is to be returned.
+  @param ChildHandle         The handle of the child controller to retrieve the name of. This is an
+                             optional parameter that may be NULL. It will be NULL for device drivers.
+                             It will also be NULL for bus drivers that attempt to retrieve the name
+                             of the bus controller. It will not be NULL for a bus driver that attempts
+                             to retrieve the name of a child controller.
+
+  @return A pointer to the Unicode string to return. This Unicode string is the name of the controller
+          specified by ControllerHandle and ChildHandle.
+**/
+CHAR16 *
+BmGetControllerName (
+  IN  EFI_HANDLE                   DriverHealthHandle,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  EFI_HANDLE                   ChildHandle
+  )
+{
+  EFI_STATUS                       Status;
+  CHAR16                           *ControllerName;
+  CHAR8                            *LanguageVariable;
+  CHAR8                            *BestLanguage;
+  BOOLEAN                          Iso639Language;
+  EFI_COMPONENT_NAME_PROTOCOL      *ComponentName;
+
+  ControllerName = NULL;
+
+  //
+  // Locate Component Name (2) protocol on the driver binging handle.
+  //
+  Iso639Language = FALSE;
+  Status = gBS->HandleProtocol (
+                 DriverHealthHandle,
+                 &gEfiComponentName2ProtocolGuid,
+                 (VOID **) &ComponentName
+                 );
+  if (EFI_ERROR (Status)) {
+    Status = gBS->HandleProtocol (
+                    DriverHealthHandle,
+                    &gEfiComponentNameProtocolGuid,
+                    (VOID **) &ComponentName
+                    );
+    if (!EFI_ERROR (Status)) {
+      Iso639Language = TRUE;
+    }
+  }
+
+  if (!EFI_ERROR (Status)) {
+    LanguageVariable = GetEfiGlobalVariable (Iso639Language ? L"Lang" : L"PlatformLang");
+    BestLanguage     = GetBestLanguage(
+                         ComponentName->SupportedLanguages,
+                         Iso639Language,
+                         (LanguageVariable != NULL) ? LanguageVariable : "",
+                         Iso639Language ? "eng" : "en-US",
+                         NULL
+                         );
+    if (LanguageVariable != NULL) {
+      FreePool (LanguageVariable);
+    }
+
+    Status = ComponentName->GetControllerName (
+                              ComponentName,
+                              ControllerHandle, 
+                              ChildHandle,
+                              BestLanguage,
+                              &ControllerName
+                              );
+  }
+
+  if (!EFI_ERROR (Status)) {
+    return AllocateCopyPool (StrSize (ControllerName), ControllerName);
+  } else {
+    return ConvertDevicePathToText (
+             DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle),
+             FALSE,
+             FALSE
+             );
+  }
+}
+
+/**
+  Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol
+
+  @param DriverHealthInfo  Pointer to the Driver Health information entry.
+**/
+VOID
+BmDisplayMessages (
+  IN  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo
+  )
+{
+  UINTN                            Index;
+  EFI_STRING                       String;
+  CHAR16                           *ControllerName;
+
+  if (DriverHealthInfo->MessageList == NULL ||
+      DriverHealthInfo->MessageList[0].HiiHandle == NULL) {
+    return;
+  }
+
+  ControllerName = BmGetControllerName (
+                     DriverHealthInfo->DriverHealthHandle,
+                     DriverHealthInfo->ControllerHandle, 
+                     DriverHealthInfo->ChildHandle
+                     );
+
+  DEBUG ((EFI_D_INFO, "Controller: %s\n", ControllerName));
+  Print (L"Controller: %s\n", ControllerName);
+  for (Index = 0; DriverHealthInfo->MessageList[Index].HiiHandle != NULL; Index++) {
+    String = HiiGetString (
+               DriverHealthInfo->MessageList[Index].HiiHandle,
+               DriverHealthInfo->MessageList[Index].StringId,
+               NULL
+               );
+    if (String != NULL) {
+      Print (L"  %s\n", String);
+      DEBUG ((EFI_D_INFO, "  %s\n", String));
+      FreePool (String);
+    }
+  }
+
+  if (ControllerName != NULL) {
+    FreePool (ControllerName);
+  }
+}
+
+/**
+  The repair notify function.
+  @param Value  A value between 0 and Limit that identifies the current progress
+                of the repair operation.
+  @param Limit  The maximum value of Value for the current repair operation.
+                If Limit is 0, then the completion progress is indeterminate.
+                For example, a driver that wants to specify progress in percent
+                would use a Limit value of 100.
+
+  @retval EFI_SUCCESS  Successfully return from the notify function.
+**/
+EFI_STATUS
+EFIAPI
+BmRepairNotify (
+  IN UINTN        Value,
+  IN UINTN        Limit
+  )
+{
+  DEBUG ((EFI_D_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit));
+  Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Collect the Driver Health status of a single controller.
+  
+  @param DriverHealthInfo        A pointer to the array containing all of the platform driver health information.
+  @param Count                   Return the updated array count.
+  @param DriverHealthHandle      The handle on which the Driver Health protocol instance is retrieved.
+  @param ControllerHandle        The handle of the controller..
+  @param ChildHandle             The handle of the child controller to retrieve the health
+                                 status on.  This is an optional parameter that may be NULL.
+
+  @retval Status                 The status returned from GetHealthStatus.
+  @retval EFI_ABORTED            The health status is healthy so no further query is needed.
+
+**/
+EFI_STATUS
+BmGetSingleControllerHealthStatus (
+  IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo,
+  IN OUT UINTN                               *Count,
+  IN  EFI_HANDLE                             DriverHealthHandle,
+  IN  EFI_HANDLE                             ControllerHandle,  OPTIONAL
+  IN  EFI_HANDLE                             ChildHandle        OPTIONAL
+  )
+{
+  EFI_STATUS                     Status;
+  EFI_DRIVER_HEALTH_PROTOCOL     *DriverHealth;
+  EFI_DRIVER_HEALTH_HII_MESSAGE  *MessageList;
+  EFI_HII_HANDLE                 FormHiiHandle;
+  EFI_DRIVER_HEALTH_STATUS       HealthStatus;
+
+  ASSERT (DriverHealthHandle != NULL);
+  //
+  // Retrieve the Driver Health Protocol from DriverHandle
+  //
+  Status = gBS->HandleProtocol (
+                  DriverHealthHandle,
+                  &gEfiDriverHealthProtocolGuid,
+                  (VOID **) &DriverHealth
+                  );
+  ASSERT_EFI_ERROR (Status);
+  
+
+  if (ControllerHandle == NULL) {
+    //
+    // If ControllerHandle is NULL, the return the cumulative health status of the driver
+    //
+    Status = DriverHealth->GetHealthStatus (DriverHealth, NULL, NULL, &HealthStatus, NULL, NULL);
+    if (!EFI_ERROR (Status) && HealthStatus == EfiDriverHealthStatusHealthy) {
+      *DriverHealthInfo = ReallocatePool (
+                            (*Count)     * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+                            (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+                            *DriverHealthInfo
+                            );
+      ASSERT (*DriverHealthInfo != NULL);
+
+      (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
+      (*DriverHealthInfo)[*Count].DriverHealth       = DriverHealth;
+      (*DriverHealthInfo)[*Count].HealthStatus       = HealthStatus;
+
+      *Count = *Count + 1;
+
+      Status = EFI_ABORTED;
+    }
+    return Status;
+  }
+
+  MessageList   = NULL;
+  FormHiiHandle = NULL;
+
+  //
+  // Collect the health status with the optional HII message list
+  //
+  Status = DriverHealth->GetHealthStatus (DriverHealth, ControllerHandle, ChildHandle, &HealthStatus, &MessageList, &FormHiiHandle);
+  if (!EFI_ERROR (Status)) {
+    *DriverHealthInfo = ReallocatePool (
+                          (*Count)     * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+                          (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+                          *DriverHealthInfo
+                          );
+    ASSERT (*DriverHealthInfo != NULL);
+    (*DriverHealthInfo)[*Count].DriverHealth       = DriverHealth;
+    (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
+    (*DriverHealthInfo)[*Count].ControllerHandle   = ControllerHandle;
+    (*DriverHealthInfo)[*Count].ChildHandle        = ChildHandle;
+    (*DriverHealthInfo)[*Count].HiiHandle          = FormHiiHandle;
+    (*DriverHealthInfo)[*Count].MessageList        = MessageList;
+    (*DriverHealthInfo)[*Count].HealthStatus       = HealthStatus;
+
+    *Count = *Count + 1;
+  }
+
+  return Status;
+}
+
+/**
+  Return all the Driver Health information.
+
+  When the cumulative health status of all the controllers managed by the
+  driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such
+  EFI_DRIVER_HEALTH_PROTOCOL instance.
+  Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
+  entry. Additionally every child controller creates one
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver.
+
+  @param Count      Return the count of the Driver Health information.
+
+  @retval NULL      No Driver Health information is returned.
+  @retval !NULL     Pointer to the Driver Health information array.
+**/
+EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *
+EFIAPI
+EfiBootManagerGetDriverHealthInfo (
+  UINTN                      *Count
+  )
+{
+  EFI_STATUS                 Status;
+  UINTN                      NumHandles;
+  EFI_HANDLE                 *DriverHealthHandles;
+  EFI_DRIVER_HEALTH_STATUS   HealthStatus;
+  UINTN                      DriverHealthIndex;
+  EFI_HANDLE                 *Handles;
+  UINTN                      HandleCount;
+  UINTN                      ControllerIndex;
+  UINTN                      ChildIndex;
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO  *DriverHealthInfo;
+
+  //
+  // Initialize local variables
+  //
+  *Count                  = 0;
+  DriverHealthInfo        = NULL;
+  Handles                 = NULL;
+  DriverHealthHandles     = NULL;
+  NumHandles              = 0;
+  HandleCount             = 0;
+
+  HealthStatus = EfiDriverHealthStatusHealthy;
+
+  Status = gBS->LocateHandleBuffer (
+                  ByProtocol,
+                  &gEfiDriverHealthProtocolGuid,
+                  NULL,
+                  &NumHandles,
+                  &DriverHealthHandles
+                  );
+
+  if (Status == EFI_NOT_FOUND || NumHandles == 0) {
+    //
+    // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND
+    //
+    return NULL;
+  }
+
+  ASSERT_EFI_ERROR (Status);
+  ASSERT (DriverHealthHandles != NULL);
+
+  //
+  // Check the health status of all controllers in the platform
+  // Start by looping through all the Driver Health Protocol handles in the handle database
+  //
+  for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) {
+    //
+    // Get the cumulative health status of the driver
+    //
+    Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL);
+    if (EFI_ERROR (Status)) {
+      continue;
+    }
+
+    //
+    // See if the list of all handles in the handle database has been retrieved
+    //
+    //
+    if (Handles == NULL) {
+      //
+      // Retrieve the list of all handles from the handle database
+      //
+      Status = gBS->LocateHandleBuffer (
+        AllHandles,
+        NULL,
+        NULL,
+        &HandleCount,
+        &Handles
+        );
+      ASSERT_EFI_ERROR (Status);
+    }
+    //
+    // Loop through all the controller handles in the handle database
+    //
+    for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) {
+      Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL);
+      if (EFI_ERROR (Status)) {
+        continue;
+      }
+
+      //
+      // Loop through all the child handles in the handle database
+      //
+      for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
+        Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]);
+        if (EFI_ERROR (Status)) {
+          continue;
+        }
+      }
+    }
+  }
+
+  Status = EFI_SUCCESS;
+
+  if (Handles != NULL) {
+    FreePool (Handles);
+  }
+  if (DriverHealthHandles != NULL) {
+    FreePool (DriverHealthHandles);
+  }
+
+  return DriverHealthInfo;
+}
+
+/**
+  Free the Driver Health information array.
+
+  @param DriverHealthInfo       Pointer to array of the Driver Health information.
+  @param Count                  Count of the array.
+
+  @retval EFI_SUCCESS           The array is freed.
+  @retval EFI_INVALID_PARAMETER The array is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeDriverHealthInfo (
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo,
+  UINTN                               Count
+  )
+{
+  UINTN                               Index;
+
+  for (Index = 0; Index < Count; Index++) {
+    if (DriverHealthInfo[Index].MessageList != NULL) {
+      FreePool (DriverHealthInfo[Index].MessageList);
+    }
+  }
+  return gBS->FreePool (DriverHealthInfo);
+}
+
+/**
+  Repair all the controllers according to the Driver Health status queried.
+**/
+VOID
+BmRepairAllControllers (
+  VOID
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
+  EFI_DRIVER_HEALTH_STATUS            HealthStatus;
+  UINTN                               Count;
+  UINTN                               Index;
+  BOOLEAN                             RepairRequired;
+  BOOLEAN                             ConfigurationRequired;
+  BOOLEAN                             ReconnectRequired;
+  BOOLEAN                             RebootRequired;
+  EFI_HII_HANDLE                      *HiiHandles;
+  EFI_FORM_BROWSER2_PROTOCOL          *FormBrowser2;
+
+  //
+  // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check.
+  //
+  if (CompareGuid (PcdGetPtr (PcdDriverHealthConfigureForm), &gZeroGuid)) {
+    return;
+  }
+
+  Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2);
+  ASSERT_EFI_ERROR (Status);
+
+  do {
+    RepairRequired        = FALSE;
+    ConfigurationRequired = FALSE;
+
+    //
+    // Deal with Repair Required
+    //
+    DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
+    for (Index = 0; Index < Count; Index++) {
+      if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) {
+        ConfigurationRequired = TRUE;
+      }
+      
+      if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) {
+        RepairRequired        = TRUE;
+
+        BmDisplayMessages (&DriverHealthInfo[Index]);
+
+        Status = DriverHealthInfo[Index].DriverHealth->Repair (
+                                                         DriverHealthInfo[Index].DriverHealth,
+                                                         DriverHealthInfo[Index].ControllerHandle,
+                                                         DriverHealthInfo[Index].ChildHandle,
+                                                         BmRepairNotify
+                                                         );
+        if (!EFI_ERROR (Status) && !ConfigurationRequired) {
+          Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus (
+                                                           DriverHealthInfo[Index].DriverHealth,
+                                                           DriverHealthInfo[Index].ControllerHandle,
+                                                           DriverHealthInfo[Index].ChildHandle,
+                                                           &HealthStatus,
+                                                           NULL,
+                                                           NULL
+                                                           );
+          if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) {
+            ConfigurationRequired = TRUE;
+          }
+        }
+      }
+    }
+
+    if (ConfigurationRequired) {
+      HiiHandles = HiiGetHiiHandles (NULL);
+      if (HiiHandles != NULL) {
+        for (Index = 0; HiiHandles[Index] != NULL; Index++) {
+          Status = FormBrowser2->SendForm (
+                                   FormBrowser2,
+                                   &HiiHandles[Index],
+                                   1,
+                                   PcdGetPtr (PcdDriverHealthConfigureForm),
+                                   0,
+                                   NULL,
+                                   NULL
+                                   );
+          if (!EFI_ERROR (Status)) {
+            break;
+          }
+        }
+        FreePool (HiiHandles);
+      }
+    }
+  
+    EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
+  } while (RepairRequired || ConfigurationRequired);
+
+  RebootRequired    = FALSE;
+  ReconnectRequired = FALSE;
+  DriverHealthInfo  = EfiBootManagerGetDriverHealthInfo (&Count);
+  for (Index = 0; Index < Count; Index++) {
+
+    BmDisplayMessages (&DriverHealthInfo[Index]);
+
+    if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) {
+      Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL);
+      if (EFI_ERROR (Status)) {
+        //
+        // Disconnect failed. Need to promote reconnect to a reboot.
+        //
+        RebootRequired    = TRUE;
+      } else {
+        gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE);
+        ReconnectRequired = TRUE;
+      }
+    }
+
+    if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) {
+      RebootRequired      = TRUE;
+    }
+  }
+  EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
+
+
+  if (ReconnectRequired) {
+    BmRepairAllControllers ();
+  }
+
+  DEBUG_CODE (
+    CHAR16 *ControllerName;
+
+    DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
+    for (Index = 0; Index < Count; Index++) {
+      ControllerName = BmGetControllerName (
+                         DriverHealthInfo[Index].DriverHealthHandle,
+                         DriverHealthInfo[Index].ControllerHandle,
+                         DriverHealthInfo[Index].ChildHandle
+                         );
+      DEBUG ((
+        EFI_D_INFO,
+        "%02d: %s - %s\n",
+        Index,
+        ControllerName,
+        mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus]
+        ));
+      if (ControllerName != NULL) {
+        FreePool (ControllerName);
+      }
+    }
+    EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
+    );
+
+  if (RebootRequired) {
+    DEBUG ((EFI_D_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n"));
+    gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
+  }
+}
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c b/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c
new file mode 100644 (file)
index 0000000..6a770f9
--- /dev/null
@@ -0,0 +1,1101 @@
+/** @file
+  Hotkey library functions.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+//
+// Lock for linked list
+//
+EFI_LOCK                     mBmHotkeyLock            = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
+LIST_ENTRY                   mBmHotkeyList            = INITIALIZE_LIST_HEAD_VARIABLE (mBmHotkeyList);
+EFI_EVENT                    mBmHotkeyTriggered       = NULL;
+BOOLEAN                      mBmHotkeyServiceStarted  = FALSE;
+UINTN                        mBmHotkeySupportCount    = 0;
+
+//
+// Set OptionNumber as unassigned value to indicate the option isn't initialized
+//
+EFI_BOOT_MANAGER_LOAD_OPTION mBmHotkeyBootOption      = { LoadOptionNumberUnassigned };
+
+EFI_BOOT_MANAGER_KEY_OPTION  *mBmContinueKeyOption    = NULL;
+VOID                         *mBmTxtInExRegistration  = NULL;
+
+/**
+
+  Check whether the input key option is valid.
+
+  @param   KeyOption               Input key option info.
+
+  @retval  TRUE               Input key option is valid.
+  @retval  FALSE              Input key option is not valid.
+**/
+BOOLEAN
+BmIsKeyOptionValid (
+  IN EFI_BOOT_MANAGER_KEY_OPTION     *KeyOption
+)
+{
+  UINT16   OptionName[sizeof (L"Boot####")];
+  UINT8    *BootOption;
+  UINTN    BootOptionSize;
+  UINT32   Crc;
+
+  //
+  // Check whether corresponding Boot Option exist
+  //
+  UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", KeyOption->BootOption);
+  GetEfiGlobalVariable2 (OptionName, (VOID **) &BootOption, &BootOptionSize);
+
+  if (BootOption == NULL) {
+    return FALSE;
+  }
+
+  //
+  // Check CRC for Boot Option
+  //
+  gBS->CalculateCrc32 (BootOption, BootOptionSize, &Crc);
+  FreePool (BootOption);
+
+  return (BOOLEAN) (KeyOption->BootOptionCrc == Crc);
+}
+
+/**
+
+  Check whether the input variable is an key option variable.
+
+  @param   Name               Input variable name.
+  @param   Guid               Input variable guid.
+  @param   OptionNumber       The option number of this key option variable.
+
+  @retval  TRUE               Input variable is a key option variable.
+  @retval  FALSE              Input variable is not a key option variable.
+**/
+BOOLEAN
+BmIsKeyOptionVariable (
+  CHAR16        *Name,
+  EFI_GUID      *Guid,
+  UINT16        *OptionNumber
+  )
+{
+  UINTN         Index;
+  
+  if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) ||
+      (StrSize (Name) != sizeof (L"Key####")) ||
+      (StrnCmp (Name, L"Key", 3) != 0)
+     ) {
+    return FALSE;
+  }
+
+  *OptionNumber = 0;
+  for (Index = 3; Index < 7; Index++) {
+    if ((Name[Index] >= L'0') && (Name[Index] <= L'9')) {
+      *OptionNumber = *OptionNumber * 16 + Name[Index] - L'0';
+    } else if ((Name[Index] >= L'A') && (Name[Index] <= L'F')) {
+      *OptionNumber = *OptionNumber * 16 + Name[Index] - L'A' + 10;
+    } else {
+      return FALSE;
+    }
+  }
+
+  return TRUE;
+}
+
+/**
+  Return the buffer size of the EFI_BOOT_MANAGER_KEY_OPTION data.
+
+  @param   KeyOption            The input key option info.
+
+  @retval  The buffer size of the key option data.
+**/
+UINTN
+BmSizeOfKeyOption (
+  EFI_BOOT_MANAGER_KEY_OPTION  *KeyOption
+  )
+{
+  return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys)
+    + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY);
+}
+
+/**
+  Return the array of key options.
+
+  @param Count  Return the number of key options.
+
+  @retval NULL  No key option.
+  @retval Other Pointer to the key options.
+**/
+EFI_BOOT_MANAGER_KEY_OPTION *
+BmGetKeyOptions (
+  OUT UINTN     *Count
+  )
+{
+  EFI_STATUS                  Status;
+  UINTN                       Index;
+  CHAR16                      *Name;
+  EFI_GUID                    Guid;
+  UINTN                       NameSize;
+  UINTN                       NewNameSize;
+  EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
+  EFI_BOOT_MANAGER_KEY_OPTION *KeyOption;
+  UINT16                      OptionNumber;
+
+  if (Count == NULL) {
+    return NULL;
+  }
+
+  *Count     = 0;
+  KeyOptions = NULL;
+
+  NameSize = sizeof (CHAR16);
+  Name     = AllocateZeroPool (NameSize);
+  ASSERT (Name != NULL);
+  while (TRUE) {
+    NewNameSize = NameSize;
+    Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+      Name = ReallocatePool (NameSize, NewNameSize, Name);
+      ASSERT (Name != NULL);
+      Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
+      NameSize = NewNameSize;
+    }
+
+    if (Status == EFI_NOT_FOUND) {
+      break;
+    }
+    ASSERT_EFI_ERROR (Status);
+
+    if (BmIsKeyOptionVariable (Name ,&Guid, &OptionNumber)) {
+      GetEfiGlobalVariable2 (Name, (VOID**) &KeyOption, NULL);
+      ASSERT (KeyOption != NULL);
+      if (BmIsKeyOptionValid (KeyOption)) {
+        KeyOptions = ReallocatePool (
+                       *Count * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
+                       (*Count + 1) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
+                       KeyOptions
+                       );
+        ASSERT (KeyOptions != NULL);
+        //
+        // Insert the key option in order
+        //
+        for (Index = 0; Index < *Count; Index++) {
+          if (OptionNumber < KeyOptions[Index].OptionNumber) {
+            break;
+          }
+        }
+        CopyMem (&KeyOptions[Index + 1], &KeyOptions[Index], (*Count - Index) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+        CopyMem (&KeyOptions[Index], KeyOption, BmSizeOfKeyOption (KeyOption));
+        KeyOptions[Index].OptionNumber = OptionNumber;
+        (*Count)++;
+      }
+      FreePool (KeyOption);
+    }
+  }
+
+  FreePool (Name);
+
+  return KeyOptions;
+}
+
+/**
+  Callback function for event.
+  
+  @param    Event          Event for this callback function.
+  @param    Context        Context pass to this function.
+**/
+VOID
+EFIAPI
+BmEmptyFunction (
+  IN EFI_EVENT                Event,
+  IN VOID                     *Context
+  )
+{
+}
+
+/**
+  Check whether the bit is set in the value.
+
+  @param   Value            The value need to be check.
+  @param   Bit              The bit filed need to be check.
+
+  @retval  TRUE             The bit is set.
+  @retval  FALSE            The bit is not set.
+**/
+BOOLEAN
+BmBitSet (
+  IN UINT32   Value,
+  IN UINT32   Bit
+  )
+{
+  return (BOOLEAN) ((Value & Bit) != 0);
+}
+
+/**
+  Initialize the KeyData and Key[] in the EFI_BOOT_MANAGER_KEY_OPTION.
+
+  @param  Modifier   Input key info.
+  @param  Args       Va_list info.
+  @param  KeyOption  Key info which need to update.
+
+  @retval  EFI_SUCCESS             Succeed to initialize the KeyData and Key[].
+  @return  EFI_INVALID_PARAMETER   Input parameter error.
+**/
+EFI_STATUS
+BmInitializeKeyFields (
+  IN UINT32                       Modifier,
+  IN VA_LIST                      Args,
+  OUT EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
+  )
+{
+  EFI_INPUT_KEY                   *Key;
+
+  if (KeyOption == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Key = NULL;
+  while (KeyOption->KeyData.Options.InputKeyCount < sizeof (KeyOption->Keys) / sizeof (KeyOption->Keys[0])) {
+    Key = VA_ARG (Args, EFI_INPUT_KEY *);
+    if (Key == NULL) {
+      break;
+    }
+    CopyMem (
+      &KeyOption->Keys[KeyOption->KeyData.Options.InputKeyCount],
+      Key,
+      sizeof (EFI_INPUT_KEY)
+      );
+    KeyOption->KeyData.Options.InputKeyCount++;
+  }
+
+  if (Key != NULL) {
+    //
+    // Too many keys
+    //
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((Modifier & ~(EFI_BOOT_MANAGER_SHIFT_PRESSED
+                 | EFI_BOOT_MANAGER_CONTROL_PRESSED
+                 | EFI_BOOT_MANAGER_ALT_PRESSED
+                 | EFI_BOOT_MANAGER_LOGO_PRESSED
+                 | EFI_BOOT_MANAGER_MENU_KEY_PRESSED
+                 | EFI_BOOT_MANAGER_SYS_REQ_PRESSED
+                 )) != 0) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SHIFT_PRESSED)) {
+    KeyOption->KeyData.Options.ShiftPressed = 1;
+  }
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_CONTROL_PRESSED)) {
+    KeyOption->KeyData.Options.ControlPressed = 1;
+  }
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_ALT_PRESSED)) {
+    KeyOption->KeyData.Options.AltPressed = 1;
+  }
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_LOGO_PRESSED)) {
+    KeyOption->KeyData.Options.LogoPressed = 1;
+  }
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_MENU_KEY_PRESSED)) {
+    KeyOption->KeyData.Options.MenuPressed = 1;
+  }
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SYS_REQ_PRESSED)) {
+    KeyOption->KeyData.Options.SysReqPressed = 1;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Try to boot the boot option triggered by hot key.
+**/
+VOID
+EFIAPI
+EfiBootManagerHotkeyBoot (
+  VOID
+  )
+{
+  if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
+    EfiBootManagerBoot (&mBmHotkeyBootOption);
+    EfiBootManagerFreeLoadOption (&mBmHotkeyBootOption);
+    mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
+  }
+}
+
+/**
+  This is the common notification function for HotKeys, it will be registered
+  with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.
+
+  @param KeyData         A pointer to a buffer that is filled in with the keystroke
+                         information for the key that was pressed.
+
+  @retval  EFI_SUCCESS   KeyData is successfully processed.
+  @return  EFI_NOT_FOUND Fail to find boot option variable.
+**/
+EFI_STATUS
+EFIAPI
+BmHotkeyCallback (
+  IN EFI_KEY_DATA     *KeyData
+)
+{
+  LIST_ENTRY                    *Link;
+  BM_HOTKEY                     *Hotkey;
+  CHAR16                        OptionName[sizeof ("Boot####")];
+  EFI_STATUS                    Status;
+  EFI_KEY_DATA                  *HotkeyData;
+
+  if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
+    //
+    // Do not process sequential hotkey stroke until the current boot option returns
+    //
+    return EFI_SUCCESS;
+  }
+
+  DEBUG ((EFI_D_INFO, "[Bds]BmHotkeyCallback: %04x:%04x\n", KeyData->Key.ScanCode, KeyData->Key.UnicodeChar));
+
+  EfiAcquireLock (&mBmHotkeyLock);
+  for ( Link = GetFirstNode (&mBmHotkeyList)
+      ; !IsNull (&mBmHotkeyList, Link)
+      ; Link = GetNextNode (&mBmHotkeyList, Link)
+      ) {
+    Hotkey = BM_HOTKEY_FROM_LINK (Link);
+
+    //
+    // Is this Key Stroke we are waiting for?
+    //
+    ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0])));
+    HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];
+    if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&
+        (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&
+        (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ? 
+          (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE
+        )
+       ) {
+
+      //
+      // Receive an expecting key stroke, transit to next waiting state
+      //
+      Hotkey->WaitingKey++;
+
+      if (Hotkey->WaitingKey == Hotkey->CodeCount) {
+        //
+        // Reset to initial waiting state
+        //
+        Hotkey->WaitingKey = 0;
+        //
+        // Received the whole key stroke sequence
+        //
+        Status = gBS->SignalEvent (mBmHotkeyTriggered);
+        ASSERT_EFI_ERROR (Status);
+
+        if (!Hotkey->IsContinue) {
+          //
+          // Launch its BootOption
+          //
+          UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", Hotkey->BootOption);
+          Status = EfiBootManagerVariableToLoadOption (OptionName, &mBmHotkeyBootOption);
+          DEBUG ((EFI_D_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status));
+          if (EFI_ERROR (Status)) {
+            mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
+          }
+        } else {
+          DEBUG ((EFI_D_INFO, "[Bds]Continue key pressed!\n"));
+        }
+      }
+    } else {
+      //
+      // Receive an unexpected key stroke, reset to initial waiting state
+      //
+      Hotkey->WaitingKey = 0;
+    }
+
+  }
+  EfiReleaseLock (&mBmHotkeyLock);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Unregister hotkey notify list.
+
+  @param    Hotkey                Hotkey list.
+
+  @retval   EFI_SUCCESS           Unregister hotkey notify success.
+  @retval   Others                Unregister hotkey notify failed.
+**/
+EFI_STATUS
+BmUnregisterHotkeyNotify (
+  IN BM_HOTKEY                          *Hotkey
+  )
+{
+  EFI_STATUS                            Status;
+  UINTN                                 Index;
+  UINTN                                 KeyIndex;
+  EFI_HANDLE                            *Handles;
+  UINTN                                 HandleCount;
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL     *TxtInEx;
+  VOID                                  *NotifyHandle;
+
+  gBS->LocateHandleBuffer (
+          ByProtocol,
+          &gEfiSimpleTextInputExProtocolGuid,
+          NULL,
+          &HandleCount,
+          &Handles
+          );
+  for (Index = 0; Index < HandleCount; Index++) {
+    Status = gBS->HandleProtocol (Handles[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
+    ASSERT_EFI_ERROR (Status);
+    for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
+      Status = TxtInEx->RegisterKeyNotify (
+                          TxtInEx,
+                          &Hotkey->KeyData[KeyIndex],
+                          BmHotkeyCallback,
+                          &NotifyHandle
+                          );
+      if (!EFI_ERROR (Status)) {
+        Status = TxtInEx->UnregisterKeyNotify (TxtInEx, NotifyHandle);
+        DEBUG ((EFI_D_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status));
+      }
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Register hotkey notify list.
+
+  @param    TxtInEx               Pointer to EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol.
+  @param    Hotkey                Hotkey list.
+
+  @retval   EFI_SUCCESS           Register hotkey notify success.
+  @retval   Others                Register hotkey notify failed.
+**/
+EFI_STATUS
+BmRegisterHotkeyNotify (
+  IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx,
+  IN BM_HOTKEY                          *Hotkey
+  )
+{
+  EFI_STATUS                            Status;
+  UINTN                                 Index;
+  VOID                                  *NotifyHandle;
+
+  for (Index = 0; Index < Hotkey->CodeCount; Index++) {
+    Status = TxtInEx->RegisterKeyNotify (
+                        TxtInEx,
+                        &Hotkey->KeyData[Index],
+                        BmHotkeyCallback,
+                        &NotifyHandle
+                        );
+    DEBUG ((
+      EFI_D_INFO,
+      "[Bds]RegisterKeyNotify: %04x/%04x %08x/%02x %r\n",
+      Hotkey->KeyData[Index].Key.ScanCode,
+      Hotkey->KeyData[Index].Key.UnicodeChar,
+      Hotkey->KeyData[Index].KeyState.KeyShiftState,
+      Hotkey->KeyData[Index].KeyState.KeyToggleState,
+      Status
+      ));
+    if (EFI_ERROR (Status)) {
+      //
+      // some of the hotkey registry failed
+      // do not unregister all in case we have both CTRL-ALT-P and CTRL-ALT-P-R
+      //
+      break;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Generate key shift state base on the input key option info.
+
+  @param    Depth                 Which key is checked.
+  @param    KeyOption             Input key option info.
+  @param    KeyShiftState         Input key shift state.
+  @param    KeyShiftStates        Return possible key shift state array.
+  @param    KeyShiftStateCount    Possible key shift state count.
+**/
+VOID
+BmGenerateKeyShiftState (
+  IN UINTN                             Depth,
+  IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption,
+  IN UINT32                            KeyShiftState,
+  IN UINT32                            *KeyShiftStates,
+  IN UINTN                             *KeyShiftStateCount
+  )
+{
+  switch (Depth) {
+  case 0:
+    if (KeyOption->KeyData.Options.ShiftPressed) {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_SHIFT_PRESSED,  KeyShiftStates, KeyShiftStateCount);
+    } else {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+    }
+    break;
+
+  case 1:
+    if (KeyOption->KeyData.Options.ControlPressed) {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_CONTROL_PRESSED,  KeyShiftStates, KeyShiftStateCount);
+    } else {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+    }
+    break;
+
+  case 2:
+    if (KeyOption->KeyData.Options.AltPressed) {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount);
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_ALT_PRESSED,  KeyShiftStates, KeyShiftStateCount);
+    } else {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+    }
+    break;
+  case  3:
+    if (KeyOption->KeyData.Options.LogoPressed) {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_LOGO_PRESSED,  KeyShiftStates, KeyShiftStateCount);
+    } else {
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+    }
+    break;
+  case 4:
+    if (KeyOption->KeyData.Options.MenuPressed) {
+      KeyShiftState |= EFI_MENU_KEY_PRESSED;
+    }
+    if (KeyOption->KeyData.Options.SysReqPressed) {
+      KeyShiftState |= EFI_SYS_REQ_PRESSED;
+    }
+    KeyShiftStates[*KeyShiftStateCount] = KeyShiftState;
+    (*KeyShiftStateCount)++;
+    break;
+  }
+}
+
+/**
+  Add it to hot key database, register it to existing TxtInEx.
+  New TxtInEx will be automatically registered with all the hot key in dababase
+
+  @param    KeyOption  Input key option info.
+**/
+EFI_STATUS
+BmProcessKeyOption (
+  IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption
+  )
+{
+  EFI_STATUS                           Status;
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL    *TxtInEx;
+  EFI_HANDLE                           *Handles;
+  UINTN                                HandleCount;
+  UINTN                                HandleIndex;
+  UINTN                                Index;
+  BM_HOTKEY                            *Hotkey;
+  UINTN                                KeyIndex;
+  //
+  // 16 is enough to enumerate all the possible combination of LEFT_XXX and RIGHT_XXX
+  //
+  UINT32                               KeyShiftStates[16];
+  UINTN                                KeyShiftStateCount;
+
+  if (KeyOption->KeyData.Options.InputKeyCount > mBmHotkeySupportCount) {
+    return EFI_UNSUPPORTED;
+  }
+
+  KeyShiftStateCount = 0;
+  BmGenerateKeyShiftState (0, KeyOption, EFI_SHIFT_STATE_VALID, KeyShiftStates, &KeyShiftStateCount);
+  ASSERT (KeyShiftStateCount <= sizeof (KeyShiftStates) / sizeof (KeyShiftStates[0]));
+
+  EfiAcquireLock (&mBmHotkeyLock);
+
+  for (Index = 0; Index < KeyShiftStateCount; Index++) {
+    Hotkey = AllocateZeroPool (sizeof (BM_HOTKEY));
+    ASSERT (Hotkey != NULL);
+
+    Hotkey->Signature  = BM_HOTKEY_SIGNATURE;
+    Hotkey->BootOption = KeyOption->BootOption;
+    Hotkey->IsContinue = (BOOLEAN) (KeyOption == mBmContinueKeyOption);
+    Hotkey->CodeCount  = (UINT8) KeyOption->KeyData.Options.InputKeyCount;
+
+    for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
+      CopyMem (&Hotkey->KeyData[KeyIndex].Key, &KeyOption->Keys[KeyIndex], sizeof (EFI_INPUT_KEY));
+      Hotkey->KeyData[KeyIndex].KeyState.KeyShiftState = KeyShiftStates[Index];
+    }
+    InsertTailList (&mBmHotkeyList, &Hotkey->Link);
+
+    gBS->LocateHandleBuffer (
+            ByProtocol,
+            &gEfiSimpleTextInputExProtocolGuid,
+            NULL,
+            &HandleCount,
+            &Handles
+            );
+    for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
+      Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
+      ASSERT_EFI_ERROR (Status);
+      BmRegisterHotkeyNotify (TxtInEx, Hotkey);
+    }
+  }
+
+  EfiReleaseLock (&mBmHotkeyLock);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Callback function for SimpleTextInEx protocol install events
+
+  @param Event           the event that is signaled.
+  @param Context         not used here.
+
+**/
+VOID
+EFIAPI
+BmTxtInExCallback (
+  IN EFI_EVENT    Event,
+  IN VOID         *Context
+  )
+{
+  EFI_STATUS                         Status;
+  UINTN                              BufferSize;
+  EFI_HANDLE                         Handle;
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx;
+  LIST_ENTRY                         *Link;
+
+  while (TRUE) {
+    BufferSize = sizeof (EFI_HANDLE);
+    Status = gBS->LocateHandle (
+                    ByRegisterNotify,
+                    NULL,
+                    mBmTxtInExRegistration,
+                    &BufferSize,
+                    &Handle
+                    );
+    if (EFI_ERROR (Status)) {
+      //
+      // If no more notification events exist
+      //
+      return ;
+    }
+
+    Status = gBS->HandleProtocol (
+                    Handle,
+                    &gEfiSimpleTextInputExProtocolGuid,
+                    (VOID **) &TxtInEx
+                    );
+    ASSERT_EFI_ERROR (Status);
+
+    //
+    // Register the hot key notification for the existing items in the list
+    //
+    EfiAcquireLock (&mBmHotkeyLock);
+    for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); Link = GetNextNode (&mBmHotkeyList, Link)) {
+      BmRegisterHotkeyNotify (TxtInEx, BM_HOTKEY_FROM_LINK (Link));
+    }
+    EfiReleaseLock (&mBmHotkeyLock);
+  }
+}
+
+/**
+  Free the key options returned from BmGetKeyOptions.
+
+  @param KeyOptions     Pointer to the key options.
+  @param KeyOptionCount Number of the key options.
+
+  @retval EFI_SUCCESS   The key options are freed.
+  @retval EFI_NOT_FOUND KeyOptions is NULL.
+**/
+EFI_STATUS
+BmFreeKeyOptions (
+  IN EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions,
+  IN UINTN                          KeyOptionCount
+  )
+{
+  if (KeyOptions != NULL) {
+    FreePool (KeyOptions);
+    return EFI_SUCCESS;
+  } else {
+    return EFI_NOT_FOUND;
+  }
+}
+
+/**
+  Register the key option to exit the waiting of the Boot Manager timeout.
+  Platform should ensure that the continue key option isn't conflict with
+  other boot key options.
+
+  @param Modifier     Key shift state.
+  @param  ...         Parameter list of pointer of EFI_INPUT_KEY.
+
+  @retval EFI_SUCCESS         Successfully register the continue key option.
+  @retval EFI_ALREADY_STARTED The continue key option is already registered.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerRegisterContinueKeyOption (
+  IN UINT32           Modifier,
+  ...
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_BOOT_MANAGER_KEY_OPTION  KeyOption;
+  VA_LIST                      Args;
+  
+  if (mBmContinueKeyOption != NULL) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+  VA_START (Args, Modifier);
+  Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
+  VA_END (Args);
+
+  if (!EFI_ERROR (Status)) {
+    mBmContinueKeyOption = AllocateCopyPool (sizeof (EFI_BOOT_MANAGER_KEY_OPTION), &KeyOption);
+    ASSERT (mBmContinueKeyOption != NULL);
+    if (mBmHotkeyServiceStarted) {
+      BmProcessKeyOption (mBmContinueKeyOption);
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Stop the hotkey processing.
+  
+  @param    Event          Event pointer related to hotkey service.
+  @param    Context        Context pass to this function.
+**/
+VOID
+EFIAPI
+BmStopHotkeyService (
+  IN EFI_EVENT    Event,
+  IN VOID         *Context
+  )
+{
+  LIST_ENTRY            *Link;
+  BM_HOTKEY             *Hotkey;
+
+  DEBUG ((EFI_D_INFO, "[Bds]Stop Hotkey Service!\n"));
+  gBS->CloseEvent (Event);
+
+  EfiAcquireLock (&mBmHotkeyLock);
+  for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
+    Hotkey = BM_HOTKEY_FROM_LINK (Link);
+    BmUnregisterHotkeyNotify (Hotkey);
+    Link   = RemoveEntryList (Link);
+    FreePool (Hotkey);
+  }
+  EfiReleaseLock (&mBmHotkeyLock);
+}
+
+/**
+  Start the hot key service so that the key press can trigger the boot option.
+
+  @param HotkeyTriggered  Return the waitable event and it will be signaled 
+                          when a valid hot key is pressed.
+
+  @retval EFI_SUCCESS     The hot key service is started.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerStartHotkeyService (
+  IN EFI_EVENT                 *HotkeyTriggered
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_BOOT_MANAGER_KEY_OPTION  *KeyOptions;
+  UINTN                        KeyOptionCount;
+  UINTN                        Index;
+  EFI_EVENT                    Event;
+  UINT32                       *BootOptionSupport;
+
+  Status = GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **) &BootOptionSupport, NULL);
+  ASSERT (BootOptionSupport != NULL);
+
+  if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY)  != 0) {
+    mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT));
+  }
+  FreePool (BootOptionSupport);
+
+  if (mBmHotkeySupportCount == 0) {
+    DEBUG ((EFI_D_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n"));
+    return EFI_UNSUPPORTED;
+  }
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_WAIT,
+                  TPL_CALLBACK,
+                  BmEmptyFunction,
+                  NULL,
+                  &mBmHotkeyTriggered
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  if (HotkeyTriggered != NULL) {
+    *HotkeyTriggered = mBmHotkeyTriggered;
+  }
+
+  KeyOptions = BmGetKeyOptions (&KeyOptionCount);
+  for (Index = 0; Index < KeyOptionCount; Index ++) {
+    BmProcessKeyOption (&KeyOptions[Index]);
+  }
+  BmFreeKeyOptions (KeyOptions, KeyOptionCount);
+
+  if (mBmContinueKeyOption != NULL) {
+    BmProcessKeyOption (mBmContinueKeyOption);
+  }
+
+  EfiCreateProtocolNotifyEvent (
+    &gEfiSimpleTextInputExProtocolGuid,
+    TPL_CALLBACK,
+    BmTxtInExCallback,
+    NULL,
+    &mBmTxtInExRegistration
+    );
+
+  Status = EfiCreateEventReadyToBootEx (
+             TPL_CALLBACK,
+             BmStopHotkeyService,
+             NULL,
+             &Event
+             );
+  ASSERT_EFI_ERROR (Status);
+
+
+  mBmHotkeyServiceStarted = TRUE;
+  return Status;
+}
+
+/**
+  Add the key option.
+  It adds the key option variable and the key option takes affect immediately.
+
+  @param AddedOption      Return the added key option.
+  @param BootOptionNumber The boot option number for the key option.
+  @param Modifier         Key shift state.
+  @param ...              Parameter list of pointer of EFI_INPUT_KEY.
+
+  @retval EFI_SUCCESS         The key option is added.
+  @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerAddKeyOptionVariable (
+  OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption,   OPTIONAL
+  IN UINT16                       BootOptionNumber,
+  IN UINT32                       Modifier,
+  ...
+  )
+{
+  EFI_STATUS                     Status;
+  VA_LIST                        Args;
+  VOID                           *BootOption;
+  UINTN                          BootOptionSize;
+  CHAR16                         BootOptionName[sizeof (L"Boot####")];
+  EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;
+  EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;
+  UINTN                          KeyOptionCount;
+  UINTN                          Index;
+  UINTN                          KeyOptionNumber;
+  CHAR16                         KeyOptionName[sizeof (L"Key####")];
+
+  UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootOptionNumber);
+  GetEfiGlobalVariable2 (BootOptionName, &BootOption, &BootOptionSize);
+
+  if (BootOption == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+  KeyOption.BootOption = BootOptionNumber;
+  Status = gBS->CalculateCrc32 (BootOption, BootOptionSize, &KeyOption.BootOptionCrc);
+  ASSERT_EFI_ERROR (Status);
+  FreePool (BootOption);
+
+  VA_START (Args, Modifier);
+  Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
+  VA_END (Args);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  KeyOptionNumber = LoadOptionNumberUnassigned;
+  //
+  // Check if the hot key sequence was defined already
+  //
+  KeyOptions = BmGetKeyOptions (&KeyOptionCount);
+  for (Index = 0; Index < KeyOptionCount; Index++) {
+    if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
+      (CompareMem (KeyOptions[Index].Keys, KeyOption.Keys, KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)) {
+      break;
+    }
+
+    if ((KeyOptionNumber == LoadOptionNumberUnassigned) &&
+        (KeyOptions[Index].OptionNumber > Index)
+       ){
+      KeyOptionNumber = Index;
+    }
+  }
+  BmFreeKeyOptions (KeyOptions, KeyOptionCount);
+
+  if (Index < KeyOptionCount) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  if (KeyOptionNumber == LoadOptionNumberUnassigned) {
+    KeyOptionNumber = KeyOptionCount;
+  }
+
+  UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber);
+
+  Status = gRT->SetVariable (
+                  KeyOptionName,
+                  &gEfiGlobalVariableGuid,
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                  BmSizeOfKeyOption (&KeyOption),
+                  &KeyOption
+                  );
+  if (!EFI_ERROR (Status)) {
+    //
+    // Return the Key Option in case needed by caller
+    //
+    if (AddedOption != NULL) {
+      CopyMem (AddedOption, &KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+    }
+
+    //
+    // Register the newly added hot key
+    // Calling this function before EfiBootManagerStartHotkeyService doesn't
+    // need to call BmProcessKeyOption
+    //
+    if (mBmHotkeyServiceStarted) {
+      BmProcessKeyOption (&KeyOption);
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Delete the Key Option variable and unregister the hot key
+
+  @param DeletedOption  Return the deleted key options.
+  @param Modifier       Key shift state.
+  @param ...            Parameter list of pointer of EFI_INPUT_KEY.
+
+  @retval EFI_SUCCESS   The key option is deleted.
+  @retval EFI_NOT_FOUND The key option cannot be found.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerDeleteKeyOptionVariable (
+  IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL
+  IN UINT32                      Modifier,
+  ...
+  )
+{
+  EFI_STATUS                     Status;
+  UINTN                          Index;
+  VA_LIST                        Args;
+  EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;
+  EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;
+  UINTN                          KeyOptionCount;
+  LIST_ENTRY                     *Link;
+  BM_HOTKEY                      *Hotkey;
+  UINT32                         ShiftState;
+  BOOLEAN                        Match;
+  CHAR16                         KeyOptionName[sizeof (L"Key####")];
+
+  ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+  VA_START (Args, Modifier);
+  Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
+  VA_END (Args);
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  EfiAcquireLock (&mBmHotkeyLock);
+  //
+  // Delete the key option from active hot key list
+  // Could have multiple entries when modifier isn't 0 because we map the ShiftPressed to RIGHT_SHIFT and RIGHT_SHIFT
+  //
+  for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
+    Hotkey = BM_HOTKEY_FROM_LINK (Link);
+    Match  = (BOOLEAN) (Hotkey->CodeCount == KeyOption.KeyData.Options.InputKeyCount);
+
+    for (Index = 0; Match && (Index < Hotkey->CodeCount); Index++) {
+      ShiftState = Hotkey->KeyData[Index].KeyState.KeyShiftState;
+      if (
+        (BmBitSet (ShiftState, EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED) != KeyOption.KeyData.Options.ShiftPressed) ||
+        (BmBitSet (ShiftState, EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) != KeyOption.KeyData.Options.ControlPressed) ||
+        (BmBitSet (ShiftState, EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) != KeyOption.KeyData.Options.AltPressed) ||
+        (BmBitSet (ShiftState, EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED) != KeyOption.KeyData.Options.LogoPressed) ||
+        (BmBitSet (ShiftState, EFI_MENU_KEY_PRESSED) != KeyOption.KeyData.Options.MenuPressed) ||
+        (BmBitSet (ShiftState, EFI_SYS_REQ_PRESSED) != KeyOption.KeyData.Options.SysReqPressed) ||
+        (CompareMem (&Hotkey->KeyData[Index].Key, &KeyOption.Keys[Index], sizeof (EFI_INPUT_KEY)) != 0)
+        ) {
+        //
+        // Break when any field doesn't match
+        //
+        Match = FALSE;
+        break;
+      }
+    }
+
+    if (Match) {
+      Link = RemoveEntryList (Link);
+      FreePool (Hotkey);
+    } else {
+      Link = GetNextNode (&mBmHotkeyList, Link);
+    }
+  }
+
+  //
+  // Delete the key option from the variable
+  //
+  Status     = EFI_NOT_FOUND;
+  KeyOptions = BmGetKeyOptions (&KeyOptionCount);
+  for (Index = 0; Index < KeyOptionCount; Index++) {
+    if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
+        (CompareMem (
+           KeyOptions[Index].Keys, KeyOption.Keys,
+           KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)
+       ) {
+      UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptions[Index].OptionNumber);
+      Status = gRT->SetVariable (
+                 KeyOptionName,
+                 &gEfiGlobalVariableGuid,
+                 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                 0,
+                 NULL
+                 );
+      //
+      // Return the deleted key option in case needed by caller
+      //
+      if (DeletedOption != NULL) {
+        CopyMem (DeletedOption, &KeyOptions[Index], sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+      }
+      break;
+    }
+  }
+  BmFreeKeyOptions (KeyOptions, KeyOptionCount);
+
+  EfiReleaseLock (&mBmHotkeyLock);
+
+  return Status;
+}
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
new file mode 100644 (file)
index 0000000..fc3e29a
--- /dev/null
@@ -0,0 +1,982 @@
+/** @file
+  Load option library functions which relate with creating and processing load options.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+/**
+  Get the Option Number that wasn't used.
+
+  @param  OrderVariableName   Could be L"BootOrder" or L"DriverOrder".
+  @param  FreeOptionNumber    To receive the minimal free option number.
+
+  @retval EFI_SUCCESS           The option number is found
+  @retval EFI_OUT_OF_RESOURCES  There is no free option number that can be used.
+  @retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL
+
+**/
+EFI_STATUS
+BmGetFreeOptionNumber (
+  IN  CHAR16    *OrderVariableName,
+  OUT UINT16    *FreeOptionNumber
+  )
+{
+  
+  UINTN         OptionNumber;
+  UINTN         Index;
+  UINT16        *OptionOrder;
+  UINTN         OptionOrderSize;
+  UINT16        *BootNext;
+
+  if (FreeOptionNumber == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  GetEfiGlobalVariable2 (OrderVariableName, (VOID **) &OptionOrder, &OptionOrderSize);
+  BootNext = NULL;
+  if (*OrderVariableName == L'B') {
+    GetEfiGlobalVariable2 (L"BootNext", (VOID**) &BootNext, NULL);
+  }
+
+  for (OptionNumber = 0; 
+       OptionNumber < OptionOrderSize / sizeof (UINT16)
+                    + ((BootNext != NULL) ? 1 : 0); 
+       OptionNumber++
+       ) {
+    //
+    // Search in OptionOrder whether the OptionNumber exists
+    //
+    for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
+      if (OptionNumber == OptionOrder[Index]) {
+        break;
+      }
+    }
+
+    //
+    // We didn't find it in the ****Order array and it doesn't equal to BootNext 
+    // Otherwise, OptionNumber equals to OptionOrderSize / sizeof (UINT16) + 1
+    //
+    if ((Index == OptionOrderSize / sizeof (UINT16)) && 
+        ((BootNext == NULL) || (OptionNumber != *BootNext))
+        ) {
+      break;
+    }
+  }
+  if (OptionOrder != NULL) {
+    FreePool (OptionOrder);
+  }
+
+  if (BootNext != NULL) {
+    FreePool (BootNext);
+  }
+
+  //
+  // When BootOrder & BootNext conver all numbers in the range [0 ... 0xffff],
+  //   OptionNumber equals to 0x10000 which is not valid.
+  //
+  ASSERT (OptionNumber <= 0x10000);
+  if (OptionNumber == 0x10000) {
+    return EFI_OUT_OF_RESOURCES;
+  } else {
+    *FreeOptionNumber = (UINT16) OptionNumber;
+    return EFI_SUCCESS;
+  }
+}
+
+/**
+  Update order variable .
+
+  @param  OptionOrderName     Order variable name which need to be updated.
+  @param  OptionNumber        Option number for the new option.
+  @param  Position            Position of the new load option to put in the ****Order variable.
+
+  @retval EFI_SUCCESS           The boot#### or driver#### have been successfully registered.
+  @retval EFI_ALREADY_STARTED   The option number of Option is being used already.
+  @retval EFI_STATUS            Return the status of gRT->SetVariable ().
+
+**/
+EFI_STATUS
+BmAddOptionNumberToOrderVariable (
+  IN CHAR16               *OptionOrderName,
+  IN UINT16               OptionNumber,
+  IN UINTN                Position
+  )
+{
+  EFI_STATUS              Status;
+  UINTN                   Index;
+  UINT16                  *OptionOrder;
+  UINT16                  *NewOptionOrder;
+  UINTN                   OptionOrderSize;
+  //
+  // Update the option order variable
+  //
+  GetEfiGlobalVariable2 (OptionOrderName, (VOID **) &OptionOrder, &OptionOrderSize);
+
+  Status = EFI_SUCCESS;
+  for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
+    if (OptionOrder[Index] == OptionNumber) {
+      Status = EFI_ALREADY_STARTED;
+      break;
+    }
+  }
+
+  if (!EFI_ERROR (Status)) {
+    Position       = MIN (Position, OptionOrderSize / sizeof (UINT16));
+
+    NewOptionOrder = AllocatePool (OptionOrderSize + sizeof (UINT16));
+    ASSERT (NewOptionOrder != NULL);
+    if (OptionOrderSize != 0) {
+      CopyMem (NewOptionOrder, OptionOrder, Position * sizeof (UINT16));
+      CopyMem (&NewOptionOrder[Position + 1], &OptionOrder[Position], OptionOrderSize - Position * sizeof (UINT16));
+    }
+    NewOptionOrder[Position] = OptionNumber;
+
+    Status = gRT->SetVariable (
+                    OptionOrderName,
+                    &gEfiGlobalVariableGuid,
+                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                    OptionOrderSize + sizeof (UINT16),
+                    NewOptionOrder
+                    );
+    FreePool (NewOptionOrder);
+  }
+
+  if (OptionOrder != NULL) {
+    FreePool (OptionOrder);
+  }
+
+  return Status;
+}
+
+/**
+  Create the Boot#### or Driver#### variable from the load option.
+  
+  @param  LoadOption      Pointer to the load option.
+
+  @retval EFI_SUCCESS     The variable was created.
+  @retval Others          Error status returned by RT->SetVariable.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerLoadOptionToVariable (
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION     *Option
+  )
+{
+  UINTN                            VariableSize;
+  UINT8                            *Variable;
+  UINT8                            *Ptr;
+  CHAR16                           OptionName[sizeof ("Driver####")];
+  CHAR16                           *Description;
+  CHAR16                           NullChar;
+
+  if ((Option->OptionNumber == LoadOptionNumberUnassigned) ||
+      (Option->FilePath == NULL) ||
+      (Option->OptionType >= LoadOptionTypeMax)
+     ) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Convert NULL description to empty description
+  //
+  NullChar    = L'\0';
+  Description = Option->Description;
+  if (Description == NULL) {
+    Description = &NullChar;
+  }
+
+  /*
+  UINT32                      Attributes;
+  UINT16                      FilePathListLength;
+  CHAR16                      Description[];
+  EFI_DEVICE_PATH_PROTOCOL    FilePathList[];
+  UINT8                       OptionalData[];
+TODO: FilePathList[] IS:
+A packed array of UEFI device paths.  The first element of the 
+array is a device path that describes the device and location of the 
+Image for this load option.  The FilePathList[0] is specific 
+to the device type.  Other device paths may optionally exist in the 
+FilePathList, but their usage is OSV specific. Each element 
+in the array is variable length, and ends at the device path end 
+structure.
+  */
+  VariableSize = sizeof (Option->Attributes)
+               + sizeof (UINT16)
+               + StrSize (Description)
+               + GetDevicePathSize (Option->FilePath)
+               + Option->OptionalDataSize;
+
+  Variable     = AllocatePool (VariableSize);
+  ASSERT (Variable != NULL);
+  
+  Ptr             = Variable;
+  *(UINT32 *) Ptr = Option->Attributes;
+  Ptr            += sizeof (Option->Attributes);
+  *(UINT16 *) Ptr = (UINT16) GetDevicePathSize (Option->FilePath);
+  Ptr            += sizeof (UINT16);
+  CopyMem (Ptr, Description, StrSize (Description));
+  Ptr            += StrSize (Description);
+  CopyMem (Ptr, Option->FilePath, GetDevicePathSize (Option->FilePath));
+  Ptr            += GetDevicePathSize (Option->FilePath);
+  CopyMem (Ptr, Option->OptionalData, Option->OptionalDataSize);
+
+  UnicodeSPrint (
+    OptionName,
+    sizeof (OptionName),
+    (Option->OptionType == LoadOptionTypeBoot) ? L"Boot%04x" : L"Driver%04x",
+    Option->OptionNumber
+    );
+
+  return gRT->SetVariable (
+                OptionName,
+                &gEfiGlobalVariableGuid,
+                EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                VariableSize,
+                Variable
+                );
+}
+
+/**
+  This function will register the new boot#### or driver#### option.
+  After the boot#### or driver#### updated, the BootOrder or DriverOrder will also be updated.
+
+  @param  Option            Pointer to load option to add.
+  @param  Position          Position of the new load option to put in the ****Order variable.
+
+  @retval EFI_SUCCESS           The boot#### or driver#### have been successfully registered.
+  @retval EFI_INVALID_PARAMETER The option number exceeds 0xFFFF.
+  @retval EFI_ALREADY_STARTED   The option number of Option is being used already.
+                                Note: this API only adds new load option, no replacement support.
+  @retval EFI_OUT_OF_RESOURCES  There is no free option number that can be used when the
+                                option number specified in the Option is LoadOptionNumberUnassigned.
+  @retval EFI_STATUS            Return the status of gRT->SetVariable ().
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerAddLoadOptionVariable (
+  IN EFI_BOOT_MANAGER_LOAD_OPTION *Option,
+  IN UINTN                        Position
+  )
+{
+  EFI_STATUS                      Status;
+  UINT16                          OptionNumber;
+
+  if (Option == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Get the free option number if the option number is unassigned
+  //
+  if (Option->OptionNumber == LoadOptionNumberUnassigned) {
+    Status = BmGetFreeOptionNumber (
+               Option->OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder",
+               &OptionNumber
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    Option->OptionNumber = OptionNumber;
+  }
+
+  if (Option->OptionNumber >= LoadOptionNumberMax) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status = BmAddOptionNumberToOrderVariable (
+             Option->OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder",
+             (UINT16) Option->OptionNumber,
+             Position
+             );
+  if (!EFI_ERROR (Status)) {
+    //
+    // Save the Boot#### or Driver#### variable
+    //
+    Status = EfiBootManagerLoadOptionToVariable (Option);
+    if (EFI_ERROR (Status)) {
+      //
+      // Remove the #### from *Order variable when the Boot####/Driver#### cannot be saved.
+      //
+      EfiBootManagerDeleteLoadOptionVariable (Option->OptionNumber, Option->OptionType);
+    }
+  }
+
+  return Status;
+}
+
+/**
+  Sort the load option. The DriverOrder or BootOrder will be re-created to 
+  reflect the new order.
+
+  @param OptionType             Load option type
+  @param CompareFunction        The comparator
+**/
+VOID
+EFIAPI
+EfiBootManagerSortLoadOptionVariable (
+  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE        OptionType,
+  SORT_COMPARE                             CompareFunction
+  )
+{
+  EFI_STATUS                     Status;
+  EFI_BOOT_MANAGER_LOAD_OPTION   *LoadOption;
+  UINTN                          LoadOptionCount;
+  UINTN                          Index;
+  UINT16                         *OptionOrder;
+
+  LoadOption = EfiBootManagerGetLoadOptions (&LoadOptionCount, OptionType);
+
+  //
+  // Insertion sort algorithm
+  //
+  PerformQuickSort (
+    LoadOption,
+    LoadOptionCount,
+    sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
+    CompareFunction
+    );
+
+  //
+  // Create new ****Order variable
+  //
+  OptionOrder = AllocatePool (LoadOptionCount * sizeof (UINT16));
+  ASSERT (OptionOrder != NULL);
+  for (Index = 0; Index < LoadOptionCount; Index++) {
+    OptionOrder[Index] = (UINT16) LoadOption[Index].OptionNumber;
+  }
+
+  Status = gRT->SetVariable (
+                  OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder",
+                  &gEfiGlobalVariableGuid,
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                  LoadOptionCount * sizeof (UINT16),
+                  OptionOrder
+                  );
+  //
+  // Changing the *Order content without increasing its size with current variable implementation shouldn't fail.
+  //
+  ASSERT_EFI_ERROR (Status);
+
+  FreePool (OptionOrder);
+  EfiBootManagerFreeLoadOptions (LoadOption, LoadOptionCount);
+}
+
+/**
+  Initialize a load option.
+
+  @param Option           Pointer to the load option to be initialized.
+  @param OptionNumber     Option number of the load option.
+  @param OptionType       Type of the load option.
+  @param Attributes       Attributes of the load option.
+  @param Description      Description of the load option.
+  @param FilePath         Device path of the load option.
+  @param OptionalData     Optional data of the load option.
+  @param OptionalDataSize Size of the optional data of the load option.
+
+  @retval EFI_SUCCESS           The load option was initialized successfully.
+  @retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerInitializeLoadOption (
+  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION   *Option,
+  IN  UINTN                             OptionNumber,
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,
+  IN  UINT32                            Attributes,
+  IN  CHAR16                            *Description,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  UINT8                             *OptionalData,   OPTIONAL
+  IN  UINT32                            OptionalDataSize
+  )
+{
+  if ((Option == NULL) || (Description == NULL) || (FilePath == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (((OptionalData != NULL) && (OptionalDataSize == 0)) ||
+      ((OptionalData == NULL) && (OptionalDataSize != 0))) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ZeroMem (Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+  Option->OptionNumber       = OptionNumber;
+  Option->OptionType         = OptionType;
+  Option->Attributes         = Attributes;
+  Option->Description        = AllocateCopyPool (StrSize (Description), Description);
+  Option->FilePath           = DuplicateDevicePath (FilePath);
+  if (OptionalData != NULL) {
+    Option->OptionalData     = AllocateCopyPool (OptionalDataSize, OptionalData);
+    Option->OptionalDataSize = OptionalDataSize;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Return the index of the load option in the load option array.
+
+  The function consider two load options are equal when the 
+  OptionType, Attributes, Description, FilePath and OptionalData are equal.
+
+  @param Key    Pointer to the load option to be found.
+  @param Array  Pointer to the array of load options to be found.
+  @param Count  Number of entries in the Array.
+
+  @retval -1          Key wasn't found in the Array.
+  @retval 0 ~ Count-1 The index of the Key in the Array.
+**/
+INTN
+BmFindLoadOption (
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
+  IN UINTN                              Count
+  )
+{
+  UINTN                             Index;
+
+  for (Index = 0; Index < Count; Index++) {
+    if ((Key->OptionType == Array[Index].OptionType) &&
+        (Key->Attributes == Array[Index].Attributes) &&
+        (StrCmp (Key->Description, Array[Index].Description) == 0) &&
+        (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
+        (Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
+        (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) {
+      return (INTN) Index;
+    }
+  }
+
+  return -1;
+}
+
+/**
+  Update the BootOrder or DriverOrder to delete OptionNumber .
+
+  @param  OptionOrderVariable  Order variable name which need to be updated.
+  @param  OptionNumber         Indicate the option number of load option
+
+  @retval EFI_NOT_FOUND         The load option cannot be found
+  @retval EFI_SUCCESS           The load option was deleted
+  @retval others                Status of RT->SetVariable()
+**/
+EFI_STATUS
+BmDeleteOptionVariable (
+  IN CHAR16                            *OptionOrderVariable,
+  IN UINT16                            OptionNumber
+  )
+{
+  UINT16           *OptionOrder;
+  UINTN            OptionOrderSize;
+  EFI_STATUS       Status;
+  UINTN            Index;
+
+  Status      = EFI_NOT_FOUND;
+  GetEfiGlobalVariable2 (OptionOrderVariable, (VOID **) &OptionOrder, &OptionOrderSize);
+  for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
+    if (OptionOrder[Index] == OptionNumber) {
+      OptionOrderSize -= sizeof (UINT16);
+      CopyMem (&OptionOrder[Index], &OptionOrder[Index + 1], OptionOrderSize - Index * sizeof (UINT16));
+      Status = gRT->SetVariable (
+                      OptionOrderVariable,
+                      &gEfiGlobalVariableGuid,
+                      EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+                      OptionOrderSize,
+                      OptionOrder
+                      );
+      break;
+    }
+  }
+  if (OptionOrder != NULL) {
+    FreePool (OptionOrder);
+  }
+
+  return Status;
+}
+
+/**
+  Update the BootOrder or DriverOrder according to the OptionType to delete OptionNumber .
+  
+  @param  OptionNumber        Indicate the option number of load option
+  @param  OptionType          Indicate the type of load option
+
+  @retval EFI_INVALID_PARAMETER OptionType or OptionNumber is invalid.
+  @retval EFI_NOT_FOUND         The load option cannot be found
+  @retval EFI_SUCCESS           The load option was deleted
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerDeleteLoadOptionVariable (
+  IN UINTN                              OptionNumber,
+  IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  OptionType
+  )
+{
+  if ((OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  return BmDeleteOptionVariable (
+           OptionType == LoadOptionTypeBoot ? L"BootOrder" : L"DriverOrder",
+           (UINT16) OptionNumber
+           );
+}
+
+/**
+  Convert a single character to number.
+  It assumes the input Char is in the scope of L'0' ~ L'9' and L'A' ~ L'F'
+
+  @param    Char   The input char which need to convert to int.
+**/
+UINTN
+BmCharToUint (
+  IN CHAR16                           Char
+  )
+{
+  if ((Char >= L'0') && (Char <= L'9')) {
+    return (UINTN) (Char - L'0');
+  }
+
+  if ((Char >= L'A') && (Char <= L'F')) {
+    return (UINTN) (Char - L'A' + 0xA);
+  }
+
+  ASSERT (FALSE);
+  return 0;
+}
+
+/**
+  Returns the size of a device path in bytes.
+
+  This function returns the size, in bytes, of the device path data structure 
+  specified by DevicePath including the end of device path node. If DevicePath 
+  is NULL, then 0 is returned. If the length of the device path is bigger than
+  MaxSize, also return 0 to indicate this is an invalidate device path.
+
+  @param  DevicePath         A pointer to a device path data structure.
+  @param  MaxSize            Max valid device path size. If big than this size, 
+                             return error.
+  
+  @retval 0                  An invalid device path.
+  @retval Others             The size of a device path in bytes.
+
+**/
+UINTN
+BmGetDevicePathSizeEx (
+  IN CONST EFI_DEVICE_PATH_PROTOCOL  *DevicePath,
+  IN UINTN                           MaxSize
+  )
+{
+  UINTN  Size;
+  UINTN  NodeSize;
+
+  if (DevicePath == NULL) {
+    return 0;
+  }
+
+  //
+  // Search for the end of the device path structure
+  //
+  Size = 0;
+  while (!IsDevicePathEnd (DevicePath)) {
+    NodeSize = DevicePathNodeLength (DevicePath);
+    if (NodeSize == 0) {
+      return 0;
+    }
+    Size += NodeSize;
+    if (Size > MaxSize) {
+      return 0;
+    }
+    DevicePath = NextDevicePathNode (DevicePath);
+  }
+  Size += DevicePathNodeLength (DevicePath);
+  if (Size > MaxSize) {
+    return 0;
+  }
+
+  return Size;
+}
+
+/**
+  Returns the length of a Null-terminated Unicode string. If the length is 
+  bigger than MaxStringLen, return length 0 to indicate that this is an 
+  invalidate string.
+
+  This function returns the number of Unicode characters in the Null-terminated
+  Unicode string specified by String. 
+
+  If String is NULL, then ASSERT().
+  If String is not aligned on a 16-bit boundary, then ASSERT().
+
+  @param  String           A pointer to a Null-terminated Unicode string.
+  @param  MaxStringLen     Max string len in this string.
+
+  @retval 0                An invalid string.
+  @retval Others           The length of String.
+
+**/
+UINTN
+BmStrSizeEx (
+  IN      CONST CHAR16              *String,
+  IN      UINTN                     MaxStringLen
+  )
+{
+  UINTN                             Length;
+
+  ASSERT (String != NULL && MaxStringLen != 0);
+  ASSERT (((UINTN) String & BIT0) == 0);
+
+  for (Length = 0; *String != L'\0' && MaxStringLen != Length; String++, Length+=2);
+
+  if (*String != L'\0' && MaxStringLen == Length) {
+    return 0;
+  }
+
+  return Length + 2;
+}
+
+/**
+  Validate the EFI Boot#### variable (VendorGuid/Name)
+
+  @param  Variable              Boot#### variable data.
+  @param  VariableSize          Returns the size of the EFI variable that was read
+
+  @retval TRUE                  The variable data is correct.
+  @retval FALSE                 The variable data is corrupted.
+
+**/
+BOOLEAN 
+BmValidateOption (
+  UINT8                     *Variable,
+  UINTN                     VariableSize
+  )
+{
+  UINT16                    FilePathSize;
+  UINT8                     *TempPtr;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+  UINTN                     TempSize;
+
+  if (VariableSize <= sizeof (UINT16) + sizeof (UINT32)) {
+    return FALSE;
+  }
+
+  //
+  // Skip the option attribute
+  //
+  TempPtr    = Variable;
+  TempPtr   += sizeof (UINT32);
+
+  //
+  // Get the option's device path size
+  //
+  FilePathSize  = *(UINT16 *) TempPtr;
+  TempPtr      += sizeof (UINT16);
+
+  //
+  // Get the option's description string size
+  //
+  TempSize = BmStrSizeEx ((CHAR16 *) TempPtr, VariableSize - sizeof (UINT16) - sizeof (UINT32));
+  TempPtr += TempSize;
+
+  //
+  // Get the option's device path
+  //
+  DevicePath =  (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
+  TempPtr   += FilePathSize;
+
+  //
+  // Validation boot option variable.
+  //
+  if ((FilePathSize == 0) || (TempSize == 0)) {
+    return FALSE;
+  }
+
+  if (TempSize + FilePathSize + sizeof (UINT16) + sizeof (UINT32) > VariableSize) {
+    return FALSE;
+  }
+
+  return (BOOLEAN) (BmGetDevicePathSizeEx (DevicePath, FilePathSize) != 0);
+}
+
+/**
+  Build the Boot#### or Driver#### option from the VariableName.
+
+  @param  VariableName          EFI Variable name indicate if it is Boot#### or
+                                Driver####
+  @param  Option                Return the Boot#### or Driver#### option.
+
+  @retval EFI_SUCCESS     Get the option just been created
+  @retval EFI_NOT_FOUND   Failed to get the new option
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerVariableToLoadOption (
+  IN  CHAR16                          *VariableName,
+  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option
+  )
+{
+  EFI_STATUS                         Status;
+  UINT32                             Attribute;
+  UINT16                             FilePathSize;
+  UINT8                              *Variable;
+  UINT8                              *TempPtr;
+  UINTN                              VariableSize;
+  EFI_DEVICE_PATH_PROTOCOL           *FilePath;
+  UINT8                              *OptionalData;
+  UINT32                             OptionalDataSize;
+  CHAR16                             *Description;
+  UINT8                              NumOff;
+  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  OptionType;
+  UINT16                             OptionNumber;
+
+  if ((VariableName == NULL) || (Option == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Read the variable
+  //
+  GetEfiGlobalVariable2 (VariableName, (VOID **) &Variable, &VariableSize);
+  if (Variable == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Validate Boot#### variable data.
+  //
+  if (!BmValidateOption(Variable, VariableSize)) {
+    FreePool (Variable);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Notes: careful defined the variable of Boot#### or
+  // Driver####, consider use some macro to abstract the code
+  //
+  //
+  // Get the option attribute
+  //
+  TempPtr   =  Variable;
+  Attribute =  *(UINT32 *) Variable;
+  TempPtr   += sizeof (UINT32);
+
+  //
+  // Get the option's device path size
+  //
+  FilePathSize =  *(UINT16 *) TempPtr;
+  TempPtr     += sizeof (UINT16);
+
+  //
+  // Get the option's description string
+  //
+  Description  = (CHAR16 *) TempPtr;
+
+  //
+  // Get the option's description string size
+  //
+  TempPtr     += StrSize ((CHAR16 *) TempPtr);
+
+  //
+  // Get the option's device path
+  //
+  FilePath     =  (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;
+  TempPtr     += FilePathSize;
+
+  OptionalDataSize = (UINT32) (VariableSize - (UINTN) (TempPtr - Variable));
+  if (OptionalDataSize == 0) {
+    OptionalData = NULL;
+  } else {
+    OptionalData = TempPtr;
+  }
+
+  if (*VariableName == L'B') {
+    OptionType = LoadOptionTypeBoot;
+    NumOff = (UINT8) (sizeof (L"Boot") / sizeof (CHAR16) - 1);
+  } else {
+    OptionType = LoadOptionTypeDriver;
+    NumOff = (UINT8) (sizeof (L"Driver") / sizeof (CHAR16) - 1);
+  }
+  
+  //
+  // Get the value from VariableName Unicode string
+  // since the ISO standard assumes ASCII equivalent abbreviations, we can be safe in converting this
+  // Unicode stream to ASCII without any loss in meaning.
+  //  
+  OptionNumber = (UINT16) (BmCharToUint (VariableName[NumOff+0]) * 0x1000) 
+               + (UINT16) (BmCharToUint (VariableName[NumOff+1]) * 0x100)
+               + (UINT16) (BmCharToUint (VariableName[NumOff+2]) * 0x10)
+               + (UINT16) (BmCharToUint (VariableName[NumOff+3]) * 0x1);
+
+  Status = EfiBootManagerInitializeLoadOption (
+             Option,
+             OptionNumber,
+             OptionType,
+             Attribute,
+             Description,
+             FilePath,
+             OptionalData,
+             OptionalDataSize
+             );
+  ASSERT_EFI_ERROR (Status);
+  
+  FreePool (Variable);
+  return Status;
+}
+
+/**
+  Returns an array of load options based on the EFI variable
+  L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it.
+  #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry. 
+
+  @param  LoadOptionCount   Returns number of entries in the array.
+  @param  LoadOptionType    The type of the load option.
+
+  @retval NULL  No load options exist.
+  @retval !NULL Array of load option entries.
+
+**/
+EFI_BOOT_MANAGER_LOAD_OPTION *
+EFIAPI
+EfiBootManagerGetLoadOptions (
+  OUT UINTN                             *OptionCount,
+  IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  LoadOptionType
+  )
+{
+  EFI_STATUS                   Status;
+  UINT16                       *OptionOrder;
+  UINTN                        OptionOrderSize;
+  UINTN                        Index;
+  UINTN                        OptionIndex;
+  EFI_BOOT_MANAGER_LOAD_OPTION *Option;
+  CHAR16                       OptionName[sizeof ("Driver####")];
+  UINT16                       OptionNumber;
+
+  *OptionCount = 0;
+
+  //
+  // Read the BootOrder, or DriverOrder variable.
+  //
+  GetEfiGlobalVariable2 (
+    (LoadOptionType == LoadOptionTypeBoot) ? L"BootOrder" : L"DriverOrder",
+    (VOID **) &OptionOrder,
+    &OptionOrderSize
+    );
+  if (OptionOrder == NULL) {
+    return NULL;
+  }
+
+  *OptionCount = OptionOrderSize / sizeof (UINT16);
+
+  Option = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+  ASSERT (Option != NULL);
+
+  OptionIndex = 0;
+  for (Index = 0; Index < *OptionCount; Index++) {
+    OptionNumber = OptionOrder[Index];
+    if (LoadOptionType == LoadOptionTypeBoot) {
+      UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionNumber);
+    } else {
+      UnicodeSPrint (OptionName, sizeof (OptionName), L"Driver%04x", OptionNumber);
+    }
+
+    Status = EfiBootManagerVariableToLoadOption (OptionName, &Option[OptionIndex]);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((EFI_D_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName));
+      EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionTypeBoot);
+    } else {
+      ASSERT (Option[OptionIndex].OptionNumber == OptionNumber);
+      OptionIndex++;
+    }
+  }
+
+  if (OptionOrder != NULL) {
+    FreePool (OptionOrder);
+  }
+
+  if (OptionIndex < *OptionCount) {
+    Option = ReallocatePool (
+               *OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
+               OptionIndex * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
+               Option
+               );
+    ASSERT (Option != NULL);
+    *OptionCount = OptionIndex;
+  }
+
+  return Option;
+}
+
+/**
+  Free an EFI_BOOT_MANGER_LOAD_OPTION entry that was allocate by the library.
+
+  @param  LoadOption   Pointer to boot option to Free.
+
+  @return EFI_SUCCESS   BootOption was freed 
+  @return EFI_NOT_FOUND BootOption == NULL 
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeLoadOption (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION  *LoadOption
+  )
+{
+  if (LoadOption == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (LoadOption->Description != NULL) {
+    FreePool (LoadOption->Description);
+  }
+  if (LoadOption->FilePath != NULL) {
+    FreePool (LoadOption->FilePath);
+  }
+  if (LoadOption->OptionalData != NULL) {
+    FreePool (LoadOption->OptionalData);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by 
+  EfiBootManagerGetLoadOptions().
+
+  @param  Option       Pointer to boot option array to free.
+  @param  OptionCount  Number of array entries in BootOption
+
+  @return EFI_SUCCESS   BootOption was freed 
+  @return EFI_NOT_FOUND BootOption == NULL 
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeLoadOptions (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION  *Option,
+  IN  UINTN                         OptionCount
+  )
+{
+  UINTN   Index;
+
+  if (Option == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  for (Index = 0;Index < OptionCount; Index++) {
+    EfiBootManagerFreeLoadOption (&Option[Index]);
+  }
+
+  FreePool (Option);
+
+  return EFI_SUCCESS;
+}
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c b/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c
new file mode 100644 (file)
index 0000000..1cfb4bb
--- /dev/null
@@ -0,0 +1,507 @@
+/** @file
+  Misc library functions.
+
+Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+/**
+  Delete the instance in Multi which matches partly with Single instance
+
+  @param  Multi                 A pointer to a multi-instance device path data
+                                structure.
+  @param  Single                A pointer to a single-instance device path data
+                                structure.
+
+  @return This function will remove the device path instances in Multi which partly
+          match with the Single, and return the result device path. If there is no
+          remaining device path as a result, this function will return NULL.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmDelPartMatchInstance (
+  IN     EFI_DEVICE_PATH_PROTOCOL  *Multi,
+  IN     EFI_DEVICE_PATH_PROTOCOL  *Single
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL  *Instance;
+  EFI_DEVICE_PATH_PROTOCOL  *NewDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;
+  UINTN                     InstanceSize;
+  UINTN                     SingleDpSize;
+
+  NewDevicePath     = NULL;
+  TempNewDevicePath = NULL;
+
+  if (Multi == NULL || Single == NULL) {
+    return Multi;
+  }
+
+  Instance        = GetNextDevicePathInstance (&Multi, &InstanceSize);
+  SingleDpSize    = GetDevicePathSize (Single) - END_DEVICE_PATH_LENGTH;
+  InstanceSize   -= END_DEVICE_PATH_LENGTH;
+
+  while (Instance != NULL) {
+
+    if (CompareMem (Instance, Single, MIN (SingleDpSize, InstanceSize)) != 0) {
+      //
+      // Append the device path instance which does not match with Single
+      //
+      TempNewDevicePath = NewDevicePath;
+      NewDevicePath = AppendDevicePathInstance (NewDevicePath, Instance);
+      if (TempNewDevicePath != NULL) {
+        FreePool(TempNewDevicePath);
+      }
+    }
+    FreePool(Instance);
+    Instance      = GetNextDevicePathInstance (&Multi, &InstanceSize);
+    InstanceSize -= END_DEVICE_PATH_LENGTH;
+  }
+
+  return NewDevicePath;
+}
+
+/**
+  Function compares a device path data structure to that of all the nodes of a
+  second device path instance.
+
+  @param  Multi                 A pointer to a multi-instance device path data
+                                structure.
+  @param  Single                A pointer to a single-instance device path data
+                                structure.
+
+  @retval TRUE                  If the Single device path is contained within Multi device path.
+  @retval FALSE                 The Single device path is not match within Multi device path.
+
+**/
+BOOLEAN
+BmMatchDevicePaths (
+  IN  EFI_DEVICE_PATH_PROTOCOL  *Multi,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *Single
+  )
+{
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePathInst;
+  UINTN                     Size;
+
+  if (Multi == NULL || Single  == NULL) {
+    return FALSE;
+  }
+
+  DevicePath     = Multi;
+  DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
+
+  //
+  // Search for the match of 'Single' in 'Multi'
+  //
+  while (DevicePathInst != NULL) {
+    //
+    // If the single device path is found in multiple device paths,
+    // return success
+    //
+    if (CompareMem (Single, DevicePathInst, Size) == 0) {
+      FreePool (DevicePathInst);
+      return TRUE;
+    }
+
+    FreePool (DevicePathInst);
+    DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
+  }
+
+  return FALSE;
+}
+
+/**
+  Get the headers (dos, image, optional header) from an image
+
+  @param  Device                SimpleFileSystem device handle
+  @param  FileName              File name for the image
+  @param  DosHeader             Pointer to dos header
+  @param  Hdr                   The buffer in which to return the PE32, PE32+, or TE header.
+
+  @retval EFI_SUCCESS           Successfully get the machine type.
+  @retval EFI_NOT_FOUND         The file is not found.
+  @retval EFI_LOAD_ERROR        File is not a valid image file.
+
+**/
+EFI_STATUS
+BmGetImageHeader (
+  IN  EFI_HANDLE                  Device,
+  IN  CHAR16                      *FileName,
+  OUT EFI_IMAGE_DOS_HEADER        *DosHeader,
+  OUT EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr
+  )
+{
+  EFI_STATUS                       Status;
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL  *Volume;
+  EFI_FILE_HANDLE                  Root;
+  EFI_FILE_HANDLE                  ThisFile;
+  UINTN                            BufferSize;
+  UINT64                           FileSize;
+  EFI_FILE_INFO                    *Info;
+
+  Root     = NULL;
+  ThisFile = NULL;
+  //
+  // Handle the file system interface to the device
+  //
+  Status = gBS->HandleProtocol (
+                  Device,
+                  &gEfiSimpleFileSystemProtocolGuid,
+                  (VOID *) &Volume
+                  );
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+
+  Status = Volume->OpenVolume (
+                     Volume,
+                     &Root
+                     );
+  if (EFI_ERROR (Status)) {
+    Root = NULL;
+    goto Done;
+  }
+  ASSERT (Root != NULL);
+  Status = Root->Open (Root, &ThisFile, FileName, EFI_FILE_MODE_READ, 0);
+  if (EFI_ERROR (Status)) {
+    goto Done;
+  }
+  ASSERT (ThisFile != NULL);
+
+  //
+  // Get file size
+  //
+  BufferSize  = SIZE_OF_EFI_FILE_INFO + 200;
+  do {
+    Info   = NULL;
+    Status = gBS->AllocatePool (EfiBootServicesData, BufferSize, (VOID **) &Info);
+    if (EFI_ERROR (Status)) {
+      goto Done;
+    }
+    Status = ThisFile->GetInfo (
+                         ThisFile,
+                         &gEfiFileInfoGuid,
+                         &BufferSize,
+                         Info
+                         );
+    if (!EFI_ERROR (Status)) {
+      break;
+    }
+    if (Status != EFI_BUFFER_TOO_SMALL) {
+      FreePool (Info);
+      goto Done;
+    }
+    FreePool (Info);
+  } while (TRUE);
+
+  FileSize = Info->FileSize;
+  FreePool (Info);
+
+  //
+  // Read dos header
+  //
+  BufferSize = sizeof (EFI_IMAGE_DOS_HEADER);
+  Status = ThisFile->Read (ThisFile, &BufferSize, DosHeader);
+  if (EFI_ERROR (Status) ||
+      BufferSize < sizeof (EFI_IMAGE_DOS_HEADER) ||
+      FileSize <= DosHeader->e_lfanew ||
+      DosHeader->e_magic != EFI_IMAGE_DOS_SIGNATURE) {
+    Status = EFI_LOAD_ERROR;
+    goto Done;
+  }
+
+  //
+  // Move to PE signature
+  //
+  Status = ThisFile->SetPosition (ThisFile, DosHeader->e_lfanew);
+  if (EFI_ERROR (Status)) {
+    Status = EFI_LOAD_ERROR;
+    goto Done;
+  }
+
+  //
+  // Read and check PE signature
+  //
+  BufferSize = sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION);
+  Status = ThisFile->Read (ThisFile, &BufferSize, Hdr.Pe32);
+  if (EFI_ERROR (Status) ||
+      BufferSize < sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION) ||
+      Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) {
+    Status = EFI_LOAD_ERROR;
+    goto Done;
+  }
+
+  //
+  // Check PE32 or PE32+ magic
+  //
+  if (Hdr.Pe32->OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC &&
+      Hdr.Pe32->OptionalHeader.Magic != EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+    Status = EFI_LOAD_ERROR;
+    goto Done;
+  }
+
+ Done:
+  if (ThisFile != NULL) {
+    ThisFile->Close (ThisFile);
+  }
+  if (Root != NULL) {
+    Root->Close (Root);
+  }
+  return Status;
+}
+
+/**
+  This routine adjust the memory information for different memory type and 
+  save them into the variables for next boot.
+**/
+VOID
+BmSetMemoryTypeInformationVariable (
+  VOID
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_MEMORY_TYPE_INFORMATION  *PreviousMemoryTypeInformation;
+  EFI_MEMORY_TYPE_INFORMATION  *CurrentMemoryTypeInformation;
+  UINTN                        VariableSize;
+  UINTN                        Index;
+  UINTN                        Index1;
+  UINT32                       Previous;
+  UINT32                       Current;
+  UINT32                       Next;
+  EFI_HOB_GUID_TYPE            *GuidHob;
+  BOOLEAN                      MemoryTypeInformationModified;
+  BOOLEAN                      MemoryTypeInformationVariableExists;
+  EFI_BOOT_MODE                BootMode;
+
+  MemoryTypeInformationModified       = FALSE;
+  MemoryTypeInformationVariableExists = FALSE;
+
+
+  BootMode = GetBootModeHob ();
+  //
+  // In BOOT_IN_RECOVERY_MODE, Variable region is not reliable.
+  //
+  if (BootMode == BOOT_IN_RECOVERY_MODE) {
+    return;
+  }
+
+  //
+  // Only check the the Memory Type Information variable in the boot mode 
+  // other than BOOT_WITH_DEFAULT_SETTINGS because the Memory Type
+  // Information is not valid in this boot mode.
+  //
+  if (BootMode != BOOT_WITH_DEFAULT_SETTINGS) {
+    VariableSize = 0;
+    Status = gRT->GetVariable (
+                    EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
+                    &gEfiMemoryTypeInformationGuid,
+                    NULL, 
+                    &VariableSize, 
+                    NULL
+                    );
+    if (Status == EFI_BUFFER_TOO_SMALL) {
+      MemoryTypeInformationVariableExists = TRUE;
+    }
+  }
+
+  //
+  // Retrieve the current memory usage statistics.  If they are not found, then
+  // no adjustments can be made to the Memory Type Information variable.
+  //
+  Status = EfiGetSystemConfigurationTable (
+             &gEfiMemoryTypeInformationGuid,
+             (VOID **) &CurrentMemoryTypeInformation
+             );
+  if (EFI_ERROR (Status) || CurrentMemoryTypeInformation == NULL) {
+    return;
+  }
+
+  //
+  // Get the Memory Type Information settings from Hob if they exist,
+  // PEI is responsible for getting them from variable and build a Hob to save them.
+  // If the previous Memory Type Information is not available, then set defaults
+  //
+  GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid);
+  if (GuidHob == NULL) {
+    //
+    // If Platform has not built Memory Type Info into the Hob, just return.
+    //
+    return;
+  }
+  PreviousMemoryTypeInformation = GET_GUID_HOB_DATA (GuidHob);
+  VariableSize = GET_GUID_HOB_DATA_SIZE (GuidHob);
+
+  //
+  // Use a heuristic to adjust the Memory Type Information for the next boot
+  //
+  DEBUG ((EFI_D_INFO, "Memory  Previous  Current    Next   \n"));
+  DEBUG ((EFI_D_INFO, " Type    Pages     Pages     Pages  \n"));
+  DEBUG ((EFI_D_INFO, "======  ========  ========  ========\n"));
+
+  for (Index = 0; PreviousMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
+
+    for (Index1 = 0; CurrentMemoryTypeInformation[Index1].Type != EfiMaxMemoryType; Index1++) {
+      if (PreviousMemoryTypeInformation[Index].Type == CurrentMemoryTypeInformation[Index1].Type) {
+        break;
+      }
+    }
+    if (CurrentMemoryTypeInformation[Index1].Type == EfiMaxMemoryType) {
+      continue;
+    }
+
+    //
+    // Previous is the number of pages pre-allocated
+    // Current is the number of pages actually needed
+    //
+    Previous = PreviousMemoryTypeInformation[Index].NumberOfPages;
+    Current  = CurrentMemoryTypeInformation[Index1].NumberOfPages;
+    Next     = Previous;
+
+    //
+    // Inconsistent Memory Reserved across bootings may lead to S4 fail
+    // Write next varible to 125% * current when the pre-allocated memory is:
+    //  1. More than 150% of needed memory and boot mode is BOOT_WITH_DEFAULT_SETTING
+    //  2. Less than the needed memory
+    //
+    if ((Current + (Current >> 1)) < Previous) {
+      if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {
+        Next = Current + (Current >> 2);
+      }
+    } else if (Current > Previous) {
+      Next = Current + (Current >> 2);
+    }
+    if (Next > 0 && Next < 4) {
+      Next = 4;
+    }
+
+    if (Next != Previous) {
+      PreviousMemoryTypeInformation[Index].NumberOfPages = Next;
+      MemoryTypeInformationModified = TRUE;
+    }
+
+    DEBUG ((EFI_D_INFO, "  %02x    %08x  %08x  %08x\n", PreviousMemoryTypeInformation[Index].Type, Previous, Current, Next));
+  }
+
+  //
+  // If any changes were made to the Memory Type Information settings, then set the new variable value;
+  // Or create the variable in first boot.
+  //
+  if (MemoryTypeInformationModified || !MemoryTypeInformationVariableExists) {
+    Status = BmSetVariableAndReportStatusCodeOnError (
+               EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
+               &gEfiMemoryTypeInformationGuid,
+               EFI_VARIABLE_NON_VOLATILE  | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+               VariableSize,
+               PreviousMemoryTypeInformation
+               );
+
+    if (!EFI_ERROR (Status)) {
+      //
+      // If the Memory Type Information settings have been modified, then reset the platform
+      // so the new Memory Type Information setting will be used to guarantee that an S4
+      // entry/resume cycle will not fail.
+      //
+      if (MemoryTypeInformationModified) {
+        DEBUG ((EFI_D_INFO, "Memory Type Information settings change. Warm Reset!!!\n"));
+        gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
+      }
+    } else {
+      DEBUG ((EFI_D_ERROR, "Memory Type Information settings cannot be saved. OS S4 may fail!\n"));
+    }
+  }
+}
+
+/**
+  Set the variable and report the error through status code upon failure.
+
+  @param  VariableName           A Null-terminated string that is the name of the vendor's variable.
+                                 Each VariableName is unique for each VendorGuid. VariableName must
+                                 contain 1 or more characters. If VariableName is an empty string,
+                                 then EFI_INVALID_PARAMETER is returned.
+  @param  VendorGuid             A unique identifier for the vendor.
+  @param  Attributes             Attributes bitmask to set for the variable.
+  @param  DataSize               The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, 
+                                 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or 
+                                 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero 
+                                 causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is 
+                                 set, then a SetVariable() call with a DataSize of zero will not cause any change to 
+                                 the variable value (the timestamp associated with the variable may be updated however 
+                                 even if no new data value is provided,see the description of the 
+                                 EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not 
+                                 be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). 
+  @param  Data                   The contents for the variable.
+
+  @retval EFI_SUCCESS            The firmware has successfully stored the variable and its data as
+                                 defined by the Attributes.
+  @retval EFI_INVALID_PARAMETER  An invalid combination of attribute bits, name, and GUID was supplied, or the
+                                 DataSize exceeds the maximum allowed.
+  @retval EFI_INVALID_PARAMETER  VariableName is an empty string.
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the variable and its data.
+  @retval EFI_DEVICE_ERROR       The variable could not be retrieved due to a hardware error.
+  @retval EFI_WRITE_PROTECTED    The variable in question is read-only.
+  @retval EFI_WRITE_PROTECTED    The variable in question cannot be deleted.
+  @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 
+                                 or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo 
+                                 does NOT pass the validation check carried out by the firmware.
+
+  @retval EFI_NOT_FOUND          The variable trying to be updated or deleted was not found.
+**/
+EFI_STATUS
+BmSetVariableAndReportStatusCodeOnError (
+  IN CHAR16     *VariableName,
+  IN EFI_GUID   *VendorGuid,
+  IN UINT32     Attributes,
+  IN UINTN      DataSize,
+  IN VOID       *Data
+  )
+{
+  EFI_STATUS                 Status;
+  EDKII_SET_VARIABLE_STATUS  *SetVariableStatus;
+  UINTN                      NameSize;
+
+  Status = gRT->SetVariable (
+                  VariableName,
+                  VendorGuid,
+                  Attributes,
+                  DataSize,
+                  Data
+                  );
+  if (EFI_ERROR (Status)) {
+    NameSize = StrSize (VariableName);
+    SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
+    if (SetVariableStatus != NULL) {
+      CopyGuid (&SetVariableStatus->Guid, VendorGuid);
+      SetVariableStatus->NameSize   = NameSize;
+      SetVariableStatus->DataSize   = DataSize;
+      SetVariableStatus->SetStatus  = Status;
+      SetVariableStatus->Attributes = Attributes;
+      CopyMem (SetVariableStatus + 1,                          VariableName, NameSize);
+      CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data,         DataSize);
+
+      REPORT_STATUS_CODE_EX (
+        EFI_ERROR_CODE,
+        PcdGet32 (PcdErrorCodeSetVariable),
+        0,
+        NULL,
+        &gEdkiiStatusCodeDataTypeVariableGuid,
+        SetVariableStatus,
+        sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
+        );
+
+      FreePool (SetVariableStatus);
+    }
+  }
+
+  return Status;
+}
+
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmPerformance.c b/MdeModulePkg/Library/UefiBootManagerLib/BmPerformance.c
new file mode 100644 (file)
index 0000000..1fdb7f1
--- /dev/null
@@ -0,0 +1,358 @@
+/** @file
+  This file include the file which can help to get the system
+  performance, all the function will only include if the performance
+  switch is set.
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "InternalBm.h"
+
+PERF_HEADER               mBmPerfHeader;
+PERF_DATA                 mBmPerfData;
+EFI_PHYSICAL_ADDRESS      mBmAcpiLowMemoryBase = 0x0FFFFFFFFULL;
+
+/**
+  Get the short verion of PDB file name to be
+  used in performance data logging.
+
+  @param PdbFileName     The long PDB file name.
+  @param GaugeString     The output string to be logged by performance logger.
+
+**/
+VOID
+BmGetShortPdbFileName (
+  IN  CONST CHAR8  *PdbFileName,
+  OUT       CHAR8  *GaugeString
+  )
+{
+  UINTN Index;
+  UINTN Index1;
+  UINTN StartIndex;
+  UINTN EndIndex;
+
+  if (PdbFileName == NULL) {
+    AsciiStrCpy (GaugeString, " ");
+  } else {
+    StartIndex = 0;
+    for (EndIndex = 0; PdbFileName[EndIndex] != 0; EndIndex++)
+      ;
+
+    for (Index = 0; PdbFileName[Index] != 0; Index++) {
+      if (PdbFileName[Index] == '\\') {
+        StartIndex = Index + 1;
+      }
+
+      if (PdbFileName[Index] == '.') {
+        EndIndex = Index;
+      }
+    }
+
+    Index1 = 0;
+    for (Index = StartIndex; Index < EndIndex; Index++) {
+      GaugeString[Index1] = PdbFileName[Index];
+      Index1++;
+      if (Index1 == PERF_TOKEN_LENGTH - 1) {
+        break;
+      }
+    }
+
+    GaugeString[Index1] = 0;
+  }
+
+  return ;
+}
+
+/**
+  Get the name from the Driver handle, which can be a handle with
+  EFI_LOADED_IMAGE_PROTOCOL or EFI_DRIVER_BINDING_PROTOCOL installed.
+  This name can be used in performance data logging.
+
+  @param Handle          Driver handle.
+  @param GaugeString     The output string to be logged by performance logger.
+
+**/
+VOID
+BmGetNameFromHandle (
+  IN  EFI_HANDLE     Handle,
+  OUT CHAR8          *GaugeString
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_LOADED_IMAGE_PROTOCOL   *Image;
+  CHAR8                       *PdbFileName;
+  EFI_DRIVER_BINDING_PROTOCOL *DriverBinding;
+
+  AsciiStrCpy (GaugeString, " ");
+
+  //
+  // Get handle name from image protocol
+  //
+  Status = gBS->HandleProtocol (
+                  Handle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **) &Image
+                  );
+
+  if (EFI_ERROR (Status)) {
+    Status = gBS->OpenProtocol (
+                    Handle,
+                    &gEfiDriverBindingProtocolGuid,
+                    (VOID **) &DriverBinding,
+                    NULL,
+                    NULL,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return ;
+    }
+    //
+    // Get handle name from image protocol
+    //
+    Status = gBS->HandleProtocol (
+                    DriverBinding->ImageHandle,
+                    &gEfiLoadedImageProtocolGuid,
+                    (VOID **) &Image
+                    );
+  }
+
+  PdbFileName = PeCoffLoaderGetPdbPointer (Image->ImageBase);
+
+  if (PdbFileName != NULL) {
+    BmGetShortPdbFileName (PdbFileName, GaugeString);
+  }
+
+  return ;
+}
+
+/**
+
+  Writes performance data of booting into the allocated memory.
+  OS can process these records.
+
+  @param  Event                 The triggered event.
+  @param  Context               Context for this event.
+
+**/
+VOID
+EFIAPI
+BmWriteBootToOsPerformanceData (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  )
+{
+  EFI_STATUS                Status;
+  UINT32                    LimitCount;
+  EFI_HANDLE                *Handles;
+  UINTN                     NoHandles;
+  CHAR8                     GaugeString[PERF_TOKEN_LENGTH];
+  UINT8                     *Ptr;
+  UINT32                    Index;
+  UINT64                    Ticker;
+  UINT64                    Freq;
+  UINT32                    Duration;
+  UINTN                     LogEntryKey;
+  CONST VOID                *Handle;
+  CONST CHAR8               *Token;
+  CONST CHAR8               *Module;
+  UINT64                    StartTicker;
+  UINT64                    EndTicker;
+  UINT64                    StartValue;
+  UINT64                    EndValue;
+  BOOLEAN                   CountUp;
+  UINTN                     EntryIndex;
+  UINTN                     NumPerfEntries;
+  //
+  // List of flags indicating PerfEntry contains DXE handle
+  //
+  BOOLEAN                   *PerfEntriesAsDxeHandle;
+  UINTN                     VarSize;
+
+  //
+  // Record the performance data for End of BDS
+  //
+  PERF_END(NULL, "BDS", NULL, 0);
+
+  //
+  // Retrieve time stamp count as early as possible
+  //
+  Ticker  = GetPerformanceCounter ();
+
+  Freq    = GetPerformanceCounterProperties (&StartValue, &EndValue);
+  
+  Freq    = DivU64x32 (Freq, 1000);
+
+  mBmPerfHeader.CpuFreq = Freq;
+
+  //
+  // Record BDS raw performance data
+  //
+  if (EndValue >= StartValue) {
+    mBmPerfHeader.BDSRaw = Ticker - StartValue;
+    CountUp            = TRUE;
+  } else {
+    mBmPerfHeader.BDSRaw = StartValue - Ticker;
+    CountUp            = FALSE;
+  }
+
+  if (mBmAcpiLowMemoryBase == 0x0FFFFFFFF) {
+    VarSize = sizeof (EFI_PHYSICAL_ADDRESS);
+    Status = gRT->GetVariable (
+                    L"PerfDataMemAddr",
+                    &gPerformanceProtocolGuid,
+                    NULL,
+                    &VarSize,
+                    &mBmAcpiLowMemoryBase
+                    );
+    if (EFI_ERROR (Status)) {
+      //
+      // Fail to get the variable, return.
+      //
+      return;
+    }
+  }
+
+  //
+  // Put Detailed performance data into memory
+  //
+  Handles = NULL;
+  Status = gBS->LocateHandleBuffer (
+                  AllHandles,
+                  NULL,
+                  NULL,
+                  &NoHandles,
+                  &Handles
+                  );
+  if (EFI_ERROR (Status)) {
+    return ;
+  }
+
+  Ptr        = (UINT8 *) ((UINT32) mBmAcpiLowMemoryBase + sizeof (PERF_HEADER));
+  LimitCount = (UINT32) (PERF_DATA_MAX_LENGTH - sizeof (PERF_HEADER)) / sizeof (PERF_DATA);
+
+  NumPerfEntries = 0;
+  LogEntryKey    = 0;
+  while ((LogEntryKey = GetPerformanceMeasurement (
+                          LogEntryKey,
+                          &Handle,
+                          &Token,
+                          &Module,
+                          &StartTicker,
+                          &EndTicker)) != 0) {
+    NumPerfEntries++;
+  }
+  PerfEntriesAsDxeHandle = AllocateZeroPool (NumPerfEntries * sizeof (BOOLEAN));
+  ASSERT (PerfEntriesAsDxeHandle != NULL);
+  
+  //
+  // Get DXE drivers performance
+  //
+  for (Index = 0; Index < NoHandles; Index++) {
+    Ticker = 0;
+    LogEntryKey = 0;
+    EntryIndex  = 0;
+    while ((LogEntryKey = GetPerformanceMeasurement (
+                            LogEntryKey,
+                            &Handle,
+                            &Token,
+                            &Module,
+                            &StartTicker,
+                            &EndTicker)) != 0) {
+      if (Handle == Handles[Index] && !PerfEntriesAsDxeHandle[EntryIndex]) {
+        PerfEntriesAsDxeHandle[EntryIndex] = TRUE;
+      }
+      EntryIndex++;
+      if ((Handle == Handles[Index]) && (EndTicker != 0)) {
+        if (StartTicker == 1) {
+          StartTicker = StartValue;
+        }
+        if (EndTicker == 1) {
+          EndTicker = StartValue;
+        }
+        Ticker += CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker);
+      }
+    }
+
+    Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq);
+
+    if (Duration > 0) {
+
+      BmGetNameFromHandle (Handles[Index], GaugeString);
+
+      AsciiStrCpy (mBmPerfData.Token, GaugeString);
+      mBmPerfData.Duration = Duration;
+
+      CopyMem (Ptr, &mBmPerfData, sizeof (PERF_DATA));
+      Ptr += sizeof (PERF_DATA);
+
+      mBmPerfHeader.Count++;
+      if (mBmPerfHeader.Count == LimitCount) {
+        goto Done;
+      }
+    }
+  }
+
+  //
+  // Get inserted performance data
+  //
+  LogEntryKey = 0;
+  EntryIndex  = 0;
+  while ((LogEntryKey = GetPerformanceMeasurement (
+                          LogEntryKey,
+                          &Handle,
+                          &Token,
+                          &Module,
+                          &StartTicker,
+                          &EndTicker)) != 0) {
+    if (!PerfEntriesAsDxeHandle[EntryIndex] && EndTicker != 0) {
+
+      ZeroMem (&mBmPerfData, sizeof (PERF_DATA));
+
+      AsciiStrnCpy (mBmPerfData.Token, Token, PERF_TOKEN_LENGTH);
+      if (StartTicker == 1) {
+        StartTicker = StartValue;
+      }
+      if (EndTicker == 1) {
+        EndTicker = StartValue;
+      }
+      Ticker = CountUp ? (EndTicker - StartTicker) : (StartTicker - EndTicker);
+
+      mBmPerfData.Duration = (UINT32) DivU64x32 (Ticker, (UINT32) Freq);
+
+      CopyMem (Ptr, &mBmPerfData, sizeof (PERF_DATA));
+      Ptr += sizeof (PERF_DATA);
+
+      mBmPerfHeader.Count++;
+      if (mBmPerfHeader.Count == LimitCount) {
+        goto Done;
+      }
+    }
+    EntryIndex++;
+  }
+
+Done:
+
+  FreePool (Handles);
+  FreePool (PerfEntriesAsDxeHandle);
+
+  mBmPerfHeader.Signiture = PERFORMANCE_SIGNATURE;
+
+  //
+  // Put performance data to Reserved memory
+  //
+  CopyMem (
+    (UINTN *) (UINTN) mBmAcpiLowMemoryBase,
+    &mBmPerfHeader,
+    sizeof (PERF_HEADER)
+    );
+
+  return ;
+}
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
new file mode 100644 (file)
index 0000000..c8dc226
--- /dev/null
@@ -0,0 +1,364 @@
+/** @file
+  BDS library definition, include the file and data structure
+
+Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution.  The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _INTERNAL_BM_H_
+#define _INTERNAL_BM_H_
+
+#include <PiDxe.h>
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/PeImage.h>
+#include <IndustryStandard/Atapi.h>
+#include <IndustryStandard/Scsi.h>
+
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/SimpleTextIn.h>
+#include <Protocol/SimpleTextInEx.h>
+#include <Protocol/SimpleTextOut.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/UsbIo.h>
+#include <Protocol/DiskInfo.h>
+#include <Protocol/IdeControllerInit.h>
+#include <Protocol/BootLogo.h>
+#include <Protocol/DriverHealth.h>
+#include <Protocol/FormBrowser2.h>
+
+#include <Guid/ZeroGuid.h>
+#include <Guid/MemoryTypeInformation.h>
+#include <Guid/FileInfo.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/Performance.h>
+#include <Guid/StatusCodeDataTypeVariable.h>
+
+#include <Library/PrintLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PerformanceLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/TimerLib.h>
+#include <Library/DxeServicesLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/CapsuleLib.h>
+#include <Library/PerformanceLib.h>
+#include <Library/HiiLib.h>
+
+#if !defined (EFI_REMOVABLE_MEDIA_FILE_NAME)
+    #if defined (MDE_CPU_EBC)
+        //
+        // Uefi specification only defines the default boot file name for IA32, X64
+        // and IPF processor, so need define boot file name for EBC architecture here.
+        //
+        #define EFI_REMOVABLE_MEDIA_FILE_NAME L"\\EFI\\BOOT\\BOOTEBC.EFI"
+    #else
+        #error "Can not determine the default boot file name for unknown processor type!"
+    #endif
+#endif
+
+typedef enum {
+  BmAcpiFloppyBoot,
+  BmHardwareDeviceBoot,
+  BmMessageAtapiBoot,
+  BmMessageSataBoot,
+  BmMessageUsbBoot,
+  BmMessageScsiBoot,
+  BmMessageNetworkBoot,
+  BmMiscBoot
+} BM_BOOT_TYPE;
+
+typedef
+CHAR16 *
+(* BM_GET_BOOT_DESCRIPTION) (
+  IN EFI_HANDLE          Handle
+  );
+
+#define BM_HOTKEY_SIGNATURE SIGNATURE_32 ('b', 'm', 'h', 'k')
+typedef struct {
+  UINT32                    Signature;
+  LIST_ENTRY                Link;
+
+  BOOLEAN                   IsContinue;
+  UINT16                    BootOption;
+  UINT8                     CodeCount;
+  UINT8                     WaitingKey;
+  EFI_KEY_DATA              KeyData[3];
+} BM_HOTKEY;
+
+#define BM_HOTKEY_FROM_LINK(a) CR (a, BM_HOTKEY, Link, BM_HOTKEY_SIGNATURE)
+
+/**
+  Get the image file buffer data and buffer size by its device path. 
+
+  @param FilePath  On input, a pointer to an allocated buffer containing the device
+                   path of the file.
+                   On output the pointer could be NULL when the function fails to
+                   load the boot option, or could point to an allocated buffer containing
+                   the device path of the file.
+                   It could be updated by either short-form device path expanding,
+                   or default boot file path appending.
+                   Caller is responsible to free it when it's non-NULL.
+  @param FileSize  A pointer to the size of the file buffer.
+
+  @retval NULL   File is NULL, or FileSize is NULL. Or, the file can't be found.
+  @retval other  The file buffer. The caller is responsible to free the memory.
+**/
+VOID *
+BmLoadEfiBootOption (
+  IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
+  OUT    UINTN                    *FileSize
+  );
+
+/**
+  Get the Option Number that wasn't used.
+
+  @param  OrderVariableName   Could be L"BootOrder" or L"DriverOrder".
+  @param  FreeOptionNumber    To receive the minimal free option number.
+
+  @retval EFI_SUCCESS           The option number is found
+  @retval EFI_OUT_OF_RESOURCES  There is no free option number that can be used.
+  @retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL
+
+**/
+EFI_STATUS
+BmGetFreeOptionNumber (
+  IN  CHAR16    *OrderVariableName,
+  OUT UINT16    *FreeOptionNumber
+  );
+
+/**
+
+  Writes performance data of booting into the allocated memory.
+  OS can process these records.
+
+  @param  Event                 The triggered event.
+  @param  Context               Context for this event.
+
+**/
+VOID
+EFIAPI
+BmWriteBootToOsPerformanceData (
+  IN EFI_EVENT  Event,
+  IN VOID       *Context
+  );
+
+
+/**
+  Get the headers (dos, image, optional header) from an image
+
+  @param  Device                SimpleFileSystem device handle
+  @param  FileName              File name for the image
+  @param  DosHeader             Pointer to dos header
+  @param  Hdr                   The buffer in which to return the PE32, PE32+, or TE header.
+
+  @retval EFI_SUCCESS           Successfully get the machine type.
+  @retval EFI_NOT_FOUND         The file is not found.
+  @retval EFI_LOAD_ERROR        File is not a valid image file.
+
+**/
+EFI_STATUS
+BmGetImageHeader (
+  IN  EFI_HANDLE                  Device,
+  IN  CHAR16                      *FileName,
+  OUT EFI_IMAGE_DOS_HEADER        *DosHeader,
+  OUT EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION   Hdr
+  );
+
+/**
+  This routine adjust the memory information for different memory type and 
+  save them into the variables for next boot.
+**/
+VOID
+BmSetMemoryTypeInformationVariable (
+  VOID
+  );
+
+/**
+  Check whether there is a instance in BlockIoDevicePath, which contain multi device path
+  instances, has the same partition node with HardDriveDevicePath device path
+
+  @param  BlockIoDevicePath      Multi device path instances which need to check
+  @param  HardDriveDevicePath    A device path which starts with a hard drive media
+                                 device path.
+
+  @retval TRUE                   There is a matched device path instance.
+  @retval FALSE                  There is no matched device path instance.
+
+**/
+BOOLEAN
+BmMatchPartitionDevicePathNode (
+  IN  EFI_DEVICE_PATH_PROTOCOL   *BlockIoDevicePath,
+  IN  HARDDRIVE_DEVICE_PATH      *HardDriveDevicePath
+  );
+
+/**
+  Connect the specific Usb device which match the short form device path.
+
+  @param  DevicePath             A short-form device path that starts with the first
+                                 element being a USB WWID or a USB Class device
+                                 path
+
+  @return EFI_INVALID_PARAMETER  DevicePath is NULL pointer.
+                                 DevicePath is not a USB device path.
+
+  @return EFI_SUCCESS            Success to connect USB device
+  @return EFI_NOT_FOUND          Fail to find handle for USB controller to connect.
+
+**/
+EFI_STATUS
+BmConnectUsbShortFormDevicePath (
+  IN EFI_DEVICE_PATH_PROTOCOL   *DevicePath
+  );
+
+/**
+  Stop the hotkey processing.
+  
+  @param    Event          Event pointer related to hotkey service. 
+  @param    Context        Context pass to this function. 
+**/
+VOID
+EFIAPI
+BmStopHotkeyService (
+  IN EFI_EVENT    Event,
+  IN VOID         *Context
+  );
+
+/**
+  Set the variable and report the error through status code upon failure.
+
+  @param  VariableName           A Null-terminated string that is the name of the vendor's variable.
+                                 Each VariableName is unique for each VendorGuid. VariableName must
+                                 contain 1 or more characters. If VariableName is an empty string,
+                                 then EFI_INVALID_PARAMETER is returned.
+  @param  VendorGuid             A unique identifier for the vendor.
+  @param  Attributes             Attributes bitmask to set for the variable.
+  @param  DataSize               The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE, 
+                                 EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS, or 
+                                 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero 
+                                 causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is 
+                                 set, then a SetVariable() call with a DataSize of zero will not cause any change to 
+                                 the variable value (the timestamp associated with the variable may be updated however 
+                                 even if no new data value is provided,see the description of the 
+                                 EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not 
+                                 be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated). 
+  @param  Data                   The contents for the variable.
+
+  @retval EFI_SUCCESS            The firmware has successfully stored the variable and its data as
+                                 defined by the Attributes.
+  @retval EFI_INVALID_PARAMETER  An invalid combination of attribute bits, name, and GUID was supplied, or the
+                                 DataSize exceeds the maximum allowed.
+  @retval EFI_INVALID_PARAMETER  VariableName is an empty string.
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the variable and its data.
+  @retval EFI_DEVICE_ERROR       The variable could not be retrieved due to a hardware error.
+  @retval EFI_WRITE_PROTECTED    The variable in question is read-only.
+  @retval EFI_WRITE_PROTECTED    The variable in question cannot be deleted.
+  @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 
+                                 or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS being set, but the AuthInfo 
+                                 does NOT pass the validation check carried out by the firmware.
+
+  @retval EFI_NOT_FOUND          The variable trying to be updated or deleted was not found.
+**/
+EFI_STATUS
+BmSetVariableAndReportStatusCodeOnError (
+  IN CHAR16     *VariableName,
+  IN EFI_GUID   *VendorGuid,
+  IN UINT32     Attributes,
+  IN UINTN      DataSize,
+  IN VOID       *Data
+  );
+
+/**
+  Function compares a device path data structure to that of all the nodes of a
+  second device path instance.
+
+  @param  Multi                 A pointer to a multi-instance device path data
+                                structure.
+  @param  Single                A pointer to a single-instance device path data
+                                structure.
+
+  @retval TRUE                  If the Single device path is contained within Multi device path.
+  @retval FALSE                 The Single device path is not match within Multi device path.
+
+**/
+BOOLEAN
+BmMatchDevicePaths (
+  IN  EFI_DEVICE_PATH_PROTOCOL  *Multi,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *Single
+  );
+
+/**
+  Delete the instance in Multi which matches partly with Single instance
+
+  @param  Multi                 A pointer to a multi-instance device path data
+                                structure.
+  @param  Single                A pointer to a single-instance device path data
+                                structure.
+
+  @return This function will remove the device path instances in Multi which partly
+          match with the Single, and return the result device path. If there is no
+          remaining device path as a result, this function will return NULL.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmDelPartMatchInstance (
+  IN     EFI_DEVICE_PATH_PROTOCOL  *Multi,
+  IN     EFI_DEVICE_PATH_PROTOCOL  *Single
+  );
+
+
+/**
+  Return the index of the load option in the load option array.
+
+  The function consider two load options are equal when the 
+  OptionType, Attributes, Description, FilePath and OptionalData are equal.
+
+  @param Key    Pointer to the load option to be found.
+  @param Array  Pointer to the array of load options to be found.
+  @param Count  Number of entries in the Array.
+
+  @retval -1          Key wasn't found in the Array.
+  @retval 0 ~ Count-1 The index of the Key in the Array.
+**/
+INTN
+BmFindLoadOption (
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
+  IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
+  IN UINTN                              Count
+  );
+
+/**
+  Repair all the controllers according to the Driver Health status queried.
+**/
+VOID
+BmRepairAllControllers (
+  VOID
+  );
+
+#endif // _INTERNAL_BM_H_
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf b/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
new file mode 100644 (file)
index 0000000..4037212
--- /dev/null
@@ -0,0 +1,110 @@
+## @file
+#  Define and produce general Boot Manager related interfaces.
+#  
+#  Copyright (c) 2007 - 2015, Intel Corporation. All rights reserved.<BR>
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution.  The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#  
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#  
+##
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = UefiBootManagerLib
+  FILE_GUID                      = 8D4752BC-595E-49a2-B4AF-F3F57B601DE9
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = UefiBootManagerLib|DXE_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC
+#
+
+[Sources]
+  BmPerformance.c
+  BmConnect.c
+  BmMisc.c
+  BmConsole.c
+  BmBoot.c
+  BmLoadOption.c
+  BmHotkey.c
+  BmDriverHealth.c
+  InternalBm.h
+  
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[LibraryClasses]
+  HobLib
+  PcdLib
+  BaseLib
+  UefiLib
+  TimerLib
+  DebugLib
+  PrintLib
+  BaseMemoryLib
+  DevicePathLib
+  PerformanceLib
+  PeCoffGetEntryPointLib
+  UefiBootServicesTableLib
+  UefiRuntimeServicesTableLib
+  DxeServicesTableLib
+  MemoryAllocationLib
+  DxeServicesLib
+  ReportStatusCodeLib
+  PerformanceLib
+  HiiLib
+  BaseSortLib
+
+[Guids]
+  gEfiMemoryTypeInformationGuid                 ## CONSUMES ## GUID (The identifier of memory type information type in system table)
+                                                ## CONSUMES ## GUID HOB (The hob holding memory type information)
+  gEfiGlobalVariableGuid                        ## SOMETIMES_PRODUCES ## Variable:L"BootCurrent" (The boot option of current boot)
+                                                ## SOMETIMES_CONSUMES ## Variable:L"BootXX" (Boot option variable)
+                                                ## CONSUMES           ## Variable:L"Timeout" (The time out value in second of showing progress bar)
+                                                ## SOMETIMES_CONSUMES ## Variable:L"BootOrder" (The boot option array)
+                                                ## SOMETIMES_CONSUMES ## Variable:L"DriverOrder" (The driver order list)
+                                                ## SOMETIMES_CONSUMES ## Variable:L"ConIn" (The device path of console in device)
+                                                ## SOMETIMES_CONSUMES ## Variable:L"ConOut" (The device path of console out device)
+                                                ## SOMETIMES_CONSUMES ## Variable:L"ErrOut" (The device path of error out device)
+  gEfiFileInfoGuid                              ## CONSUMES ## GUID
+  gPerformanceProtocolGuid                      ## SOMETIMES_PRODUCES ## Variable:L"PerfDataMemAddr" (The ACPI address of performance data)
+  gEdkiiStatusCodeDataTypeVariableGuid          ## SOMETIMES_CONSUMES ## GUID
+  gEfiDiskInfoAhciInterfaceGuid                 ## SOMETIMES_CONSUMES ## GUID
+  gEfiDiskInfoIdeInterfaceGuid                  ## SOMETIMES_CONSUMES ## GUID
+  gEfiDiskInfoScsiInterfaceGuid                 ## SOMETIMES_CONSUMES ## GUID
+  gZeroGuid                                     ## CONSUMES ## GUID
+
+[Protocols]
+  gEfiPciRootBridgeIoProtocolGuid               ## CONSUMES
+  gEfiSimpleFileSystemProtocolGuid              ## CONSUMES
+  gEfiLoadFileProtocolGuid                      ## CONSUMES
+  gEfiSimpleTextOutProtocolGuid                 ## CONSUMES
+  gEfiPciIoProtocolGuid                         ## CONSUMES
+  gEfiLoadedImageProtocolGuid                   ## CONSUMES
+  gEfiSimpleNetworkProtocolGuid                 ## CONSUMES
+  gEfiSimpleTextInProtocolGuid                  ## CONSUMES
+  gEfiBlockIoProtocolGuid                       ## CONSUMES
+  gEfiFirmwareVolume2ProtocolGuid               ## CONSUMES
+  gEfiDevicePathProtocolGuid                    ## CONSUMES
+  gEfiBootLogoProtocolGuid                      ## CONSUMES
+  gEfiSimpleTextInputExProtocolGuid             ## CONSUMES
+  gEfiGraphicsOutputProtocolGuid                ## SOMETIMES_CONSUMES
+  gEfiUsbIoProtocolGuid                         ## SOMETIMES_CONSUMES
+  gEfiDiskInfoProtocolGuid                      ## SOMETIMES_CONSUMES
+  gEfiDriverHealthProtocolGuid                  ## SOMETIMES_CONSUMES
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange      ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderLoad                ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderStart               ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdErrorCodeSetVariable                    ## SOMETIMES_CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile                     ## CONSUMES
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDriverHealthConfigureForm               ## SOMETIMES_CONSUMES
+
index 1edb49afbd2e5f06d0749e2769b44e1a7fa60aca..6e7247016c47e7629670d842e864e9091f5deb22 100644 (file)
   ## @libraryclass   Provides sorting functions\r
   SortLib|Include/Library/SortLib.h\r
 \r
+  ## @libraryclass   Provides core boot manager functions\r
+  UefiBootManagerLib|Include/Library/UefiBootManagerLib.h\r
+\r
 [Guids]\r
   ## MdeModule package token space guid\r
   # Include/Guid/MdeModulePkgTokenSpace.h\r
   ## Serial Port Extended Transmit FIFO Size.  The default is 64 bytes. \r
   # @Prompt Serial Port Extended Transmit FIFO Size in Bytes\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdSerialExtendedTxFifoSize|64|UINT32|0x00010068\r
-  \r
+\r
+  ## This PCD points to the file name GUID of the BootManagerMenuApp\r
+  #  Platform can customize the PCD to point to different application for Boot Manager Menu\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile|{ 0xdc, 0x5b, 0xc2, 0xee, 0xf2, 0x67, 0x95, 0x4d, 0xb1, 0xd5, 0xf8, 0x1b, 0x20, 0x39, 0xd1, 0x1d }|VOID*|0x0001006b\r
+\r
+  ## This PCD points to the formset GUID of the driver health management form\r
+  #  The form will be popped up by BDS core when there are Configuration Required driver health intances.\r
+  #  Platform can customize the PCD to point to different formset.\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDriverHealthConfigureForm|{ 0xf4, 0xd9, 0x96, 0x42, 0xfc, 0xf6, 0xde, 0x4d, 0x86, 0x85, 0x8c, 0xe2, 0xd7, 0x9d, 0x90, 0xf0 }|VOID*|0x0001006c\r
+\r
   ## The number of bytes between registers in serial device.  The default is 1 byte.\r
   # @Prompt Serial Port Register Stride in Bytes\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdSerialRegisterStride|1|UINT32|0x0001006B\r
index 0bf3cca871db1ded522f166c74c8c49055435e1a..f937e83eb516c21ba06afe64fb5a3602a1e991cc 100644 (file)
@@ -46,6 +46,7 @@
   CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf\r
   PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf\r
   PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf\r
+  BaseSortLib|MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf
   #\r
   # UEFI & PI\r
   #\r
   MdeModulePkg/Library/PlatformHookLibSerialPortPpi/PlatformHookLibSerialPortPpi.inf\r
   MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf\r
   MdeModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf\r
+  MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
 \r
   MdeModulePkg/Universal/CapsulePei/CapsulePei.inf\r
   MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf\r