1 /** @file
2   Hotkey library functions.
3 
4 Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "InternalBm.h"
16 
17 //
18 // Lock for linked list
19 //
20 EFI_LOCK                     mBmHotkeyLock            = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
21 LIST_ENTRY                   mBmHotkeyList            = INITIALIZE_LIST_HEAD_VARIABLE (mBmHotkeyList);
22 EFI_EVENT                    mBmHotkeyTriggered       = NULL;
23 BOOLEAN                      mBmHotkeyServiceStarted  = FALSE;
24 UINTN                        mBmHotkeySupportCount    = 0;
25 
26 //
27 // Set OptionNumber as unassigned value to indicate the option isn't initialized
28 //
29 EFI_BOOT_MANAGER_LOAD_OPTION mBmHotkeyBootOption      = { LoadOptionNumberUnassigned };
30 
31 EFI_BOOT_MANAGER_KEY_OPTION  *mBmContinueKeyOption    = NULL;
32 VOID                         *mBmTxtInExRegistration  = NULL;
33 
34 
35 /**
36   Return the buffer size of the EFI_BOOT_MANAGER_KEY_OPTION data.
37 
38   @param   KeyOption            The input key option info.
39 
40   @retval  The buffer size of the key option data.
41 **/
42 UINTN
BmSizeOfKeyOption(EFI_BOOT_MANAGER_KEY_OPTION * KeyOption)43 BmSizeOfKeyOption (
44   EFI_BOOT_MANAGER_KEY_OPTION  *KeyOption
45   )
46 {
47   return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys)
48     + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY);
49 }
50 
51 /**
52 
53   Check whether the input key option is valid.
54 
55   @param   KeyOption          Key option.
56   @param   KeyOptionSize      Size of the key option.
57 
58   @retval  TRUE               Input key option is valid.
59   @retval  FALSE              Input key option is not valid.
60 **/
61 BOOLEAN
BmIsKeyOptionValid(IN EFI_BOOT_MANAGER_KEY_OPTION * KeyOption,IN UINTN KeyOptionSize)62 BmIsKeyOptionValid (
63   IN EFI_BOOT_MANAGER_KEY_OPTION     *KeyOption,
64   IN UINTN                           KeyOptionSize
65 )
66 {
67   UINT16   OptionName[BM_OPTION_NAME_LEN];
68   UINT8    *BootOption;
69   UINTN    BootOptionSize;
70   UINT32   Crc;
71 
72   if (BmSizeOfKeyOption (KeyOption) != KeyOptionSize) {
73     return FALSE;
74   }
75 
76   //
77   // Check whether corresponding Boot Option exist
78   //
79   UnicodeSPrint (
80     OptionName, sizeof (OptionName), L"%s%04x",
81     mBmLoadOptionName[LoadOptionTypeBoot], KeyOption->BootOption
82     );
83   GetEfiGlobalVariable2 (OptionName, (VOID **) &BootOption, &BootOptionSize);
84 
85   if (BootOption == NULL) {
86     return FALSE;
87   }
88 
89   //
90   // Check CRC for Boot Option
91   //
92   gBS->CalculateCrc32 (BootOption, BootOptionSize, &Crc);
93   FreePool (BootOption);
94 
95   return (BOOLEAN) (KeyOption->BootOptionCrc == Crc);
96 }
97 
98 /**
99 
100   Check whether the input variable is an key option variable.
101 
102   @param   Name               Input variable name.
103   @param   Guid               Input variable guid.
104   @param   OptionNumber       The option number of this key option variable.
105 
106   @retval  TRUE               Input variable is a key option variable.
107   @retval  FALSE              Input variable is not a key option variable.
108 **/
109 BOOLEAN
BmIsKeyOptionVariable(CHAR16 * Name,EFI_GUID * Guid,UINT16 * OptionNumber)110 BmIsKeyOptionVariable (
111   CHAR16        *Name,
112   EFI_GUID      *Guid,
113   UINT16        *OptionNumber
114   )
115 {
116   UINTN         Index;
117   UINTN         Uint;
118 
119   if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) ||
120       (StrSize (Name) != sizeof (L"Key####")) ||
121       (StrnCmp (Name, L"Key", 3) != 0)
122      ) {
123     return FALSE;
124   }
125 
126   *OptionNumber = 0;
127   for (Index = 3; Index < 7; Index++) {
128     Uint = BmCharToUint (Name[Index]);
129     if (Uint == -1) {
130       return FALSE;
131     } else {
132       *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10;
133     }
134   }
135 
136   return TRUE;
137 }
138 
139 typedef struct {
140   EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
141   UINTN                       KeyOptionCount;
142 } BM_COLLECT_KEY_OPTIONS_PARAM;
143 
144 /**
145   Visitor function to collect the key options from NV storage.
146 
147   @param Name    Variable name.
148   @param Guid    Variable GUID.
149   @param Context The same context passed to BmForEachVariable.
150 **/
151 VOID
BmCollectKeyOptions(CHAR16 * Name,EFI_GUID * Guid,VOID * Context)152 BmCollectKeyOptions (
153   CHAR16               *Name,
154   EFI_GUID             *Guid,
155   VOID                 *Context
156   )
157 {
158   UINTN                        Index;
159   BM_COLLECT_KEY_OPTIONS_PARAM *Param;
160   EFI_BOOT_MANAGER_KEY_OPTION  *KeyOption;
161   UINT16                       OptionNumber;
162   UINTN                        KeyOptionSize;
163 
164   Param = (BM_COLLECT_KEY_OPTIONS_PARAM *) Context;
165 
166   if (BmIsKeyOptionVariable (Name, Guid, &OptionNumber)) {
167     GetEfiGlobalVariable2 (Name, (VOID**) &KeyOption, &KeyOptionSize);
168     ASSERT (KeyOption != NULL);
169     KeyOption->OptionNumber = OptionNumber;
170     if (BmIsKeyOptionValid (KeyOption, KeyOptionSize)) {
171       Param->KeyOptions = ReallocatePool (
172                             Param->KeyOptionCount * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
173                             (Param->KeyOptionCount + 1) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
174                             Param->KeyOptions
175                             );
176       ASSERT (Param->KeyOptions != NULL);
177       //
178       // Insert the key option in order
179       //
180       for (Index = 0; Index < Param->KeyOptionCount; Index++) {
181         if (KeyOption->OptionNumber < Param->KeyOptions[Index].OptionNumber) {
182           break;
183         }
184       }
185       CopyMem (&Param->KeyOptions[Index + 1], &Param->KeyOptions[Index], (Param->KeyOptionCount - Index) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
186       CopyMem (&Param->KeyOptions[Index], KeyOption, BmSizeOfKeyOption (KeyOption));
187       Param->KeyOptionCount++;
188     }
189     FreePool (KeyOption);
190   }
191 }
192 
193 /**
194   Return the array of key options.
195 
196   @param Count  Return the number of key options.
197 
198   @retval NULL  No key option.
199   @retval Other Pointer to the key options.
200 **/
201 EFI_BOOT_MANAGER_KEY_OPTION *
BmGetKeyOptions(OUT UINTN * Count)202 BmGetKeyOptions (
203   OUT UINTN     *Count
204   )
205 {
206   BM_COLLECT_KEY_OPTIONS_PARAM Param;
207 
208   if (Count == NULL) {
209     return NULL;
210   }
211 
212   Param.KeyOptions = NULL;
213   Param.KeyOptionCount = 0;
214 
215   BmForEachVariable (BmCollectKeyOptions, (VOID *) &Param);
216 
217   *Count = Param.KeyOptionCount;
218 
219   return Param.KeyOptions;
220 }
221 
222 /**
223   Callback function for event.
224 
225   @param    Event          Event for this callback function.
226   @param    Context        Context pass to this function.
227 **/
228 VOID
229 EFIAPI
BmEmptyFunction(IN EFI_EVENT Event,IN VOID * Context)230 BmEmptyFunction (
231   IN EFI_EVENT                Event,
232   IN VOID                     *Context
233   )
234 {
235 }
236 
237 /**
238   Check whether the bit is set in the value.
239 
240   @param   Value            The value need to be check.
241   @param   Bit              The bit filed need to be check.
242 
243   @retval  TRUE             The bit is set.
244   @retval  FALSE            The bit is not set.
245 **/
246 BOOLEAN
BmBitSet(IN UINT32 Value,IN UINT32 Bit)247 BmBitSet (
248   IN UINT32   Value,
249   IN UINT32   Bit
250   )
251 {
252   return (BOOLEAN) ((Value & Bit) != 0);
253 }
254 
255 /**
256   Initialize the KeyData and Key[] in the EFI_BOOT_MANAGER_KEY_OPTION.
257 
258   @param  Modifier   Input key info.
259   @param  Args       Va_list info.
260   @param  KeyOption  Key info which need to update.
261 
262   @retval  EFI_SUCCESS             Succeed to initialize the KeyData and Key[].
263   @return  EFI_INVALID_PARAMETER   Input parameter error.
264 **/
265 EFI_STATUS
BmInitializeKeyFields(IN UINT32 Modifier,IN VA_LIST Args,OUT EFI_BOOT_MANAGER_KEY_OPTION * KeyOption)266 BmInitializeKeyFields (
267   IN UINT32                       Modifier,
268   IN VA_LIST                      Args,
269   OUT EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
270   )
271 {
272   EFI_INPUT_KEY                   *Key;
273 
274   if (KeyOption == NULL) {
275     return EFI_INVALID_PARAMETER;
276   }
277 
278   Key = NULL;
279   while (KeyOption->KeyData.Options.InputKeyCount < sizeof (KeyOption->Keys) / sizeof (KeyOption->Keys[0])) {
280     Key = VA_ARG (Args, EFI_INPUT_KEY *);
281     if (Key == NULL) {
282       break;
283     }
284     CopyMem (
285       &KeyOption->Keys[KeyOption->KeyData.Options.InputKeyCount],
286       Key,
287       sizeof (EFI_INPUT_KEY)
288       );
289     KeyOption->KeyData.Options.InputKeyCount++;
290   }
291 
292   if (Key != NULL) {
293     //
294     // Too many keys
295     //
296     return EFI_INVALID_PARAMETER;
297   }
298 
299   if ((Modifier & ~(EFI_BOOT_MANAGER_SHIFT_PRESSED
300                  | EFI_BOOT_MANAGER_CONTROL_PRESSED
301                  | EFI_BOOT_MANAGER_ALT_PRESSED
302                  | EFI_BOOT_MANAGER_LOGO_PRESSED
303                  | EFI_BOOT_MANAGER_MENU_KEY_PRESSED
304                  | EFI_BOOT_MANAGER_SYS_REQ_PRESSED
305                  )) != 0) {
306     return EFI_INVALID_PARAMETER;
307   }
308 
309   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SHIFT_PRESSED)) {
310     KeyOption->KeyData.Options.ShiftPressed = 1;
311   }
312   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_CONTROL_PRESSED)) {
313     KeyOption->KeyData.Options.ControlPressed = 1;
314   }
315   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_ALT_PRESSED)) {
316     KeyOption->KeyData.Options.AltPressed = 1;
317   }
318   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_LOGO_PRESSED)) {
319     KeyOption->KeyData.Options.LogoPressed = 1;
320   }
321   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_MENU_KEY_PRESSED)) {
322     KeyOption->KeyData.Options.MenuPressed = 1;
323   }
324   if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SYS_REQ_PRESSED)) {
325     KeyOption->KeyData.Options.SysReqPressed = 1;
326   }
327 
328   return EFI_SUCCESS;
329 }
330 
331 /**
332   Try to boot the boot option triggered by hot key.
333 **/
334 VOID
335 EFIAPI
EfiBootManagerHotkeyBoot(VOID)336 EfiBootManagerHotkeyBoot (
337   VOID
338   )
339 {
340   if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
341     EfiBootManagerBoot (&mBmHotkeyBootOption);
342     EfiBootManagerFreeLoadOption (&mBmHotkeyBootOption);
343     mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
344   }
345 }
346 
347 /**
348   This is the common notification function for HotKeys, it will be registered
349   with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.
350 
351   @param KeyData         A pointer to a buffer that is filled in with the keystroke
352                          information for the key that was pressed.
353 
354   @retval  EFI_SUCCESS   KeyData is successfully processed.
355   @return  EFI_NOT_FOUND Fail to find boot option variable.
356 **/
357 EFI_STATUS
358 EFIAPI
BmHotkeyCallback(IN EFI_KEY_DATA * KeyData)359 BmHotkeyCallback (
360   IN EFI_KEY_DATA     *KeyData
361 )
362 {
363   LIST_ENTRY                    *Link;
364   BM_HOTKEY                     *Hotkey;
365   CHAR16                        OptionName[BM_OPTION_NAME_LEN];
366   EFI_STATUS                    Status;
367   EFI_KEY_DATA                  *HotkeyData;
368 
369   if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
370     //
371     // Do not process sequential hotkey stroke until the current boot option returns
372     //
373     return EFI_SUCCESS;
374   }
375 
376   DEBUG ((EFI_D_INFO, "[Bds]BmHotkeyCallback: %04x:%04x\n", KeyData->Key.ScanCode, KeyData->Key.UnicodeChar));
377 
378   EfiAcquireLock (&mBmHotkeyLock);
379   for ( Link = GetFirstNode (&mBmHotkeyList)
380       ; !IsNull (&mBmHotkeyList, Link)
381       ; Link = GetNextNode (&mBmHotkeyList, Link)
382       ) {
383     Hotkey = BM_HOTKEY_FROM_LINK (Link);
384 
385     //
386     // Is this Key Stroke we are waiting for?
387     //
388     ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0])));
389     HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];
390     if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&
391         (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&
392         (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ?
393           (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE
394         )
395        ) {
396 
397       //
398       // Receive an expecting key stroke, transit to next waiting state
399       //
400       Hotkey->WaitingKey++;
401 
402       if (Hotkey->WaitingKey == Hotkey->CodeCount) {
403         //
404         // Reset to initial waiting state
405         //
406         Hotkey->WaitingKey = 0;
407         //
408         // Received the whole key stroke sequence
409         //
410         Status = gBS->SignalEvent (mBmHotkeyTriggered);
411         ASSERT_EFI_ERROR (Status);
412 
413         if (!Hotkey->IsContinue) {
414           //
415           // Launch its BootOption
416           //
417           UnicodeSPrint (
418             OptionName, sizeof (OptionName), L"%s%04x",
419             mBmLoadOptionName[LoadOptionTypeBoot], Hotkey->BootOption
420             );
421           Status = EfiBootManagerVariableToLoadOption (OptionName, &mBmHotkeyBootOption);
422           DEBUG ((EFI_D_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status));
423           if (EFI_ERROR (Status)) {
424             mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
425           }
426         } else {
427           DEBUG ((EFI_D_INFO, "[Bds]Continue key pressed!\n"));
428         }
429       }
430     } else {
431       //
432       // Receive an unexpected key stroke, reset to initial waiting state
433       //
434       Hotkey->WaitingKey = 0;
435     }
436 
437   }
438   EfiReleaseLock (&mBmHotkeyLock);
439 
440   return EFI_SUCCESS;
441 }
442 
443 /**
444   Unregister hotkey notify list.
445 
446   @param    Hotkey                Hotkey list.
447 
448   @retval   EFI_SUCCESS           Unregister hotkey notify success.
449   @retval   Others                Unregister hotkey notify failed.
450 **/
451 EFI_STATUS
BmUnregisterHotkeyNotify(IN BM_HOTKEY * Hotkey)452 BmUnregisterHotkeyNotify (
453   IN BM_HOTKEY                          *Hotkey
454   )
455 {
456   EFI_STATUS                            Status;
457   UINTN                                 Index;
458   UINTN                                 KeyIndex;
459   EFI_HANDLE                            *Handles;
460   UINTN                                 HandleCount;
461   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL     *TxtInEx;
462   VOID                                  *NotifyHandle;
463 
464   gBS->LocateHandleBuffer (
465           ByProtocol,
466           &gEfiSimpleTextInputExProtocolGuid,
467           NULL,
468           &HandleCount,
469           &Handles
470           );
471   for (Index = 0; Index < HandleCount; Index++) {
472     Status = gBS->HandleProtocol (Handles[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
473     ASSERT_EFI_ERROR (Status);
474     for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
475       Status = TxtInEx->RegisterKeyNotify (
476                           TxtInEx,
477                           &Hotkey->KeyData[KeyIndex],
478                           BmHotkeyCallback,
479                           &NotifyHandle
480                           );
481       if (!EFI_ERROR (Status)) {
482         Status = TxtInEx->UnregisterKeyNotify (TxtInEx, NotifyHandle);
483         DEBUG ((EFI_D_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status));
484       }
485     }
486   }
487 
488   return EFI_SUCCESS;
489 }
490 
491 /**
492   Register hotkey notify list.
493 
494   @param    TxtInEx               Pointer to EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol.
495   @param    Hotkey                Hotkey list.
496 
497   @retval   EFI_SUCCESS           Register hotkey notify success.
498   @retval   Others                Register hotkey notify failed.
499 **/
500 EFI_STATUS
BmRegisterHotkeyNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * TxtInEx,IN BM_HOTKEY * Hotkey)501 BmRegisterHotkeyNotify (
502   IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx,
503   IN BM_HOTKEY                          *Hotkey
504   )
505 {
506   EFI_STATUS                            Status;
507   UINTN                                 Index;
508   VOID                                  *NotifyHandle;
509 
510   for (Index = 0; Index < Hotkey->CodeCount; Index++) {
511     Status = TxtInEx->RegisterKeyNotify (
512                         TxtInEx,
513                         &Hotkey->KeyData[Index],
514                         BmHotkeyCallback,
515                         &NotifyHandle
516                         );
517     DEBUG ((
518       EFI_D_INFO,
519       "[Bds]RegisterKeyNotify: %04x/%04x %08x/%02x %r\n",
520       Hotkey->KeyData[Index].Key.ScanCode,
521       Hotkey->KeyData[Index].Key.UnicodeChar,
522       Hotkey->KeyData[Index].KeyState.KeyShiftState,
523       Hotkey->KeyData[Index].KeyState.KeyToggleState,
524       Status
525       ));
526     if (EFI_ERROR (Status)) {
527       //
528       // some of the hotkey registry failed
529       // do not unregister all in case we have both CTRL-ALT-P and CTRL-ALT-P-R
530       //
531       break;
532     }
533   }
534 
535   return EFI_SUCCESS;
536 }
537 
538 /**
539   Generate key shift state base on the input key option info.
540 
541   @param    Depth                 Which key is checked.
542   @param    KeyOption             Input key option info.
543   @param    KeyShiftState         Input key shift state.
544   @param    KeyShiftStates        Return possible key shift state array.
545   @param    KeyShiftStateCount    Possible key shift state count.
546 **/
547 VOID
BmGenerateKeyShiftState(IN UINTN Depth,IN EFI_BOOT_MANAGER_KEY_OPTION * KeyOption,IN UINT32 KeyShiftState,IN UINT32 * KeyShiftStates,IN UINTN * KeyShiftStateCount)548 BmGenerateKeyShiftState (
549   IN UINTN                             Depth,
550   IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption,
551   IN UINT32                            KeyShiftState,
552   IN UINT32                            *KeyShiftStates,
553   IN UINTN                             *KeyShiftStateCount
554   )
555 {
556   switch (Depth) {
557   case 0:
558     if (KeyOption->KeyData.Options.ShiftPressed) {
559       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);
560       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_SHIFT_PRESSED,  KeyShiftStates, KeyShiftStateCount);
561     } else {
562       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
563     }
564     break;
565 
566   case 1:
567     if (KeyOption->KeyData.Options.ControlPressed) {
568       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);
569       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_CONTROL_PRESSED,  KeyShiftStates, KeyShiftStateCount);
570     } else {
571       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
572     }
573     break;
574 
575   case 2:
576     if (KeyOption->KeyData.Options.AltPressed) {
577       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount);
578       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_ALT_PRESSED,  KeyShiftStates, KeyShiftStateCount);
579     } else {
580       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
581     }
582     break;
583   case  3:
584     if (KeyOption->KeyData.Options.LogoPressed) {
585       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);
586       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_LOGO_PRESSED,  KeyShiftStates, KeyShiftStateCount);
587     } else {
588       BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
589     }
590     break;
591   case 4:
592     if (KeyOption->KeyData.Options.MenuPressed) {
593       KeyShiftState |= EFI_MENU_KEY_PRESSED;
594     }
595     if (KeyOption->KeyData.Options.SysReqPressed) {
596       KeyShiftState |= EFI_SYS_REQ_PRESSED;
597     }
598     KeyShiftStates[*KeyShiftStateCount] = KeyShiftState;
599     (*KeyShiftStateCount)++;
600     break;
601   }
602 }
603 
604 /**
605   Add it to hot key database, register it to existing TxtInEx.
606   New TxtInEx will be automatically registered with all the hot key in dababase
607 
608   @param    KeyOption  Input key option info.
609 **/
610 EFI_STATUS
BmProcessKeyOption(IN EFI_BOOT_MANAGER_KEY_OPTION * KeyOption)611 BmProcessKeyOption (
612   IN EFI_BOOT_MANAGER_KEY_OPTION       *KeyOption
613   )
614 {
615   EFI_STATUS                           Status;
616   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL    *TxtInEx;
617   EFI_HANDLE                           *Handles;
618   UINTN                                HandleCount;
619   UINTN                                HandleIndex;
620   UINTN                                Index;
621   BM_HOTKEY                            *Hotkey;
622   UINTN                                KeyIndex;
623   //
624   // 16 is enough to enumerate all the possible combination of LEFT_XXX and RIGHT_XXX
625   //
626   UINT32                               KeyShiftStates[16];
627   UINTN                                KeyShiftStateCount;
628 
629   if (KeyOption->KeyData.Options.InputKeyCount > mBmHotkeySupportCount) {
630     return EFI_UNSUPPORTED;
631   }
632 
633   KeyShiftStateCount = 0;
634   BmGenerateKeyShiftState (0, KeyOption, EFI_SHIFT_STATE_VALID, KeyShiftStates, &KeyShiftStateCount);
635   ASSERT (KeyShiftStateCount <= sizeof (KeyShiftStates) / sizeof (KeyShiftStates[0]));
636 
637   EfiAcquireLock (&mBmHotkeyLock);
638 
639   for (Index = 0; Index < KeyShiftStateCount; Index++) {
640     Hotkey = AllocateZeroPool (sizeof (BM_HOTKEY));
641     ASSERT (Hotkey != NULL);
642 
643     Hotkey->Signature  = BM_HOTKEY_SIGNATURE;
644     Hotkey->BootOption = KeyOption->BootOption;
645     Hotkey->IsContinue = (BOOLEAN) (KeyOption == mBmContinueKeyOption);
646     Hotkey->CodeCount  = (UINT8) KeyOption->KeyData.Options.InputKeyCount;
647 
648     for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
649       CopyMem (&Hotkey->KeyData[KeyIndex].Key, &KeyOption->Keys[KeyIndex], sizeof (EFI_INPUT_KEY));
650       Hotkey->KeyData[KeyIndex].KeyState.KeyShiftState = KeyShiftStates[Index];
651     }
652     InsertTailList (&mBmHotkeyList, &Hotkey->Link);
653 
654     gBS->LocateHandleBuffer (
655             ByProtocol,
656             &gEfiSimpleTextInputExProtocolGuid,
657             NULL,
658             &HandleCount,
659             &Handles
660             );
661     for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
662       Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
663       ASSERT_EFI_ERROR (Status);
664       BmRegisterHotkeyNotify (TxtInEx, Hotkey);
665     }
666   }
667 
668   EfiReleaseLock (&mBmHotkeyLock);
669 
670   return EFI_SUCCESS;
671 }
672 
673 /**
674   Callback function for SimpleTextInEx protocol install events
675 
676   @param Event           the event that is signaled.
677   @param Context         not used here.
678 
679 **/
680 VOID
681 EFIAPI
BmTxtInExCallback(IN EFI_EVENT Event,IN VOID * Context)682 BmTxtInExCallback (
683   IN EFI_EVENT    Event,
684   IN VOID         *Context
685   )
686 {
687   EFI_STATUS                         Status;
688   UINTN                              BufferSize;
689   EFI_HANDLE                         Handle;
690   EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL  *TxtInEx;
691   LIST_ENTRY                         *Link;
692 
693   while (TRUE) {
694     BufferSize = sizeof (EFI_HANDLE);
695     Status = gBS->LocateHandle (
696                     ByRegisterNotify,
697                     NULL,
698                     mBmTxtInExRegistration,
699                     &BufferSize,
700                     &Handle
701                     );
702     if (EFI_ERROR (Status)) {
703       //
704       // If no more notification events exist
705       //
706       return ;
707     }
708 
709     Status = gBS->HandleProtocol (
710                     Handle,
711                     &gEfiSimpleTextInputExProtocolGuid,
712                     (VOID **) &TxtInEx
713                     );
714     ASSERT_EFI_ERROR (Status);
715 
716     //
717     // Register the hot key notification for the existing items in the list
718     //
719     EfiAcquireLock (&mBmHotkeyLock);
720     for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); Link = GetNextNode (&mBmHotkeyList, Link)) {
721       BmRegisterHotkeyNotify (TxtInEx, BM_HOTKEY_FROM_LINK (Link));
722     }
723     EfiReleaseLock (&mBmHotkeyLock);
724   }
725 }
726 
727 /**
728   Free the key options returned from BmGetKeyOptions.
729 
730   @param KeyOptions     Pointer to the key options.
731   @param KeyOptionCount Number of the key options.
732 
733   @retval EFI_SUCCESS   The key options are freed.
734   @retval EFI_NOT_FOUND KeyOptions is NULL.
735 **/
736 EFI_STATUS
BmFreeKeyOptions(IN EFI_BOOT_MANAGER_KEY_OPTION * KeyOptions,IN UINTN KeyOptionCount)737 BmFreeKeyOptions (
738   IN EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions,
739   IN UINTN                          KeyOptionCount
740   )
741 {
742   if (KeyOptions != NULL) {
743     FreePool (KeyOptions);
744     return EFI_SUCCESS;
745   } else {
746     return EFI_NOT_FOUND;
747   }
748 }
749 
750 /**
751   Register the key option to exit the waiting of the Boot Manager timeout.
752   Platform should ensure that the continue key option isn't conflict with
753   other boot key options.
754 
755   @param Modifier     Key shift state.
756   @param  ...         Parameter list of pointer of EFI_INPUT_KEY.
757 
758   @retval EFI_SUCCESS         Successfully register the continue key option.
759   @retval EFI_ALREADY_STARTED The continue key option is already registered.
760 **/
761 EFI_STATUS
762 EFIAPI
EfiBootManagerRegisterContinueKeyOption(IN UINT32 Modifier,...)763 EfiBootManagerRegisterContinueKeyOption (
764   IN UINT32           Modifier,
765   ...
766   )
767 {
768   EFI_STATUS                   Status;
769   EFI_BOOT_MANAGER_KEY_OPTION  KeyOption;
770   VA_LIST                      Args;
771 
772   if (mBmContinueKeyOption != NULL) {
773     return EFI_ALREADY_STARTED;
774   }
775 
776   ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
777   VA_START (Args, Modifier);
778   Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
779   VA_END (Args);
780 
781   if (!EFI_ERROR (Status)) {
782     mBmContinueKeyOption = AllocateCopyPool (sizeof (EFI_BOOT_MANAGER_KEY_OPTION), &KeyOption);
783     ASSERT (mBmContinueKeyOption != NULL);
784     if (mBmHotkeyServiceStarted) {
785       BmProcessKeyOption (mBmContinueKeyOption);
786     }
787   }
788 
789   return Status;
790 }
791 
792 /**
793   Stop the hotkey processing.
794 
795   @param    Event          Event pointer related to hotkey service.
796   @param    Context        Context pass to this function.
797 **/
798 VOID
799 EFIAPI
BmStopHotkeyService(IN EFI_EVENT Event,IN VOID * Context)800 BmStopHotkeyService (
801   IN EFI_EVENT    Event,
802   IN VOID         *Context
803   )
804 {
805   LIST_ENTRY            *Link;
806   BM_HOTKEY             *Hotkey;
807 
808   DEBUG ((EFI_D_INFO, "[Bds]Stop Hotkey Service!\n"));
809   gBS->CloseEvent (Event);
810 
811   EfiAcquireLock (&mBmHotkeyLock);
812   for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
813     Hotkey = BM_HOTKEY_FROM_LINK (Link);
814     BmUnregisterHotkeyNotify (Hotkey);
815     Link   = RemoveEntryList (Link);
816     FreePool (Hotkey);
817   }
818   EfiReleaseLock (&mBmHotkeyLock);
819 }
820 
821 /**
822   Start the hot key service so that the key press can trigger the boot option.
823 
824   @param HotkeyTriggered  Return the waitable event and it will be signaled
825                           when a valid hot key is pressed.
826 
827   @retval EFI_SUCCESS     The hot key service is started.
828 **/
829 EFI_STATUS
830 EFIAPI
EfiBootManagerStartHotkeyService(IN EFI_EVENT * HotkeyTriggered)831 EfiBootManagerStartHotkeyService (
832   IN EFI_EVENT                 *HotkeyTriggered
833   )
834 {
835   EFI_STATUS                   Status;
836   EFI_BOOT_MANAGER_KEY_OPTION  *KeyOptions;
837   UINTN                        KeyOptionCount;
838   UINTN                        Index;
839   EFI_EVENT                    Event;
840   UINT32                       *BootOptionSupport;
841 
842   Status = GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **) &BootOptionSupport, NULL);
843   ASSERT (BootOptionSupport != NULL);
844 
845   if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY)  != 0) {
846     mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT));
847   }
848   FreePool (BootOptionSupport);
849 
850   if (mBmHotkeySupportCount == 0) {
851     DEBUG ((EFI_D_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n"));
852     return EFI_UNSUPPORTED;
853   }
854 
855   Status = gBS->CreateEvent (
856                   EVT_NOTIFY_WAIT,
857                   TPL_CALLBACK,
858                   BmEmptyFunction,
859                   NULL,
860                   &mBmHotkeyTriggered
861                   );
862   ASSERT_EFI_ERROR (Status);
863 
864   if (HotkeyTriggered != NULL) {
865     *HotkeyTriggered = mBmHotkeyTriggered;
866   }
867 
868   KeyOptions = BmGetKeyOptions (&KeyOptionCount);
869   for (Index = 0; Index < KeyOptionCount; Index ++) {
870     BmProcessKeyOption (&KeyOptions[Index]);
871   }
872   BmFreeKeyOptions (KeyOptions, KeyOptionCount);
873 
874   if (mBmContinueKeyOption != NULL) {
875     BmProcessKeyOption (mBmContinueKeyOption);
876   }
877 
878   EfiCreateProtocolNotifyEvent (
879     &gEfiSimpleTextInputExProtocolGuid,
880     TPL_CALLBACK,
881     BmTxtInExCallback,
882     NULL,
883     &mBmTxtInExRegistration
884     );
885 
886   Status = EfiCreateEventReadyToBootEx (
887              TPL_CALLBACK,
888              BmStopHotkeyService,
889              NULL,
890              &Event
891              );
892   ASSERT_EFI_ERROR (Status);
893 
894 
895   mBmHotkeyServiceStarted = TRUE;
896   return Status;
897 }
898 
899 /**
900   Add the key option.
901   It adds the key option variable and the key option takes affect immediately.
902 
903   @param AddedOption      Return the added key option.
904   @param BootOptionNumber The boot option number for the key option.
905   @param Modifier         Key shift state.
906   @param ...              Parameter list of pointer of EFI_INPUT_KEY.
907 
908   @retval EFI_SUCCESS         The key option is added.
909   @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.
910 **/
911 EFI_STATUS
912 EFIAPI
EfiBootManagerAddKeyOptionVariable(OUT EFI_BOOT_MANAGER_KEY_OPTION * AddedOption,OPTIONAL IN UINT16 BootOptionNumber,IN UINT32 Modifier,...)913 EfiBootManagerAddKeyOptionVariable (
914   OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption,   OPTIONAL
915   IN UINT16                       BootOptionNumber,
916   IN UINT32                       Modifier,
917   ...
918   )
919 {
920   EFI_STATUS                     Status;
921   VA_LIST                        Args;
922   VOID                           *BootOption;
923   UINTN                          BootOptionSize;
924   CHAR16                         BootOptionName[BM_OPTION_NAME_LEN];
925   EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;
926   EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;
927   UINTN                          KeyOptionCount;
928   UINTN                          Index;
929   UINTN                          KeyOptionNumber;
930   CHAR16                         KeyOptionName[sizeof ("Key####")];
931 
932   UnicodeSPrint (
933     BootOptionName, sizeof (BootOptionName), L"%s%04x",
934     mBmLoadOptionName[LoadOptionTypeBoot], BootOptionNumber
935     );
936   GetEfiGlobalVariable2 (BootOptionName, &BootOption, &BootOptionSize);
937 
938   if (BootOption == NULL) {
939     return EFI_NOT_FOUND;
940   }
941 
942   ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
943   KeyOption.BootOption = BootOptionNumber;
944   Status = gBS->CalculateCrc32 (BootOption, BootOptionSize, &KeyOption.BootOptionCrc);
945   ASSERT_EFI_ERROR (Status);
946   FreePool (BootOption);
947 
948   VA_START (Args, Modifier);
949   Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
950   VA_END (Args);
951   if (EFI_ERROR (Status)) {
952     return Status;
953   }
954 
955   KeyOptionNumber = LoadOptionNumberUnassigned;
956   //
957   // Check if the hot key sequence was defined already
958   //
959   KeyOptions = BmGetKeyOptions (&KeyOptionCount);
960   for (Index = 0; Index < KeyOptionCount; Index++) {
961     if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
962       (CompareMem (KeyOptions[Index].Keys, KeyOption.Keys, KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)) {
963       break;
964     }
965 
966     if ((KeyOptionNumber == LoadOptionNumberUnassigned) &&
967         (KeyOptions[Index].OptionNumber > Index)
968        ){
969       KeyOptionNumber = Index;
970     }
971   }
972   BmFreeKeyOptions (KeyOptions, KeyOptionCount);
973 
974   if (Index < KeyOptionCount) {
975     return EFI_ALREADY_STARTED;
976   }
977 
978   if (KeyOptionNumber == LoadOptionNumberUnassigned) {
979     KeyOptionNumber = KeyOptionCount;
980   }
981 
982   UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber);
983 
984   Status = gRT->SetVariable (
985                   KeyOptionName,
986                   &gEfiGlobalVariableGuid,
987                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
988                   BmSizeOfKeyOption (&KeyOption),
989                   &KeyOption
990                   );
991   if (!EFI_ERROR (Status)) {
992     //
993     // Return the Key Option in case needed by caller
994     //
995     if (AddedOption != NULL) {
996       CopyMem (AddedOption, &KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
997     }
998 
999     //
1000     // Register the newly added hot key
1001     // Calling this function before EfiBootManagerStartHotkeyService doesn't
1002     // need to call BmProcessKeyOption
1003     //
1004     if (mBmHotkeyServiceStarted) {
1005       BmProcessKeyOption (&KeyOption);
1006     }
1007   }
1008 
1009   return Status;
1010 }
1011 
1012 /**
1013   Delete the Key Option variable and unregister the hot key
1014 
1015   @param DeletedOption  Return the deleted key options.
1016   @param Modifier       Key shift state.
1017   @param ...            Parameter list of pointer of EFI_INPUT_KEY.
1018 
1019   @retval EFI_SUCCESS   The key option is deleted.
1020   @retval EFI_NOT_FOUND The key option cannot be found.
1021 **/
1022 EFI_STATUS
1023 EFIAPI
EfiBootManagerDeleteKeyOptionVariable(IN EFI_BOOT_MANAGER_KEY_OPTION * DeletedOption,OPTIONAL IN UINT32 Modifier,...)1024 EfiBootManagerDeleteKeyOptionVariable (
1025   IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL
1026   IN UINT32                      Modifier,
1027   ...
1028   )
1029 {
1030   EFI_STATUS                     Status;
1031   UINTN                          Index;
1032   VA_LIST                        Args;
1033   EFI_BOOT_MANAGER_KEY_OPTION    KeyOption;
1034   EFI_BOOT_MANAGER_KEY_OPTION    *KeyOptions;
1035   UINTN                          KeyOptionCount;
1036   LIST_ENTRY                     *Link;
1037   BM_HOTKEY                      *Hotkey;
1038   UINT32                         ShiftState;
1039   BOOLEAN                        Match;
1040   CHAR16                         KeyOptionName[sizeof ("Key####")];
1041 
1042   ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
1043   VA_START (Args, Modifier);
1044   Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
1045   VA_END (Args);
1046 
1047   if (EFI_ERROR (Status)) {
1048     return Status;
1049   }
1050 
1051   EfiAcquireLock (&mBmHotkeyLock);
1052   //
1053   // Delete the key option from active hot key list
1054   // Could have multiple entries when modifier isn't 0 because we map the ShiftPressed to RIGHT_SHIFT and RIGHT_SHIFT
1055   //
1056   for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
1057     Hotkey = BM_HOTKEY_FROM_LINK (Link);
1058     Match  = (BOOLEAN) (Hotkey->CodeCount == KeyOption.KeyData.Options.InputKeyCount);
1059 
1060     for (Index = 0; Match && (Index < Hotkey->CodeCount); Index++) {
1061       ShiftState = Hotkey->KeyData[Index].KeyState.KeyShiftState;
1062       if (
1063         (BmBitSet (ShiftState, EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED) != KeyOption.KeyData.Options.ShiftPressed) ||
1064         (BmBitSet (ShiftState, EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) != KeyOption.KeyData.Options.ControlPressed) ||
1065         (BmBitSet (ShiftState, EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) != KeyOption.KeyData.Options.AltPressed) ||
1066         (BmBitSet (ShiftState, EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED) != KeyOption.KeyData.Options.LogoPressed) ||
1067         (BmBitSet (ShiftState, EFI_MENU_KEY_PRESSED) != KeyOption.KeyData.Options.MenuPressed) ||
1068         (BmBitSet (ShiftState, EFI_SYS_REQ_PRESSED) != KeyOption.KeyData.Options.SysReqPressed) ||
1069         (CompareMem (&Hotkey->KeyData[Index].Key, &KeyOption.Keys[Index], sizeof (EFI_INPUT_KEY)) != 0)
1070         ) {
1071         //
1072         // Break when any field doesn't match
1073         //
1074         Match = FALSE;
1075         break;
1076       }
1077     }
1078 
1079     if (Match) {
1080       Link = RemoveEntryList (Link);
1081       FreePool (Hotkey);
1082     } else {
1083       Link = GetNextNode (&mBmHotkeyList, Link);
1084     }
1085   }
1086 
1087   //
1088   // Delete the key option from the variable
1089   //
1090   Status     = EFI_NOT_FOUND;
1091   KeyOptions = BmGetKeyOptions (&KeyOptionCount);
1092   for (Index = 0; Index < KeyOptionCount; Index++) {
1093     if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
1094         (CompareMem (
1095            KeyOptions[Index].Keys, KeyOption.Keys,
1096            KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)
1097        ) {
1098       UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptions[Index].OptionNumber);
1099       Status = gRT->SetVariable (
1100                  KeyOptionName,
1101                  &gEfiGlobalVariableGuid,
1102                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
1103                  0,
1104                  NULL
1105                  );
1106       //
1107       // Return the deleted key option in case needed by caller
1108       //
1109       if (DeletedOption != NULL) {
1110         CopyMem (DeletedOption, &KeyOptions[Index], sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
1111       }
1112       break;
1113     }
1114   }
1115   BmFreeKeyOptions (KeyOptions, KeyOptionCount);
1116 
1117   EfiReleaseLock (&mBmHotkeyLock);
1118 
1119   return Status;
1120 }
1121