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