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