Home | History | Annotate | Download | only in BdsDxe
      1 /** @file
      2   Provides a way for 3rd party applications to register themselves for launch by the
      3   Boot Manager based on hot key
      4 
      5 Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "Hotkey.h"
     17 
     18 
     19 LIST_ENTRY        mHotkeyList = INITIALIZE_LIST_HEAD_VARIABLE (mHotkeyList);
     20 BDS_COMMON_OPTION *mHotkeyBootOption = NULL;
     21 EFI_EVENT         mHotkeyEvent;
     22 VOID              *mHotkeyRegistration;
     23 
     24 
     25 /**
     26   Check if the Key Option is valid or not.
     27 
     28   @param KeyOption       The Hot Key Option to be checked.
     29 
     30   @retval  TRUE          The Hot Key Option is valid.
     31   @retval  FALSE         The Hot Key Option is invalid.
     32 
     33 **/
     34 BOOLEAN
     35 IsKeyOptionValid (
     36   IN EFI_KEY_OPTION     *KeyOption
     37 )
     38 {
     39   UINT16   BootOptionName[10];
     40   UINT8    *BootOptionVar;
     41   UINTN    BootOptionSize;
     42   UINT32   Crc;
     43 
     44   //
     45   // Check whether corresponding Boot Option exist
     46   //
     47   UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", KeyOption->BootOption);
     48   BootOptionVar = BdsLibGetVariableAndSize (
     49                     BootOptionName,
     50                     &gEfiGlobalVariableGuid,
     51                     &BootOptionSize
     52                     );
     53 
     54   if (BootOptionVar == NULL || BootOptionSize == 0) {
     55     return FALSE;
     56   }
     57 
     58   //
     59   // Check CRC for Boot Option
     60   //
     61   gBS->CalculateCrc32 (BootOptionVar, BootOptionSize, &Crc);
     62   FreePool (BootOptionVar);
     63 
     64   return (BOOLEAN) ((KeyOption->BootOptionCrc == Crc) ? TRUE : FALSE);
     65 }
     66 
     67 /**
     68   Try to boot the boot option triggered by hotkey.
     69   @retval  EFI_SUCCESS             There is HotkeyBootOption & it is processed
     70   @retval  EFI_NOT_FOUND           There is no HotkeyBootOption
     71 **/
     72 EFI_STATUS
     73 HotkeyBoot (
     74   VOID
     75   )
     76 {
     77   EFI_STATUS           Status;
     78   UINTN                ExitDataSize;
     79   CHAR16               *ExitData;
     80 
     81   if (mHotkeyBootOption == NULL) {
     82     return EFI_NOT_FOUND;
     83   }
     84 
     85   BdsLibConnectDevicePath (mHotkeyBootOption->DevicePath);
     86 
     87   //
     88   // Clear the screen before launch this BootOption
     89   //
     90   gST->ConOut->Reset (gST->ConOut, FALSE);
     91 
     92   Status = BdsLibBootViaBootOption (mHotkeyBootOption, mHotkeyBootOption->DevicePath, &ExitDataSize, &ExitData);
     93 
     94   if (EFI_ERROR (Status)) {
     95     //
     96     // Call platform action to indicate the boot fail
     97     //
     98     mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED));
     99     PlatformBdsBootFail (mHotkeyBootOption, Status, ExitData, ExitDataSize);
    100   } else {
    101     //
    102     // Call platform action to indicate the boot success
    103     //
    104     mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED));
    105     PlatformBdsBootSuccess (mHotkeyBootOption);
    106   }
    107   FreePool (mHotkeyBootOption->Description);
    108   FreePool (mHotkeyBootOption->DevicePath);
    109   FreePool (mHotkeyBootOption->LoadOptions);
    110   FreePool (mHotkeyBootOption);
    111 
    112   mHotkeyBootOption = NULL;
    113 
    114   return EFI_SUCCESS;
    115 }
    116 
    117 /**
    118 
    119   This is the common notification function for HotKeys, it will be registered
    120   with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.
    121 
    122   @param KeyData         A pointer to a buffer that is filled in with the keystroke
    123                          information for the key that was pressed.
    124 
    125   @retval  EFI_SUCCESS   KeyData is successfully processed.
    126   @return  EFI_NOT_FOUND Fail to find boot option variable.
    127 **/
    128 EFI_STATUS
    129 EFIAPI
    130 HotkeyCallback (
    131   IN EFI_KEY_DATA     *KeyData
    132 )
    133 {
    134   BOOLEAN            HotkeyCatched;
    135   LIST_ENTRY         BootLists;
    136   LIST_ENTRY         *Link;
    137   BDS_HOTKEY_OPTION  *Hotkey;
    138   UINT16             Buffer[10];
    139   EFI_STATUS         Status;
    140   EFI_KEY_DATA       *HotkeyData;
    141 
    142   if (mHotkeyBootOption != NULL) {
    143     //
    144     // Do not process sequential hotkey stroke until the current boot option returns
    145     //
    146     return EFI_SUCCESS;
    147   }
    148 
    149   Status                 = EFI_SUCCESS;
    150 
    151   for ( Link = GetFirstNode (&mHotkeyList)
    152       ; !IsNull (&mHotkeyList, Link)
    153       ; Link = GetNextNode (&mHotkeyList, Link)
    154       ) {
    155     HotkeyCatched = FALSE;
    156     Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link);
    157 
    158     //
    159     // Is this Key Stroke we are waiting for?
    160     //
    161     ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0])));
    162     HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];
    163     if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&
    164         (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&
    165         (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ?
    166           (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE
    167         )
    168        ) {
    169       //
    170       // For hotkey of key combination, transit to next waiting state
    171       //
    172       Hotkey->WaitingKey++;
    173 
    174       if (Hotkey->WaitingKey == Hotkey->CodeCount) {
    175         //
    176         // Received the whole key stroke sequence
    177         //
    178         HotkeyCatched = TRUE;
    179       }
    180     } else {
    181       //
    182       // Receive an unexpected key stroke, reset to initial waiting state
    183       //
    184       Hotkey->WaitingKey = 0;
    185     }
    186 
    187     if (HotkeyCatched) {
    188       //
    189       // Reset to initial waiting state
    190       //
    191       Hotkey->WaitingKey = 0;
    192 
    193       //
    194       // Launch its BootOption
    195       //
    196       InitializeListHead (&BootLists);
    197 
    198       UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", Hotkey->BootOptionNumber);
    199       mHotkeyBootOption = BdsLibVariableToOption (&BootLists, Buffer);
    200     }
    201   }
    202 
    203   return Status;
    204 }
    205 
    206 /**
    207   Register the common HotKey notify function to given SimpleTextInEx protocol instance.
    208 
    209   @param SimpleTextInEx  Simple Text Input Ex protocol instance
    210 
    211   @retval  EFI_SUCCESS            Register hotkey notification function successfully.
    212   @retval  EFI_OUT_OF_RESOURCES   Unable to allocate necessary data structures.
    213 
    214 **/
    215 EFI_STATUS
    216 HotkeyRegisterNotify (
    217   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *SimpleTextInEx
    218 )
    219 {
    220   UINTN              Index;
    221   EFI_STATUS         Status;
    222   LIST_ENTRY         *Link;
    223   BDS_HOTKEY_OPTION  *Hotkey;
    224 
    225   //
    226   // Register notification function for each hotkey
    227   //
    228   Link = GetFirstNode (&mHotkeyList);
    229 
    230   while (!IsNull (&mHotkeyList, Link)) {
    231     Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link);
    232 
    233     Index = 0;
    234     do {
    235       Status = SimpleTextInEx->RegisterKeyNotify (
    236                                  SimpleTextInEx,
    237                                  &Hotkey->KeyData[Index],
    238                                  HotkeyCallback,
    239                                  &Hotkey->NotifyHandle
    240                                  );
    241       if (EFI_ERROR (Status)) {
    242         //
    243         // some of the hotkey registry failed
    244         //
    245         return Status;
    246       }
    247       Index ++;
    248     } while ((Index < Hotkey->CodeCount) && (Index < (sizeof (Hotkey->KeyData) / sizeof (EFI_KEY_DATA))));
    249 
    250     Link = GetNextNode (&mHotkeyList, Link);
    251   }
    252 
    253   return EFI_SUCCESS;
    254 }
    255 
    256 /**
    257   Callback function for SimpleTextInEx protocol install events
    258 
    259   @param Event           the event that is signaled.
    260   @param Context         not used here.
    261 
    262 **/
    263 VOID
    264 EFIAPI
    265 HotkeyEvent (
    266   IN EFI_EVENT    Event,
    267   IN VOID         *Context
    268   )
    269 {
    270   EFI_STATUS                         Status;
    271   UINTN                              BufferSize;
    272   EFI_HANDLE                         Handle;
    273   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *SimpleTextInEx;
    274 
    275   while (TRUE) {
    276     BufferSize = sizeof (EFI_HANDLE);
    277     Status = gBS->LocateHandle (
    278                     ByRegisterNotify,
    279                     NULL,
    280                     mHotkeyRegistration,
    281                     &BufferSize,
    282                     &Handle
    283                     );
    284     if (EFI_ERROR (Status)) {
    285       //
    286       // If no more notification events exist
    287       //
    288       return ;
    289     }
    290 
    291     Status = gBS->HandleProtocol (
    292                     Handle,
    293                     &gEfiSimpleTextInputExProtocolGuid,
    294                     (VOID **) &SimpleTextInEx
    295                     );
    296     ASSERT_EFI_ERROR (Status);
    297 
    298     HotkeyRegisterNotify (SimpleTextInEx);
    299   }
    300 }
    301 
    302 /**
    303   Insert Key Option to hotkey list.
    304 
    305   @param KeyOption       The Hot Key Option to be added to hotkey list.
    306 
    307   @retval EFI_SUCCESS           Add to hotkey list success.
    308   @retval EFI_OUT_OF_RESOURCES  Fail to allocate memory resource.
    309 **/
    310 EFI_STATUS
    311 HotkeyInsertList (
    312   IN EFI_KEY_OPTION     *KeyOption
    313 )
    314 {
    315   BDS_HOTKEY_OPTION  *HotkeyLeft;
    316   BDS_HOTKEY_OPTION  *HotkeyRight;
    317   UINTN              Index;
    318   EFI_BOOT_KEY_DATA  KeyOptions;
    319   UINT32             KeyShiftStateLeft;
    320   UINT32             KeyShiftStateRight;
    321   EFI_INPUT_KEY      *InputKey;
    322   EFI_KEY_DATA       *KeyData;
    323 
    324   HotkeyLeft = AllocateZeroPool (sizeof (BDS_HOTKEY_OPTION));
    325   if (HotkeyLeft == NULL) {
    326     return EFI_OUT_OF_RESOURCES;
    327   }
    328 
    329   HotkeyLeft->Signature = BDS_HOTKEY_OPTION_SIGNATURE;
    330   HotkeyLeft->BootOptionNumber = KeyOption->BootOption;
    331 
    332   KeyOptions = KeyOption->KeyData;
    333 
    334   HotkeyLeft->CodeCount = (UINT8) KeyOptions.Options.InputKeyCount;
    335 
    336   //
    337   // Map key shift state from KeyOptions to EFI_KEY_DATA.KeyState
    338   //
    339   KeyShiftStateRight = EFI_SHIFT_STATE_VALID;
    340   if (KeyOptions.Options.ShiftPressed) {
    341     KeyShiftStateRight |= EFI_RIGHT_SHIFT_PRESSED;
    342   }
    343   if (KeyOptions.Options.ControlPressed) {
    344     KeyShiftStateRight |= EFI_RIGHT_CONTROL_PRESSED;
    345   }
    346   if (KeyOptions.Options.AltPressed) {
    347     KeyShiftStateRight |= EFI_RIGHT_ALT_PRESSED;
    348   }
    349   if (KeyOptions.Options.LogoPressed) {
    350     KeyShiftStateRight |= EFI_RIGHT_LOGO_PRESSED;
    351   }
    352   if (KeyOptions.Options.MenuPressed) {
    353     KeyShiftStateRight |= EFI_MENU_KEY_PRESSED;
    354   }
    355   if (KeyOptions.Options.SysReqPressed) {
    356     KeyShiftStateRight |= EFI_SYS_REQ_PRESSED;
    357   }
    358 
    359   KeyShiftStateLeft = (KeyShiftStateRight & 0xffffff00) | ((KeyShiftStateRight & 0xff) << 1);
    360 
    361   InputKey = (EFI_INPUT_KEY *) (((UINT8 *) KeyOption) + sizeof (EFI_KEY_OPTION));
    362 
    363   Index = 0;
    364   KeyData = &HotkeyLeft->KeyData[0];
    365   do {
    366     //
    367     // If Key CodeCount is 0, then only KeyData[0] is used;
    368     // if Key CodeCount is n, then KeyData[0]~KeyData[n-1] are used
    369     //
    370     KeyData->Key.ScanCode = InputKey[Index].ScanCode;
    371     KeyData->Key.UnicodeChar = InputKey[Index].UnicodeChar;
    372     KeyData->KeyState.KeyShiftState = KeyShiftStateLeft;
    373 
    374     Index++;
    375     KeyData++;
    376   } while (Index < HotkeyLeft->CodeCount);
    377   InsertTailList (&mHotkeyList, &HotkeyLeft->Link);
    378 
    379   if (KeyShiftStateLeft != KeyShiftStateRight) {
    380     //
    381     // Need an extra hotkey for shift key on right
    382     //
    383     HotkeyRight = AllocateCopyPool (sizeof (BDS_HOTKEY_OPTION), HotkeyLeft);
    384     if (HotkeyRight == NULL) {
    385       return EFI_OUT_OF_RESOURCES;
    386     }
    387 
    388     Index = 0;
    389     KeyData = &HotkeyRight->KeyData[0];
    390     do {
    391       //
    392       // Key.ScanCode and Key.UnicodeChar have already been initialized,
    393       // only need to update KeyState.KeyShiftState
    394       //
    395       KeyData->KeyState.KeyShiftState = KeyShiftStateRight;
    396 
    397       Index++;
    398       KeyData++;
    399     } while (Index < HotkeyRight->CodeCount);
    400     InsertTailList (&mHotkeyList, &HotkeyRight->Link);
    401   }
    402 
    403   return EFI_SUCCESS;
    404 }
    405 
    406 /**
    407   Return TRUE when the variable pointed by Name and Guid is a Key#### variable.
    408 
    409   @param Name         The name of the variable.
    410   @param Guid         The GUID of the variable.
    411   @param OptionNumber Return the option number parsed from the Name.
    412 
    413   @retval TRUE  The variable pointed by Name and Guid is a Key#### variable.
    414   @retval FALSE The variable pointed by Name and Guid isn't a Key#### variable.
    415 **/
    416 BOOLEAN
    417 IsKeyOptionVariable (
    418   CHAR16        *Name,
    419   EFI_GUID      *Guid,
    420   UINT16        *OptionNumber
    421   )
    422 {
    423   UINTN         Index;
    424 
    425   if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) ||
    426       (StrSize (Name) != sizeof (L"Key####")) ||
    427       (StrnCmp (Name, L"Key", 3) != 0)
    428      ) {
    429     return FALSE;
    430   }
    431 
    432   *OptionNumber = 0;
    433   for (Index = 3; Index < 7; Index++) {
    434     if ((Name[Index] >= L'0') && (Name[Index] <= L'9')) {
    435       *OptionNumber = *OptionNumber * 16 + Name[Index] - L'0';
    436     } else if ((Name[Index] >= L'A') && (Name[Index] <= L'F')) {
    437       *OptionNumber = *OptionNumber * 16 + Name[Index] - L'A' + 10;
    438     } else {
    439       return FALSE;
    440     }
    441   }
    442 
    443   return TRUE;
    444 }
    445 
    446 /**
    447   Return an array of key option numbers.
    448 
    449   @param Count       Return the count of key option numbers.
    450 
    451   @return UINT16*    Pointer to an array of key option numbers;
    452 **/
    453 UINT16 *
    454 EFIAPI
    455 HotkeyGetOptionNumbers (
    456   OUT UINTN     *Count
    457   )
    458 {
    459   EFI_STATUS                  Status;
    460   UINTN                       Index;
    461   CHAR16                      *Name;
    462   EFI_GUID                    Guid;
    463   UINTN                       NameSize;
    464   UINTN                       NewNameSize;
    465   UINT16                      *OptionNumbers;
    466   UINT16                      OptionNumber;
    467 
    468   if (Count == NULL) {
    469     return NULL;
    470   }
    471 
    472   *Count        = 0;
    473   OptionNumbers = NULL;
    474 
    475   NameSize = sizeof (CHAR16);
    476   Name     = AllocateZeroPool (NameSize);
    477   ASSERT (Name != NULL);
    478   while (TRUE) {
    479     NewNameSize = NameSize;
    480     Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
    481     if (Status == EFI_BUFFER_TOO_SMALL) {
    482       Name = ReallocatePool (NameSize, NewNameSize, Name);
    483       ASSERT (Name != NULL);
    484       Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
    485       NameSize = NewNameSize;
    486     }
    487 
    488     if (Status == EFI_NOT_FOUND) {
    489       break;
    490     }
    491     ASSERT_EFI_ERROR (Status);
    492 
    493     if (IsKeyOptionVariable (Name ,&Guid, &OptionNumber)) {
    494       OptionNumbers = ReallocatePool (
    495                         *Count * sizeof (UINT16),
    496                         (*Count + 1) * sizeof (UINT16),
    497                         OptionNumbers
    498                         );
    499       ASSERT (OptionNumbers != NULL);
    500       for (Index = 0; Index < *Count; Index++) {
    501         if (OptionNumber < OptionNumbers[Index]) {
    502           break;
    503         }
    504       }
    505       CopyMem (&OptionNumbers[Index + 1], &OptionNumbers[Index], (*Count - Index) * sizeof (UINT16));
    506       OptionNumbers[Index] = OptionNumber;
    507       (*Count)++;
    508     }
    509   }
    510 
    511   FreePool (Name);
    512 
    513   return OptionNumbers;
    514 }
    515 
    516 /**
    517 
    518   Process all the "Key####" variables, associate Hotkeys with corresponding Boot Options.
    519 
    520   @retval  EFI_SUCCESS    Hotkey services successfully initialized.
    521 **/
    522 EFI_STATUS
    523 InitializeHotkeyService (
    524   VOID
    525   )
    526 {
    527   EFI_STATUS      Status;
    528   UINT32          BootOptionSupport;
    529   UINT16          *KeyOptionNumbers;
    530   UINTN           KeyOptionCount;
    531   UINTN           Index;
    532   CHAR16          KeyOptionName[8];
    533   EFI_KEY_OPTION  *KeyOption;
    534 
    535   //
    536   // Export our capability - EFI_BOOT_OPTION_SUPPORT_KEY and EFI_BOOT_OPTION_SUPPORT_APP.
    537   // with maximum number of key presses of 3
    538   // Do not report the hotkey capability if PcdConInConnectOnDemand is enabled.
    539   //
    540   BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP;
    541   if (!PcdGetBool (PcdConInConnectOnDemand)) {
    542     BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY;
    543     SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3);
    544   }
    545 
    546   Status = gRT->SetVariable (
    547                   L"BootOptionSupport",
    548                   &gEfiGlobalVariableGuid,
    549                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
    550                   sizeof (UINT32),
    551                   &BootOptionSupport
    552                   );
    553   //
    554   // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail.
    555   //
    556   ASSERT_EFI_ERROR (Status);
    557 
    558   KeyOptionNumbers = HotkeyGetOptionNumbers (&KeyOptionCount);
    559   for (Index = 0; Index < KeyOptionCount; Index ++) {
    560     UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumbers[Index]);
    561     GetEfiGlobalVariable2 (KeyOptionName, (VOID **) &KeyOption, NULL);
    562     ASSERT (KeyOption != NULL);
    563     if (IsKeyOptionValid (KeyOption)) {
    564       HotkeyInsertList (KeyOption);
    565     }
    566     FreePool (KeyOption);
    567   }
    568 
    569   if (KeyOptionNumbers != NULL) {
    570     FreePool (KeyOptionNumbers);
    571   }
    572 
    573   //
    574   // Register Protocol notify for Hotkey service
    575   //
    576   Status = gBS->CreateEvent (
    577                   EVT_NOTIFY_SIGNAL,
    578                   TPL_CALLBACK,
    579                   HotkeyEvent,
    580                   NULL,
    581                   &mHotkeyEvent
    582                   );
    583   ASSERT_EFI_ERROR (Status);
    584 
    585   //
    586   // Register for protocol notifications on this event
    587   //
    588   Status = gBS->RegisterProtocolNotify (
    589                   &gEfiSimpleTextInputExProtocolGuid,
    590                   mHotkeyEvent,
    591                   &mHotkeyRegistration
    592                   );
    593   ASSERT_EFI_ERROR (Status);
    594 
    595   return Status;
    596 }
    597 
    598