1 /** @file 2 Provides a way for 3rd party applications to register themselves for launch by the 3 Boot Manager based on hot key 4 5 Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR> 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions of the BSD License 8 which accompanies this distribution. The full text of the license may be found at 9 http://opensource.org/licenses/bsd-license.php 10 11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 13 14 **/ 15 16 #include "Hotkey.h" 17 18 19 LIST_ENTRY mHotkeyList = INITIALIZE_LIST_HEAD_VARIABLE (mHotkeyList); 20 BDS_COMMON_OPTION *mHotkeyBootOption = NULL; 21 EFI_EVENT mHotkeyEvent; 22 VOID *mHotkeyRegistration; 23 24 25 /** 26 Check if the Key Option is valid or not. 27 28 @param KeyOption The Hot Key Option to be checked. 29 30 @retval TRUE The Hot Key Option is valid. 31 @retval FALSE The Hot Key Option is invalid. 32 33 **/ 34 BOOLEAN 35 IsKeyOptionValid ( 36 IN EFI_KEY_OPTION *KeyOption 37 ) 38 { 39 UINT16 BootOptionName[10]; 40 UINT8 *BootOptionVar; 41 UINTN BootOptionSize; 42 UINT32 Crc; 43 44 // 45 // Check whether corresponding Boot Option exist 46 // 47 UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", KeyOption->BootOption); 48 BootOptionVar = BdsLibGetVariableAndSize ( 49 BootOptionName, 50 &gEfiGlobalVariableGuid, 51 &BootOptionSize 52 ); 53 54 if (BootOptionVar == NULL || BootOptionSize == 0) { 55 return FALSE; 56 } 57 58 // 59 // Check CRC for Boot Option 60 // 61 gBS->CalculateCrc32 (BootOptionVar, BootOptionSize, &Crc); 62 FreePool (BootOptionVar); 63 64 return (BOOLEAN) ((KeyOption->BootOptionCrc == Crc) ? TRUE : FALSE); 65 } 66 67 /** 68 Try to boot the boot option triggered by hotkey. 69 @retval EFI_SUCCESS There is HotkeyBootOption & it is processed 70 @retval EFI_NOT_FOUND There is no HotkeyBootOption 71 **/ 72 EFI_STATUS 73 HotkeyBoot ( 74 VOID 75 ) 76 { 77 EFI_STATUS Status; 78 UINTN ExitDataSize; 79 CHAR16 *ExitData; 80 81 if (mHotkeyBootOption == NULL) { 82 return EFI_NOT_FOUND; 83 } 84 85 BdsLibConnectDevicePath (mHotkeyBootOption->DevicePath); 86 87 // 88 // Clear the screen before launch this BootOption 89 // 90 gST->ConOut->Reset (gST->ConOut, FALSE); 91 92 Status = BdsLibBootViaBootOption (mHotkeyBootOption, mHotkeyBootOption->DevicePath, &ExitDataSize, &ExitData); 93 94 if (EFI_ERROR (Status)) { 95 // 96 // Call platform action to indicate the boot fail 97 // 98 mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED)); 99 PlatformBdsBootFail (mHotkeyBootOption, Status, ExitData, ExitDataSize); 100 } else { 101 // 102 // Call platform action to indicate the boot success 103 // 104 mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED)); 105 PlatformBdsBootSuccess (mHotkeyBootOption); 106 } 107 FreePool (mHotkeyBootOption->Description); 108 FreePool (mHotkeyBootOption->DevicePath); 109 FreePool (mHotkeyBootOption->LoadOptions); 110 FreePool (mHotkeyBootOption); 111 112 mHotkeyBootOption = NULL; 113 114 return EFI_SUCCESS; 115 } 116 117 /** 118 119 This is the common notification function for HotKeys, it will be registered 120 with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle. 121 122 @param KeyData A pointer to a buffer that is filled in with the keystroke 123 information for the key that was pressed. 124 125 @retval EFI_SUCCESS KeyData is successfully processed. 126 @return EFI_NOT_FOUND Fail to find boot option variable. 127 **/ 128 EFI_STATUS 129 EFIAPI 130 HotkeyCallback ( 131 IN EFI_KEY_DATA *KeyData 132 ) 133 { 134 BOOLEAN HotkeyCatched; 135 LIST_ENTRY BootLists; 136 LIST_ENTRY *Link; 137 BDS_HOTKEY_OPTION *Hotkey; 138 UINT16 Buffer[10]; 139 EFI_STATUS Status; 140 EFI_KEY_DATA *HotkeyData; 141 142 if (mHotkeyBootOption != NULL) { 143 // 144 // Do not process sequential hotkey stroke until the current boot option returns 145 // 146 return EFI_SUCCESS; 147 } 148 149 Status = EFI_SUCCESS; 150 151 for ( Link = GetFirstNode (&mHotkeyList) 152 ; !IsNull (&mHotkeyList, Link) 153 ; Link = GetNextNode (&mHotkeyList, Link) 154 ) { 155 HotkeyCatched = FALSE; 156 Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link); 157 158 // 159 // Is this Key Stroke we are waiting for? 160 // 161 ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0]))); 162 HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey]; 163 if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) && 164 (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) && 165 (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ? 166 (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE 167 ) 168 ) { 169 // 170 // For hotkey of key combination, transit to next waiting state 171 // 172 Hotkey->WaitingKey++; 173 174 if (Hotkey->WaitingKey == Hotkey->CodeCount) { 175 // 176 // Received the whole key stroke sequence 177 // 178 HotkeyCatched = TRUE; 179 } 180 } else { 181 // 182 // Receive an unexpected key stroke, reset to initial waiting state 183 // 184 Hotkey->WaitingKey = 0; 185 } 186 187 if (HotkeyCatched) { 188 // 189 // Reset to initial waiting state 190 // 191 Hotkey->WaitingKey = 0; 192 193 // 194 // Launch its BootOption 195 // 196 InitializeListHead (&BootLists); 197 198 UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", Hotkey->BootOptionNumber); 199 mHotkeyBootOption = BdsLibVariableToOption (&BootLists, Buffer); 200 } 201 } 202 203 return Status; 204 } 205 206 /** 207 Register the common HotKey notify function to given SimpleTextInEx protocol instance. 208 209 @param SimpleTextInEx Simple Text Input Ex protocol instance 210 211 @retval EFI_SUCCESS Register hotkey notification function successfully. 212 @retval EFI_OUT_OF_RESOURCES Unable to allocate necessary data structures. 213 214 **/ 215 EFI_STATUS 216 HotkeyRegisterNotify ( 217 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx 218 ) 219 { 220 UINTN Index; 221 EFI_STATUS Status; 222 LIST_ENTRY *Link; 223 BDS_HOTKEY_OPTION *Hotkey; 224 225 // 226 // Register notification function for each hotkey 227 // 228 Link = GetFirstNode (&mHotkeyList); 229 230 while (!IsNull (&mHotkeyList, Link)) { 231 Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link); 232 233 Index = 0; 234 do { 235 Status = SimpleTextInEx->RegisterKeyNotify ( 236 SimpleTextInEx, 237 &Hotkey->KeyData[Index], 238 HotkeyCallback, 239 &Hotkey->NotifyHandle 240 ); 241 if (EFI_ERROR (Status)) { 242 // 243 // some of the hotkey registry failed 244 // 245 return Status; 246 } 247 Index ++; 248 } while ((Index < Hotkey->CodeCount) && (Index < (sizeof (Hotkey->KeyData) / sizeof (EFI_KEY_DATA)))); 249 250 Link = GetNextNode (&mHotkeyList, Link); 251 } 252 253 return EFI_SUCCESS; 254 } 255 256 /** 257 Callback function for SimpleTextInEx protocol install events 258 259 @param Event the event that is signaled. 260 @param Context not used here. 261 262 **/ 263 VOID 264 EFIAPI 265 HotkeyEvent ( 266 IN EFI_EVENT Event, 267 IN VOID *Context 268 ) 269 { 270 EFI_STATUS Status; 271 UINTN BufferSize; 272 EFI_HANDLE Handle; 273 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx; 274 275 while (TRUE) { 276 BufferSize = sizeof (EFI_HANDLE); 277 Status = gBS->LocateHandle ( 278 ByRegisterNotify, 279 NULL, 280 mHotkeyRegistration, 281 &BufferSize, 282 &Handle 283 ); 284 if (EFI_ERROR (Status)) { 285 // 286 // If no more notification events exist 287 // 288 return ; 289 } 290 291 Status = gBS->HandleProtocol ( 292 Handle, 293 &gEfiSimpleTextInputExProtocolGuid, 294 (VOID **) &SimpleTextInEx 295 ); 296 ASSERT_EFI_ERROR (Status); 297 298 HotkeyRegisterNotify (SimpleTextInEx); 299 } 300 } 301 302 /** 303 Insert Key Option to hotkey list. 304 305 @param KeyOption The Hot Key Option to be added to hotkey list. 306 307 @retval EFI_SUCCESS Add to hotkey list success. 308 @retval EFI_OUT_OF_RESOURCES Fail to allocate memory resource. 309 **/ 310 EFI_STATUS 311 HotkeyInsertList ( 312 IN EFI_KEY_OPTION *KeyOption 313 ) 314 { 315 BDS_HOTKEY_OPTION *HotkeyLeft; 316 BDS_HOTKEY_OPTION *HotkeyRight; 317 UINTN Index; 318 EFI_BOOT_KEY_DATA KeyOptions; 319 UINT32 KeyShiftStateLeft; 320 UINT32 KeyShiftStateRight; 321 EFI_INPUT_KEY *InputKey; 322 EFI_KEY_DATA *KeyData; 323 324 HotkeyLeft = AllocateZeroPool (sizeof (BDS_HOTKEY_OPTION)); 325 if (HotkeyLeft == NULL) { 326 return EFI_OUT_OF_RESOURCES; 327 } 328 329 HotkeyLeft->Signature = BDS_HOTKEY_OPTION_SIGNATURE; 330 HotkeyLeft->BootOptionNumber = KeyOption->BootOption; 331 332 KeyOptions = KeyOption->KeyData; 333 334 HotkeyLeft->CodeCount = (UINT8) KeyOptions.Options.InputKeyCount; 335 336 // 337 // Map key shift state from KeyOptions to EFI_KEY_DATA.KeyState 338 // 339 KeyShiftStateRight = EFI_SHIFT_STATE_VALID; 340 if (KeyOptions.Options.ShiftPressed) { 341 KeyShiftStateRight |= EFI_RIGHT_SHIFT_PRESSED; 342 } 343 if (KeyOptions.Options.ControlPressed) { 344 KeyShiftStateRight |= EFI_RIGHT_CONTROL_PRESSED; 345 } 346 if (KeyOptions.Options.AltPressed) { 347 KeyShiftStateRight |= EFI_RIGHT_ALT_PRESSED; 348 } 349 if (KeyOptions.Options.LogoPressed) { 350 KeyShiftStateRight |= EFI_RIGHT_LOGO_PRESSED; 351 } 352 if (KeyOptions.Options.MenuPressed) { 353 KeyShiftStateRight |= EFI_MENU_KEY_PRESSED; 354 } 355 if (KeyOptions.Options.SysReqPressed) { 356 KeyShiftStateRight |= EFI_SYS_REQ_PRESSED; 357 } 358 359 KeyShiftStateLeft = (KeyShiftStateRight & 0xffffff00) | ((KeyShiftStateRight & 0xff) << 1); 360 361 InputKey = (EFI_INPUT_KEY *) (((UINT8 *) KeyOption) + sizeof (EFI_KEY_OPTION)); 362 363 Index = 0; 364 KeyData = &HotkeyLeft->KeyData[0]; 365 do { 366 // 367 // If Key CodeCount is 0, then only KeyData[0] is used; 368 // if Key CodeCount is n, then KeyData[0]~KeyData[n-1] are used 369 // 370 KeyData->Key.ScanCode = InputKey[Index].ScanCode; 371 KeyData->Key.UnicodeChar = InputKey[Index].UnicodeChar; 372 KeyData->KeyState.KeyShiftState = KeyShiftStateLeft; 373 374 Index++; 375 KeyData++; 376 } while (Index < HotkeyLeft->CodeCount); 377 InsertTailList (&mHotkeyList, &HotkeyLeft->Link); 378 379 if (KeyShiftStateLeft != KeyShiftStateRight) { 380 // 381 // Need an extra hotkey for shift key on right 382 // 383 HotkeyRight = AllocateCopyPool (sizeof (BDS_HOTKEY_OPTION), HotkeyLeft); 384 if (HotkeyRight == NULL) { 385 return EFI_OUT_OF_RESOURCES; 386 } 387 388 Index = 0; 389 KeyData = &HotkeyRight->KeyData[0]; 390 do { 391 // 392 // Key.ScanCode and Key.UnicodeChar have already been initialized, 393 // only need to update KeyState.KeyShiftState 394 // 395 KeyData->KeyState.KeyShiftState = KeyShiftStateRight; 396 397 Index++; 398 KeyData++; 399 } while (Index < HotkeyRight->CodeCount); 400 InsertTailList (&mHotkeyList, &HotkeyRight->Link); 401 } 402 403 return EFI_SUCCESS; 404 } 405 406 /** 407 Return TRUE when the variable pointed by Name and Guid is a Key#### variable. 408 409 @param Name The name of the variable. 410 @param Guid The GUID of the variable. 411 @param OptionNumber Return the option number parsed from the Name. 412 413 @retval TRUE The variable pointed by Name and Guid is a Key#### variable. 414 @retval FALSE The variable pointed by Name and Guid isn't a Key#### variable. 415 **/ 416 BOOLEAN 417 IsKeyOptionVariable ( 418 CHAR16 *Name, 419 EFI_GUID *Guid, 420 UINT16 *OptionNumber 421 ) 422 { 423 UINTN Index; 424 425 if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) || 426 (StrSize (Name) != sizeof (L"Key####")) || 427 (StrnCmp (Name, L"Key", 3) != 0) 428 ) { 429 return FALSE; 430 } 431 432 *OptionNumber = 0; 433 for (Index = 3; Index < 7; Index++) { 434 if ((Name[Index] >= L'0') && (Name[Index] <= L'9')) { 435 *OptionNumber = *OptionNumber * 16 + Name[Index] - L'0'; 436 } else if ((Name[Index] >= L'A') && (Name[Index] <= L'F')) { 437 *OptionNumber = *OptionNumber * 16 + Name[Index] - L'A' + 10; 438 } else { 439 return FALSE; 440 } 441 } 442 443 return TRUE; 444 } 445 446 /** 447 Return an array of key option numbers. 448 449 @param Count Return the count of key option numbers. 450 451 @return UINT16* Pointer to an array of key option numbers; 452 **/ 453 UINT16 * 454 EFIAPI 455 HotkeyGetOptionNumbers ( 456 OUT UINTN *Count 457 ) 458 { 459 EFI_STATUS Status; 460 UINTN Index; 461 CHAR16 *Name; 462 EFI_GUID Guid; 463 UINTN NameSize; 464 UINTN NewNameSize; 465 UINT16 *OptionNumbers; 466 UINT16 OptionNumber; 467 468 if (Count == NULL) { 469 return NULL; 470 } 471 472 *Count = 0; 473 OptionNumbers = NULL; 474 475 NameSize = sizeof (CHAR16); 476 Name = AllocateZeroPool (NameSize); 477 ASSERT (Name != NULL); 478 while (TRUE) { 479 NewNameSize = NameSize; 480 Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid); 481 if (Status == EFI_BUFFER_TOO_SMALL) { 482 Name = ReallocatePool (NameSize, NewNameSize, Name); 483 ASSERT (Name != NULL); 484 Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid); 485 NameSize = NewNameSize; 486 } 487 488 if (Status == EFI_NOT_FOUND) { 489 break; 490 } 491 ASSERT_EFI_ERROR (Status); 492 493 if (IsKeyOptionVariable (Name ,&Guid, &OptionNumber)) { 494 OptionNumbers = ReallocatePool ( 495 *Count * sizeof (UINT16), 496 (*Count + 1) * sizeof (UINT16), 497 OptionNumbers 498 ); 499 ASSERT (OptionNumbers != NULL); 500 for (Index = 0; Index < *Count; Index++) { 501 if (OptionNumber < OptionNumbers[Index]) { 502 break; 503 } 504 } 505 CopyMem (&OptionNumbers[Index + 1], &OptionNumbers[Index], (*Count - Index) * sizeof (UINT16)); 506 OptionNumbers[Index] = OptionNumber; 507 (*Count)++; 508 } 509 } 510 511 FreePool (Name); 512 513 return OptionNumbers; 514 } 515 516 /** 517 518 Process all the "Key####" variables, associate Hotkeys with corresponding Boot Options. 519 520 @retval EFI_SUCCESS Hotkey services successfully initialized. 521 **/ 522 EFI_STATUS 523 InitializeHotkeyService ( 524 VOID 525 ) 526 { 527 EFI_STATUS Status; 528 UINT32 BootOptionSupport; 529 UINT16 *KeyOptionNumbers; 530 UINTN KeyOptionCount; 531 UINTN Index; 532 CHAR16 KeyOptionName[8]; 533 EFI_KEY_OPTION *KeyOption; 534 535 // 536 // Export our capability - EFI_BOOT_OPTION_SUPPORT_KEY and EFI_BOOT_OPTION_SUPPORT_APP. 537 // with maximum number of key presses of 3 538 // Do not report the hotkey capability if PcdConInConnectOnDemand is enabled. 539 // 540 BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP; 541 if (!PcdGetBool (PcdConInConnectOnDemand)) { 542 BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY; 543 SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3); 544 } 545 546 Status = gRT->SetVariable ( 547 L"BootOptionSupport", 548 &gEfiGlobalVariableGuid, 549 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, 550 sizeof (UINT32), 551 &BootOptionSupport 552 ); 553 // 554 // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail. 555 // 556 ASSERT_EFI_ERROR (Status); 557 558 KeyOptionNumbers = HotkeyGetOptionNumbers (&KeyOptionCount); 559 for (Index = 0; Index < KeyOptionCount; Index ++) { 560 UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumbers[Index]); 561 GetEfiGlobalVariable2 (KeyOptionName, (VOID **) &KeyOption, NULL); 562 ASSERT (KeyOption != NULL); 563 if (IsKeyOptionValid (KeyOption)) { 564 HotkeyInsertList (KeyOption); 565 } 566 FreePool (KeyOption); 567 } 568 569 if (KeyOptionNumbers != NULL) { 570 FreePool (KeyOptionNumbers); 571 } 572 573 // 574 // Register Protocol notify for Hotkey service 575 // 576 Status = gBS->CreateEvent ( 577 EVT_NOTIFY_SIGNAL, 578 TPL_CALLBACK, 579 HotkeyEvent, 580 NULL, 581 &mHotkeyEvent 582 ); 583 ASSERT_EFI_ERROR (Status); 584 585 // 586 // Register for protocol notifications on this event 587 // 588 Status = gBS->RegisterProtocolNotify ( 589 &gEfiSimpleTextInputExProtocolGuid, 590 mHotkeyEvent, 591 &mHotkeyRegistration 592 ); 593 ASSERT_EFI_ERROR (Status); 594 595 return Status; 596 } 597 598