/** @file | |
Provides a way for 3rd party applications to register themselves for launch by the | |
Boot Manager based on hot key | |
Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> | |
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 "Hotkey.h" | |
LIST_ENTRY mHotkeyList = INITIALIZE_LIST_HEAD_VARIABLE (mHotkeyList); | |
BDS_COMMON_OPTION *mHotkeyBootOption = NULL; | |
EFI_EVENT mHotkeyEvent; | |
VOID *mHotkeyRegistration; | |
/** | |
Check if the Key Option is valid or not. | |
@param KeyOption The Hot Key Option to be checked. | |
@retval TRUE The Hot Key Option is valid. | |
@retval FALSE The Hot Key Option is invalid. | |
**/ | |
BOOLEAN | |
IsKeyOptionValid ( | |
IN EFI_KEY_OPTION *KeyOption | |
) | |
{ | |
UINT16 BootOptionName[10]; | |
UINT8 *BootOptionVar; | |
UINTN BootOptionSize; | |
UINT32 Crc; | |
// | |
// Check whether corresponding Boot Option exist | |
// | |
UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", KeyOption->BootOption); | |
BootOptionVar = BdsLibGetVariableAndSize ( | |
BootOptionName, | |
&gEfiGlobalVariableGuid, | |
&BootOptionSize | |
); | |
if (BootOptionVar == NULL || BootOptionSize == 0) { | |
return FALSE; | |
} | |
// | |
// Check CRC for Boot Option | |
// | |
gBS->CalculateCrc32 (BootOptionVar, BootOptionSize, &Crc); | |
FreePool (BootOptionVar); | |
return (BOOLEAN) ((KeyOption->BootOptionCrc == Crc) ? TRUE : FALSE); | |
} | |
/** | |
Try to boot the boot option triggered by hotkey. | |
@retval EFI_SUCCESS There is HotkeyBootOption & it is processed | |
@retval EFI_NOT_FOUND There is no HotkeyBootOption | |
**/ | |
EFI_STATUS | |
HotkeyBoot ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN ExitDataSize; | |
CHAR16 *ExitData; | |
if (mHotkeyBootOption == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
BdsLibConnectDevicePath (mHotkeyBootOption->DevicePath); | |
// | |
// Clear the screen before launch this BootOption | |
// | |
gST->ConOut->Reset (gST->ConOut, FALSE); | |
Status = BdsLibBootViaBootOption (mHotkeyBootOption, mHotkeyBootOption->DevicePath, &ExitDataSize, &ExitData); | |
if (EFI_ERROR (Status)) { | |
// | |
// Call platform action to indicate the boot fail | |
// | |
mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED)); | |
PlatformBdsBootFail (mHotkeyBootOption, Status, ExitData, ExitDataSize); | |
} else { | |
// | |
// Call platform action to indicate the boot success | |
// | |
mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED)); | |
PlatformBdsBootSuccess (mHotkeyBootOption); | |
} | |
FreePool (mHotkeyBootOption->Description); | |
FreePool (mHotkeyBootOption->DevicePath); | |
FreePool (mHotkeyBootOption->LoadOptions); | |
FreePool (mHotkeyBootOption); | |
mHotkeyBootOption = NULL; | |
return EFI_SUCCESS; | |
} | |
/** | |
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 | |
HotkeyCallback ( | |
IN EFI_KEY_DATA *KeyData | |
) | |
{ | |
BOOLEAN HotkeyCatched; | |
LIST_ENTRY BootLists; | |
LIST_ENTRY *Link; | |
BDS_HOTKEY_OPTION *Hotkey; | |
UINT16 Buffer[10]; | |
EFI_STATUS Status; | |
EFI_KEY_DATA *HotkeyData; | |
if (mHotkeyBootOption != NULL) { | |
// | |
// Do not process sequential hotkey stroke until the current boot option returns | |
// | |
return EFI_SUCCESS; | |
} | |
Status = EFI_SUCCESS; | |
for ( Link = GetFirstNode (&mHotkeyList) | |
; !IsNull (&mHotkeyList, Link) | |
; Link = GetNextNode (&mHotkeyList, Link) | |
) { | |
HotkeyCatched = FALSE; | |
Hotkey = BDS_HOTKEY_OPTION_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 | |
) | |
) { | |
// | |
// For hotkey of key combination, transit to next waiting state | |
// | |
Hotkey->WaitingKey++; | |
if (Hotkey->WaitingKey == Hotkey->CodeCount) { | |
// | |
// Received the whole key stroke sequence | |
// | |
HotkeyCatched = TRUE; | |
} | |
} else { | |
// | |
// Receive an unexpected key stroke, reset to initial waiting state | |
// | |
Hotkey->WaitingKey = 0; | |
} | |
if (HotkeyCatched) { | |
// | |
// Reset to initial waiting state | |
// | |
Hotkey->WaitingKey = 0; | |
// | |
// Launch its BootOption | |
// | |
InitializeListHead (&BootLists); | |
UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", Hotkey->BootOptionNumber); | |
mHotkeyBootOption = BdsLibVariableToOption (&BootLists, Buffer); | |
} | |
} | |
return Status; | |
} | |
/** | |
Register the common HotKey notify function to given SimpleTextInEx protocol instance. | |
@param SimpleTextInEx Simple Text Input Ex protocol instance | |
@retval EFI_SUCCESS Register hotkey notification function successfully. | |
@retval EFI_OUT_OF_RESOURCES Unable to allocate necessary data structures. | |
**/ | |
EFI_STATUS | |
HotkeyRegisterNotify ( | |
IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx | |
) | |
{ | |
UINTN Index; | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
BDS_HOTKEY_OPTION *Hotkey; | |
// | |
// Register notification function for each hotkey | |
// | |
Link = GetFirstNode (&mHotkeyList); | |
while (!IsNull (&mHotkeyList, Link)) { | |
Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link); | |
Index = 0; | |
do { | |
Status = SimpleTextInEx->RegisterKeyNotify ( | |
SimpleTextInEx, | |
&Hotkey->KeyData[Index], | |
HotkeyCallback, | |
&Hotkey->NotifyHandle | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// some of the hotkey registry failed | |
// | |
return Status; | |
} | |
Index ++; | |
} while ((Index < Hotkey->CodeCount) && (Index < (sizeof (Hotkey->KeyData) / sizeof (EFI_KEY_DATA)))); | |
Link = GetNextNode (&mHotkeyList, Link); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Callback function for SimpleTextInEx protocol install events | |
@param Event the event that is signaled. | |
@param Context not used here. | |
**/ | |
VOID | |
EFIAPI | |
HotkeyEvent ( | |
IN EFI_EVENT Event, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN BufferSize; | |
EFI_HANDLE Handle; | |
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx; | |
while (TRUE) { | |
BufferSize = sizeof (EFI_HANDLE); | |
Status = gBS->LocateHandle ( | |
ByRegisterNotify, | |
NULL, | |
mHotkeyRegistration, | |
&BufferSize, | |
&Handle | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// If no more notification events exist | |
// | |
return ; | |
} | |
Status = gBS->HandleProtocol ( | |
Handle, | |
&gEfiSimpleTextInputExProtocolGuid, | |
(VOID **) &SimpleTextInEx | |
); | |
ASSERT_EFI_ERROR (Status); | |
HotkeyRegisterNotify (SimpleTextInEx); | |
} | |
} | |
/** | |
Insert Key Option to hotkey list. | |
@param KeyOption The Hot Key Option to be added to hotkey list. | |
@retval EFI_SUCCESS Add to hotkey list success. | |
@retval EFI_OUT_OF_RESOURCES Fail to allocate memory resource. | |
**/ | |
EFI_STATUS | |
HotkeyInsertList ( | |
IN EFI_KEY_OPTION *KeyOption | |
) | |
{ | |
BDS_HOTKEY_OPTION *HotkeyLeft; | |
BDS_HOTKEY_OPTION *HotkeyRight; | |
UINTN Index; | |
EFI_BOOT_KEY_DATA KeyOptions; | |
UINT32 KeyShiftStateLeft; | |
UINT32 KeyShiftStateRight; | |
EFI_INPUT_KEY *InputKey; | |
EFI_KEY_DATA *KeyData; | |
HotkeyLeft = AllocateZeroPool (sizeof (BDS_HOTKEY_OPTION)); | |
if (HotkeyLeft == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
HotkeyLeft->Signature = BDS_HOTKEY_OPTION_SIGNATURE; | |
HotkeyLeft->BootOptionNumber = KeyOption->BootOption; | |
KeyOptions = KeyOption->KeyData; | |
HotkeyLeft->CodeCount = (UINT8) KeyOptions.Options.InputKeyCount; | |
// | |
// Map key shift state from KeyOptions to EFI_KEY_DATA.KeyState | |
// | |
KeyShiftStateRight = EFI_SHIFT_STATE_VALID; | |
if (KeyOptions.Options.ShiftPressed) { | |
KeyShiftStateRight |= EFI_RIGHT_SHIFT_PRESSED; | |
} | |
if (KeyOptions.Options.ControlPressed) { | |
KeyShiftStateRight |= EFI_RIGHT_CONTROL_PRESSED; | |
} | |
if (KeyOptions.Options.AltPressed) { | |
KeyShiftStateRight |= EFI_RIGHT_ALT_PRESSED; | |
} | |
if (KeyOptions.Options.LogoPressed) { | |
KeyShiftStateRight |= EFI_RIGHT_LOGO_PRESSED; | |
} | |
if (KeyOptions.Options.MenuPressed) { | |
KeyShiftStateRight |= EFI_MENU_KEY_PRESSED; | |
} | |
if (KeyOptions.Options.SysReqPressed) { | |
KeyShiftStateRight |= EFI_SYS_REQ_PRESSED; | |
} | |
KeyShiftStateLeft = (KeyShiftStateRight & 0xffffff00) | ((KeyShiftStateRight & 0xff) << 1); | |
InputKey = (EFI_INPUT_KEY *) (((UINT8 *) KeyOption) + sizeof (EFI_KEY_OPTION)); | |
Index = 0; | |
KeyData = &HotkeyLeft->KeyData[0]; | |
do { | |
// | |
// If Key CodeCount is 0, then only KeyData[0] is used; | |
// if Key CodeCount is n, then KeyData[0]~KeyData[n-1] are used | |
// | |
KeyData->Key.ScanCode = InputKey[Index].ScanCode; | |
KeyData->Key.UnicodeChar = InputKey[Index].UnicodeChar; | |
KeyData->KeyState.KeyShiftState = KeyShiftStateLeft; | |
Index++; | |
KeyData++; | |
} while (Index < HotkeyLeft->CodeCount); | |
InsertTailList (&mHotkeyList, &HotkeyLeft->Link); | |
if (KeyShiftStateLeft != KeyShiftStateRight) { | |
// | |
// Need an extra hotkey for shift key on right | |
// | |
HotkeyRight = AllocateCopyPool (sizeof (BDS_HOTKEY_OPTION), HotkeyLeft); | |
if (HotkeyRight == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
Index = 0; | |
KeyData = &HotkeyRight->KeyData[0]; | |
do { | |
// | |
// Key.ScanCode and Key.UnicodeChar have already been initialized, | |
// only need to update KeyState.KeyShiftState | |
// | |
KeyData->KeyState.KeyShiftState = KeyShiftStateRight; | |
Index++; | |
KeyData++; | |
} while (Index < HotkeyRight->CodeCount); | |
InsertTailList (&mHotkeyList, &HotkeyRight->Link); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Return TRUE when the variable pointed by Name and Guid is a Key#### variable. | |
@param Name The name of the variable. | |
@param Guid The GUID of the variable. | |
@param OptionNumber Return the option number parsed from the Name. | |
@retval TRUE The variable pointed by Name and Guid is a Key#### variable. | |
@retval FALSE The variable pointed by Name and Guid isn't a Key#### variable. | |
**/ | |
BOOLEAN | |
IsKeyOptionVariable ( | |
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 an array of key option numbers. | |
@param Count Return the count of key option numbers. | |
@return UINT16* Pointer to an array of key option numbers; | |
**/ | |
UINT16 * | |
EFIAPI | |
HotkeyGetOptionNumbers ( | |
OUT UINTN *Count | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
CHAR16 *Name; | |
EFI_GUID Guid; | |
UINTN NameSize; | |
UINTN NewNameSize; | |
UINT16 *OptionNumbers; | |
UINT16 OptionNumber; | |
if (Count == NULL) { | |
return NULL; | |
} | |
*Count = 0; | |
OptionNumbers = 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 (IsKeyOptionVariable (Name ,&Guid, &OptionNumber)) { | |
OptionNumbers = ReallocatePool ( | |
*Count * sizeof (UINT16), | |
(*Count + 1) * sizeof (UINT16), | |
OptionNumbers | |
); | |
ASSERT (OptionNumbers != NULL); | |
for (Index = 0; Index < *Count; Index++) { | |
if (OptionNumber < OptionNumbers[Index]) { | |
break; | |
} | |
} | |
CopyMem (&OptionNumbers[Index + 1], &OptionNumbers[Index], (*Count - Index) * sizeof (UINT16)); | |
OptionNumbers[Index] = OptionNumber; | |
(*Count)++; | |
} | |
} | |
FreePool (Name); | |
return OptionNumbers; | |
} | |
/** | |
Process all the "Key####" variables, associate Hotkeys with corresponding Boot Options. | |
@retval EFI_SUCCESS Hotkey services successfully initialized. | |
**/ | |
EFI_STATUS | |
InitializeHotkeyService ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 BootOptionSupport; | |
UINT16 *KeyOptionNumbers; | |
UINTN KeyOptionCount; | |
UINTN Index; | |
CHAR16 KeyOptionName[8]; | |
EFI_KEY_OPTION *KeyOption; | |
// | |
// Export our capability - EFI_BOOT_OPTION_SUPPORT_KEY and EFI_BOOT_OPTION_SUPPORT_APP. | |
// with maximum number of key presses of 3 | |
// Do not report the hotkey capability if PcdConInConnectOnDemand is enabled. | |
// | |
BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP; | |
if (!PcdGetBool (PcdConInConnectOnDemand)) { | |
BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY; | |
SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3); | |
} | |
Status = gRT->SetVariable ( | |
L"BootOptionSupport", | |
&gEfiGlobalVariableGuid, | |
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
sizeof (UINT32), | |
&BootOptionSupport | |
); | |
// | |
// Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail. | |
// | |
ASSERT_EFI_ERROR (Status); | |
KeyOptionNumbers = HotkeyGetOptionNumbers (&KeyOptionCount); | |
for (Index = 0; Index < KeyOptionCount; Index ++) { | |
UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumbers[Index]); | |
GetEfiGlobalVariable2 (KeyOptionName, (VOID **) &KeyOption, NULL); | |
ASSERT (KeyOption != NULL); | |
if (IsKeyOptionValid (KeyOption)) { | |
HotkeyInsertList (KeyOption); | |
} | |
FreePool (KeyOption); | |
} | |
if (KeyOptionNumbers != NULL) { | |
FreePool (KeyOptionNumbers); | |
} | |
// | |
// Register Protocol notify for Hotkey service | |
// | |
Status = gBS->CreateEvent ( | |
EVT_NOTIFY_SIGNAL, | |
TPL_CALLBACK, | |
HotkeyEvent, | |
NULL, | |
&mHotkeyEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Register for protocol notifications on this event | |
// | |
Status = gBS->RegisterProtocolNotify ( | |
&gEfiSimpleTextInputExProtocolGuid, | |
mHotkeyEvent, | |
&mHotkeyRegistration | |
); | |
ASSERT_EFI_ERROR (Status); | |
return Status; | |
} | |