| /** @file | |
| Hotkey library functions. | |
| Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR> | |
| (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #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; | |
| /** | |
| 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 ( | |
| IN CONST EFI_BOOT_MANAGER_KEY_OPTION *KeyOption | |
| ) | |
| { | |
| return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys) | |
| + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY); | |
| } | |
| /** | |
| Check whether the input key option is valid. | |
| @param KeyOption Key option. | |
| @param KeyOptionSize Size of the key option. | |
| @retval TRUE Input key option is valid. | |
| @retval FALSE Input key option is not valid. | |
| **/ | |
| BOOLEAN | |
| BmIsKeyOptionValid ( | |
| IN CONST EFI_BOOT_MANAGER_KEY_OPTION *KeyOption, | |
| IN UINTN KeyOptionSize | |
| ) | |
| { | |
| UINT16 OptionName[BM_OPTION_NAME_LEN]; | |
| UINT8 *BootOption; | |
| UINTN BootOptionSize; | |
| UINT32 Crc; | |
| if (BmSizeOfKeyOption (KeyOption) != KeyOptionSize) { | |
| return FALSE; | |
| } | |
| // | |
| // Check whether corresponding Boot Option exist | |
| // | |
| UnicodeSPrint ( | |
| OptionName, | |
| sizeof (OptionName), | |
| L"%s%04x", | |
| mBmLoadOptionName[LoadOptionTypeBoot], | |
| 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; | |
| UINTN Uint; | |
| 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++) { | |
| Uint = BmCharToUint (Name[Index]); | |
| if (Uint == -1) { | |
| return FALSE; | |
| } else { | |
| *OptionNumber = (UINT16)Uint + *OptionNumber * 0x10; | |
| } | |
| } | |
| return TRUE; | |
| } | |
| typedef struct { | |
| EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions; | |
| UINTN KeyOptionCount; | |
| } BM_COLLECT_KEY_OPTIONS_PARAM; | |
| /** | |
| Visitor function to collect the key options from NV storage. | |
| @param Name Variable name. | |
| @param Guid Variable GUID. | |
| @param Context The same context passed to BmForEachVariable. | |
| **/ | |
| VOID | |
| BmCollectKeyOptions ( | |
| CHAR16 *Name, | |
| EFI_GUID *Guid, | |
| VOID *Context | |
| ) | |
| { | |
| UINTN Index; | |
| BM_COLLECT_KEY_OPTIONS_PARAM *Param; | |
| VOID *KeyOption; | |
| UINT16 OptionNumber; | |
| UINTN KeyOptionSize; | |
| Param = (BM_COLLECT_KEY_OPTIONS_PARAM *)Context; | |
| if (BmIsKeyOptionVariable (Name, Guid, &OptionNumber)) { | |
| GetEfiGlobalVariable2 (Name, &KeyOption, &KeyOptionSize); | |
| ASSERT (KeyOption != NULL); | |
| if (BmIsKeyOptionValid (KeyOption, KeyOptionSize)) { | |
| Param->KeyOptions = ReallocatePool ( | |
| Param->KeyOptionCount * sizeof (EFI_BOOT_MANAGER_KEY_OPTION), | |
| (Param->KeyOptionCount + 1) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION), | |
| Param->KeyOptions | |
| ); | |
| ASSERT (Param->KeyOptions != NULL); | |
| // | |
| // Insert the key option in order | |
| // | |
| for (Index = 0; Index < Param->KeyOptionCount; Index++) { | |
| if (OptionNumber < Param->KeyOptions[Index].OptionNumber) { | |
| break; | |
| } | |
| } | |
| CopyMem (&Param->KeyOptions[Index + 1], &Param->KeyOptions[Index], (Param->KeyOptionCount - Index) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION)); | |
| CopyMem (&Param->KeyOptions[Index], KeyOption, KeyOptionSize); | |
| Param->KeyOptions[Index].OptionNumber = OptionNumber; | |
| Param->KeyOptionCount++; | |
| } | |
| FreePool (KeyOption); | |
| } | |
| } | |
| /** | |
| 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 | |
| ) | |
| { | |
| BM_COLLECT_KEY_OPTIONS_PARAM Param; | |
| if (Count == NULL) { | |
| return NULL; | |
| } | |
| Param.KeyOptions = NULL; | |
| Param.KeyOptionCount = 0; | |
| BmForEachVariable (BmCollectKeyOptions, (VOID *)&Param); | |
| *Count = Param.KeyOptionCount; | |
| return Param.KeyOptions; | |
| } | |
| /** | |
| 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[BM_OPTION_NAME_LEN]; | |
| 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 ((DEBUG_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"%s%04x", | |
| mBmLoadOptionName[LoadOptionTypeBoot], | |
| Hotkey->BootOption | |
| ); | |
| Status = EfiBootManagerVariableToLoadOption (OptionName, &mBmHotkeyBootOption); | |
| DEBUG ((DEBUG_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status)); | |
| if (EFI_ERROR (Status)) { | |
| mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned; | |
| } | |
| } else { | |
| DEBUG ((DEBUG_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; | |
| } | |
| /** | |
| Return the active Simple Text Input Ex handle array. | |
| If the SystemTable.ConsoleInHandle is NULL, the function returns all | |
| founded Simple Text Input Ex handles. | |
| Otherwise, it just returns the ConsoleInHandle. | |
| @param Count Return the handle count. | |
| @retval The active console handles. | |
| **/ | |
| EFI_HANDLE * | |
| BmGetActiveConsoleIn ( | |
| OUT UINTN *Count | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE *Handles; | |
| Handles = NULL; | |
| *Count = 0; | |
| if (gST->ConsoleInHandle != NULL) { | |
| Status = gBS->OpenProtocol ( | |
| gST->ConsoleInHandle, | |
| &gEfiSimpleTextInputExProtocolGuid, | |
| NULL, | |
| gImageHandle, | |
| NULL, | |
| EFI_OPEN_PROTOCOL_TEST_PROTOCOL | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| Handles = AllocateCopyPool (sizeof (EFI_HANDLE), &gST->ConsoleInHandle); | |
| if (Handles != NULL) { | |
| *Count = 1; | |
| } | |
| } | |
| } else { | |
| Status = gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiSimpleTextInputExProtocolGuid, | |
| NULL, | |
| Count, | |
| &Handles | |
| ); | |
| } | |
| return Handles; | |
| } | |
| /** | |
| 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; | |
| Handles = BmGetActiveConsoleIn (&HandleCount); | |
| 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 ((DEBUG_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status)); | |
| } | |
| } | |
| } | |
| if (Handles != NULL) { | |
| FreePool (Handles); | |
| } | |
| 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 (( | |
| DEBUG_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 <= ARRAY_SIZE (KeyShiftStates)); | |
| EfiAcquireLock (&mBmHotkeyLock); | |
| Handles = BmGetActiveConsoleIn (&HandleCount); | |
| 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); | |
| for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) { | |
| Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **)&TxtInEx); | |
| ASSERT_EFI_ERROR (Status); | |
| BmRegisterHotkeyNotify (TxtInEx, Hotkey); | |
| } | |
| } | |
| if (Handles != NULL) { | |
| FreePool (Handles); | |
| } | |
| 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 ((DEBUG_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; | |
| GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **)&BootOptionSupport, NULL); | |
| if (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 ((DEBUG_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n")); | |
| return EFI_UNSUPPORTED; | |
| } | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_WAIT, | |
| TPL_CALLBACK, | |
| EfiEventEmptyFunction, | |
| 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); | |
| } | |
| // | |
| // Hook hotkey on every future SimpleTextInputEx instance when | |
| // SystemTable.ConsoleInHandle == NULL, which means the console | |
| // manager (ConSplitter) is absent. | |
| // | |
| if (gST->ConsoleInHandle == NULL) { | |
| 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[BM_OPTION_NAME_LEN]; | |
| EFI_BOOT_MANAGER_KEY_OPTION KeyOption; | |
| EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions; | |
| UINTN KeyOptionCount; | |
| UINTN Index; | |
| UINTN KeyOptionNumber; | |
| CHAR16 KeyOptionName[sizeof ("Key####")]; | |
| UnicodeSPrint ( | |
| BootOptionName, | |
| sizeof (BootOptionName), | |
| L"%s%04x", | |
| mBmLoadOptionName[LoadOptionTypeBoot], | |
| 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 ("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; | |
| } |