EmulatorPkg: Make the shell be the first boot option
[mirror_edk2.git] / EmulatorPkg / Library / PlatformBmLib / PlatformBm.c
1 /*++ @file\r
2 \r
3 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>\r
4 Portions copyright (c) 2011, Apple Inc. All rights reserved.\r
5 SPDX-License-Identifier: BSD-2-Clause-Patent\r
6 \r
7 **/\r
8 \r
9 #include "PlatformBm.h"\r
10 \r
11 EFI_GUID mBootMenuFile = {\r
12   0xEEC25BDC, 0x67F2, 0x4D95, { 0xB1, 0xD5, 0xF8, 0x1B, 0x20, 0x39, 0xD1, 0x1D }\r
13 };\r
14 \r
15 /**\r
16   Initialize the "Setup" variable.\r
17 **/\r
18 VOID\r
19 SetupVariableInit (\r
20   VOID\r
21   )\r
22 {\r
23   EFI_STATUS                      Status;\r
24   UINTN                           Size;\r
25   EMU_SYSTEM_CONFIGURATION        SystemConfigData;\r
26 \r
27   Size = sizeof (SystemConfigData);\r
28   Status = gRT->GetVariable (\r
29                   L"Setup",\r
30                   &gEmuSystemConfigGuid,\r
31                   NULL,\r
32                   &Size,\r
33                   (VOID *) &SystemConfigData\r
34                   );\r
35 \r
36   if (EFI_ERROR (Status)) {\r
37     //\r
38     // SetupVariable is corrupt\r
39     //\r
40     SystemConfigData.ConOutRow = PcdGet32 (PcdConOutColumn);\r
41     SystemConfigData.ConOutColumn = PcdGet32 (PcdConOutRow);\r
42 \r
43     Status = gRT->SetVariable (\r
44                     L"Setup",\r
45                     &gEmuSystemConfigGuid,\r
46                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
47                     sizeof (SystemConfigData),\r
48                     (VOID *) &SystemConfigData\r
49                     );\r
50     if (EFI_ERROR (Status)) {\r
51       DEBUG ((DEBUG_ERROR, "Failed to save Setup Variable to non-volatile storage, Status = %r\n", Status));\r
52     }\r
53   }\r
54 }\r
55 \r
56 EFI_DEVICE_PATH *\r
57 FvFilePath (\r
58   EFI_GUID                     *FileGuid\r
59   )\r
60 {\r
61 \r
62   EFI_STATUS                         Status;\r
63   EFI_LOADED_IMAGE_PROTOCOL          *LoadedImage;\r
64   MEDIA_FW_VOL_FILEPATH_DEVICE_PATH  FileNode;\r
65 \r
66   EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);\r
67 \r
68   Status = gBS->HandleProtocol (\r
69                   gImageHandle,\r
70                   &gEfiLoadedImageProtocolGuid,\r
71                   (VOID **) &LoadedImage\r
72                   );\r
73   ASSERT_EFI_ERROR (Status);\r
74   return AppendDevicePathNode (\r
75            DevicePathFromHandle (LoadedImage->DeviceHandle),\r
76            (EFI_DEVICE_PATH_PROTOCOL *) &FileNode\r
77            );\r
78 }\r
79 \r
80 /**\r
81   Create one boot option for BootManagerMenuApp.\r
82 \r
83   @param  FileGuid          Input file guid for the BootManagerMenuApp.\r
84   @param  Description       Description of the BootManagerMenuApp boot option.\r
85   @param  Position          Position of the new load option to put in the ****Order variable.\r
86   @param  IsBootCategory    Whether this is a boot category.\r
87 \r
88 \r
89   @retval OptionNumber      Return the option number info.\r
90 \r
91 **/\r
92 UINTN\r
93 RegisterBootManagerMenuAppBootOption (\r
94   EFI_GUID                         *FileGuid,\r
95   CHAR16                           *Description,\r
96   UINTN                            Position,\r
97   BOOLEAN                          IsBootCategory\r
98   )\r
99 {\r
100   EFI_STATUS                       Status;\r
101   EFI_BOOT_MANAGER_LOAD_OPTION     NewOption;\r
102   EFI_DEVICE_PATH_PROTOCOL         *DevicePath;\r
103   UINTN                            OptionNumber;\r
104 \r
105   DevicePath = FvFilePath (FileGuid);\r
106   Status = EfiBootManagerInitializeLoadOption (\r
107              &NewOption,\r
108              LoadOptionNumberUnassigned,\r
109              LoadOptionTypeBoot,\r
110              IsBootCategory ? LOAD_OPTION_ACTIVE : LOAD_OPTION_CATEGORY_APP,\r
111              Description,\r
112              DevicePath,\r
113              NULL,\r
114              0\r
115              );\r
116   ASSERT_EFI_ERROR (Status);\r
117   FreePool (DevicePath);\r
118 \r
119   Status = EfiBootManagerAddLoadOptionVariable (&NewOption, Position);\r
120   ASSERT_EFI_ERROR (Status);\r
121 \r
122   OptionNumber = NewOption.OptionNumber;\r
123 \r
124   EfiBootManagerFreeLoadOption (&NewOption);\r
125 \r
126   return OptionNumber;\r
127 }\r
128 \r
129 /**\r
130   Check if it's a Device Path pointing to BootManagerMenuApp.\r
131 \r
132   @param  DevicePath     Input device path.\r
133 \r
134   @retval TRUE   The device path is BootManagerMenuApp File Device Path.\r
135   @retval FALSE  The device path is NOT BootManagerMenuApp File Device Path.\r
136 **/\r
137 BOOLEAN\r
138 IsBootManagerMenuAppFilePath (\r
139   EFI_DEVICE_PATH_PROTOCOL     *DevicePath\r
140 )\r
141 {\r
142   EFI_HANDLE                      FvHandle;\r
143   VOID                            *NameGuid;\r
144   EFI_STATUS                      Status;\r
145 \r
146   Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle);\r
147   if (!EFI_ERROR (Status)) {\r
148     NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);\r
149     if (NameGuid != NULL) {\r
150       return CompareGuid (NameGuid, &mBootMenuFile);\r
151     }\r
152   }\r
153 \r
154   return FALSE;\r
155 }\r
156 \r
157 /**\r
158   Return the boot option number to the BootManagerMenuApp.\r
159 \r
160   If not found it in the current boot option, create a new one.\r
161 \r
162   @retval OptionNumber   Return the boot option number to the BootManagerMenuApp.\r
163 \r
164 **/\r
165 UINTN\r
166 GetBootManagerMenuAppOption (\r
167   VOID\r
168   )\r
169 {\r
170   UINTN                        BootOptionCount;\r
171   EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
172   UINTN                        Index;\r
173   UINTN                        OptionNumber;\r
174 \r
175   OptionNumber = 0;\r
176 \r
177   BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
178 \r
179   for (Index = 0; Index < BootOptionCount; Index++) {\r
180     if (IsBootManagerMenuAppFilePath (BootOptions[Index].FilePath)) {\r
181       OptionNumber = BootOptions[Index].OptionNumber;\r
182       break;\r
183     }\r
184   }\r
185 \r
186   EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
187 \r
188   if (Index >= BootOptionCount) {\r
189     //\r
190     // If not found the BootManagerMenuApp, create it.\r
191     //\r
192     OptionNumber = (UINT16) RegisterBootManagerMenuAppBootOption (&mBootMenuFile, L"UEFI BootManagerMenuApp", (UINTN) -1, FALSE);\r
193   }\r
194 \r
195   return OptionNumber;\r
196 }\r
197 \r
198 /**\r
199   Platform Bds init. Include the platform firmware vendor, revision\r
200   and so crc check.\r
201 **/\r
202 VOID\r
203 EFIAPI\r
204 PlatformBootManagerBeforeConsole (\r
205   VOID\r
206   )\r
207 {\r
208   UINTN       Index;\r
209 \r
210   SetupVariableInit ();\r
211 \r
212   EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid);\r
213 \r
214   Index   = 0;\r
215   while (gPlatformConsole[Index].DevicePath != NULL) {\r
216     //\r
217     // Update the console variable with the connect type\r
218     //\r
219     if ((gPlatformConsole[Index].ConnectType & CONSOLE_IN) == CONSOLE_IN) {\r
220       EfiBootManagerUpdateConsoleVariable (ConIn, gPlatformConsole[Index].DevicePath, NULL);\r
221     }\r
222 \r
223     if ((gPlatformConsole[Index].ConnectType & CONSOLE_OUT) == CONSOLE_OUT) {\r
224       EfiBootManagerUpdateConsoleVariable (ConOut, gPlatformConsole[Index].DevicePath, NULL);\r
225     }\r
226 \r
227     if ((gPlatformConsole[Index].ConnectType & STD_ERROR) == STD_ERROR) {\r
228       EfiBootManagerUpdateConsoleVariable (ErrOut, gPlatformConsole[Index].DevicePath, NULL);\r
229     }\r
230 \r
231     Index++;\r
232   }\r
233 }\r
234 \r
235 /**\r
236   Connect with predefined platform connect sequence,\r
237   the OEM/IBV can customize with their own connect sequence.\r
238 **/\r
239 VOID\r
240 PlatformBdsConnectSequence (\r
241   VOID\r
242   )\r
243 {\r
244   //\r
245   // Just use the simple policy to connect all devices\r
246   //\r
247   EfiBootManagerConnectAll ();\r
248 }\r
249 \r
250 /**\r
251   Perform the platform diagnostic, such like test memory. OEM/IBV also\r
252   can customize this fuction to support specific platform diagnostic.\r
253 \r
254   @param MemoryTestLevel The memory test intensive level\r
255   @param QuietBoot       Indicate if need to enable the quiet boot\r
256 **/\r
257 VOID\r
258 PlatformBdsDiagnostics (\r
259   IN EXTENDMEM_COVERAGE_LEVEL    MemoryTestLevel,\r
260   IN BOOLEAN                     QuietBoot\r
261   )\r
262 {\r
263   EFI_STATUS  Status;\r
264 \r
265   //\r
266   // Here we can decide if we need to show\r
267   // the diagnostics screen\r
268   //\r
269   if (QuietBoot) {\r
270     BootLogoEnableLogo ();\r
271 \r
272     //\r
273     // Perform system diagnostic\r
274     //\r
275     Status = PlatformBootManagerMemoryTest (MemoryTestLevel);\r
276     if (EFI_ERROR (Status)) {\r
277       BootLogoDisableLogo ();\r
278     }\r
279 \r
280     return;\r
281   }\r
282 \r
283   //\r
284   // Perform system diagnostic\r
285   //\r
286   PlatformBootManagerMemoryTest (MemoryTestLevel);\r
287 }\r
288 \r
289 /**\r
290   Register the static boot options.\r
291 **/\r
292 VOID\r
293 PlatformBdsRegisterStaticBootOptions (\r
294   VOID\r
295   )\r
296 {\r
297   EFI_GRAPHICS_OUTPUT_BLT_PIXEL  Black;\r
298   EFI_GRAPHICS_OUTPUT_BLT_PIXEL  White;\r
299   EFI_INPUT_KEY                  Enter;\r
300   EFI_INPUT_KEY                  F2;\r
301   EFI_INPUT_KEY                  F7;\r
302   EFI_BOOT_MANAGER_LOAD_OPTION   BootOption;\r
303   UINTN                          OptionNumber;\r
304 \r
305   Black.Blue = Black.Green = Black.Red = Black.Reserved = 0;\r
306   White.Blue = White.Green = White.Red = White.Reserved = 0xFF;\r
307 \r
308   //\r
309   // Register ENTER as CONTINUE key\r
310   //\r
311   Enter.ScanCode    = SCAN_NULL;\r
312   Enter.UnicodeChar = CHAR_CARRIAGE_RETURN;\r
313   EfiBootManagerRegisterContinueKeyOption (0, &Enter, NULL);\r
314   //\r
315   // Map F2 to Boot Manager Menu\r
316   //\r
317   F2.ScanCode    = SCAN_F2;\r
318   F2.UnicodeChar = CHAR_NULL;\r
319   EfiBootManagerGetBootManagerMenu (&BootOption);\r
320   EfiBootManagerAddKeyOptionVariable (NULL, (UINT16) BootOption.OptionNumber, 0, &F2, NULL);\r
321 \r
322   //\r
323   // 3. Boot Device List menu\r
324   //\r
325   F7.ScanCode     = SCAN_F7;\r
326   F7.UnicodeChar  = CHAR_NULL;\r
327   OptionNumber    = GetBootManagerMenuAppOption ();\r
328   EfiBootManagerAddKeyOptionVariable (NULL, (UINT16)OptionNumber, 0, &F7, NULL);\r
329 \r
330   PrintXY (10, 10, &White, &Black, L"F2    to enter Setup.                              ");\r
331   PrintXY (10, 30, &White, &Black, L"F7    to enter Boot Manager Menu.");\r
332   PrintXY (10, 50, &White, &Black, L"Enter to boot directly.");\r
333 }\r
334 \r
335 /**\r
336   Returns the priority number.\r
337 \r
338   @param BootOption\r
339 **/\r
340 UINTN\r
341 BootOptionPriority (\r
342   CONST EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
343   )\r
344 {\r
345   //\r
346   // Make sure Shell is first\r
347   //\r
348   if (StrCmp (BootOption->Description, L"UEFI Shell") == 0) {\r
349     return 0;\r
350   }\r
351   return 100;\r
352 }\r
353 \r
354 INTN\r
355 EFIAPI\r
356 CompareBootOption (\r
357   CONST EFI_BOOT_MANAGER_LOAD_OPTION  *Left,\r
358   CONST EFI_BOOT_MANAGER_LOAD_OPTION  *Right\r
359   )\r
360 {\r
361   return BootOptionPriority (Left) - BootOptionPriority (Right);\r
362 }\r
363 \r
364 /**\r
365   Do the platform specific action after the console is connected.\r
366 \r
367   Such as:\r
368     Dynamically switch output mode;\r
369     Signal console ready platform customized event;\r
370     Run diagnostics like memory testing;\r
371     Connect certain devices;\r
372     Dispatch aditional option roms.\r
373 **/\r
374 VOID\r
375 EFIAPI\r
376 PlatformBootManagerAfterConsole (\r
377   VOID\r
378   )\r
379 {\r
380 \r
381   //\r
382   // Go the different platform policy with different boot mode\r
383   // Notes: this part code can be change with the table policy\r
384   //\r
385   switch (GetBootModeHob ()) {\r
386 \r
387   case BOOT_ASSUMING_NO_CONFIGURATION_CHANGES:\r
388   case BOOT_WITH_MINIMAL_CONFIGURATION:\r
389     PlatformBdsDiagnostics (IGNORE, TRUE);\r
390 \r
391     //\r
392     // Perform some platform specific connect sequence\r
393     //\r
394     PlatformBdsConnectSequence ();\r
395     break;\r
396 \r
397   case BOOT_IN_RECOVERY_MODE:\r
398     PlatformBdsDiagnostics (EXTENSIVE, FALSE);\r
399     break;\r
400 \r
401   case BOOT_WITH_FULL_CONFIGURATION:\r
402   case BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS:\r
403   case BOOT_WITH_DEFAULT_SETTINGS:\r
404   default:\r
405     PlatformBdsDiagnostics (IGNORE, TRUE);\r
406     PlatformBdsRegisterStaticBootOptions ();\r
407     PlatformBdsConnectSequence ();\r
408     EfiBootManagerRefreshAllBootOption ();\r
409     EfiBootManagerSortLoadOptionVariable (LoadOptionTypeBoot, (SORT_COMPARE)CompareBootOption);\r
410     break;\r
411   }\r
412 }\r
413 \r
414 /**\r
415   This function is called each second during the boot manager waits the timeout.\r
416 \r
417   @param TimeoutRemain  The remaining timeout.\r
418 **/\r
419 VOID\r
420 EFIAPI\r
421 PlatformBootManagerWaitCallback (\r
422   UINT16          TimeoutRemain\r
423   )\r
424 {\r
425   EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION Black;\r
426   EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION White;\r
427   UINT16                              Timeout;\r
428 \r
429   Timeout = PcdGet16 (PcdPlatformBootTimeOut);\r
430 \r
431   Black.Raw = 0x00000000;\r
432   White.Raw = 0x00FFFFFF;\r
433 \r
434   BootLogoUpdateProgress (\r
435     White.Pixel,\r
436     Black.Pixel,\r
437     L"Start boot option",\r
438     White.Pixel,\r
439     (Timeout - TimeoutRemain) * 100 / Timeout,\r
440     0\r
441     );\r
442 }\r
443 \r
444 /**\r
445   The function is called when no boot option could be launched,\r
446   including platform recovery options and options pointing to applications\r
447   built into firmware volumes.\r
448 \r
449   If this function returns, BDS attempts to enter an infinite loop.\r
450 **/\r
451 VOID\r
452 EFIAPI\r
453 PlatformBootManagerUnableToBoot (\r
454   VOID\r
455   )\r
456 {\r
457   return;\r
458 }\r
459 \r