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