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