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