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