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
IsKeyOptionValid(IN EFI_KEY_OPTION * KeyOption)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
HotkeyBoot(VOID)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
HotkeyCallback(IN EFI_KEY_DATA * KeyData)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
HotkeyRegisterNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * SimpleTextInEx)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
HotkeyEvent(IN EFI_EVENT Event,IN VOID * Context)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
HotkeyInsertList(IN EFI_KEY_OPTION * KeyOption)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
IsKeyOptionVariable(CHAR16 * Name,EFI_GUID * Guid,UINT16 * OptionNumber)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
HotkeyGetOptionNumbers(OUT UINTN * Count)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
InitializeHotkeyService(VOID)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