Home | History | Annotate | Download | only in UefiBootManagerLib
      1 /** @file
      2   Hotkey library functions.
      3 
      4 Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "InternalBm.h"
     16 
     17 //
     18 // Lock for linked list
     19 //
     20 EFI_LOCK                     mBmHotkeyLock            = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
     21 LIST_ENTRY                   mBmHotkeyList            = INITIALIZE_LIST_HEAD_VARIABLE (mBmHotkeyList);
     22 EFI_EVENT                    mBmHotkeyTriggered       = NULL;
     23 BOOLEAN                      mBmHotkeyServiceStarted  = FALSE;
     24 UINTN                        mBmHotkeySupportCount    = 0;
     25 
     26 //
     27 // Set OptionNumber as unassigned value to indicate the option isn't initialized
     28 //
     29 EFI_BOOT_MANAGER_LOAD_OPTION mBmHotkeyBootOption      = { LoadOptionNumberUnassigned };
     30 
     31 EFI_BOOT_MANAGER_KEY_OPTION  *mBmContinueKeyOption    = NULL;
     32 VOID                         *mBmTxtInExRegistration  = NULL;
     33 
     34 
     35 /**
     36   Return the buffer size of the EFI_BOOT_MANAGER_KEY_OPTION data.
     37 
     38   @param   KeyOption            The input key option info.
     39 
     40   @retval  The buffer size of the key option data.
     41 **/
     42 UINTN
     43 BmSizeOfKeyOption (
     44   EFI_BOOT_MANAGER_KEY_OPTION  *KeyOption
     45   )
     46 {
     47   return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys)
     48     + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY);
     49 }
     50 
     51 /**
     52 
     53   Check whether the input key option is valid.
     54 
     55   @param   KeyOption          Key option.
     56   @param   KeyOptionSize      Size of the key option.
     57 
     58   @retval  TRUE               Input key option is valid.
     59   @retval  FALSE              Input key option is not valid.
     60 **/
     61 BOOLEAN
     62 BmIsKeyOptionValid (
     63   IN EFI_BOOT_MANAGER_KEY_OPTION     *KeyOption,
     64   IN UINTN                           KeyOptionSize
     65 )
     66 {
     67   UINT16   OptionName[BM_OPTION_NAME_LEN];
     68   UINT8    *BootOption;
     69   UINTN    BootOptionSize;
     70   UINT32   Crc;
     71 
     72   if (BmSizeOfKeyOption (KeyOption) != KeyOptionSize) {
     73     return FALSE;
     74   }
     75 
     76   //
     77   // Check whether corresponding Boot Option exist
     78   //
     79   UnicodeSPrint (
     80     OptionName, sizeof (OptionName), L"%s%04x",
     81     mBmLoadOptionName[LoadOptionTypeBoot], KeyOption->BootOption
     82     );
     83   GetEfiGlobalVariable2 (OptionName, (VOID **) &BootOption, &BootOptionSize);
     84 
     85   if (BootOption == NULL) {
     86     return FALSE;
     87   }
     88 
     89   //
     90   // Check CRC for Boot Option
     91   //
     92   gBS->CalculateCrc32 (BootOption, BootOptionSize, &Crc);
     93   FreePool (BootOption);
     94 
     95   return (BOOLEAN) (KeyOption->BootOptionCrc == Crc);
     96 }
     97 
     98 /**
     99 
    100   Check whether the input variable is an key option variable.
    101 
    102   @param   Name               Input variable name.
    103   @param   Guid               Input variable guid.
    104   @param   OptionNumber       The option number of this key option variable.
    105 
    106   @retval  TRUE               Input variable is a key option variable.
    107   @retval  FALSE              Input variable is not a key option variable.
    108 **/
    109 BOOLEAN
    110 BmIsKeyOptionVariable (
    111   CHAR16        *Name,
    112   EFI_GUID      *Guid,
    113   UINT16        *OptionNumber
    114   )
    115 {
    116   UINTN         Index;
    117   UINTN         Uint;
    118 
    119   if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) ||
    120       (StrSize (Name) != sizeof (L"Key####")) ||
    121       (StrnCmp (Name, L"Key", 3) != 0)
    122      ) {
    123     return FALSE;
    124   }
    125 
    126   *OptionNumber = 0;
    127   for (Index = 3; Index < 7; Index++) {
    128     Uint = BmCharToUint (Name[Index]);
    129     if (Uint == -1) {
    130       return FALSE;
    131     } else {
    132       *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10;
    133     }
    134   }
    135 
    136   return TRUE;
    137 }
    138 
    139 typedef struct {
    140   EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
    141   UINTN                       KeyOptionCount;
    142 } BM_COLLECT_KEY_OPTIONS_PARAM;
    143 
    144 /**
    145   Visitor function to collect the key options from NV storage.
    146 
    147   @param Name    Variable name.
    148   @param Guid    Variable GUID.
    149   @param Context The same context passed to BmForEachVariable.
    150 **/
    151 VOID
    152 BmCollectKeyOptions (
    153   CHAR16               *Name,
    154   EFI_GUID             *Guid,
    155   VOID                 *Context
    156   )
    157 {
    158   UINTN                        Index;
    159   BM_COLLECT_KEY_OPTIONS_PARAM *Param;
    160   EFI_BOOT_MANAGER_KEY_OPTION  *KeyOption;
    161   UINT16                       OptionNumber;
    162   UINTN                        KeyOptionSize;
    163 
    164   Param = (BM_COLLECT_KEY_OPTIONS_PARAM *) Context;
    165 
    166   if (BmIsKeyOptionVariable (Name, Guid, &OptionNumber)) {
    167     GetEfiGlobalVariable2 (Name, (VOID**) &KeyOption, &KeyOptionSize);
    168     ASSERT (KeyOption != NULL);
    169     KeyOption->OptionNumber = OptionNumber;
    170     if (BmIsKeyOptionValid (KeyOption, KeyOptionSize)) {
    171       Param->KeyOptions = ReallocatePool (
    172                             Param->KeyOptionCount * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
    173                             (Param->KeyOptionCount + 1) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
    174                             Param->KeyOptions
    175                             );
    176       ASSERT (Param->KeyOptions != NULL);
    177       //
    178       // Insert the key option in order
    179       //
    180       for (Index = 0; Index < Param->KeyOptionCount; Index++) {
    181         if (KeyOption->OptionNumber < Param->KeyOptions[Index].OptionNumber) {
    182           break;
    183         }
    184       }
    185       CopyMem (&Param->KeyOptions[Index + 1], &Param->KeyOptions[Index], (Param->KeyOptionCount - Index) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
    186       CopyMem (&Param->KeyOptions[Index], KeyOption, BmSizeOfKeyOption (KeyOption));
    187       Param->KeyOptionCount++;
    188     }
    189     FreePool (KeyOption);
    190   }
    191 }
    192 
    193 /**
    194   Return the array of key options.
    195 
    196   @param Count  Return the number of key options.
    197 
    198   @retval NULL  No key option.
    199   @retval Other Pointer to the key options.
    200 **/
    201 EFI_BOOT_MANAGER_KEY_OPTION *
    202 BmGetKeyOptions (
    203   OUT UINTN     *Count
    204   )
    205 {
    206   BM_COLLECT_KEY_OPTIONS_PARAM Param;
    207 
    208   if (Count == NULL) {
    209     return NULL;
    210   }
    211 
    212   Param.KeyOptions = NULL;
    213   Param.KeyOptionCount = 0;
    214 
    215   BmForEachVariable (BmCollectKeyOptions, (VOID *) &Param);
    216 
    217   *Count = Param.KeyOptionCount;
    218 
    219   return Param.KeyOptions;
    220 }
    221 
    222 /**
    223   Callback function for event.
    224 
    225   @param    Event          Event for this callback function.
    226   @param    Context        Context pass to this function.
    227 **/
    228 VOID
    229 EFIAPI
    230 BmEmptyFunction (
    231   IN EFI_EVENT                Event,
    232   IN VOID                     *Context
    233   )
    234 {
    235 }
    236 
    237 /**
    238   Check whether the bit is set in the value.
    239 
    240   @param   Value            The value need to be check.
    241   @param   Bit              The bit filed need to be check.
    242 
    243   @retval  TRUE             The bit is set.
    244   @retval  FALSE            The bit is not set.
    245 **/
    246 BOOLEAN
    247 BmBitSet (
    248   IN UINT32   Value,
    249   IN UINT32   Bit
    250   )
    251 {
    252   return (BOOLEAN) ((Value & Bit) != 0);
    253 }
    254 
    255 /**
    256   Initialize the KeyData and Key[] in the EFI_BOOT_MANAGER_KEY_OPTION.
    257 
    258   @param  Modifier   Input key info.
    259   @param  Args       Va_list info.
    260   @param  KeyOption  Key info which need to update.
    261 
    262   @retval  EFI_SUCCESS             Succeed to initialize the KeyData and Key[].
    263   @return  EFI_INVALID_PARAMETER   Input parameter error.
    264 **/
    265 EFI_STATUS
    266 BmInitializeKeyFields (
    267   IN UINT32                       Modifier,
    268   IN VA_LIST                      Args,
    269   OUT EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
    270   )
    271 {
    272   EFI_INPUT_KEY                   *Key;
    273 
    274   if (KeyOption == NULL) {
    275     return EFI_INVALID_PARAMETER;
    276   }
    277 
    278   Key = NULL;
    279   while (KeyOption->KeyData.Options.InputKeyCount < sizeof (KeyOption->Keys) / sizeof (KeyOption->Keys[0])) {
    280     Key = VA_ARG (Args, EFI_INPUT_KEY *);
    281     if (Key == NULL) {
    282       break;
    283     }
    284     CopyMem (
    285       &KeyOption->Keys[KeyOption->KeyData.Options.InputKeyCount],
    286       Key,
    287       sizeof (EFI_INPUT_KEY)
    288       );
    289     KeyOption->KeyData.Options.InputKeyCount++;
    290   }
    291 
    292   if (Key != NULL) {
    293     //
    294     // Too many keys
    295     //
    296     return EFI_INVALID_PARAMETER;
    297   }
    298 
    299   if ((Modifier & ~(EFI_BOOT_MANAGER_SHIFT_PRESSED
    300                  | EFI_BOOT_MANAGER_CONTROL_PRESSED
    301                  | EFI_BOOT_MANAGER_ALT_PRESSED
    302                  | EFI_BOOT_MANAGER_LOGO_PRESSED
    303                  | EFI_BOOT_MANAGER_MENU_KEY_PRESSED
    304                  | EFI_BOOT_MANAGER_SYS_REQ_PRESSED
    305                  )) != 0) {
    306     return EFI_INVALID_PARAMETER;
    307   }
    308 
    309   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SHIFT_PRESSED)) {
    310     KeyOption->KeyData.Options.ShiftPressed = 1;
    311   }
    312   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_CONTROL_PRESSED)) {
    313     KeyOption->KeyData.Options.ControlPressed = 1;
    314   }
    315   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_ALT_PRESSED)) {
    316     KeyOption->KeyData.Options.AltPressed = 1;
    317   }
    318   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_LOGO_PRESSED)) {
    319     KeyOption->KeyData.Options.LogoPressed = 1;
    320   }
    321   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_MENU_KEY_PRESSED)) {
    322     KeyOption->KeyData.Options.MenuPressed = 1;
    323   }
    324   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SYS_REQ_PRESSED)) {
    325     KeyOption->KeyData.Options.SysReqPressed = 1;
    326   }
    327 
    328   return EFI_SUCCESS;
    329 }
    330 
    331 /**
    332   Try to boot the boot option triggered by hot key.
    333 **/
    334 VOID
    335 EFIAPI
    336 EfiBootManagerHotkeyBoot (
    337   VOID
    338   )
    339 {
    340   if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
    341     EfiBootManagerBoot (&mBmHotkeyBootOption);
    342     EfiBootManagerFreeLoadOption (&mBmHotkeyBootOption);
    343     mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
    344   }
    345 }
    346 
    347 /**
    348   This is the common notification function for HotKeys, it will be registered
    349   with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.
    350 
    351   @param KeyData         A pointer to a buffer that is filled in with the keystroke
    352                          information for the key that was pressed.
    353 
    354   @retval  EFI_SUCCESS   KeyData is successfully processed.
    355   @return  EFI_NOT_FOUND Fail to find boot option variable.
    356 **/
    357 EFI_STATUS
    358 EFIAPI
    359 BmHotkeyCallback (
    360   IN EFI_KEY_DATA     *KeyData
    361 )
    362 {
    363   LIST_ENTRY                    *Link;
    364   BM_HOTKEY                     *Hotkey;
    365   CHAR16                        OptionName[BM_OPTION_NAME_LEN];
    366   EFI_STATUS                    Status;
    367   EFI_KEY_DATA                  *HotkeyData;
    368 
    369   if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
    370     //
    371     // Do not process sequential hotkey stroke until the current boot option returns
    372     //
    373     return EFI_SUCCESS;
    374   }
    375 
    376   DEBUG ((EFI_D_INFO, "[Bds]BmHotkeyCallback: %04x:%04x\n", KeyData->Key.ScanCode, KeyData->Key.UnicodeChar));
    377 
    378   EfiAcquireLock (&mBmHotkeyLock);
    379   for ( Link = GetFirstNode (&mBmHotkeyList)
    380       ; !IsNull (&mBmHotkeyList, Link)
    381       ; Link = GetNextNode (&mBmHotkeyList, Link)
    382       ) {
    383     Hotkey = BM_HOTKEY_FROM_LINK (Link);
    384 
    385     //
    386     // Is this Key Stroke we are waiting for?
    387     //
    388     ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0])));
    389     HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];
    390     if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&
    391         (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&
    392         (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ?
    393           (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE
    394         )
    395        ) {
    396 
    397       //
    398       // Receive an expecting key stroke, transit to next waiting state
    399       //
    400       Hotkey->WaitingKey++;
    401 
    402       if (Hotkey->WaitingKey == Hotkey->CodeCount) {
    403         //
    404         // Reset to initial waiting state
    405         //
    406         Hotkey->WaitingKey = 0;
    407         //
    408         // Received the whole key stroke sequence
    409         //
    410         Status = gBS->SignalEvent (mBmHotkeyTriggered);
    411         ASSERT_EFI_ERROR (Status);
    412 
    413         if (!Hotkey->IsContinue) {
    414           //
    415           // Launch its BootOption
    416           //
    417           UnicodeSPrint (
    418             OptionName, sizeof (OptionName), L"%s%04x",
    419             mBmLoadOptionName[LoadOptionTypeBoot], Hotkey->BootOption
    420             );
    421           Status = EfiBootManagerVariableToLoadOption (OptionName, &mBmHotkeyBootOption);
    422           DEBUG ((EFI_D_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status));
    423           if (EFI_ERROR (Status)) {
    424             mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
    425           }
    426         } else {
    427           DEBUG ((EFI_D_INFO, "[Bds]Continue key pressed!\n"));
    428         }
    429       }
    430     } else {
    431       //
    432       // Receive an unexpected key stroke, reset to initial waiting state
    433       //
    434       Hotkey->WaitingKey = 0;
    435     }
    436 
    437   }
    438   EfiReleaseLock (&mBmHotkeyLock);
    439 
    440   return EFI_SUCCESS;
    441 }
    442 
    443 /**
    444   Unregister hotkey notify list.
    445 
    446   @param    Hotkey                Hotkey list.
    447 
    448   @retval   EFI_SUCCESS           Unregister hotkey notify success.
    449   @retval   Others                Unregister hotkey notify failed.
    450 **/
    451 EFI_STATUS
    452 BmUnregisterHotkeyNotify (
    453   IN BM_HOTKEY                          *Hotkey
    454   )
    455 {
    456   EFI_STATUS                            Status;
    457   UINTN                                 Index;
    458   UINTN                                 KeyIndex;
    459   EFI_HANDLE                            *Handles;
    460   UINTN                                 HandleCount;
    461   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL     *TxtInEx;
    462   VOID                                  *NotifyHandle;
    463 
    464   gBS->LocateHandleBuffer (
    465           ByProtocol,
    466           &gEfiSimpleTextInputExProtocolGuid,
    467           NULL,
    468           &HandleCount,
    469           &Handles
    470           );
    471   for (Index = 0; Index < HandleCount; Index++) {
    472     Status = gBS->HandleProtocol (Handles[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
    473     ASSERT_EFI_ERROR (Status);
    474     for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
    475       Status = TxtInEx->RegisterKeyNotify (
    476                           TxtInEx,
    477                           &Hotkey->KeyData[KeyIndex],
    478                           BmHotkeyCallback,
    479                           &NotifyHandle
    480                           );
    481       if (!EFI_ERROR (Status)) {
    482         Status = TxtInEx->UnregisterKeyNotify (TxtInEx, NotifyHandle);
    483         DEBUG ((EFI_D_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status));
    484       }
    485     }
    486   }
    487 
    488   return EFI_SUCCESS;
    489 }
    490 
    491 /**
    492   Register hotkey notify list.
    493 
    494   @param    TxtInEx               Pointer to EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol.
    495   @param    Hotkey                Hotkey list.
    496 
    497   @retval   EFI_SUCCESS           Register hotkey notify success.
    498   @retval   Others                Register hotkey notify failed.
    499 **/
    500 EFI_STATUS
    501 BmRegisterHotkeyNotify (
    502   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx,
    503   IN BM_HOTKEY                          *Hotkey
    504   )
    505 {
    506   EFI_STATUS                            Status;
    507   UINTN                                 Index;
    508   VOID                                  *NotifyHandle;
    509 
    510   for (Index = 0; Index < Hotkey->CodeCount; Index++) {
    511     Status = TxtInEx->RegisterKeyNotify (
    512                         TxtInEx,
    513                         &Hotkey->KeyData[Index],
    514                         BmHotkeyCallback,
    515                         &NotifyHandle
    516                         );
    517     DEBUG ((
    518       EFI_D_INFO,
    519       "[Bds]RegisterKeyNotify: %04x/%04x %08x/%02x %r\n",
    520       Hotkey->KeyData[Index].Key.ScanCode,
    521       Hotkey->KeyData[Index].Key.UnicodeChar,
    522       Hotkey->KeyData[Index].KeyState.KeyShiftState,
    523       Hotkey->KeyData[Index].KeyState.KeyToggleState,
    524       Status
    525       ));
    526     if (EFI_ERROR (Status)) {
    527       //
    528       // some of the hotkey registry failed
    529       // do not unregister all in case we have both CTRL-ALT-P and CTRL-ALT-P-R
    530       //
    531       break;
    532     }
    533   }
    534 
    535   return EFI_SUCCESS;
    536 }
    537 
    538 /**
    539   Generate key shift state base on the input key option info.
    540 
    541   @param    Depth                 Which key is checked.
    542   @param    KeyOption             Input key option info.
    543   @param    KeyShiftState         Input key shift state.
    544   @param    KeyShiftStates        Return possible key shift state array.
    545   @param    KeyShiftStateCount    Possible key shift state count.
    546 **/
    547 VOID
    548 BmGenerateKeyShiftState (
    549   IN UINTN                             Depth,
    550   IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption,
    551   IN UINT32                            KeyShiftState,
    552   IN UINT32                            *KeyShiftStates,
    553   IN UINTN                             *KeyShiftStateCount
    554   )
    555 {
    556   switch (Depth) {
    557   case 0:
    558     if (KeyOption->KeyData.Options.ShiftPressed) {
    559       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);
    560       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_SHIFT_PRESSED,  KeyShiftStates, KeyShiftStateCount);
    561     } else {
    562       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
    563     }
    564     break;
    565 
    566   case 1:
    567     if (KeyOption->KeyData.Options.ControlPressed) {
    568       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);
    569       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_CONTROL_PRESSED,  KeyShiftStates, KeyShiftStateCount);
    570     } else {
    571       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
    572     }
    573     break;
    574 
    575   case 2:
    576     if (KeyOption->KeyData.Options.AltPressed) {
    577       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount);
    578       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_ALT_PRESSED,  KeyShiftStates, KeyShiftStateCount);
    579     } else {
    580       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
    581     }
    582     break;
    583   case  3:
    584     if (KeyOption->KeyData.Options.LogoPressed) {
    585       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);
    586       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_LOGO_PRESSED,  KeyShiftStates, KeyShiftStateCount);
    587     } else {
    588       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
    589     }
    590     break;
    591   case 4:
    592     if (KeyOption->KeyData.Options.MenuPressed) {
    593       KeyShiftState |= EFI_MENU_KEY_PRESSED;
    594     }
    595     if (KeyOption->KeyData.Options.SysReqPressed) {
    596       KeyShiftState |= EFI_SYS_REQ_PRESSED;
    597     }
    598     KeyShiftStates[*KeyShiftStateCount] = KeyShiftState;
    599     (*KeyShiftStateCount)++;
    600     break;
    601   }
    602 }
    603 
    604 /**
    605   Add it to hot key database, register it to existing TxtInEx.
    606   New TxtInEx will be automatically registered with all the hot key in dababase
    607 
    608   @param    KeyOption  Input key option info.
    609 **/
    610 EFI_STATUS
    611 BmProcessKeyOption (
    612   IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption
    613   )
    614 {
    615   EFI_STATUS                           Status;
    616   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL    *TxtInEx;
    617   EFI_HANDLE                           *Handles;
    618   UINTN                                HandleCount;
    619   UINTN                                HandleIndex;
    620   UINTN                                Index;
    621   BM_HOTKEY                            *Hotkey;
    622   UINTN                                KeyIndex;
    623   //
    624   // 16 is enough to enumerate all the possible combination of LEFT_XXX and RIGHT_XXX
    625   //
    626   UINT32                               KeyShiftStates[16];
    627   UINTN                                KeyShiftStateCount;
    628 
    629   if (KeyOption->KeyData.Options.InputKeyCount > mBmHotkeySupportCount) {
    630     return EFI_UNSUPPORTED;
    631   }
    632 
    633   KeyShiftStateCount = 0;
    634   BmGenerateKeyShiftState (0, KeyOption, EFI_SHIFT_STATE_VALID, KeyShiftStates, &KeyShiftStateCount);
    635   ASSERT (KeyShiftStateCount <= sizeof (KeyShiftStates) / sizeof (KeyShiftStates[0]));
    636 
    637   EfiAcquireLock (&mBmHotkeyLock);
    638 
    639   for (Index = 0; Index < KeyShiftStateCount; Index++) {
    640     Hotkey = AllocateZeroPool (sizeof (BM_HOTKEY));
    641     ASSERT (Hotkey != NULL);
    642 
    643     Hotkey->Signature  = BM_HOTKEY_SIGNATURE;
    644     Hotkey->BootOption = KeyOption->BootOption;
    645     Hotkey->IsContinue = (BOOLEAN) (KeyOption == mBmContinueKeyOption);
    646     Hotkey->CodeCount  = (UINT8) KeyOption->KeyData.Options.InputKeyCount;
    647 
    648     for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
    649       CopyMem (&Hotkey->KeyData[KeyIndex].Key, &KeyOption->Keys[KeyIndex], sizeof (EFI_INPUT_KEY));
    650       Hotkey->KeyData[KeyIndex].KeyState.KeyShiftState = KeyShiftStates[Index];
    651     }
    652     InsertTailList (&mBmHotkeyList, &Hotkey->Link);
    653 
    654     gBS->LocateHandleBuffer (
    655             ByProtocol,
    656             &gEfiSimpleTextInputExProtocolGuid,
    657             NULL,
    658             &HandleCount,
    659             &Handles
    660             );
    661     for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
    662       Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
    663       ASSERT_EFI_ERROR (Status);
    664       BmRegisterHotkeyNotify (TxtInEx, Hotkey);
    665     }
    666   }
    667 
    668   EfiReleaseLock (&mBmHotkeyLock);
    669 
    670   return EFI_SUCCESS;
    671 }
    672 
    673 /**
    674   Callback function for SimpleTextInEx protocol install events
    675 
    676   @param Event           the event that is signaled.
    677   @param Context         not used here.
    678 
    679 **/
    680 VOID
    681 EFIAPI
    682 BmTxtInExCallback (
    683   IN EFI_EVENT    Event,
    684   IN VOID         *Context
    685   )
    686 {
    687   EFI_STATUS                         Status;
    688   UINTN                              BufferSize;
    689   EFI_HANDLE                         Handle;
    690   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx;
    691   LIST_ENTRY                         *Link;
    692 
    693   while (TRUE) {
    694     BufferSize = sizeof (EFI_HANDLE);
    695     Status = gBS->LocateHandle (
    696                     ByRegisterNotify,
    697                     NULL,
    698                     mBmTxtInExRegistration,
    699                     &BufferSize,
    700                     &Handle
    701                     );
    702     if (EFI_ERROR (Status)) {
    703       //
    704       // If no more notification events exist
    705       //
    706       return ;
    707     }
    708 
    709     Status = gBS->HandleProtocol (
    710                     Handle,
    711                     &gEfiSimpleTextInputExProtocolGuid,
    712                     (VOID **) &TxtInEx
    713                     );
    714     ASSERT_EFI_ERROR (Status);
    715 
    716     //
    717     // Register the hot key notification for the existing items in the list
    718     //
    719     EfiAcquireLock (&mBmHotkeyLock);
    720     for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); Link = GetNextNode (&mBmHotkeyList, Link)) {
    721       BmRegisterHotkeyNotify (TxtInEx, BM_HOTKEY_FROM_LINK (Link));
    722     }
    723     EfiReleaseLock (&mBmHotkeyLock);
    724   }
    725 }
    726 
    727 /**
    728   Free the key options returned from BmGetKeyOptions.
    729 
    730   @param KeyOptions     Pointer to the key options.
    731   @param KeyOptionCount Number of the key options.
    732 
    733   @retval EFI_SUCCESS   The key options are freed.
    734   @retval EFI_NOT_FOUND KeyOptions is NULL.
    735 **/
    736 EFI_STATUS
    737 BmFreeKeyOptions (
    738   IN EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions,
    739   IN UINTN                          KeyOptionCount
    740   )
    741 {
    742   if (KeyOptions != NULL) {
    743     FreePool (KeyOptions);
    744     return EFI_SUCCESS;
    745   } else {
    746     return EFI_NOT_FOUND;
    747   }
    748 }
    749 
    750 /**
    751   Register the key option to exit the waiting of the Boot Manager timeout.
    752   Platform should ensure that the continue key option isn't conflict with
    753   other boot key options.
    754 
    755   @param Modifier     Key shift state.
    756   @param  ...         Parameter list of pointer of EFI_INPUT_KEY.
    757 
    758   @retval EFI_SUCCESS         Successfully register the continue key option.
    759   @retval EFI_ALREADY_STARTED The continue key option is already registered.
    760 **/
    761 EFI_STATUS
    762 EFIAPI
    763 EfiBootManagerRegisterContinueKeyOption (
    764   IN UINT32           Modifier,
    765   ...
    766   )
    767 {
    768   EFI_STATUS                   Status;
    769   EFI_BOOT_MANAGER_KEY_OPTION  KeyOption;
    770   VA_LIST                      Args;
    771 
    772   if (mBmContinueKeyOption != NULL) {
    773     return EFI_ALREADY_STARTED;
    774   }
    775 
    776   ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
    777   VA_START (Args, Modifier);
    778   Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
    779   VA_END (Args);
    780 
    781   if (!EFI_ERROR (Status)) {
    782     mBmContinueKeyOption = AllocateCopyPool (sizeof (EFI_BOOT_MANAGER_KEY_OPTION), &KeyOption);
    783     ASSERT (mBmContinueKeyOption != NULL);
    784     if (mBmHotkeyServiceStarted) {
    785       BmProcessKeyOption (mBmContinueKeyOption);
    786     }
    787   }
    788 
    789   return Status;
    790 }
    791 
    792 /**
    793   Stop the hotkey processing.
    794 
    795   @param    Event          Event pointer related to hotkey service.
    796   @param    Context        Context pass to this function.
    797 **/
    798 VOID
    799 EFIAPI
    800 BmStopHotkeyService (
    801   IN EFI_EVENT    Event,
    802   IN VOID         *Context
    803   )
    804 {
    805   LIST_ENTRY            *Link;
    806   BM_HOTKEY             *Hotkey;
    807 
    808   DEBUG ((EFI_D_INFO, "[Bds]Stop Hotkey Service!\n"));
    809   gBS->CloseEvent (Event);
    810 
    811   EfiAcquireLock (&mBmHotkeyLock);
    812   for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
    813     Hotkey = BM_HOTKEY_FROM_LINK (Link);
    814     BmUnregisterHotkeyNotify (Hotkey);
    815     Link   = RemoveEntryList (Link);
    816     FreePool (Hotkey);
    817   }
    818   EfiReleaseLock (&mBmHotkeyLock);
    819 }
    820 
    821 /**
    822   Start the hot key service so that the key press can trigger the boot option.
    823 
    824   @param HotkeyTriggered  Return the waitable event and it will be signaled
    825                           when a valid hot key is pressed.
    826 
    827   @retval EFI_SUCCESS     The hot key service is started.
    828 **/
    829 EFI_STATUS
    830 EFIAPI
    831 EfiBootManagerStartHotkeyService (
    832   IN EFI_EVENT                 *HotkeyTriggered
    833   )
    834 {
    835   EFI_STATUS                   Status;
    836   EFI_BOOT_MANAGER_KEY_OPTION  *KeyOptions;
    837   UINTN                        KeyOptionCount;
    838   UINTN                        Index;
    839   EFI_EVENT                    Event;
    840   UINT32                       *BootOptionSupport;
    841 
    842   Status = GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **) &BootOptionSupport, NULL);
    843   ASSERT (BootOptionSupport != NULL);
    844 
    845   if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY)  != 0) {
    846     mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT));
    847   }
    848   FreePool (BootOptionSupport);
    849 
    850   if (mBmHotkeySupportCount == 0) {
    851     DEBUG ((EFI_D_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n"));
    852     return EFI_UNSUPPORTED;
    853   }
    854 
    855   Status = gBS->CreateEvent (
    856                   EVT_NOTIFY_WAIT,
    857                   TPL_CALLBACK,
    858                   BmEmptyFunction,
    859                   NULL,
    860                   &mBmHotkeyTriggered
    861                   );
    862   ASSERT_EFI_ERROR (Status);
    863 
    864   if (HotkeyTriggered != NULL) {
    865     *HotkeyTriggered = mBmHotkeyTriggered;
    866   }
    867 
    868   KeyOptions = BmGetKeyOptions (&KeyOptionCount);
    869   for (Index = 0; Index < KeyOptionCount; Index ++) {
    870     BmProcessKeyOption (&KeyOptions[Index]);
    871   }
    872   BmFreeKeyOptions (KeyOptions, KeyOptionCount);
    873 
    874   if (mBmContinueKeyOption != NULL) {
    875     BmProcessKeyOption (mBmContinueKeyOption);
    876   }
    877 
    878   EfiCreateProtocolNotifyEvent (
    879     &gEfiSimpleTextInputExProtocolGuid,
    880     TPL_CALLBACK,
    881     BmTxtInExCallback,
    882     NULL,
    883     &mBmTxtInExRegistration
    884     );
    885 
    886   Status = EfiCreateEventReadyToBootEx (
    887              TPL_CALLBACK,
    888              BmStopHotkeyService,
    889              NULL,
    890              &Event
    891              );
    892   ASSERT_EFI_ERROR (Status);
    893 
    894 
    895   mBmHotkeyServiceStarted = TRUE;
    896   return Status;
    897 }
    898 
    899 /**
    900   Add the key option.
    901   It adds the key option variable and the key option takes affect immediately.
    902 
    903   @param AddedOption      Return the added key option.
    904   @param BootOptionNumber The boot option number for the key option.
    905   @param Modifier         Key shift state.
    906   @param ...              Parameter list of pointer of EFI_INPUT_KEY.
    907 
    908   @retval EFI_SUCCESS         The key option is added.
    909   @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.
    910 **/
    911 EFI_STATUS
    912 EFIAPI
    913 EfiBootManagerAddKeyOptionVariable (
    914   OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption,   OPTIONAL
    915   IN UINT16                       BootOptionNumber,
    916   IN UINT32                       Modifier,
    917   ...
    918   )
    919 {
    920   EFI_STATUS                     Status;
    921   VA_LIST                        Args;
    922   VOID                           *BootOption;
    923   UINTN                          BootOptionSize;
    924   CHAR16                         BootOptionName[BM_OPTION_NAME_LEN];
    925   EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;
    926   EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;
    927   UINTN                          KeyOptionCount;
    928   UINTN                          Index;
    929   UINTN                          KeyOptionNumber;
    930   CHAR16                         KeyOptionName[sizeof ("Key####")];
    931 
    932   UnicodeSPrint (
    933     BootOptionName, sizeof (BootOptionName), L"%s%04x",
    934     mBmLoadOptionName[LoadOptionTypeBoot], BootOptionNumber
    935     );
    936   GetEfiGlobalVariable2 (BootOptionName, &BootOption, &BootOptionSize);
    937 
    938   if (BootOption == NULL) {
    939     return EFI_NOT_FOUND;
    940   }
    941 
    942   ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
    943   KeyOption.BootOption = BootOptionNumber;
    944   Status = gBS->CalculateCrc32 (BootOption, BootOptionSize, &KeyOption.BootOptionCrc);
    945   ASSERT_EFI_ERROR (Status);
    946   FreePool (BootOption);
    947 
    948   VA_START (Args, Modifier);
    949   Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
    950   VA_END (Args);
    951   if (EFI_ERROR (Status)) {
    952     return Status;
    953   }
    954 
    955   KeyOptionNumber = LoadOptionNumberUnassigned;
    956   //
    957   // Check if the hot key sequence was defined already
    958   //
    959   KeyOptions = BmGetKeyOptions (&KeyOptionCount);
    960   for (Index = 0; Index < KeyOptionCount; Index++) {
    961     if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
    962       (CompareMem (KeyOptions[Index].Keys, KeyOption.Keys, KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)) {
    963       break;
    964     }
    965 
    966     if ((KeyOptionNumber == LoadOptionNumberUnassigned) &&
    967         (KeyOptions[Index].OptionNumber > Index)
    968        ){
    969       KeyOptionNumber = Index;
    970     }
    971   }
    972   BmFreeKeyOptions (KeyOptions, KeyOptionCount);
    973 
    974   if (Index < KeyOptionCount) {
    975     return EFI_ALREADY_STARTED;
    976   }
    977 
    978   if (KeyOptionNumber == LoadOptionNumberUnassigned) {
    979     KeyOptionNumber = KeyOptionCount;
    980   }
    981 
    982   UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber);
    983 
    984   Status = gRT->SetVariable (
    985                   KeyOptionName,
    986                   &gEfiGlobalVariableGuid,
    987                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
    988                   BmSizeOfKeyOption (&KeyOption),
    989                   &KeyOption
    990                   );
    991   if (!EFI_ERROR (Status)) {
    992     //
    993     // Return the Key Option in case needed by caller
    994     //
    995     if (AddedOption != NULL) {
    996       CopyMem (AddedOption, &KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
    997     }
    998 
    999     //
   1000     // Register the newly added hot key
   1001     // Calling this function before EfiBootManagerStartHotkeyService doesn't
   1002     // need to call BmProcessKeyOption
   1003     //
   1004     if (mBmHotkeyServiceStarted) {
   1005       BmProcessKeyOption (&KeyOption);
   1006     }
   1007   }
   1008 
   1009   return Status;
   1010 }
   1011 
   1012 /**
   1013   Delete the Key Option variable and unregister the hot key
   1014 
   1015   @param DeletedOption  Return the deleted key options.
   1016   @param Modifier       Key shift state.
   1017   @param ...            Parameter list of pointer of EFI_INPUT_KEY.
   1018 
   1019   @retval EFI_SUCCESS   The key option is deleted.
   1020   @retval EFI_NOT_FOUND The key option cannot be found.
   1021 **/
   1022 EFI_STATUS
   1023 EFIAPI
   1024 EfiBootManagerDeleteKeyOptionVariable (
   1025   IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL
   1026   IN UINT32                      Modifier,
   1027   ...
   1028   )
   1029 {
   1030   EFI_STATUS                     Status;
   1031   UINTN                          Index;
   1032   VA_LIST                        Args;
   1033   EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;
   1034   EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;
   1035   UINTN                          KeyOptionCount;
   1036   LIST_ENTRY                     *Link;
   1037   BM_HOTKEY                      *Hotkey;
   1038   UINT32                         ShiftState;
   1039   BOOLEAN                        Match;
   1040   CHAR16                         KeyOptionName[sizeof ("Key####")];
   1041 
   1042   ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
   1043   VA_START (Args, Modifier);
   1044   Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
   1045   VA_END (Args);
   1046 
   1047   if (EFI_ERROR (Status)) {
   1048     return Status;
   1049   }
   1050 
   1051   EfiAcquireLock (&mBmHotkeyLock);
   1052   //
   1053   // Delete the key option from active hot key list
   1054   // Could have multiple entries when modifier isn't 0 because we map the ShiftPressed to RIGHT_SHIFT and RIGHT_SHIFT
   1055   //
   1056   for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
   1057     Hotkey = BM_HOTKEY_FROM_LINK (Link);
   1058     Match  = (BOOLEAN) (Hotkey->CodeCount == KeyOption.KeyData.Options.InputKeyCount);
   1059 
   1060     for (Index = 0; Match && (Index < Hotkey->CodeCount); Index++) {
   1061       ShiftState = Hotkey->KeyData[Index].KeyState.KeyShiftState;
   1062       if (
   1063         (BmBitSet (ShiftState, EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED) != KeyOption.KeyData.Options.ShiftPressed) ||
   1064         (BmBitSet (ShiftState, EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) != KeyOption.KeyData.Options.ControlPressed) ||
   1065         (BmBitSet (ShiftState, EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) != KeyOption.KeyData.Options.AltPressed) ||
   1066         (BmBitSet (ShiftState, EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED) != KeyOption.KeyData.Options.LogoPressed) ||
   1067         (BmBitSet (ShiftState, EFI_MENU_KEY_PRESSED) != KeyOption.KeyData.Options.MenuPressed) ||
   1068         (BmBitSet (ShiftState, EFI_SYS_REQ_PRESSED) != KeyOption.KeyData.Options.SysReqPressed) ||
   1069         (CompareMem (&Hotkey->KeyData[Index].Key, &KeyOption.Keys[Index], sizeof (EFI_INPUT_KEY)) != 0)
   1070         ) {
   1071         //
   1072         // Break when any field doesn't match
   1073         //
   1074         Match = FALSE;
   1075         break;
   1076       }
   1077     }
   1078 
   1079     if (Match) {
   1080       Link = RemoveEntryList (Link);
   1081       FreePool (Hotkey);
   1082     } else {
   1083       Link = GetNextNode (&mBmHotkeyList, Link);
   1084     }
   1085   }
   1086 
   1087   //
   1088   // Delete the key option from the variable
   1089   //
   1090   Status     = EFI_NOT_FOUND;
   1091   KeyOptions = BmGetKeyOptions (&KeyOptionCount);
   1092   for (Index = 0; Index < KeyOptionCount; Index++) {
   1093     if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
   1094         (CompareMem (
   1095            KeyOptions[Index].Keys, KeyOption.Keys,
   1096            KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)
   1097        ) {
   1098       UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptions[Index].OptionNumber);
   1099       Status = gRT->SetVariable (
   1100                  KeyOptionName,
   1101                  &gEfiGlobalVariableGuid,
   1102                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
   1103                  0,
   1104                  NULL
   1105                  );
   1106       //
   1107       // Return the deleted key option in case needed by caller
   1108       //
   1109       if (DeletedOption != NULL) {
   1110         CopyMem (DeletedOption, &KeyOptions[Index], sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
   1111       }
   1112       break;
   1113     }
   1114   }
   1115   BmFreeKeyOptions (KeyOptions, KeyOptionCount);
   1116 
   1117   EfiReleaseLock (&mBmHotkeyLock);
   1118 
   1119   return Status;
   1120 }
   1121