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