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