]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c
MdeModulePkg/Bds: Fix VS2012 build failure.
[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 Handles = NULL;
462 *Count = 0;
463
464 if (gST->ConsoleInHandle != NULL) {
465 Status = gBS->OpenProtocol (
466 gST->ConsoleInHandle,
467 &gEfiSimpleTextInputExProtocolGuid,
468 NULL,
469 gImageHandle,
470 NULL,
471 EFI_OPEN_PROTOCOL_TEST_PROTOCOL
472 );
473 if (!EFI_ERROR (Status)) {
474 Handles = AllocateCopyPool (sizeof (EFI_HANDLE *), &gST->ConsoleInHandle);
475 *Count = 1;
476 }
477 } else {
478 Status = gBS->LocateHandleBuffer (
479 ByProtocol,
480 &gEfiSimpleTextInputExProtocolGuid,
481 NULL,
482 Count,
483 &Handles
484 );
485 }
486
487 return Handles;
488 }
489
490 /**
491 Unregister hotkey notify list.
492
493 @param Hotkey Hotkey list.
494
495 @retval EFI_SUCCESS Unregister hotkey notify success.
496 @retval Others Unregister hotkey notify failed.
497 **/
498 EFI_STATUS
499 BmUnregisterHotkeyNotify (
500 IN BM_HOTKEY *Hotkey
501 )
502 {
503 EFI_STATUS Status;
504 UINTN Index;
505 UINTN KeyIndex;
506 EFI_HANDLE *Handles;
507 UINTN HandleCount;
508 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
509 VOID *NotifyHandle;
510
511 Handles = BmGetActiveConsoleIn (&HandleCount);
512 for (Index = 0; Index < HandleCount; Index++) {
513 Status = gBS->HandleProtocol (Handles[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
514 ASSERT_EFI_ERROR (Status);
515 for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
516 Status = TxtInEx->RegisterKeyNotify (
517 TxtInEx,
518 &Hotkey->KeyData[KeyIndex],
519 BmHotkeyCallback,
520 &NotifyHandle
521 );
522 if (!EFI_ERROR (Status)) {
523 Status = TxtInEx->UnregisterKeyNotify (TxtInEx, NotifyHandle);
524 DEBUG ((EFI_D_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status));
525 }
526 }
527 }
528
529 if (Handles != NULL) {
530 FreePool (Handles);
531 }
532
533 return EFI_SUCCESS;
534 }
535
536 /**
537 Register hotkey notify list.
538
539 @param TxtInEx Pointer to EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol.
540 @param Hotkey Hotkey list.
541
542 @retval EFI_SUCCESS Register hotkey notify success.
543 @retval Others Register hotkey notify failed.
544 **/
545 EFI_STATUS
546 BmRegisterHotkeyNotify (
547 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx,
548 IN BM_HOTKEY *Hotkey
549 )
550 {
551 EFI_STATUS Status;
552 UINTN Index;
553 VOID *NotifyHandle;
554
555 for (Index = 0; Index < Hotkey->CodeCount; Index++) {
556 Status = TxtInEx->RegisterKeyNotify (
557 TxtInEx,
558 &Hotkey->KeyData[Index],
559 BmHotkeyCallback,
560 &NotifyHandle
561 );
562 DEBUG ((
563 EFI_D_INFO,
564 "[Bds]RegisterKeyNotify: %04x/%04x %08x/%02x %r\n",
565 Hotkey->KeyData[Index].Key.ScanCode,
566 Hotkey->KeyData[Index].Key.UnicodeChar,
567 Hotkey->KeyData[Index].KeyState.KeyShiftState,
568 Hotkey->KeyData[Index].KeyState.KeyToggleState,
569 Status
570 ));
571 if (EFI_ERROR (Status)) {
572 //
573 // some of the hotkey registry failed
574 // do not unregister all in case we have both CTRL-ALT-P and CTRL-ALT-P-R
575 //
576 break;
577 }
578 }
579
580 return EFI_SUCCESS;
581 }
582
583 /**
584 Generate key shift state base on the input key option info.
585
586 @param Depth Which key is checked.
587 @param KeyOption Input key option info.
588 @param KeyShiftState Input key shift state.
589 @param KeyShiftStates Return possible key shift state array.
590 @param KeyShiftStateCount Possible key shift state count.
591 **/
592 VOID
593 BmGenerateKeyShiftState (
594 IN UINTN Depth,
595 IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOption,
596 IN UINT32 KeyShiftState,
597 IN UINT32 *KeyShiftStates,
598 IN UINTN *KeyShiftStateCount
599 )
600 {
601 switch (Depth) {
602 case 0:
603 if (KeyOption->KeyData.Options.ShiftPressed) {
604 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);
605 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);
606 } else {
607 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
608 }
609 break;
610
611 case 1:
612 if (KeyOption->KeyData.Options.ControlPressed) {
613 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);
614 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);
615 } else {
616 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
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 break;
628 case 3:
629 if (KeyOption->KeyData.Options.LogoPressed) {
630 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);
631 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);
632 } else {
633 BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
634 }
635 break;
636 case 4:
637 if (KeyOption->KeyData.Options.MenuPressed) {
638 KeyShiftState |= EFI_MENU_KEY_PRESSED;
639 }
640 if (KeyOption->KeyData.Options.SysReqPressed) {
641 KeyShiftState |= EFI_SYS_REQ_PRESSED;
642 }
643 KeyShiftStates[*KeyShiftStateCount] = KeyShiftState;
644 (*KeyShiftStateCount)++;
645 break;
646 }
647 }
648
649 /**
650 Add it to hot key database, register it to existing TxtInEx.
651 New TxtInEx will be automatically registered with all the hot key in dababase
652
653 @param KeyOption Input key option info.
654 **/
655 EFI_STATUS
656 BmProcessKeyOption (
657 IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
658 )
659 {
660 EFI_STATUS Status;
661 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
662 EFI_HANDLE *Handles;
663 UINTN HandleCount;
664 UINTN HandleIndex;
665 UINTN Index;
666 BM_HOTKEY *Hotkey;
667 UINTN KeyIndex;
668 //
669 // 16 is enough to enumerate all the possible combination of LEFT_XXX and RIGHT_XXX
670 //
671 UINT32 KeyShiftStates[16];
672 UINTN KeyShiftStateCount;
673
674 if (KeyOption->KeyData.Options.InputKeyCount > mBmHotkeySupportCount) {
675 return EFI_UNSUPPORTED;
676 }
677
678 KeyShiftStateCount = 0;
679 BmGenerateKeyShiftState (0, KeyOption, EFI_SHIFT_STATE_VALID, KeyShiftStates, &KeyShiftStateCount);
680 ASSERT (KeyShiftStateCount <= sizeof (KeyShiftStates) / sizeof (KeyShiftStates[0]));
681
682 EfiAcquireLock (&mBmHotkeyLock);
683
684 Handles = BmGetActiveConsoleIn (&HandleCount);
685
686 for (Index = 0; Index < KeyShiftStateCount; Index++) {
687 Hotkey = AllocateZeroPool (sizeof (BM_HOTKEY));
688 ASSERT (Hotkey != NULL);
689
690 Hotkey->Signature = BM_HOTKEY_SIGNATURE;
691 Hotkey->BootOption = KeyOption->BootOption;
692 Hotkey->IsContinue = (BOOLEAN) (KeyOption == mBmContinueKeyOption);
693 Hotkey->CodeCount = (UINT8) KeyOption->KeyData.Options.InputKeyCount;
694
695 for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
696 CopyMem (&Hotkey->KeyData[KeyIndex].Key, &KeyOption->Keys[KeyIndex], sizeof (EFI_INPUT_KEY));
697 Hotkey->KeyData[KeyIndex].KeyState.KeyShiftState = KeyShiftStates[Index];
698 }
699 InsertTailList (&mBmHotkeyList, &Hotkey->Link);
700
701 for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
702 Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
703 ASSERT_EFI_ERROR (Status);
704 BmRegisterHotkeyNotify (TxtInEx, Hotkey);
705 }
706 }
707
708 if (Handles != NULL) {
709 FreePool (Handles);
710 }
711 EfiReleaseLock (&mBmHotkeyLock);
712
713 return EFI_SUCCESS;
714 }
715
716 /**
717 Callback function for SimpleTextInEx protocol install events
718
719 @param Event the event that is signaled.
720 @param Context not used here.
721
722 **/
723 VOID
724 EFIAPI
725 BmTxtInExCallback (
726 IN EFI_EVENT Event,
727 IN VOID *Context
728 )
729 {
730 EFI_STATUS Status;
731 UINTN BufferSize;
732 EFI_HANDLE Handle;
733 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
734 LIST_ENTRY *Link;
735
736 while (TRUE) {
737 BufferSize = sizeof (EFI_HANDLE);
738 Status = gBS->LocateHandle (
739 ByRegisterNotify,
740 NULL,
741 mBmTxtInExRegistration,
742 &BufferSize,
743 &Handle
744 );
745 if (EFI_ERROR (Status)) {
746 //
747 // If no more notification events exist
748 //
749 return ;
750 }
751
752 Status = gBS->HandleProtocol (
753 Handle,
754 &gEfiSimpleTextInputExProtocolGuid,
755 (VOID **) &TxtInEx
756 );
757 ASSERT_EFI_ERROR (Status);
758
759 //
760 // Register the hot key notification for the existing items in the list
761 //
762 EfiAcquireLock (&mBmHotkeyLock);
763 for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); Link = GetNextNode (&mBmHotkeyList, Link)) {
764 BmRegisterHotkeyNotify (TxtInEx, BM_HOTKEY_FROM_LINK (Link));
765 }
766 EfiReleaseLock (&mBmHotkeyLock);
767 }
768 }
769
770 /**
771 Free the key options returned from BmGetKeyOptions.
772
773 @param KeyOptions Pointer to the key options.
774 @param KeyOptionCount Number of the key options.
775
776 @retval EFI_SUCCESS The key options are freed.
777 @retval EFI_NOT_FOUND KeyOptions is NULL.
778 **/
779 EFI_STATUS
780 BmFreeKeyOptions (
781 IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions,
782 IN UINTN KeyOptionCount
783 )
784 {
785 if (KeyOptions != NULL) {
786 FreePool (KeyOptions);
787 return EFI_SUCCESS;
788 } else {
789 return EFI_NOT_FOUND;
790 }
791 }
792
793 /**
794 Register the key option to exit the waiting of the Boot Manager timeout.
795 Platform should ensure that the continue key option isn't conflict with
796 other boot key options.
797
798 @param Modifier Key shift state.
799 @param ... Parameter list of pointer of EFI_INPUT_KEY.
800
801 @retval EFI_SUCCESS Successfully register the continue key option.
802 @retval EFI_ALREADY_STARTED The continue key option is already registered.
803 **/
804 EFI_STATUS
805 EFIAPI
806 EfiBootManagerRegisterContinueKeyOption (
807 IN UINT32 Modifier,
808 ...
809 )
810 {
811 EFI_STATUS Status;
812 EFI_BOOT_MANAGER_KEY_OPTION KeyOption;
813 VA_LIST Args;
814
815 if (mBmContinueKeyOption != NULL) {
816 return EFI_ALREADY_STARTED;
817 }
818
819 ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
820 VA_START (Args, Modifier);
821 Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
822 VA_END (Args);
823
824 if (!EFI_ERROR (Status)) {
825 mBmContinueKeyOption = AllocateCopyPool (sizeof (EFI_BOOT_MANAGER_KEY_OPTION), &KeyOption);
826 ASSERT (mBmContinueKeyOption != NULL);
827 if (mBmHotkeyServiceStarted) {
828 BmProcessKeyOption (mBmContinueKeyOption);
829 }
830 }
831
832 return Status;
833 }
834
835 /**
836 Stop the hotkey processing.
837
838 @param Event Event pointer related to hotkey service.
839 @param Context Context pass to this function.
840 **/
841 VOID
842 EFIAPI
843 BmStopHotkeyService (
844 IN EFI_EVENT Event,
845 IN VOID *Context
846 )
847 {
848 LIST_ENTRY *Link;
849 BM_HOTKEY *Hotkey;
850
851 DEBUG ((EFI_D_INFO, "[Bds]Stop Hotkey Service!\n"));
852 gBS->CloseEvent (Event);
853
854 EfiAcquireLock (&mBmHotkeyLock);
855 for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
856 Hotkey = BM_HOTKEY_FROM_LINK (Link);
857 BmUnregisterHotkeyNotify (Hotkey);
858 Link = RemoveEntryList (Link);
859 FreePool (Hotkey);
860 }
861 EfiReleaseLock (&mBmHotkeyLock);
862 }
863
864 /**
865 Start the hot key service so that the key press can trigger the boot option.
866
867 @param HotkeyTriggered Return the waitable event and it will be signaled
868 when a valid hot key is pressed.
869
870 @retval EFI_SUCCESS The hot key service is started.
871 **/
872 EFI_STATUS
873 EFIAPI
874 EfiBootManagerStartHotkeyService (
875 IN EFI_EVENT *HotkeyTriggered
876 )
877 {
878 EFI_STATUS Status;
879 EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
880 UINTN KeyOptionCount;
881 UINTN Index;
882 EFI_EVENT Event;
883 UINT32 *BootOptionSupport;
884
885 Status = GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **) &BootOptionSupport, NULL);
886 ASSERT (BootOptionSupport != NULL);
887
888 if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY) != 0) {
889 mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT));
890 }
891 FreePool (BootOptionSupport);
892
893 if (mBmHotkeySupportCount == 0) {
894 DEBUG ((EFI_D_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n"));
895 return EFI_UNSUPPORTED;
896 }
897
898 Status = gBS->CreateEvent (
899 EVT_NOTIFY_WAIT,
900 TPL_CALLBACK,
901 BmEmptyFunction,
902 NULL,
903 &mBmHotkeyTriggered
904 );
905 ASSERT_EFI_ERROR (Status);
906
907 if (HotkeyTriggered != NULL) {
908 *HotkeyTriggered = mBmHotkeyTriggered;
909 }
910
911 KeyOptions = BmGetKeyOptions (&KeyOptionCount);
912 for (Index = 0; Index < KeyOptionCount; Index ++) {
913 BmProcessKeyOption (&KeyOptions[Index]);
914 }
915 BmFreeKeyOptions (KeyOptions, KeyOptionCount);
916
917 if (mBmContinueKeyOption != NULL) {
918 BmProcessKeyOption (mBmContinueKeyOption);
919 }
920
921 //
922 // Hook hotkey on every future SimpleTextInputEx instance when
923 // SystemTable.ConsoleInHandle == NULL, which means the console
924 // manager (ConSplitter) is absent.
925 //
926 if (gST->ConsoleInHandle == NULL) {
927 EfiCreateProtocolNotifyEvent (
928 &gEfiSimpleTextInputExProtocolGuid,
929 TPL_CALLBACK,
930 BmTxtInExCallback,
931 NULL,
932 &mBmTxtInExRegistration
933 );
934 }
935
936 Status = EfiCreateEventReadyToBootEx (
937 TPL_CALLBACK,
938 BmStopHotkeyService,
939 NULL,
940 &Event
941 );
942 ASSERT_EFI_ERROR (Status);
943
944 mBmHotkeyServiceStarted = TRUE;
945 return Status;
946 }
947
948 /**
949 Add the key option.
950 It adds the key option variable and the key option takes affect immediately.
951
952 @param AddedOption Return the added key option.
953 @param BootOptionNumber The boot option number for the key option.
954 @param Modifier Key shift state.
955 @param ... Parameter list of pointer of EFI_INPUT_KEY.
956
957 @retval EFI_SUCCESS The key option is added.
958 @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.
959 **/
960 EFI_STATUS
961 EFIAPI
962 EfiBootManagerAddKeyOptionVariable (
963 OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption, OPTIONAL
964 IN UINT16 BootOptionNumber,
965 IN UINT32 Modifier,
966 ...
967 )
968 {
969 EFI_STATUS Status;
970 VA_LIST Args;
971 VOID *BootOption;
972 UINTN BootOptionSize;
973 CHAR16 BootOptionName[BM_OPTION_NAME_LEN];
974 EFI_BOOT_MANAGER_KEY_OPTION KeyOption;
975 EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
976 UINTN KeyOptionCount;
977 UINTN Index;
978 UINTN KeyOptionNumber;
979 CHAR16 KeyOptionName[sizeof ("Key####")];
980
981 UnicodeSPrint (
982 BootOptionName, sizeof (BootOptionName), L"%s%04x",
983 mBmLoadOptionName[LoadOptionTypeBoot], BootOptionNumber
984 );
985 GetEfiGlobalVariable2 (BootOptionName, &BootOption, &BootOptionSize);
986
987 if (BootOption == NULL) {
988 return EFI_NOT_FOUND;
989 }
990
991 ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
992 KeyOption.BootOption = BootOptionNumber;
993 Status = gBS->CalculateCrc32 (BootOption, BootOptionSize, &KeyOption.BootOptionCrc);
994 ASSERT_EFI_ERROR (Status);
995 FreePool (BootOption);
996
997 VA_START (Args, Modifier);
998 Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
999 VA_END (Args);
1000 if (EFI_ERROR (Status)) {
1001 return Status;
1002 }
1003
1004 KeyOptionNumber = LoadOptionNumberUnassigned;
1005 //
1006 // Check if the hot key sequence was defined already
1007 //
1008 KeyOptions = BmGetKeyOptions (&KeyOptionCount);
1009 for (Index = 0; Index < KeyOptionCount; Index++) {
1010 if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
1011 (CompareMem (KeyOptions[Index].Keys, KeyOption.Keys, KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)) {
1012 break;
1013 }
1014
1015 if ((KeyOptionNumber == LoadOptionNumberUnassigned) &&
1016 (KeyOptions[Index].OptionNumber > Index)
1017 ){
1018 KeyOptionNumber = Index;
1019 }
1020 }
1021 BmFreeKeyOptions (KeyOptions, KeyOptionCount);
1022
1023 if (Index < KeyOptionCount) {
1024 return EFI_ALREADY_STARTED;
1025 }
1026
1027 if (KeyOptionNumber == LoadOptionNumberUnassigned) {
1028 KeyOptionNumber = KeyOptionCount;
1029 }
1030
1031 UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber);
1032
1033 Status = gRT->SetVariable (
1034 KeyOptionName,
1035 &gEfiGlobalVariableGuid,
1036 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
1037 BmSizeOfKeyOption (&KeyOption),
1038 &KeyOption
1039 );
1040 if (!EFI_ERROR (Status)) {
1041 //
1042 // Return the Key Option in case needed by caller
1043 //
1044 if (AddedOption != NULL) {
1045 CopyMem (AddedOption, &KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
1046 }
1047
1048 //
1049 // Register the newly added hot key
1050 // Calling this function before EfiBootManagerStartHotkeyService doesn't
1051 // need to call BmProcessKeyOption
1052 //
1053 if (mBmHotkeyServiceStarted) {
1054 BmProcessKeyOption (&KeyOption);
1055 }
1056 }
1057
1058 return Status;
1059 }
1060
1061 /**
1062 Delete the Key Option variable and unregister the hot key
1063
1064 @param DeletedOption Return the deleted key options.
1065 @param Modifier Key shift state.
1066 @param ... Parameter list of pointer of EFI_INPUT_KEY.
1067
1068 @retval EFI_SUCCESS The key option is deleted.
1069 @retval EFI_NOT_FOUND The key option cannot be found.
1070 **/
1071 EFI_STATUS
1072 EFIAPI
1073 EfiBootManagerDeleteKeyOptionVariable (
1074 IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL
1075 IN UINT32 Modifier,
1076 ...
1077 )
1078 {
1079 EFI_STATUS Status;
1080 UINTN Index;
1081 VA_LIST Args;
1082 EFI_BOOT_MANAGER_KEY_OPTION KeyOption;
1083 EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
1084 UINTN KeyOptionCount;
1085 LIST_ENTRY *Link;
1086 BM_HOTKEY *Hotkey;
1087 UINT32 ShiftState;
1088 BOOLEAN Match;
1089 CHAR16 KeyOptionName[sizeof ("Key####")];
1090
1091 ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
1092 VA_START (Args, Modifier);
1093 Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
1094 VA_END (Args);
1095
1096 if (EFI_ERROR (Status)) {
1097 return Status;
1098 }
1099
1100 EfiAcquireLock (&mBmHotkeyLock);
1101 //
1102 // Delete the key option from active hot key list
1103 // Could have multiple entries when modifier isn't 0 because we map the ShiftPressed to RIGHT_SHIFT and RIGHT_SHIFT
1104 //
1105 for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
1106 Hotkey = BM_HOTKEY_FROM_LINK (Link);
1107 Match = (BOOLEAN) (Hotkey->CodeCount == KeyOption.KeyData.Options.InputKeyCount);
1108
1109 for (Index = 0; Match && (Index < Hotkey->CodeCount); Index++) {
1110 ShiftState = Hotkey->KeyData[Index].KeyState.KeyShiftState;
1111 if (
1112 (BmBitSet (ShiftState, EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED) != KeyOption.KeyData.Options.ShiftPressed) ||
1113 (BmBitSet (ShiftState, EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) != KeyOption.KeyData.Options.ControlPressed) ||
1114 (BmBitSet (ShiftState, EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) != KeyOption.KeyData.Options.AltPressed) ||
1115 (BmBitSet (ShiftState, EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED) != KeyOption.KeyData.Options.LogoPressed) ||
1116 (BmBitSet (ShiftState, EFI_MENU_KEY_PRESSED) != KeyOption.KeyData.Options.MenuPressed) ||
1117 (BmBitSet (ShiftState, EFI_SYS_REQ_PRESSED) != KeyOption.KeyData.Options.SysReqPressed) ||
1118 (CompareMem (&Hotkey->KeyData[Index].Key, &KeyOption.Keys[Index], sizeof (EFI_INPUT_KEY)) != 0)
1119 ) {
1120 //
1121 // Break when any field doesn't match
1122 //
1123 Match = FALSE;
1124 break;
1125 }
1126 }
1127
1128 if (Match) {
1129 Link = RemoveEntryList (Link);
1130 FreePool (Hotkey);
1131 } else {
1132 Link = GetNextNode (&mBmHotkeyList, Link);
1133 }
1134 }
1135
1136 //
1137 // Delete the key option from the variable
1138 //
1139 Status = EFI_NOT_FOUND;
1140 KeyOptions = BmGetKeyOptions (&KeyOptionCount);
1141 for (Index = 0; Index < KeyOptionCount; Index++) {
1142 if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
1143 (CompareMem (
1144 KeyOptions[Index].Keys, KeyOption.Keys,
1145 KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)
1146 ) {
1147 UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptions[Index].OptionNumber);
1148 Status = gRT->SetVariable (
1149 KeyOptionName,
1150 &gEfiGlobalVariableGuid,
1151 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
1152 0,
1153 NULL
1154 );
1155 //
1156 // Return the deleted key option in case needed by caller
1157 //
1158 if (DeletedOption != NULL) {
1159 CopyMem (DeletedOption, &KeyOptions[Index], sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
1160 }
1161 break;
1162 }
1163 }
1164 BmFreeKeyOptions (KeyOptions, KeyOptionCount);
1165
1166 EfiReleaseLock (&mBmHotkeyLock);
1167
1168 return Status;
1169 }