]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/BdsDxe/Hotkey.c
sync comments, fix function header, rename variable name to follow coding style.
[mirror_edk2.git] / MdeModulePkg / Universal / BdsDxe / Hotkey.c
1 /** @file
2 Provides a way for 3rd party applications to register themselves for launch by the
3 Boot Manager based on hot key
4
5 Copyright (c) 2007 - 2008, Intel Corporation. <BR>
6 All rights reserved. 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 "Hotkey.h"
17
18
19 LIST_ENTRY mHotkeyList = INITIALIZE_LIST_HEAD_VARIABLE (mHotkeyList);
20 BOOLEAN mHotkeyCallbackPending = FALSE;
21 EFI_EVENT mHotkeyEvent;
22 VOID *mHotkeyRegistration;
23
24
25 /**
26
27 Check if the Key Option is valid or not.
28
29
30 @param KeyOption The Hot Key Option to be checked.
31
32 @retval TRUE The Hot Key Option is valid.
33 @retval FALSE The Hot Key Option is invalid.
34
35 **/
36 BOOLEAN
37 IsKeyOptionValid (
38 IN EFI_KEY_OPTION *KeyOption
39 )
40 {
41 UINT16 BootOptionName[10];
42 UINT8 *BootOptionVar;
43 UINTN BootOptionSize;
44 UINT32 Crc;
45
46 //
47 // Check whether corresponding Boot Option exist
48 //
49 UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", KeyOption->BootOption);
50 BootOptionVar = BdsLibGetVariableAndSize (
51 BootOptionName,
52 &gEfiGlobalVariableGuid,
53 &BootOptionSize
54 );
55
56 if (BootOptionVar == NULL || BootOptionSize == 0) {
57 return FALSE;
58 }
59
60 //
61 // Check CRC for Boot Option
62 //
63 gBS->CalculateCrc32 (BootOptionVar, BootOptionSize, &Crc);
64 FreePool (BootOptionVar);
65
66 return (BOOLEAN) ((KeyOption->BootOptionCrc == Crc) ? TRUE : FALSE);
67 }
68
69 /**
70
71 Create Key#### for the given hotkey.
72
73
74 @param KeyOption The Hot Key Option to be added.
75 @param KeyOptionNumber The key option number for Key#### (optional).
76
77 @retval EFI_SUCCESS Register hotkey successfully.
78 @retval EFI_INVALID_PARAMETER The hotkey option is invalid.
79
80 **/
81 EFI_STATUS
82 RegisterHotkey (
83 IN EFI_KEY_OPTION *KeyOption,
84 OUT UINT16 *KeyOptionNumber
85 )
86 {
87 UINT16 KeyOptionName[10];
88 UINT16 *KeyOrder;
89 UINTN KeyOrderSize;
90 UINT16 *NewKeyOrder;
91 UINTN Index;
92 UINT16 MaxOptionNumber;
93 UINT16 RegisterOptionNumber;
94 EFI_KEY_OPTION *TempOption;
95 UINTN TempOptionSize;
96 EFI_STATUS Status;
97 UINTN KeyOptionSize;
98 BOOLEAN UpdateBootOption;
99
100 //
101 // Validate the given key option
102 //
103 if (!IsKeyOptionValid (KeyOption)) {
104 return EFI_INVALID_PARAMETER;
105 }
106
107 KeyOptionSize = sizeof (EFI_KEY_OPTION) + GET_KEY_CODE_COUNT (KeyOption->KeyOptions.PackedValue) * sizeof (EFI_INPUT_KEY);
108 UpdateBootOption = FALSE;
109
110 //
111 // check whether HotKey conflict with keys used by Setup Browser
112 //
113
114 KeyOrder = BdsLibGetVariableAndSize (
115 VAR_KEY_ORDER,
116 &gEfiGlobalVariableGuid,
117 &KeyOrderSize
118 );
119 if (KeyOrder == NULL) {
120 KeyOrderSize = 0;
121 }
122
123 //
124 // Find free key option number
125 //
126 MaxOptionNumber = 0;
127 TempOption = NULL;
128 for (Index = 0; Index < KeyOrderSize / sizeof (UINT16); Index++) {
129 if (MaxOptionNumber < KeyOrder[Index]) {
130 MaxOptionNumber = KeyOrder[Index];
131 }
132
133 UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOrder[Index]);
134 TempOption = BdsLibGetVariableAndSize (
135 KeyOptionName,
136 &gEfiGlobalVariableGuid,
137 &TempOptionSize
138 );
139
140 if (CompareMem (TempOption, KeyOption, TempOptionSize) == 0) {
141 //
142 // Got the option, so just return
143 //
144 FreePool (TempOption);
145 FreePool (KeyOrder);
146 return EFI_SUCCESS;
147 }
148
149 if (KeyOption->KeyOptions.PackedValue == TempOption->KeyOptions.PackedValue) {
150 if (GET_KEY_CODE_COUNT (KeyOption->KeyOptions.PackedValue) == 0 ||
151 CompareMem (
152 ((UINT8 *) TempOption) + sizeof (EFI_KEY_OPTION),
153 ((UINT8 *) KeyOption) + sizeof (EFI_KEY_OPTION),
154 KeyOptionSize - sizeof (EFI_KEY_OPTION)
155 ) == 0) {
156 //
157 // Hotkey is the same but BootOption changed, need update
158 //
159 UpdateBootOption = TRUE;
160 break;
161 }
162 }
163
164 FreePool (TempOption);
165 }
166
167 if (UpdateBootOption) {
168 RegisterOptionNumber = KeyOrder[Index];
169 FreePool (TempOption);
170 } else {
171 RegisterOptionNumber = (UINT16) (MaxOptionNumber + 1);
172 }
173
174 if (KeyOptionNumber != NULL) {
175 *KeyOptionNumber = RegisterOptionNumber;
176 }
177
178 //
179 // Create variable Key####
180 //
181 UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", RegisterOptionNumber);
182 Status = gRT->SetVariable (
183 KeyOptionName,
184 &gEfiGlobalVariableGuid,
185 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
186 KeyOptionSize,
187 KeyOption
188 );
189 if (EFI_ERROR (Status)) {
190 gBS->FreePool (KeyOrder);
191 return Status;
192 }
193
194 //
195 // Update the key order variable - "KeyOrder"
196 //
197 if (!UpdateBootOption) {
198 Index = KeyOrderSize / sizeof (UINT16);
199 KeyOrderSize += sizeof (UINT16);
200 }
201
202 NewKeyOrder = AllocatePool (KeyOrderSize);
203 if (NewKeyOrder == NULL) {
204 return EFI_OUT_OF_RESOURCES;
205 }
206
207 if (KeyOrder != NULL) {
208 CopyMem (NewKeyOrder, KeyOrder, KeyOrderSize);
209 }
210
211 NewKeyOrder[Index] = RegisterOptionNumber;
212
213 Status = gRT->SetVariable (
214 VAR_KEY_ORDER,
215 &gEfiGlobalVariableGuid,
216 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
217 KeyOrderSize,
218 NewKeyOrder
219 );
220
221 FreePool (KeyOrder);
222 FreePool (NewKeyOrder);
223
224 return Status;
225 }
226
227 /**
228
229 Delete Key#### for the given Key Option number.
230
231 @param KeyOptionNumber Key option number for Key####
232
233 @retval EFI_SUCCESS Unregister hotkey successfully.
234 @retval EFI_NOT_FOUND No Key#### is found for the given Key Option number.
235
236 **/
237 EFI_STATUS
238 UnregisterHotkey (
239 IN UINT16 KeyOptionNumber
240 )
241 {
242 UINT16 KeyOption[10];
243 UINTN Index;
244 EFI_STATUS Status;
245 UINTN Index2Del;
246 UINT16 *KeyOrder;
247 UINTN KeyOrderSize;
248
249 //
250 // Delete variable Key####
251 //
252 UnicodeSPrint (KeyOption, sizeof (KeyOption), L"Key%04x", KeyOptionNumber);
253 gRT->SetVariable (
254 KeyOption,
255 &gEfiGlobalVariableGuid,
256 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
257 0,
258 NULL
259 );
260
261 //
262 // Adjust key order array
263 //
264 KeyOrder = BdsLibGetVariableAndSize (
265 VAR_KEY_ORDER,
266 &gEfiGlobalVariableGuid,
267 &KeyOrderSize
268 );
269 if (KeyOrder == NULL) {
270 return EFI_SUCCESS;
271 }
272
273 Index2Del = 0;
274 for (Index = 0; Index < KeyOrderSize / sizeof (UINT16); Index++) {
275 if (KeyOrder[Index] == KeyOptionNumber) {
276 Index2Del = Index;
277 break;
278 }
279 }
280
281 if (Index != KeyOrderSize / sizeof (UINT16)) {
282 //
283 // KeyOptionNumber found in "KeyOrder", delete it
284 //
285 for (Index = Index2Del; Index < KeyOrderSize / sizeof (UINT16) - 1; Index++) {
286 KeyOrder[Index] = KeyOrder[Index + 1];
287 }
288
289 KeyOrderSize -= sizeof (UINT16);
290 }
291
292 Status = gRT->SetVariable (
293 VAR_KEY_ORDER,
294 &gEfiGlobalVariableGuid,
295 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
296 KeyOrderSize,
297 KeyOrder
298 );
299
300 FreePool (KeyOrder);
301
302 return Status;
303 }
304
305 /**
306
307 This is the common notification function for HotKeys, it will be registered
308 with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.
309
310
311 @param KeyData A pointer to a buffer that is filled in with the keystroke
312 information for the key that was pressed.
313
314 @retval EFI_SUCCESS KeyData is successfully processed.
315
316 **/
317 EFI_STATUS
318 HotkeyCallback (
319 IN EFI_KEY_DATA *KeyData
320 )
321 {
322 BOOLEAN HotkeyCatched;
323 LIST_ENTRY BootLists;
324 LIST_ENTRY *Link;
325 BDS_HOTKEY_OPTION *Hotkey;
326 UINT16 Buffer[10];
327 BDS_COMMON_OPTION *BootOption;
328 UINTN ExitDataSize;
329 CHAR16 *ExitData;
330 EFI_STATUS Status;
331 EFI_KEY_DATA *HotkeyData;
332
333 if (mHotkeyCallbackPending) {
334 //
335 // When responsing to a Hotkey, ignore sequential hotkey stroke until
336 // the current Boot#### load option returned
337 //
338 return EFI_SUCCESS;
339 }
340
341 Status = EFI_SUCCESS;
342 Link = GetFirstNode (&mHotkeyList);
343
344 while (!IsNull (&mHotkeyList, Link)) {
345 HotkeyCatched = FALSE;
346 Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link);
347
348 //
349 // Is this Key Stroke we are waiting for?
350 //
351 HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];
352 if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&
353 (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&
354 ((HotkeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) ? (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : 1)) {
355 //
356 // Receive an expecting key stroke
357 //
358 if (Hotkey->CodeCount > 1) {
359 //
360 // For hotkey of key combination, transit to next waiting state
361 //
362 Hotkey->WaitingKey++;
363
364 if (Hotkey->WaitingKey == Hotkey->CodeCount) {
365 //
366 // Received the whole key stroke sequence
367 //
368 HotkeyCatched = TRUE;
369 }
370 } else {
371 //
372 // For hotkey of single key stroke
373 //
374 HotkeyCatched = TRUE;
375 }
376 } else {
377 //
378 // Receive an unexpected key stroke, reset to initial waiting state
379 //
380 Hotkey->WaitingKey = 0;
381 }
382
383 if (HotkeyCatched) {
384 //
385 // Reset to initial waiting state
386 //
387 Hotkey->WaitingKey = 0;
388
389 //
390 // Launch its BootOption
391 //
392 InitializeListHead (&BootLists);
393
394 UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", Hotkey->BootOptionNumber);
395 BootOption = BdsLibVariableToOption (&BootLists, Buffer);
396 BootOption->BootCurrent = Hotkey->BootOptionNumber;
397 BdsLibConnectDevicePath (BootOption->DevicePath);
398
399 //
400 // Clear the screen before launch this BootOption
401 //
402 gST->ConOut->Reset (gST->ConOut, FALSE);
403
404 mHotkeyCallbackPending = TRUE;
405 Status = BdsLibBootViaBootOption (BootOption, BootOption->DevicePath, &ExitDataSize, &ExitData);
406 mHotkeyCallbackPending = FALSE;
407
408 if (EFI_ERROR (Status)) {
409 //
410 // Call platform action to indicate the boot fail
411 //
412 BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED));
413 PlatformBdsBootFail (BootOption, Status, ExitData, ExitDataSize);
414 } else {
415 //
416 // Call platform action to indicate the boot success
417 //
418 BootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED));
419 PlatformBdsBootSuccess (BootOption);
420 }
421 }
422
423 Link = GetNextNode (&mHotkeyList, Link);
424 }
425
426 return Status;
427 }
428
429 /**
430
431 Register the common HotKey notify function to given SimpleTextInEx protocol instance.
432
433
434 @param SimpleTextInEx Simple Text Input Ex protocol instance
435
436 @retval EFI_SUCCESS Register hotkey notification function successfully.
437 @retval EFI_OUT_OF_RESOURCES Unable to allocate necessary data structures.
438
439 **/
440 EFI_STATUS
441 HotkeyRegisterNotify (
442 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx
443 )
444 {
445 UINTN Index;
446 EFI_STATUS Status;
447 LIST_ENTRY *Link;
448 BDS_HOTKEY_OPTION *Hotkey;
449
450 //
451 // Register notification function for each hotkey
452 //
453 Link = GetFirstNode (&mHotkeyList);
454
455 while (!IsNull (&mHotkeyList, Link)) {
456 Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link);
457
458 Index = 0;
459 do {
460 Status = SimpleTextInEx->RegisterKeyNotify (
461 SimpleTextInEx,
462 &Hotkey->KeyData[Index],
463 HotkeyCallback,
464 &Hotkey->NotifyHandle
465 );
466 if (EFI_ERROR (Status)) {
467 //
468 // some of the hotkey registry failed
469 //
470 return Status;
471 }
472 Index ++;
473 } while (Index < Hotkey->CodeCount);
474
475 Link = GetNextNode (&mHotkeyList, Link);
476 }
477
478 return EFI_SUCCESS;
479 }
480
481 /**
482 Callback function for SimpleTextInEx protocol install events
483
484
485 @param Event the event that is signaled.
486 @param Context not used here.
487
488 @return VOID
489
490 **/
491 VOID
492 EFIAPI
493 HotkeyEvent (
494 IN EFI_EVENT Event,
495 IN VOID *Context
496 )
497 {
498 EFI_STATUS Status;
499 UINTN BufferSize;
500 EFI_HANDLE Handle;
501 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
502
503 while (TRUE) {
504 BufferSize = sizeof (EFI_HANDLE);
505 Status = gBS->LocateHandle (
506 ByRegisterNotify,
507 NULL,
508 mHotkeyRegistration,
509 &BufferSize,
510 &Handle
511 );
512 if (EFI_ERROR (Status)) {
513 //
514 // If no more notification events exist
515 //
516 return ;
517 }
518
519 Status = gBS->HandleProtocol (
520 Handle,
521 &gEfiSimpleTextInputExProtocolGuid,
522 (VOID **) &SimpleTextInEx
523 );
524 ASSERT_EFI_ERROR (Status);
525
526 HotkeyRegisterNotify (SimpleTextInEx);
527 }
528 }
529
530 /**
531
532 Insert Key Option to hotkey list.
533
534
535 @param KeyOption The Hot Key Option to be added to hotkey list.
536
537 @retval EFI_SUCCESS Add to hotkey list success.
538
539 **/
540 EFI_STATUS
541 HotkeyInsertList (
542 IN EFI_KEY_OPTION *KeyOption
543 )
544 {
545 BDS_HOTKEY_OPTION *HotkeyLeft;
546 BDS_HOTKEY_OPTION *HotkeyRight;
547 UINTN Index;
548 UINT32 KeyOptions;
549 UINT32 KeyShiftStateLeft;
550 UINT32 KeyShiftStateRight;
551 EFI_INPUT_KEY *InputKey;
552 EFI_KEY_DATA *KeyData;
553
554 HotkeyLeft = AllocateZeroPool (sizeof (BDS_HOTKEY_OPTION));
555 if (HotkeyLeft == NULL) {
556 return EFI_OUT_OF_RESOURCES;
557 }
558
559 HotkeyLeft->Signature = BDS_HOTKEY_OPTION_SIGNATURE;
560 HotkeyLeft->BootOptionNumber = KeyOption->BootOption;
561
562 KeyOptions = KeyOption->KeyOptions.PackedValue;
563
564 HotkeyLeft->CodeCount = (UINT8) GET_KEY_CODE_COUNT (KeyOptions);
565
566 //
567 // Map key shift state from KeyOptions to EFI_KEY_DATA.KeyState
568 //
569 KeyShiftStateRight = (KeyOptions & EFI_KEY_OPTION_SHIFT) |
570 ((KeyOptions & EFI_KEY_OPTION_CONTROL) << 1) |
571 ((KeyOptions & EFI_KEY_OPTION_ALT) << 2) |
572 ((KeyOptions & EFI_KEY_OPTION_LOGO) << 3) |
573 ((KeyOptions & (EFI_KEY_OPTION_MENU | EFI_KEY_OPTION_SYSREQ)) << 4) |
574 EFI_SHIFT_STATE_VALID;
575
576 KeyShiftStateLeft = (KeyShiftStateRight & 0xffffff00) | ((KeyShiftStateRight & 0xff) << 1);
577
578 InputKey = (EFI_INPUT_KEY *) (((UINT8 *) KeyOption) + sizeof (EFI_KEY_OPTION));
579
580 Index = 0;
581 KeyData = &HotkeyLeft->KeyData[0];
582 do {
583 //
584 // If Key CodeCount is 0, then only KeyData[0] is used;
585 // if Key CodeCount is n, then KeyData[0]~KeyData[n-1] are used
586 //
587 KeyData->Key.ScanCode = InputKey[Index].ScanCode;
588 KeyData->Key.UnicodeChar = InputKey[Index].UnicodeChar;
589 KeyData->KeyState.KeyShiftState = KeyShiftStateLeft;
590
591 Index++;
592 KeyData++;
593 } while (Index < HotkeyLeft->CodeCount);
594 InsertTailList (&mHotkeyList, &HotkeyLeft->Link);
595
596 if (KeyShiftStateLeft != KeyShiftStateRight) {
597 //
598 // Need an extra hotkey for shift key on right
599 //
600 HotkeyRight = AllocateCopyPool (sizeof (BDS_HOTKEY_OPTION), HotkeyLeft);
601 if (HotkeyRight == NULL) {
602 return EFI_OUT_OF_RESOURCES;
603 }
604
605 Index = 0;
606 KeyData = &HotkeyRight->KeyData[0];
607 do {
608 //
609 // Key.ScanCode and Key.UnicodeChar have already been initialized,
610 // only need to update KeyState.KeyShiftState
611 //
612 KeyData->KeyState.KeyShiftState = KeyShiftStateRight;
613
614 Index++;
615 KeyData++;
616 } while (Index < HotkeyRight->CodeCount);
617 InsertTailList (&mHotkeyList, &HotkeyRight->Link);
618 }
619
620 return EFI_SUCCESS;
621 }
622
623 /**
624
625 Process all the "Key####" variables, associate Hotkeys with corresponding Boot Options.
626
627 @retval EFI_SUCCESS Hotkey services successfully initialized.
628 @retval EFI_NOT_FOUND Can not find the "KeyOrder" variable
629 **/
630 EFI_STATUS
631 InitializeHotkeyService (
632 VOID
633 )
634 {
635 EFI_STATUS Status;
636 UINT32 BootOptionSupport;
637 UINT16 *KeyOrder;
638 UINTN KeyOrderSize;
639 UINTN Index;
640 UINT16 KeyOptionName[8];
641 UINTN KeyOptionSize;
642 EFI_KEY_OPTION *KeyOption;
643
644 //
645 // Export our capability - EFI_BOOT_OPTION_SUPPORT_KEY and EFI_BOOT_OPTION_SUPPORT_APP
646 //
647 BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_KEY | EFI_BOOT_OPTION_SUPPORT_APP;
648 Status = gRT->SetVariable (
649 L"BootOptionSupport",
650 &gEfiGlobalVariableGuid,
651 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
652 sizeof (UINT32),
653 &BootOptionSupport
654 );
655
656 //
657 // Get valid Key Option List from private EFI variable "KeyOrder"
658 //
659 KeyOrder = BdsLibGetVariableAndSize (
660 VAR_KEY_ORDER,
661 &gEfiGlobalVariableGuid,
662 &KeyOrderSize
663 );
664
665 if (KeyOrder == NULL) {
666 return EFI_NOT_FOUND;
667 }
668
669 for (Index = 0; Index < KeyOrderSize / sizeof (UINT16); Index ++) {
670 UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOrder[Index]);
671 KeyOption = BdsLibGetVariableAndSize (
672 KeyOptionName,
673 &gEfiGlobalVariableGuid,
674 &KeyOptionSize
675 );
676
677 if (KeyOption == NULL || !IsKeyOptionValid (KeyOption)) {
678 UnregisterHotkey (KeyOrder[Index]);
679 } else {
680 HotkeyInsertList (KeyOption);
681 }
682 }
683
684 //
685 // Register Protocol notify for Hotkey service
686 //
687 Status = gBS->CreateEvent (
688 EVT_NOTIFY_SIGNAL,
689 TPL_CALLBACK,
690 HotkeyEvent,
691 NULL,
692 &mHotkeyEvent
693 );
694 ASSERT_EFI_ERROR (Status);
695
696 //
697 // Register for protocol notifications on this event
698 //
699 Status = gBS->RegisterProtocolNotify (
700 &gEfiSimpleTextInputExProtocolGuid,
701 mHotkeyEvent,
702 &mHotkeyRegistration
703 );
704 ASSERT_EFI_ERROR (Status);
705
706 return Status;
707 }
708