/** @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 ((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;
}