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