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 - 2014, Intel Corporation. All rights reserved.<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 "Hotkey.h"
17
18
19 LIST_ENTRY mHotkeyList = INITIALIZE_LIST_HEAD_VARIABLE (mHotkeyList);
20 BDS_COMMON_OPTION *mHotkeyBootOption = NULL;
21 EFI_EVENT mHotkeyEvent;
22 VOID *mHotkeyRegistration;
23
24
25 /**
26 Check if the Key Option is valid or not.
27
28 @param KeyOption The Hot Key Option to be checked.
29
30 @retval TRUE The Hot Key Option is valid.
31 @retval FALSE The Hot Key Option is invalid.
32
33 **/
34 BOOLEAN
IsKeyOptionValid(IN EFI_KEY_OPTION * KeyOption)35 IsKeyOptionValid (
36 IN EFI_KEY_OPTION *KeyOption
37 )
38 {
39 UINT16 BootOptionName[10];
40 UINT8 *BootOptionVar;
41 UINTN BootOptionSize;
42 UINT32 Crc;
43
44 //
45 // Check whether corresponding Boot Option exist
46 //
47 UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", KeyOption->BootOption);
48 BootOptionVar = BdsLibGetVariableAndSize (
49 BootOptionName,
50 &gEfiGlobalVariableGuid,
51 &BootOptionSize
52 );
53
54 if (BootOptionVar == NULL || BootOptionSize == 0) {
55 return FALSE;
56 }
57
58 //
59 // Check CRC for Boot Option
60 //
61 gBS->CalculateCrc32 (BootOptionVar, BootOptionSize, &Crc);
62 FreePool (BootOptionVar);
63
64 return (BOOLEAN) ((KeyOption->BootOptionCrc == Crc) ? TRUE : FALSE);
65 }
66
67 /**
68 Try to boot the boot option triggered by hotkey.
69 @retval EFI_SUCCESS There is HotkeyBootOption & it is processed
70 @retval EFI_NOT_FOUND There is no HotkeyBootOption
71 **/
72 EFI_STATUS
HotkeyBoot(VOID)73 HotkeyBoot (
74 VOID
75 )
76 {
77 EFI_STATUS Status;
78 UINTN ExitDataSize;
79 CHAR16 *ExitData;
80
81 if (mHotkeyBootOption == NULL) {
82 return EFI_NOT_FOUND;
83 }
84
85 BdsLibConnectDevicePath (mHotkeyBootOption->DevicePath);
86
87 //
88 // Clear the screen before launch this BootOption
89 //
90 gST->ConOut->Reset (gST->ConOut, FALSE);
91
92 Status = BdsLibBootViaBootOption (mHotkeyBootOption, mHotkeyBootOption->DevicePath, &ExitDataSize, &ExitData);
93
94 if (EFI_ERROR (Status)) {
95 //
96 // Call platform action to indicate the boot fail
97 //
98 mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED));
99 PlatformBdsBootFail (mHotkeyBootOption, Status, ExitData, ExitDataSize);
100 } else {
101 //
102 // Call platform action to indicate the boot success
103 //
104 mHotkeyBootOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED));
105 PlatformBdsBootSuccess (mHotkeyBootOption);
106 }
107 FreePool (mHotkeyBootOption->Description);
108 FreePool (mHotkeyBootOption->DevicePath);
109 FreePool (mHotkeyBootOption->LoadOptions);
110 FreePool (mHotkeyBootOption);
111
112 mHotkeyBootOption = NULL;
113
114 return EFI_SUCCESS;
115 }
116
117 /**
118
119 This is the common notification function for HotKeys, it will be registered
120 with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.
121
122 @param KeyData A pointer to a buffer that is filled in with the keystroke
123 information for the key that was pressed.
124
125 @retval EFI_SUCCESS KeyData is successfully processed.
126 @return EFI_NOT_FOUND Fail to find boot option variable.
127 **/
128 EFI_STATUS
129 EFIAPI
HotkeyCallback(IN EFI_KEY_DATA * KeyData)130 HotkeyCallback (
131 IN EFI_KEY_DATA *KeyData
132 )
133 {
134 BOOLEAN HotkeyCatched;
135 LIST_ENTRY BootLists;
136 LIST_ENTRY *Link;
137 BDS_HOTKEY_OPTION *Hotkey;
138 UINT16 Buffer[10];
139 EFI_STATUS Status;
140 EFI_KEY_DATA *HotkeyData;
141
142 if (mHotkeyBootOption != NULL) {
143 //
144 // Do not process sequential hotkey stroke until the current boot option returns
145 //
146 return EFI_SUCCESS;
147 }
148
149 Status = EFI_SUCCESS;
150
151 for ( Link = GetFirstNode (&mHotkeyList)
152 ; !IsNull (&mHotkeyList, Link)
153 ; Link = GetNextNode (&mHotkeyList, Link)
154 ) {
155 HotkeyCatched = FALSE;
156 Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link);
157
158 //
159 // Is this Key Stroke we are waiting for?
160 //
161 ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0])));
162 HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];
163 if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&
164 (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&
165 (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ?
166 (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE
167 )
168 ) {
169 //
170 // For hotkey of key combination, transit to next waiting state
171 //
172 Hotkey->WaitingKey++;
173
174 if (Hotkey->WaitingKey == Hotkey->CodeCount) {
175 //
176 // Received the whole key stroke sequence
177 //
178 HotkeyCatched = TRUE;
179 }
180 } else {
181 //
182 // Receive an unexpected key stroke, reset to initial waiting state
183 //
184 Hotkey->WaitingKey = 0;
185 }
186
187 if (HotkeyCatched) {
188 //
189 // Reset to initial waiting state
190 //
191 Hotkey->WaitingKey = 0;
192
193 //
194 // Launch its BootOption
195 //
196 InitializeListHead (&BootLists);
197
198 UnicodeSPrint (Buffer, sizeof (Buffer), L"Boot%04x", Hotkey->BootOptionNumber);
199 mHotkeyBootOption = BdsLibVariableToOption (&BootLists, Buffer);
200 }
201 }
202
203 return Status;
204 }
205
206 /**
207 Register the common HotKey notify function to given SimpleTextInEx protocol instance.
208
209 @param SimpleTextInEx Simple Text Input Ex protocol instance
210
211 @retval EFI_SUCCESS Register hotkey notification function successfully.
212 @retval EFI_OUT_OF_RESOURCES Unable to allocate necessary data structures.
213
214 **/
215 EFI_STATUS
HotkeyRegisterNotify(IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL * SimpleTextInEx)216 HotkeyRegisterNotify (
217 IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx
218 )
219 {
220 UINTN Index;
221 EFI_STATUS Status;
222 LIST_ENTRY *Link;
223 BDS_HOTKEY_OPTION *Hotkey;
224
225 //
226 // Register notification function for each hotkey
227 //
228 Link = GetFirstNode (&mHotkeyList);
229
230 while (!IsNull (&mHotkeyList, Link)) {
231 Hotkey = BDS_HOTKEY_OPTION_FROM_LINK (Link);
232
233 Index = 0;
234 do {
235 Status = SimpleTextInEx->RegisterKeyNotify (
236 SimpleTextInEx,
237 &Hotkey->KeyData[Index],
238 HotkeyCallback,
239 &Hotkey->NotifyHandle
240 );
241 if (EFI_ERROR (Status)) {
242 //
243 // some of the hotkey registry failed
244 //
245 return Status;
246 }
247 Index ++;
248 } while ((Index < Hotkey->CodeCount) && (Index < (sizeof (Hotkey->KeyData) / sizeof (EFI_KEY_DATA))));
249
250 Link = GetNextNode (&mHotkeyList, Link);
251 }
252
253 return EFI_SUCCESS;
254 }
255
256 /**
257 Callback function for SimpleTextInEx protocol install events
258
259 @param Event the event that is signaled.
260 @param Context not used here.
261
262 **/
263 VOID
264 EFIAPI
HotkeyEvent(IN EFI_EVENT Event,IN VOID * Context)265 HotkeyEvent (
266 IN EFI_EVENT Event,
267 IN VOID *Context
268 )
269 {
270 EFI_STATUS Status;
271 UINTN BufferSize;
272 EFI_HANDLE Handle;
273 EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleTextInEx;
274
275 while (TRUE) {
276 BufferSize = sizeof (EFI_HANDLE);
277 Status = gBS->LocateHandle (
278 ByRegisterNotify,
279 NULL,
280 mHotkeyRegistration,
281 &BufferSize,
282 &Handle
283 );
284 if (EFI_ERROR (Status)) {
285 //
286 // If no more notification events exist
287 //
288 return ;
289 }
290
291 Status = gBS->HandleProtocol (
292 Handle,
293 &gEfiSimpleTextInputExProtocolGuid,
294 (VOID **) &SimpleTextInEx
295 );
296 ASSERT_EFI_ERROR (Status);
297
298 HotkeyRegisterNotify (SimpleTextInEx);
299 }
300 }
301
302 /**
303 Insert Key Option to hotkey list.
304
305 @param KeyOption The Hot Key Option to be added to hotkey list.
306
307 @retval EFI_SUCCESS Add to hotkey list success.
308 @retval EFI_OUT_OF_RESOURCES Fail to allocate memory resource.
309 **/
310 EFI_STATUS
HotkeyInsertList(IN EFI_KEY_OPTION * KeyOption)311 HotkeyInsertList (
312 IN EFI_KEY_OPTION *KeyOption
313 )
314 {
315 BDS_HOTKEY_OPTION *HotkeyLeft;
316 BDS_HOTKEY_OPTION *HotkeyRight;
317 UINTN Index;
318 EFI_BOOT_KEY_DATA KeyOptions;
319 UINT32 KeyShiftStateLeft;
320 UINT32 KeyShiftStateRight;
321 EFI_INPUT_KEY *InputKey;
322 EFI_KEY_DATA *KeyData;
323
324 HotkeyLeft = AllocateZeroPool (sizeof (BDS_HOTKEY_OPTION));
325 if (HotkeyLeft == NULL) {
326 return EFI_OUT_OF_RESOURCES;
327 }
328
329 HotkeyLeft->Signature = BDS_HOTKEY_OPTION_SIGNATURE;
330 HotkeyLeft->BootOptionNumber = KeyOption->BootOption;
331
332 KeyOptions = KeyOption->KeyData;
333
334 HotkeyLeft->CodeCount = (UINT8) KeyOptions.Options.InputKeyCount;
335
336 //
337 // Map key shift state from KeyOptions to EFI_KEY_DATA.KeyState
338 //
339 KeyShiftStateRight = EFI_SHIFT_STATE_VALID;
340 if (KeyOptions.Options.ShiftPressed) {
341 KeyShiftStateRight |= EFI_RIGHT_SHIFT_PRESSED;
342 }
343 if (KeyOptions.Options.ControlPressed) {
344 KeyShiftStateRight |= EFI_RIGHT_CONTROL_PRESSED;
345 }
346 if (KeyOptions.Options.AltPressed) {
347 KeyShiftStateRight |= EFI_RIGHT_ALT_PRESSED;
348 }
349 if (KeyOptions.Options.LogoPressed) {
350 KeyShiftStateRight |= EFI_RIGHT_LOGO_PRESSED;
351 }
352 if (KeyOptions.Options.MenuPressed) {
353 KeyShiftStateRight |= EFI_MENU_KEY_PRESSED;
354 }
355 if (KeyOptions.Options.SysReqPressed) {
356 KeyShiftStateRight |= EFI_SYS_REQ_PRESSED;
357 }
358
359 KeyShiftStateLeft = (KeyShiftStateRight & 0xffffff00) | ((KeyShiftStateRight & 0xff) << 1);
360
361 InputKey = (EFI_INPUT_KEY *) (((UINT8 *) KeyOption) + sizeof (EFI_KEY_OPTION));
362
363 Index = 0;
364 KeyData = &HotkeyLeft->KeyData[0];
365 do {
366 //
367 // If Key CodeCount is 0, then only KeyData[0] is used;
368 // if Key CodeCount is n, then KeyData[0]~KeyData[n-1] are used
369 //
370 KeyData->Key.ScanCode = InputKey[Index].ScanCode;
371 KeyData->Key.UnicodeChar = InputKey[Index].UnicodeChar;
372 KeyData->KeyState.KeyShiftState = KeyShiftStateLeft;
373
374 Index++;
375 KeyData++;
376 } while (Index < HotkeyLeft->CodeCount);
377 InsertTailList (&mHotkeyList, &HotkeyLeft->Link);
378
379 if (KeyShiftStateLeft != KeyShiftStateRight) {
380 //
381 // Need an extra hotkey for shift key on right
382 //
383 HotkeyRight = AllocateCopyPool (sizeof (BDS_HOTKEY_OPTION), HotkeyLeft);
384 if (HotkeyRight == NULL) {
385 return EFI_OUT_OF_RESOURCES;
386 }
387
388 Index = 0;
389 KeyData = &HotkeyRight->KeyData[0];
390 do {
391 //
392 // Key.ScanCode and Key.UnicodeChar have already been initialized,
393 // only need to update KeyState.KeyShiftState
394 //
395 KeyData->KeyState.KeyShiftState = KeyShiftStateRight;
396
397 Index++;
398 KeyData++;
399 } while (Index < HotkeyRight->CodeCount);
400 InsertTailList (&mHotkeyList, &HotkeyRight->Link);
401 }
402
403 return EFI_SUCCESS;
404 }
405
406 /**
407 Return TRUE when the variable pointed by Name and Guid is a Key#### variable.
408
409 @param Name The name of the variable.
410 @param Guid The GUID of the variable.
411 @param OptionNumber Return the option number parsed from the Name.
412
413 @retval TRUE The variable pointed by Name and Guid is a Key#### variable.
414 @retval FALSE The variable pointed by Name and Guid isn't a Key#### variable.
415 **/
416 BOOLEAN
IsKeyOptionVariable(CHAR16 * Name,EFI_GUID * Guid,UINT16 * OptionNumber)417 IsKeyOptionVariable (
418 CHAR16 *Name,
419 EFI_GUID *Guid,
420 UINT16 *OptionNumber
421 )
422 {
423 UINTN Index;
424
425 if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) ||
426 (StrSize (Name) != sizeof (L"Key####")) ||
427 (StrnCmp (Name, L"Key", 3) != 0)
428 ) {
429 return FALSE;
430 }
431
432 *OptionNumber = 0;
433 for (Index = 3; Index < 7; Index++) {
434 if ((Name[Index] >= L'0') && (Name[Index] <= L'9')) {
435 *OptionNumber = *OptionNumber * 16 + Name[Index] - L'0';
436 } else if ((Name[Index] >= L'A') && (Name[Index] <= L'F')) {
437 *OptionNumber = *OptionNumber * 16 + Name[Index] - L'A' + 10;
438 } else {
439 return FALSE;
440 }
441 }
442
443 return TRUE;
444 }
445
446 /**
447 Return an array of key option numbers.
448
449 @param Count Return the count of key option numbers.
450
451 @return UINT16* Pointer to an array of key option numbers;
452 **/
453 UINT16 *
454 EFIAPI
HotkeyGetOptionNumbers(OUT UINTN * Count)455 HotkeyGetOptionNumbers (
456 OUT UINTN *Count
457 )
458 {
459 EFI_STATUS Status;
460 UINTN Index;
461 CHAR16 *Name;
462 EFI_GUID Guid;
463 UINTN NameSize;
464 UINTN NewNameSize;
465 UINT16 *OptionNumbers;
466 UINT16 OptionNumber;
467
468 if (Count == NULL) {
469 return NULL;
470 }
471
472 *Count = 0;
473 OptionNumbers = NULL;
474
475 NameSize = sizeof (CHAR16);
476 Name = AllocateZeroPool (NameSize);
477 ASSERT (Name != NULL);
478 while (TRUE) {
479 NewNameSize = NameSize;
480 Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
481 if (Status == EFI_BUFFER_TOO_SMALL) {
482 Name = ReallocatePool (NameSize, NewNameSize, Name);
483 ASSERT (Name != NULL);
484 Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
485 NameSize = NewNameSize;
486 }
487
488 if (Status == EFI_NOT_FOUND) {
489 break;
490 }
491 ASSERT_EFI_ERROR (Status);
492
493 if (IsKeyOptionVariable (Name ,&Guid, &OptionNumber)) {
494 OptionNumbers = ReallocatePool (
495 *Count * sizeof (UINT16),
496 (*Count + 1) * sizeof (UINT16),
497 OptionNumbers
498 );
499 ASSERT (OptionNumbers != NULL);
500 for (Index = 0; Index < *Count; Index++) {
501 if (OptionNumber < OptionNumbers[Index]) {
502 break;
503 }
504 }
505 CopyMem (&OptionNumbers[Index + 1], &OptionNumbers[Index], (*Count - Index) * sizeof (UINT16));
506 OptionNumbers[Index] = OptionNumber;
507 (*Count)++;
508 }
509 }
510
511 FreePool (Name);
512
513 return OptionNumbers;
514 }
515
516 /**
517
518 Process all the "Key####" variables, associate Hotkeys with corresponding Boot Options.
519
520 @retval EFI_SUCCESS Hotkey services successfully initialized.
521 **/
522 EFI_STATUS
InitializeHotkeyService(VOID)523 InitializeHotkeyService (
524 VOID
525 )
526 {
527 EFI_STATUS Status;
528 UINT32 BootOptionSupport;
529 UINT16 *KeyOptionNumbers;
530 UINTN KeyOptionCount;
531 UINTN Index;
532 CHAR16 KeyOptionName[8];
533 EFI_KEY_OPTION *KeyOption;
534
535 //
536 // Export our capability - EFI_BOOT_OPTION_SUPPORT_KEY and EFI_BOOT_OPTION_SUPPORT_APP.
537 // with maximum number of key presses of 3
538 // Do not report the hotkey capability if PcdConInConnectOnDemand is enabled.
539 //
540 BootOptionSupport = EFI_BOOT_OPTION_SUPPORT_APP;
541 if (!PcdGetBool (PcdConInConnectOnDemand)) {
542 BootOptionSupport |= EFI_BOOT_OPTION_SUPPORT_KEY;
543 SET_BOOT_OPTION_SUPPORT_KEY_COUNT (BootOptionSupport, 3);
544 }
545
546 Status = gRT->SetVariable (
547 L"BootOptionSupport",
548 &gEfiGlobalVariableGuid,
549 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
550 sizeof (UINT32),
551 &BootOptionSupport
552 );
553 //
554 // Platform needs to make sure setting volatile variable before calling 3rd party code shouldn't fail.
555 //
556 ASSERT_EFI_ERROR (Status);
557
558 KeyOptionNumbers = HotkeyGetOptionNumbers (&KeyOptionCount);
559 for (Index = 0; Index < KeyOptionCount; Index ++) {
560 UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumbers[Index]);
561 GetEfiGlobalVariable2 (KeyOptionName, (VOID **) &KeyOption, NULL);
562 ASSERT (KeyOption != NULL);
563 if (IsKeyOptionValid (KeyOption)) {
564 HotkeyInsertList (KeyOption);
565 }
566 FreePool (KeyOption);
567 }
568
569 if (KeyOptionNumbers != NULL) {
570 FreePool (KeyOptionNumbers);
571 }
572
573 //
574 // Register Protocol notify for Hotkey service
575 //
576 Status = gBS->CreateEvent (
577 EVT_NOTIFY_SIGNAL,
578 TPL_CALLBACK,
579 HotkeyEvent,
580 NULL,
581 &mHotkeyEvent
582 );
583 ASSERT_EFI_ERROR (Status);
584
585 //
586 // Register for protocol notifications on this event
587 //
588 Status = gBS->RegisterProtocolNotify (
589 &gEfiSimpleTextInputExProtocolGuid,
590 mHotkeyEvent,
591 &mHotkeyRegistration
592 );
593 ASSERT_EFI_ERROR (Status);
594
595 return Status;
596 }
597
598