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