diff options
| author | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
|---|---|---|
| committer | kx <kx@radix.pro> | 2024-06-04 06:43:08 +0300 |
| commit | 7e2ccccace24636f29ddc210b94606abd4c7e42b (patch) | |
| tree | 545d43ea12671048956cafeb12303e84d601e571 /MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c | |
| parent | 27b044605cd5f6b33a3d231576003850b3fe305b (diff) | |
| download | edk2-trunk.tar.xz | |
Renormalized end-of-lines from master@27b044605cd5f6b33a3d231576003850b3fe305btrunk
Diffstat (limited to 'MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c')
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c | 2382 |
1 files changed, 1191 insertions, 1191 deletions
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c b/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c index 90bee73a23..54eea738cf 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c @@ -1,1191 +1,1191 @@ -/** @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;
-}
+/** @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; +} |
