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