]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c
MdeModulePkg/UefiBootManagerLib: Remove assertion
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmHotkey.c
index 6a770f940c4b3666dc4fef9374a610301a26072a..d18ce02eb3461a0ad52cedcbea73ec130ec9383c 100644 (file)
-/** @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;
-}
+/** @file\r
+  Hotkey library functions.\r
+\r
+Copyright (c) 2011 - 2017, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "InternalBm.h"\r
+\r
+//\r
+// Lock for linked list\r
+//\r
+EFI_LOCK                     mBmHotkeyLock            = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);\r
+LIST_ENTRY                   mBmHotkeyList            = INITIALIZE_LIST_HEAD_VARIABLE (mBmHotkeyList);\r
+EFI_EVENT                    mBmHotkeyTriggered       = NULL;\r
+BOOLEAN                      mBmHotkeyServiceStarted  = FALSE;\r
+UINTN                        mBmHotkeySupportCount    = 0;\r
+\r
+//\r
+// Set OptionNumber as unassigned value to indicate the option isn't initialized\r
+//\r
+EFI_BOOT_MANAGER_LOAD_OPTION mBmHotkeyBootOption      = { LoadOptionNumberUnassigned };\r
+\r
+EFI_BOOT_MANAGER_KEY_OPTION  *mBmContinueKeyOption    = NULL;\r
+VOID                         *mBmTxtInExRegistration  = NULL;\r
+\r
+\r
+/**\r
+  Return the buffer size of the EFI_BOOT_MANAGER_KEY_OPTION data.\r
+\r
+  @param   KeyOption            The input key option info.\r
+\r
+  @retval  The buffer size of the key option data.\r
+**/\r
+UINTN\r
+BmSizeOfKeyOption (\r
+  IN CONST EFI_BOOT_MANAGER_KEY_OPTION  *KeyOption\r
+  )\r
+{\r
+  return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys)\r
+    + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY);\r
+}\r
+\r
+/**\r
+\r
+  Check whether the input key option is valid.\r
+\r
+  @param   KeyOption          Key option.\r
+  @param   KeyOptionSize      Size of the key option.\r
+\r
+  @retval  TRUE               Input key option is valid.\r
+  @retval  FALSE              Input key option is not valid.\r
+**/\r
+BOOLEAN\r
+BmIsKeyOptionValid (\r
+  IN CONST EFI_BOOT_MANAGER_KEY_OPTION *KeyOption,\r
+  IN       UINTN                       KeyOptionSize\r
+)\r
+{\r
+  UINT16   OptionName[BM_OPTION_NAME_LEN];\r
+  UINT8    *BootOption;\r
+  UINTN    BootOptionSize;\r
+  UINT32   Crc;\r
+\r
+  if (BmSizeOfKeyOption (KeyOption) != KeyOptionSize) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check whether corresponding Boot Option exist\r
+  //\r
+  UnicodeSPrint (\r
+    OptionName, sizeof (OptionName), L"%s%04x",\r
+    mBmLoadOptionName[LoadOptionTypeBoot], KeyOption->BootOption\r
+    );\r
+  GetEfiGlobalVariable2 (OptionName, (VOID **) &BootOption, &BootOptionSize);\r
+\r
+  if (BootOption == NULL) {\r
+    return FALSE;\r
+  }\r
+\r
+  //\r
+  // Check CRC for Boot Option\r
+  //\r
+  gBS->CalculateCrc32 (BootOption, BootOptionSize, &Crc);\r
+  FreePool (BootOption);\r
+\r
+  return (BOOLEAN) (KeyOption->BootOptionCrc == Crc);\r
+}\r
+\r
+/**\r
+\r
+  Check whether the input variable is an key option variable.\r
+\r
+  @param   Name               Input variable name.\r
+  @param   Guid               Input variable guid.\r
+  @param   OptionNumber       The option number of this key option variable.\r
+\r
+  @retval  TRUE               Input variable is a key option variable.\r
+  @retval  FALSE              Input variable is not a key option variable.\r
+**/\r
+BOOLEAN\r
+BmIsKeyOptionVariable (\r
+  CHAR16        *Name,\r
+  EFI_GUID      *Guid,\r
+  UINT16        *OptionNumber\r
+  )\r
+{\r
+  UINTN         Index;\r
+  UINTN         Uint;\r
+  \r
+  if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) ||\r
+      (StrSize (Name) != sizeof (L"Key####")) ||\r
+      (StrnCmp (Name, L"Key", 3) != 0)\r
+     ) {\r
+    return FALSE;\r
+  }\r
+\r
+  *OptionNumber = 0;\r
+  for (Index = 3; Index < 7; Index++) {\r
+    Uint = BmCharToUint (Name[Index]);\r
+    if (Uint == -1) {\r
+      return FALSE;\r
+    } else {\r
+      *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10;\r
+    }\r
+  }\r
+\r
+  return TRUE;\r
+}\r
+\r
+typedef struct {\r
+  EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;\r
+  UINTN                       KeyOptionCount;\r
+} BM_COLLECT_KEY_OPTIONS_PARAM;\r
+\r
+/**\r
+  Visitor function to collect the key options from NV storage.\r
+\r
+  @param Name    Variable name.\r
+  @param Guid    Variable GUID.\r
+  @param Context The same context passed to BmForEachVariable.\r
+**/\r
+VOID\r
+BmCollectKeyOptions (\r
+  CHAR16               *Name,\r
+  EFI_GUID             *Guid,\r
+  VOID                 *Context\r
+  )\r
+{\r
+  UINTN                        Index;\r
+  BM_COLLECT_KEY_OPTIONS_PARAM *Param;\r
+  VOID                         *KeyOption;\r
+  UINT16                       OptionNumber;\r
+  UINTN                        KeyOptionSize;\r
+\r
+  Param = (BM_COLLECT_KEY_OPTIONS_PARAM *) Context;\r
+\r
+  if (BmIsKeyOptionVariable (Name, Guid, &OptionNumber)) {\r
+    GetEfiGlobalVariable2 (Name, &KeyOption, &KeyOptionSize);\r
+    ASSERT (KeyOption != NULL);\r
+    if (BmIsKeyOptionValid (KeyOption, KeyOptionSize)) {\r
+      Param->KeyOptions = ReallocatePool (\r
+                            Param->KeyOptionCount * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),\r
+                            (Param->KeyOptionCount + 1) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),\r
+                            Param->KeyOptions\r
+                            );\r
+      ASSERT (Param->KeyOptions != NULL);\r
+      //\r
+      // Insert the key option in order\r
+      //\r
+      for (Index = 0; Index < Param->KeyOptionCount; Index++) {\r
+        if (OptionNumber < Param->KeyOptions[Index].OptionNumber) {\r
+          break;\r
+        }\r
+      }\r
+      CopyMem (&Param->KeyOptions[Index + 1], &Param->KeyOptions[Index], (Param->KeyOptionCount - Index) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION));\r
+      CopyMem (&Param->KeyOptions[Index], KeyOption, KeyOptionSize);\r
+      Param->KeyOptions[Index].OptionNumber = OptionNumber;\r
+      Param->KeyOptionCount++;\r
+    }\r
+    FreePool (KeyOption);\r
+  }\r
+}\r
+\r
+/**\r
+  Return the array of key options.\r
+\r
+  @param Count  Return the number of key options.\r
+\r
+  @retval NULL  No key option.\r
+  @retval Other Pointer to the key options.\r
+**/\r
+EFI_BOOT_MANAGER_KEY_OPTION *\r
+BmGetKeyOptions (\r
+  OUT UINTN     *Count\r
+  )\r
+{\r
+  BM_COLLECT_KEY_OPTIONS_PARAM Param;\r
+\r
+  if (Count == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Param.KeyOptions = NULL;\r
+  Param.KeyOptionCount = 0;\r
+\r
+  BmForEachVariable (BmCollectKeyOptions, (VOID *) &Param);\r
+\r
+  *Count = Param.KeyOptionCount;\r
+\r
+  return Param.KeyOptions;\r
+}\r
+\r
+/**\r
+  Check whether the bit is set in the value.\r
+\r
+  @param   Value            The value need to be check.\r
+  @param   Bit              The bit filed need to be check.\r
+\r
+  @retval  TRUE             The bit is set.\r
+  @retval  FALSE            The bit is not set.\r
+**/\r
+BOOLEAN\r
+BmBitSet (\r
+  IN UINT32   Value,\r
+  IN UINT32   Bit\r
+  )\r
+{\r
+  return (BOOLEAN) ((Value & Bit) != 0);\r
+}\r
+\r
+/**\r
+  Initialize the KeyData and Key[] in the EFI_BOOT_MANAGER_KEY_OPTION.\r
+\r
+  @param  Modifier   Input key info.\r
+  @param  Args       Va_list info.\r
+  @param  KeyOption  Key info which need to update.\r
+\r
+  @retval  EFI_SUCCESS             Succeed to initialize the KeyData and Key[].\r
+  @return  EFI_INVALID_PARAMETER   Input parameter error.\r
+**/\r
+EFI_STATUS\r
+BmInitializeKeyFields (\r
+  IN UINT32                       Modifier,\r
+  IN VA_LIST                      Args,\r
+  OUT EFI_BOOT_MANAGER_KEY_OPTION *KeyOption\r
+  )\r
+{\r
+  EFI_INPUT_KEY                   *Key;\r
+\r
+  if (KeyOption == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Key = NULL;\r
+  while (KeyOption->KeyData.Options.InputKeyCount < sizeof (KeyOption->Keys) / sizeof (KeyOption->Keys[0])) {\r
+    Key = VA_ARG (Args, EFI_INPUT_KEY *);\r
+    if (Key == NULL) {\r
+      break;\r
+    }\r
+    CopyMem (\r
+      &KeyOption->Keys[KeyOption->KeyData.Options.InputKeyCount],\r
+      Key,\r
+      sizeof (EFI_INPUT_KEY)\r
+      );\r
+    KeyOption->KeyData.Options.InputKeyCount++;\r
+  }\r
+\r
+  if (Key != NULL) {\r
+    //\r
+    // Too many keys\r
+    //\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Modifier & ~(EFI_BOOT_MANAGER_SHIFT_PRESSED\r
+                 | EFI_BOOT_MANAGER_CONTROL_PRESSED\r
+                 | EFI_BOOT_MANAGER_ALT_PRESSED\r
+                 | EFI_BOOT_MANAGER_LOGO_PRESSED\r
+                 | EFI_BOOT_MANAGER_MENU_KEY_PRESSED\r
+                 | EFI_BOOT_MANAGER_SYS_REQ_PRESSED\r
+                 )) != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SHIFT_PRESSED)) {\r
+    KeyOption->KeyData.Options.ShiftPressed = 1;\r
+  }\r
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_CONTROL_PRESSED)) {\r
+    KeyOption->KeyData.Options.ControlPressed = 1;\r
+  }\r
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_ALT_PRESSED)) {\r
+    KeyOption->KeyData.Options.AltPressed = 1;\r
+  }\r
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_LOGO_PRESSED)) {\r
+    KeyOption->KeyData.Options.LogoPressed = 1;\r
+  }\r
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_MENU_KEY_PRESSED)) {\r
+    KeyOption->KeyData.Options.MenuPressed = 1;\r
+  }\r
+  if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SYS_REQ_PRESSED)) {\r
+    KeyOption->KeyData.Options.SysReqPressed = 1;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Try to boot the boot option triggered by hot key.\r
+**/\r
+VOID\r
+EFIAPI\r
+EfiBootManagerHotkeyBoot (\r
+  VOID\r
+  )\r
+{\r
+  if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {\r
+    EfiBootManagerBoot (&mBmHotkeyBootOption);\r
+    EfiBootManagerFreeLoadOption (&mBmHotkeyBootOption);\r
+    mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;\r
+  }\r
+}\r
+\r
+/**\r
+  This is the common notification function for HotKeys, it will be registered\r
+  with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.\r
+\r
+  @param KeyData         A pointer to a buffer that is filled in with the keystroke\r
+                         information for the key that was pressed.\r
+\r
+  @retval  EFI_SUCCESS   KeyData is successfully processed.\r
+  @return  EFI_NOT_FOUND Fail to find boot option variable.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+BmHotkeyCallback (\r
+  IN EFI_KEY_DATA     *KeyData\r
+)\r
+{\r
+  LIST_ENTRY                    *Link;\r
+  BM_HOTKEY                     *Hotkey;\r
+  CHAR16                        OptionName[BM_OPTION_NAME_LEN];\r
+  EFI_STATUS                    Status;\r
+  EFI_KEY_DATA                  *HotkeyData;\r
+\r
+  if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {\r
+    //\r
+    // Do not process sequential hotkey stroke until the current boot option returns\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  DEBUG ((EFI_D_INFO, "[Bds]BmHotkeyCallback: %04x:%04x\n", KeyData->Key.ScanCode, KeyData->Key.UnicodeChar));\r
+\r
+  EfiAcquireLock (&mBmHotkeyLock);\r
+  for ( Link = GetFirstNode (&mBmHotkeyList)\r
+      ; !IsNull (&mBmHotkeyList, Link)\r
+      ; Link = GetNextNode (&mBmHotkeyList, Link)\r
+      ) {\r
+    Hotkey = BM_HOTKEY_FROM_LINK (Link);\r
+\r
+    //\r
+    // Is this Key Stroke we are waiting for?\r
+    //\r
+    ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0])));\r
+    HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];\r
+    if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&\r
+        (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&\r
+        (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ? \r
+          (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE\r
+        )\r
+       ) {\r
+\r
+      //\r
+      // Receive an expecting key stroke, transit to next waiting state\r
+      //\r
+      Hotkey->WaitingKey++;\r
+\r
+      if (Hotkey->WaitingKey == Hotkey->CodeCount) {\r
+        //\r
+        // Reset to initial waiting state\r
+        //\r
+        Hotkey->WaitingKey = 0;\r
+        //\r
+        // Received the whole key stroke sequence\r
+        //\r
+        Status = gBS->SignalEvent (mBmHotkeyTriggered);\r
+        ASSERT_EFI_ERROR (Status);\r
+\r
+        if (!Hotkey->IsContinue) {\r
+          //\r
+          // Launch its BootOption\r
+          //\r
+          UnicodeSPrint (\r
+            OptionName, sizeof (OptionName), L"%s%04x",\r
+            mBmLoadOptionName[LoadOptionTypeBoot], Hotkey->BootOption\r
+            );\r
+          Status = EfiBootManagerVariableToLoadOption (OptionName, &mBmHotkeyBootOption);\r
+          DEBUG ((EFI_D_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status));\r
+          if (EFI_ERROR (Status)) {\r
+            mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;\r
+          }\r
+        } else {\r
+          DEBUG ((EFI_D_INFO, "[Bds]Continue key pressed!\n"));\r
+        }\r
+      }\r
+    } else {\r
+      //\r
+      // Receive an unexpected key stroke, reset to initial waiting state\r
+      //\r
+      Hotkey->WaitingKey = 0;\r
+    }\r
+\r
+  }\r
+  EfiReleaseLock (&mBmHotkeyLock);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Return the active Simple Text Input Ex handle array.\r
+  If the SystemTable.ConsoleInHandle is NULL, the function returns all\r
+  founded Simple Text Input Ex handles.\r
+  Otherwise, it just returns the ConsoleInHandle.\r
+\r
+  @param Count  Return the handle count.\r
+\r
+  @retval The active console handles.\r
+**/\r
+EFI_HANDLE *\r
+BmGetActiveConsoleIn (\r
+  OUT UINTN                             *Count\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  EFI_HANDLE                            *Handles;\r
+\r
+  Handles = NULL;\r
+  *Count  = 0;\r
+\r
+  if (gST->ConsoleInHandle != NULL) {\r
+    Status = gBS->OpenProtocol (\r
+                    gST->ConsoleInHandle,\r
+                    &gEfiSimpleTextInputExProtocolGuid,\r
+                    NULL,\r
+                    gImageHandle,\r
+                    NULL,\r
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      Handles = AllocateCopyPool (sizeof (EFI_HANDLE), &gST->ConsoleInHandle);\r
+      if (Handles != NULL) {\r
+        *Count = 1;\r
+      }\r
+    }\r
+  } else {\r
+    Status = gBS->LocateHandleBuffer (\r
+                    ByProtocol,\r
+                    &gEfiSimpleTextInputExProtocolGuid,\r
+                    NULL,\r
+                    Count,\r
+                    &Handles\r
+                    );\r
+  }\r
+\r
+  return Handles;\r
+}\r
+\r
+/**\r
+  Unregister hotkey notify list.\r
+\r
+  @param    Hotkey                Hotkey list.\r
+\r
+  @retval   EFI_SUCCESS           Unregister hotkey notify success.\r
+  @retval   Others                Unregister hotkey notify failed.\r
+**/\r
+EFI_STATUS\r
+BmUnregisterHotkeyNotify (\r
+  IN BM_HOTKEY                          *Hotkey\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  UINTN                                 Index;\r
+  UINTN                                 KeyIndex;\r
+  EFI_HANDLE                            *Handles;\r
+  UINTN                                 HandleCount;\r
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL     *TxtInEx;\r
+  VOID                                  *NotifyHandle;\r
+\r
+  Handles = BmGetActiveConsoleIn (&HandleCount);\r
+  for (Index = 0; Index < HandleCount; Index++) {\r
+    Status = gBS->HandleProtocol (Handles[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);\r
+    ASSERT_EFI_ERROR (Status);\r
+    for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {\r
+      Status = TxtInEx->RegisterKeyNotify (\r
+                          TxtInEx,\r
+                          &Hotkey->KeyData[KeyIndex],\r
+                          BmHotkeyCallback,\r
+                          &NotifyHandle\r
+                          );\r
+      if (!EFI_ERROR (Status)) {\r
+        Status = TxtInEx->UnregisterKeyNotify (TxtInEx, NotifyHandle);\r
+        DEBUG ((EFI_D_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status));\r
+      }\r
+    }\r
+  }\r
+\r
+  if (Handles != NULL) {\r
+    FreePool (Handles);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Register hotkey notify list.\r
+\r
+  @param    TxtInEx               Pointer to EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol.\r
+  @param    Hotkey                Hotkey list.\r
+\r
+  @retval   EFI_SUCCESS           Register hotkey notify success.\r
+  @retval   Others                Register hotkey notify failed.\r
+**/\r
+EFI_STATUS\r
+BmRegisterHotkeyNotify (\r
+  IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx,\r
+  IN BM_HOTKEY                          *Hotkey\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  UINTN                                 Index;\r
+  VOID                                  *NotifyHandle;\r
+\r
+  for (Index = 0; Index < Hotkey->CodeCount; Index++) {\r
+    Status = TxtInEx->RegisterKeyNotify (\r
+                        TxtInEx,\r
+                        &Hotkey->KeyData[Index],\r
+                        BmHotkeyCallback,\r
+                        &NotifyHandle\r
+                        );\r
+    DEBUG ((\r
+      EFI_D_INFO,\r
+      "[Bds]RegisterKeyNotify: %04x/%04x %08x/%02x %r\n",\r
+      Hotkey->KeyData[Index].Key.ScanCode,\r
+      Hotkey->KeyData[Index].Key.UnicodeChar,\r
+      Hotkey->KeyData[Index].KeyState.KeyShiftState,\r
+      Hotkey->KeyData[Index].KeyState.KeyToggleState,\r
+      Status\r
+      ));\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // some of the hotkey registry failed\r
+      // do not unregister all in case we have both CTRL-ALT-P and CTRL-ALT-P-R\r
+      //\r
+      break;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Generate key shift state base on the input key option info.\r
+\r
+  @param    Depth                 Which key is checked.\r
+  @param    KeyOption             Input key option info.\r
+  @param    KeyShiftState         Input key shift state.\r
+  @param    KeyShiftStates        Return possible key shift state array.\r
+  @param    KeyShiftStateCount    Possible key shift state count.\r
+**/\r
+VOID\r
+BmGenerateKeyShiftState (\r
+  IN UINTN                             Depth,\r
+  IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption,\r
+  IN UINT32                            KeyShiftState,\r
+  IN UINT32                            *KeyShiftStates,\r
+  IN UINTN                             *KeyShiftStateCount\r
+  )\r
+{\r
+  switch (Depth) {\r
+  case 0:\r
+    if (KeyOption->KeyData.Options.ShiftPressed) {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_SHIFT_PRESSED,  KeyShiftStates, KeyShiftStateCount);\r
+    } else {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);\r
+    }\r
+    break;\r
+\r
+  case 1:\r
+    if (KeyOption->KeyData.Options.ControlPressed) {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_CONTROL_PRESSED,  KeyShiftStates, KeyShiftStateCount);\r
+    } else {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);\r
+    }\r
+    break;\r
+\r
+  case 2:\r
+    if (KeyOption->KeyData.Options.AltPressed) {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount);\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_ALT_PRESSED,  KeyShiftStates, KeyShiftStateCount);\r
+    } else {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);\r
+    }\r
+    break;\r
+  case  3:\r
+    if (KeyOption->KeyData.Options.LogoPressed) {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_LOGO_PRESSED,  KeyShiftStates, KeyShiftStateCount);\r
+    } else {\r
+      BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);\r
+    }\r
+    break;\r
+  case 4:\r
+    if (KeyOption->KeyData.Options.MenuPressed) {\r
+      KeyShiftState |= EFI_MENU_KEY_PRESSED;\r
+    }\r
+    if (KeyOption->KeyData.Options.SysReqPressed) {\r
+      KeyShiftState |= EFI_SYS_REQ_PRESSED;\r
+    }\r
+    KeyShiftStates[*KeyShiftStateCount] = KeyShiftState;\r
+    (*KeyShiftStateCount)++;\r
+    break;\r
+  }\r
+}\r
+\r
+/**\r
+  Add it to hot key database, register it to existing TxtInEx.\r
+  New TxtInEx will be automatically registered with all the hot key in dababase\r
+\r
+  @param    KeyOption  Input key option info.\r
+**/\r
+EFI_STATUS\r
+BmProcessKeyOption (\r
+  IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL    *TxtInEx;\r
+  EFI_HANDLE                           *Handles;\r
+  UINTN                                HandleCount;\r
+  UINTN                                HandleIndex;\r
+  UINTN                                Index;\r
+  BM_HOTKEY                            *Hotkey;\r
+  UINTN                                KeyIndex;\r
+  //\r
+  // 16 is enough to enumerate all the possible combination of LEFT_XXX and RIGHT_XXX\r
+  //\r
+  UINT32                               KeyShiftStates[16];\r
+  UINTN                                KeyShiftStateCount;\r
+\r
+  if (KeyOption->KeyData.Options.InputKeyCount > mBmHotkeySupportCount) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  KeyShiftStateCount = 0;\r
+  BmGenerateKeyShiftState (0, KeyOption, EFI_SHIFT_STATE_VALID, KeyShiftStates, &KeyShiftStateCount);\r
+  ASSERT (KeyShiftStateCount <= ARRAY_SIZE (KeyShiftStates));\r
+\r
+  EfiAcquireLock (&mBmHotkeyLock);\r
+\r
+  Handles = BmGetActiveConsoleIn (&HandleCount);\r
+\r
+  for (Index = 0; Index < KeyShiftStateCount; Index++) {\r
+    Hotkey = AllocateZeroPool (sizeof (BM_HOTKEY));\r
+    ASSERT (Hotkey != NULL);\r
+\r
+    Hotkey->Signature  = BM_HOTKEY_SIGNATURE;\r
+    Hotkey->BootOption = KeyOption->BootOption;\r
+    Hotkey->IsContinue = (BOOLEAN) (KeyOption == mBmContinueKeyOption);\r
+    Hotkey->CodeCount  = (UINT8) KeyOption->KeyData.Options.InputKeyCount;\r
+\r
+    for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {\r
+      CopyMem (&Hotkey->KeyData[KeyIndex].Key, &KeyOption->Keys[KeyIndex], sizeof (EFI_INPUT_KEY));\r
+      Hotkey->KeyData[KeyIndex].KeyState.KeyShiftState = KeyShiftStates[Index];\r
+    }\r
+    InsertTailList (&mBmHotkeyList, &Hotkey->Link);\r
+\r
+    for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {\r
+      Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);\r
+      ASSERT_EFI_ERROR (Status);\r
+      BmRegisterHotkeyNotify (TxtInEx, Hotkey);\r
+    }\r
+  }\r
+\r
+  if (Handles != NULL) {\r
+    FreePool (Handles);\r
+  }\r
+  EfiReleaseLock (&mBmHotkeyLock);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Callback function for SimpleTextInEx protocol install events\r
+\r
+  @param Event           the event that is signaled.\r
+  @param Context         not used here.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+BmTxtInExCallback (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  EFI_STATUS                         Status;\r
+  UINTN                              BufferSize;\r
+  EFI_HANDLE                         Handle;\r
+  EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx;\r
+  LIST_ENTRY                         *Link;\r
+\r
+  while (TRUE) {\r
+    BufferSize = sizeof (EFI_HANDLE);\r
+    Status = gBS->LocateHandle (\r
+                    ByRegisterNotify,\r
+                    NULL,\r
+                    mBmTxtInExRegistration,\r
+                    &BufferSize,\r
+                    &Handle\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // If no more notification events exist\r
+      //\r
+      return ;\r
+    }\r
+\r
+    Status = gBS->HandleProtocol (\r
+                    Handle,\r
+                    &gEfiSimpleTextInputExProtocolGuid,\r
+                    (VOID **) &TxtInEx\r
+                    );\r
+    ASSERT_EFI_ERROR (Status);\r
+\r
+    //\r
+    // Register the hot key notification for the existing items in the list\r
+    //\r
+    EfiAcquireLock (&mBmHotkeyLock);\r
+    for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); Link = GetNextNode (&mBmHotkeyList, Link)) {\r
+      BmRegisterHotkeyNotify (TxtInEx, BM_HOTKEY_FROM_LINK (Link));\r
+    }\r
+    EfiReleaseLock (&mBmHotkeyLock);\r
+  }\r
+}\r
+\r
+/**\r
+  Free the key options returned from BmGetKeyOptions.\r
+\r
+  @param KeyOptions     Pointer to the key options.\r
+  @param KeyOptionCount Number of the key options.\r
+\r
+  @retval EFI_SUCCESS   The key options are freed.\r
+  @retval EFI_NOT_FOUND KeyOptions is NULL.\r
+**/\r
+EFI_STATUS\r
+BmFreeKeyOptions (\r
+  IN EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions,\r
+  IN UINTN                          KeyOptionCount\r
+  )\r
+{\r
+  if (KeyOptions != NULL) {\r
+    FreePool (KeyOptions);\r
+    return EFI_SUCCESS;\r
+  } else {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+}\r
+\r
+/**\r
+  Register the key option to exit the waiting of the Boot Manager timeout.\r
+  Platform should ensure that the continue key option isn't conflict with\r
+  other boot key options.\r
+\r
+  @param Modifier     Key shift state.\r
+  @param  ...         Parameter list of pointer of EFI_INPUT_KEY.\r
+\r
+  @retval EFI_SUCCESS         Successfully register the continue key option.\r
+  @retval EFI_ALREADY_STARTED The continue key option is already registered.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiBootManagerRegisterContinueKeyOption (\r
+  IN UINT32           Modifier,\r
+  ...\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_BOOT_MANAGER_KEY_OPTION  KeyOption;\r
+  VA_LIST                      Args;\r
+  \r
+  if (mBmContinueKeyOption != NULL) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));\r
+  VA_START (Args, Modifier);\r
+  Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);\r
+  VA_END (Args);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    mBmContinueKeyOption = AllocateCopyPool (sizeof (EFI_BOOT_MANAGER_KEY_OPTION), &KeyOption);\r
+    ASSERT (mBmContinueKeyOption != NULL);\r
+    if (mBmHotkeyServiceStarted) {\r
+      BmProcessKeyOption (mBmContinueKeyOption);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Stop the hotkey processing.\r
+  \r
+  @param    Event          Event pointer related to hotkey service.\r
+  @param    Context        Context pass to this function.\r
+**/\r
+VOID\r
+EFIAPI\r
+BmStopHotkeyService (\r
+  IN EFI_EVENT    Event,\r
+  IN VOID         *Context\r
+  )\r
+{\r
+  LIST_ENTRY            *Link;\r
+  BM_HOTKEY             *Hotkey;\r
+\r
+  DEBUG ((EFI_D_INFO, "[Bds]Stop Hotkey Service!\n"));\r
+  gBS->CloseEvent (Event);\r
+\r
+  EfiAcquireLock (&mBmHotkeyLock);\r
+  for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {\r
+    Hotkey = BM_HOTKEY_FROM_LINK (Link);\r
+    BmUnregisterHotkeyNotify (Hotkey);\r
+    Link   = RemoveEntryList (Link);\r
+    FreePool (Hotkey);\r
+  }\r
+  EfiReleaseLock (&mBmHotkeyLock);\r
+}\r
+\r
+/**\r
+  Start the hot key service so that the key press can trigger the boot option.\r
+\r
+  @param HotkeyTriggered  Return the waitable event and it will be signaled \r
+                          when a valid hot key is pressed.\r
+\r
+  @retval EFI_SUCCESS     The hot key service is started.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiBootManagerStartHotkeyService (\r
+  IN EFI_EVENT                 *HotkeyTriggered\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_BOOT_MANAGER_KEY_OPTION  *KeyOptions;\r
+  UINTN                        KeyOptionCount;\r
+  UINTN                        Index;\r
+  EFI_EVENT                    Event;\r
+  UINT32                       *BootOptionSupport;\r
+\r
+  GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **) &BootOptionSupport, NULL);\r
+  if (BootOptionSupport != NULL) {\r
+    if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY)  != 0) {\r
+      mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT));\r
+    }\r
+    FreePool (BootOptionSupport);\r
+  }\r
+\r
+  if (mBmHotkeySupportCount == 0) {\r
+    DEBUG ((EFI_D_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n"));\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_NOTIFY_WAIT,\r
+                  TPL_CALLBACK,\r
+                  EfiEventEmptyFunction,\r
+                  NULL,\r
+                  &mBmHotkeyTriggered\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  if (HotkeyTriggered != NULL) {\r
+    *HotkeyTriggered = mBmHotkeyTriggered;\r
+  }\r
+\r
+  KeyOptions = BmGetKeyOptions (&KeyOptionCount);\r
+  for (Index = 0; Index < KeyOptionCount; Index ++) {\r
+    BmProcessKeyOption (&KeyOptions[Index]);\r
+  }\r
+  BmFreeKeyOptions (KeyOptions, KeyOptionCount);\r
+\r
+  if (mBmContinueKeyOption != NULL) {\r
+    BmProcessKeyOption (mBmContinueKeyOption);\r
+  }\r
+\r
+  //\r
+  // Hook hotkey on every future SimpleTextInputEx instance when\r
+  // SystemTable.ConsoleInHandle == NULL, which means the console\r
+  // manager (ConSplitter) is absent.\r
+  //\r
+  if (gST->ConsoleInHandle == NULL) {\r
+    EfiCreateProtocolNotifyEvent (\r
+      &gEfiSimpleTextInputExProtocolGuid,\r
+      TPL_CALLBACK,\r
+      BmTxtInExCallback,\r
+      NULL,\r
+      &mBmTxtInExRegistration\r
+      );\r
+  }\r
+\r
+  Status = EfiCreateEventReadyToBootEx (\r
+             TPL_CALLBACK,\r
+             BmStopHotkeyService,\r
+             NULL,\r
+             &Event\r
+             );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  mBmHotkeyServiceStarted = TRUE;\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Add the key option.\r
+  It adds the key option variable and the key option takes affect immediately.\r
+\r
+  @param AddedOption      Return the added key option.\r
+  @param BootOptionNumber The boot option number for the key option.\r
+  @param Modifier         Key shift state.\r
+  @param ...              Parameter list of pointer of EFI_INPUT_KEY.\r
+\r
+  @retval EFI_SUCCESS         The key option is added.\r
+  @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiBootManagerAddKeyOptionVariable (\r
+  OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption,   OPTIONAL\r
+  IN UINT16                       BootOptionNumber,\r
+  IN UINT32                       Modifier,\r
+  ...\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  VA_LIST                        Args;\r
+  VOID                           *BootOption;\r
+  UINTN                          BootOptionSize;\r
+  CHAR16                         BootOptionName[BM_OPTION_NAME_LEN];\r
+  EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;\r
+  EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;\r
+  UINTN                          KeyOptionCount;\r
+  UINTN                          Index;\r
+  UINTN                          KeyOptionNumber;\r
+  CHAR16                         KeyOptionName[sizeof ("Key####")];\r
+\r
+  UnicodeSPrint (\r
+    BootOptionName, sizeof (BootOptionName), L"%s%04x",\r
+    mBmLoadOptionName[LoadOptionTypeBoot], BootOptionNumber\r
+    );\r
+  GetEfiGlobalVariable2 (BootOptionName, &BootOption, &BootOptionSize);\r
+\r
+  if (BootOption == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));\r
+  KeyOption.BootOption = BootOptionNumber;\r
+  Status = gBS->CalculateCrc32 (BootOption, BootOptionSize, &KeyOption.BootOptionCrc);\r
+  ASSERT_EFI_ERROR (Status);\r
+  FreePool (BootOption);\r
+\r
+  VA_START (Args, Modifier);\r
+  Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);\r
+  VA_END (Args);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  KeyOptionNumber = LoadOptionNumberUnassigned;\r
+  //\r
+  // Check if the hot key sequence was defined already\r
+  //\r
+  KeyOptions = BmGetKeyOptions (&KeyOptionCount);\r
+  for (Index = 0; Index < KeyOptionCount; Index++) {\r
+    if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&\r
+      (CompareMem (KeyOptions[Index].Keys, KeyOption.Keys, KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)) {\r
+      break;\r
+    }\r
+\r
+    if ((KeyOptionNumber == LoadOptionNumberUnassigned) &&\r
+        (KeyOptions[Index].OptionNumber > Index)\r
+       ){\r
+      KeyOptionNumber = Index;\r
+    }\r
+  }\r
+  BmFreeKeyOptions (KeyOptions, KeyOptionCount);\r
+\r
+  if (Index < KeyOptionCount) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  if (KeyOptionNumber == LoadOptionNumberUnassigned) {\r
+    KeyOptionNumber = KeyOptionCount;\r
+  }\r
+\r
+  UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber);\r
+\r
+  Status = gRT->SetVariable (\r
+                  KeyOptionName,\r
+                  &gEfiGlobalVariableGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                  BmSizeOfKeyOption (&KeyOption),\r
+                  &KeyOption\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Return the Key Option in case needed by caller\r
+    //\r
+    if (AddedOption != NULL) {\r
+      CopyMem (AddedOption, &KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));\r
+    }\r
+\r
+    //\r
+    // Register the newly added hot key\r
+    // Calling this function before EfiBootManagerStartHotkeyService doesn't\r
+    // need to call BmProcessKeyOption\r
+    //\r
+    if (mBmHotkeyServiceStarted) {\r
+      BmProcessKeyOption (&KeyOption);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Delete the Key Option variable and unregister the hot key\r
+\r
+  @param DeletedOption  Return the deleted key options.\r
+  @param Modifier       Key shift state.\r
+  @param ...            Parameter list of pointer of EFI_INPUT_KEY.\r
+\r
+  @retval EFI_SUCCESS   The key option is deleted.\r
+  @retval EFI_NOT_FOUND The key option cannot be found.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiBootManagerDeleteKeyOptionVariable (\r
+  IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL\r
+  IN UINT32                      Modifier,\r
+  ...\r
+  )\r
+{\r
+  EFI_STATUS                     Status;\r
+  UINTN                          Index;\r
+  VA_LIST                        Args;\r
+  EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;\r
+  EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;\r
+  UINTN                          KeyOptionCount;\r
+  LIST_ENTRY                     *Link;\r
+  BM_HOTKEY                      *Hotkey;\r
+  UINT32                         ShiftState;\r
+  BOOLEAN                        Match;\r
+  CHAR16                         KeyOptionName[sizeof ("Key####")];\r
+\r
+  ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));\r
+  VA_START (Args, Modifier);\r
+  Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);\r
+  VA_END (Args);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  EfiAcquireLock (&mBmHotkeyLock);\r
+  //\r
+  // Delete the key option from active hot key list\r
+  // Could have multiple entries when modifier isn't 0 because we map the ShiftPressed to RIGHT_SHIFT and RIGHT_SHIFT\r
+  //\r
+  for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {\r
+    Hotkey = BM_HOTKEY_FROM_LINK (Link);\r
+    Match  = (BOOLEAN) (Hotkey->CodeCount == KeyOption.KeyData.Options.InputKeyCount);\r
+\r
+    for (Index = 0; Match && (Index < Hotkey->CodeCount); Index++) {\r
+      ShiftState = Hotkey->KeyData[Index].KeyState.KeyShiftState;\r
+      if (\r
+        (BmBitSet (ShiftState, EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED) != KeyOption.KeyData.Options.ShiftPressed) ||\r
+        (BmBitSet (ShiftState, EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) != KeyOption.KeyData.Options.ControlPressed) ||\r
+        (BmBitSet (ShiftState, EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) != KeyOption.KeyData.Options.AltPressed) ||\r
+        (BmBitSet (ShiftState, EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED) != KeyOption.KeyData.Options.LogoPressed) ||\r
+        (BmBitSet (ShiftState, EFI_MENU_KEY_PRESSED) != KeyOption.KeyData.Options.MenuPressed) ||\r
+        (BmBitSet (ShiftState, EFI_SYS_REQ_PRESSED) != KeyOption.KeyData.Options.SysReqPressed) ||\r
+        (CompareMem (&Hotkey->KeyData[Index].Key, &KeyOption.Keys[Index], sizeof (EFI_INPUT_KEY)) != 0)\r
+        ) {\r
+        //\r
+        // Break when any field doesn't match\r
+        //\r
+        Match = FALSE;\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (Match) {\r
+      Link = RemoveEntryList (Link);\r
+      FreePool (Hotkey);\r
+    } else {\r
+      Link = GetNextNode (&mBmHotkeyList, Link);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Delete the key option from the variable\r
+  //\r
+  Status     = EFI_NOT_FOUND;\r
+  KeyOptions = BmGetKeyOptions (&KeyOptionCount);\r
+  for (Index = 0; Index < KeyOptionCount; Index++) {\r
+    if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&\r
+        (CompareMem (\r
+           KeyOptions[Index].Keys, KeyOption.Keys,\r
+           KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)\r
+       ) {\r
+      UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptions[Index].OptionNumber);\r
+      Status = gRT->SetVariable (\r
+                 KeyOptionName,\r
+                 &gEfiGlobalVariableGuid,\r
+                 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                 0,\r
+                 NULL\r
+                 );\r
+      //\r
+      // Return the deleted key option in case needed by caller\r
+      //\r
+      if (DeletedOption != NULL) {\r
+        CopyMem (DeletedOption, &KeyOptions[Index], sizeof (EFI_BOOT_MANAGER_KEY_OPTION));\r
+      }\r
+      break;\r
+    }\r
+  }\r
+  BmFreeKeyOptions (KeyOptions, KeyOptionCount);\r
+\r
+  EfiReleaseLock (&mBmHotkeyLock);\r
+\r
+  return Status;\r
+}\r