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