1 /** @file
2   The platform boot manager reference implementation
3 
4 Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "BootManager.h"
16 
17 UINT16             mKeyInput;
18 LIST_ENTRY         mBootOptionsList;
19 BDS_COMMON_OPTION  *gOption;
20 CHAR16             *mDeviceTypeStr[] = {
21   L"Legacy BEV",
22   L"Legacy Floppy",
23   L"Legacy Hard Drive",
24   L"Legacy CD ROM",
25   L"Legacy PCMCIA",
26   L"Legacy USB",
27   L"Legacy Embedded Network",
28   L"Legacy Unknown Device"
29 };
30 
31 
32 HII_VENDOR_DEVICE_PATH  mBootManagerHiiVendorDevicePath = {
33   {
34     {
35       HARDWARE_DEVICE_PATH,
36       HW_VENDOR_DP,
37       {
38         (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
39         (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
40       }
41     },
42     BOOT_MANAGER_FORMSET_GUID
43   },
44   {
45     END_DEVICE_PATH_TYPE,
46     END_ENTIRE_DEVICE_PATH_SUBTYPE,
47     {
48       (UINT8) (END_DEVICE_PATH_LENGTH),
49       (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
50     }
51   }
52 };
53 
54 BOOT_MANAGER_CALLBACK_DATA  gBootManagerPrivate = {
55   BOOT_MANAGER_CALLBACK_DATA_SIGNATURE,
56   NULL,
57   NULL,
58   {
59     FakeExtractConfig,
60     FakeRouteConfig,
61     BootManagerCallback
62   }
63 };
64 
65 /**
66   This call back function is registered with Boot Manager formset.
67   When user selects a boot option, this call back function will
68   be triggered. The boot option is saved for later processing.
69 
70 
71   @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
72   @param Action          Specifies the type of action taken by the browser.
73   @param QuestionId      A unique value which is sent to the original exporting driver
74                          so that it can identify the type of data to expect.
75   @param Type            The type of value for the question.
76   @param Value           A pointer to the data being sent to the original exporting driver.
77   @param ActionRequest   On return, points to the action requested by the callback function.
78 
79   @retval  EFI_SUCCESS           The callback successfully handled the action.
80   @retval  EFI_INVALID_PARAMETER The setup browser call this function with invalid parameters.
81 
82 **/
83 EFI_STATUS
84 EFIAPI
BootManagerCallback(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN EFI_BROWSER_ACTION Action,IN EFI_QUESTION_ID QuestionId,IN UINT8 Type,IN EFI_IFR_TYPE_VALUE * Value,OUT EFI_BROWSER_ACTION_REQUEST * ActionRequest)85 BootManagerCallback (
86   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
87   IN  EFI_BROWSER_ACTION                     Action,
88   IN  EFI_QUESTION_ID                        QuestionId,
89   IN  UINT8                                  Type,
90   IN  EFI_IFR_TYPE_VALUE                     *Value,
91   OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
92   )
93 {
94   BDS_COMMON_OPTION       *Option;
95   LIST_ENTRY              *Link;
96   UINT16                  KeyCount;
97 
98   if (Action == EFI_BROWSER_ACTION_CHANGED) {
99     if ((Value == NULL) || (ActionRequest == NULL)) {
100       return EFI_INVALID_PARAMETER;
101     }
102 
103     //
104     // Initialize the key count
105     //
106     KeyCount = 0;
107 
108     for (Link = GetFirstNode (&mBootOptionsList); !IsNull (&mBootOptionsList, Link); Link = GetNextNode (&mBootOptionsList, Link)) {
109       Option = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);
110 
111       KeyCount++;
112 
113       gOption = Option;
114 
115       //
116       // Is this device the one chosen?
117       //
118       if (KeyCount == QuestionId) {
119         //
120         // Assigning the returned Key to a global allows the original routine to know what was chosen
121         //
122         mKeyInput = QuestionId;
123 
124         //
125         // Request to exit SendForm(), so that we could boot the selected option
126         //
127         *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
128         break;
129       }
130     }
131 
132     return EFI_SUCCESS;
133   }
134 
135   //
136   // All other action return unsupported.
137   //
138   return EFI_UNSUPPORTED;
139 }
140 
141 /**
142 
143   Registers HII packages for the Boot Manger to HII Database.
144   It also registers the browser call back function.
145 
146   @retval  EFI_SUCCESS           HII packages for the Boot Manager were registered successfully.
147   @retval  EFI_OUT_OF_RESOURCES  HII packages for the Boot Manager failed to be registered.
148 
149 **/
150 EFI_STATUS
InitializeBootManager(VOID)151 InitializeBootManager (
152   VOID
153   )
154 {
155   EFI_STATUS                  Status;
156 
157   //
158   // Install Device Path Protocol and Config Access protocol to driver handle
159   //
160   Status = gBS->InstallMultipleProtocolInterfaces (
161                   &gBootManagerPrivate.DriverHandle,
162                   &gEfiDevicePathProtocolGuid,
163                   &mBootManagerHiiVendorDevicePath,
164                   &gEfiHiiConfigAccessProtocolGuid,
165                   &gBootManagerPrivate.ConfigAccess,
166                   NULL
167                   );
168   ASSERT_EFI_ERROR (Status);
169 
170   //
171   // Publish our HII data
172   //
173   gBootManagerPrivate.HiiHandle = HiiAddPackages (
174                                     &gBootManagerFormSetGuid,
175                                     gBootManagerPrivate.DriverHandle,
176                                     BootManagerVfrBin,
177                                     BdsDxeStrings,
178                                     NULL
179                                     );
180   if (gBootManagerPrivate.HiiHandle == NULL) {
181     Status = EFI_OUT_OF_RESOURCES;
182   } else {
183     Status = EFI_SUCCESS;
184   }
185   return Status;
186 }
187 
188 /**
189   This function invokes Boot Manager. If all devices have not a chance to be connected,
190   the connect all will be triggered. It then enumerate all boot options. If
191   a boot option from the Boot Manager page is selected, Boot Manager will boot
192   from this boot option.
193 
194 **/
195 VOID
CallBootManager(VOID)196 CallBootManager (
197   VOID
198   )
199 {
200   EFI_STATUS                  Status;
201   BDS_COMMON_OPTION           *Option;
202   LIST_ENTRY                  *Link;
203   CHAR16                      *ExitData;
204   UINTN                       ExitDataSize;
205   EFI_STRING_ID               Token;
206   EFI_INPUT_KEY               Key;
207   CHAR16                      *HelpString;
208   UINTN                       HelpSize;
209   EFI_STRING_ID               HelpToken;
210   UINT16                      *TempStr;
211   EFI_HII_HANDLE              HiiHandle;
212   EFI_BROWSER_ACTION_REQUEST  ActionRequest;
213   VOID                        *StartOpCodeHandle;
214   VOID                        *EndOpCodeHandle;
215   EFI_IFR_GUID_LABEL          *StartLabel;
216   EFI_IFR_GUID_LABEL          *EndLabel;
217   UINT16                      DeviceType;
218   BOOLEAN                     IsLegacyOption;
219   BOOLEAN                     NeedEndOp;
220 
221   DeviceType = (UINT16) -1;
222   gOption    = NULL;
223   InitializeListHead (&mBootOptionsList);
224 
225   //
226   // Connect all prior to entering the platform setup menu.
227   //
228   if (!gConnectAllHappened) {
229     BdsLibConnectAllDriversToAllControllers ();
230     gConnectAllHappened = TRUE;
231   }
232 
233   BdsLibEnumerateAllBootOption (&mBootOptionsList);
234 
235   //
236   // Group the legacy boot options for the same device type
237   //
238   GroupMultipleLegacyBootOption4SameType ();
239 
240   InitializeListHead (&mBootOptionsList);
241   BdsLibBuildOptionFromVar (&mBootOptionsList, L"BootOrder");
242 
243   HiiHandle = gBootManagerPrivate.HiiHandle;
244 
245   //
246   // Allocate space for creation of UpdateData Buffer
247   //
248   StartOpCodeHandle = HiiAllocateOpCodeHandle ();
249   ASSERT (StartOpCodeHandle != NULL);
250 
251   EndOpCodeHandle = HiiAllocateOpCodeHandle ();
252   ASSERT (EndOpCodeHandle != NULL);
253 
254   //
255   // Create Hii Extend Label OpCode as the start opcode
256   //
257   StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
258   StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
259   StartLabel->Number       = LABEL_BOOT_OPTION;
260 
261   //
262   // Create Hii Extend Label OpCode as the end opcode
263   //
264   EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
265   EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
266   EndLabel->Number       = LABEL_BOOT_OPTION_END;
267 
268   mKeyInput = 0;
269   NeedEndOp = FALSE;
270   for (Link = GetFirstNode (&mBootOptionsList); !IsNull (&mBootOptionsList, Link); Link = GetNextNode (&mBootOptionsList, Link)) {
271     Option = CR (Link, BDS_COMMON_OPTION, Link, BDS_LOAD_OPTION_SIGNATURE);
272 
273     //
274     // At this stage we are creating a menu entry, thus the Keys are reproduceable
275     //
276     mKeyInput++;
277 
278     //
279     // Don't display the hidden/inactive boot option
280     //
281     if (((Option->Attribute & LOAD_OPTION_HIDDEN) != 0) || ((Option->Attribute & LOAD_OPTION_ACTIVE) == 0)) {
282       continue;
283     }
284 
285     //
286     // Group the legacy boot option in the sub title created dynamically
287     //
288     IsLegacyOption = (BOOLEAN) (
289                        (DevicePathType (Option->DevicePath) == BBS_DEVICE_PATH) &&
290                        (DevicePathSubType (Option->DevicePath) == BBS_BBS_DP)
291                        );
292 
293     if (!IsLegacyOption && NeedEndOp) {
294       NeedEndOp = FALSE;
295       HiiCreateEndOpCode (StartOpCodeHandle);
296     }
297 
298     if (IsLegacyOption && DeviceType != ((BBS_BBS_DEVICE_PATH *) Option->DevicePath)->DeviceType) {
299       if (NeedEndOp) {
300         HiiCreateEndOpCode (StartOpCodeHandle);
301       }
302 
303       DeviceType = ((BBS_BBS_DEVICE_PATH *) Option->DevicePath)->DeviceType;
304       Token      = HiiSetString (
305                      HiiHandle,
306                      0,
307                      mDeviceTypeStr[
308                        MIN (DeviceType & 0xF, sizeof (mDeviceTypeStr) / sizeof (mDeviceTypeStr[0]) - 1)
309                        ],
310                      NULL
311                      );
312       HiiCreateSubTitleOpCode (StartOpCodeHandle, Token, 0, 0, 1);
313       NeedEndOp = TRUE;
314     }
315 
316     ASSERT (Option->Description != NULL);
317 
318     Token = HiiSetString (HiiHandle, 0, Option->Description, NULL);
319 
320     TempStr = DevicePathToStr (Option->DevicePath);
321     HelpSize = StrSize (TempStr) + StrSize (L"Device Path : ");
322     HelpString = AllocateZeroPool (HelpSize);
323     ASSERT (HelpString != NULL);
324     StrCatS (HelpString, HelpSize / sizeof (CHAR16), L"Device Path : ");
325     StrCatS (HelpString, HelpSize / sizeof (CHAR16), TempStr);
326 
327     HelpToken = HiiSetString (HiiHandle, 0, HelpString, NULL);
328 
329     HiiCreateActionOpCode (
330       StartOpCodeHandle,
331       mKeyInput,
332       Token,
333       HelpToken,
334       EFI_IFR_FLAG_CALLBACK,
335       0
336       );
337   }
338 
339   if (NeedEndOp) {
340     HiiCreateEndOpCode (StartOpCodeHandle);
341   }
342 
343   HiiUpdateForm (
344     HiiHandle,
345     &gBootManagerFormSetGuid,
346     BOOT_MANAGER_FORM_ID,
347     StartOpCodeHandle,
348     EndOpCodeHandle
349     );
350 
351   HiiFreeOpCodeHandle (StartOpCodeHandle);
352   HiiFreeOpCodeHandle (EndOpCodeHandle);
353 
354   ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
355   Status = gFormBrowser2->SendForm (
356                            gFormBrowser2,
357                            &HiiHandle,
358                            1,
359                            &gBootManagerFormSetGuid,
360                            0,
361                            NULL,
362                            &ActionRequest
363                            );
364   if (ActionRequest == EFI_BROWSER_ACTION_REQUEST_RESET) {
365     EnableResetRequired ();
366   }
367 
368   if (gOption == NULL) {
369     return ;
370   }
371 
372   //
373   // Will leave browser, check any reset required change is applied? if yes, reset system
374   //
375   SetupResetReminder ();
376 
377   //
378   // Restore to original mode before launching boot option.
379   //
380   BdsSetConsoleMode (FALSE);
381 
382   //
383   // parse the selected option
384   //
385   Status = BdsLibBootViaBootOption (gOption, gOption->DevicePath, &ExitDataSize, &ExitData);
386 
387   if (!EFI_ERROR (Status)) {
388     gOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_SUCCEEDED));
389     PlatformBdsBootSuccess (gOption);
390   } else {
391     gOption->StatusString = GetStringById (STRING_TOKEN (STR_BOOT_FAILED));
392     PlatformBdsBootFail (gOption, Status, ExitData, ExitDataSize);
393     gST->ConOut->OutputString (
394                   gST->ConOut,
395                   GetStringById (STRING_TOKEN (STR_ANY_KEY_CONTINUE))
396                   );
397     gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
398   }
399 }
400