1 /** @file
2   File explorer related functions.
3 
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "BootMaint.h"
10 
11 /**
12   Update the File Explore page.
13 
14   @param CallbackData    The BMM context data.
15   @param MenuOption      Pointer to menu options to display.
16 
17 **/
18 VOID
UpdateFileExplorePage(IN BMM_CALLBACK_DATA * CallbackData,BM_MENU_OPTION * MenuOption)19 UpdateFileExplorePage (
20   IN BMM_CALLBACK_DATA            *CallbackData,
21   BM_MENU_OPTION                  *MenuOption
22   )
23 {
24   UINTN           Index;
25   BM_MENU_ENTRY   *NewMenuEntry;
26   BM_FILE_CONTEXT *NewFileContext;
27   EFI_FORM_ID     FormId;
28 
29   NewMenuEntry    = NULL;
30   NewFileContext  = NULL;
31   FormId          = 0;
32 
33   RefreshUpdateData ();
34   mStartLabel->Number = FORM_FILE_EXPLORER_ID;
35 
36   for (Index = 0; Index < MenuOption->MenuNumber; Index++) {
37     NewMenuEntry    = BOpt_GetMenuEntry (MenuOption, Index);
38     NewFileContext  = (BM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
39 
40     if (NewFileContext->IsBootLegacy) {
41       continue;
42     }
43 
44     if ((NewFileContext->IsDir) || (FileExplorerStateBootFromFile == CallbackData->FeCurrentState)) {
45       //
46       // Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile.
47       //
48       HiiCreateActionOpCode (
49         mStartOpCodeHandle,
50         (UINT16) (FILE_OPTION_OFFSET + Index),
51         NewMenuEntry->DisplayStringToken,
52         STRING_TOKEN (STR_NULL_STRING),
53         EFI_IFR_FLAG_CALLBACK,
54         0
55         );
56     } else {
57       //
58       // Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState.
59       //
60       if (FileExplorerStateAddBootOption == CallbackData->FeCurrentState) {
61         FormId = FORM_BOOT_ADD_DESCRIPTION_ID;
62       } else if (FileExplorerStateAddDriverOptionState == CallbackData->FeCurrentState) {
63         FormId = FORM_DRIVER_ADD_FILE_DESCRIPTION_ID;
64       }
65 
66       HiiCreateGotoOpCode (
67         mStartOpCodeHandle,
68         FormId,
69         NewMenuEntry->DisplayStringToken,
70         STRING_TOKEN (STR_NULL_STRING),
71         EFI_IFR_FLAG_CALLBACK,
72         (UINT16) (FILE_OPTION_GOTO_OFFSET + Index)
73         );
74     }
75   }
76 
77   HiiUpdateForm (
78     CallbackData->FeHiiHandle,
79     &gFileExploreFormSetGuid,
80     FORM_FILE_EXPLORER_ID,
81     mStartOpCodeHandle, // Label FORM_FILE_EXPLORER_ID
82     mEndOpCodeHandle    // LABEL_END
83     );
84 }
85 
86 /**
87   Update the file explower page with the refershed file system.
88 
89 
90   @param CallbackData    BMM context data
91   @param KeyValue        Key value to identify the type of data to expect.
92 
93   @retval  TRUE           Inform the caller to create a callback packet to exit file explorer.
94   @retval  FALSE          Indicate that there is no need to exit file explorer.
95 
96 **/
97 BOOLEAN
UpdateFileExplorer(IN BMM_CALLBACK_DATA * CallbackData,IN UINT16 KeyValue)98 UpdateFileExplorer (
99   IN BMM_CALLBACK_DATA            *CallbackData,
100   IN UINT16                       KeyValue
101   )
102 {
103   UINT16          FileOptionMask;
104   BM_MENU_ENTRY   *NewMenuEntry;
105   BM_FILE_CONTEXT *NewFileContext;
106   EFI_FORM_ID     FormId;
107   BOOLEAN         ExitFileExplorer;
108   EFI_STATUS      Status;
109 
110   NewMenuEntry      = NULL;
111   NewFileContext    = NULL;
112   ExitFileExplorer  = FALSE;
113 
114   FileOptionMask    = (UINT16) (FILE_OPTION_MASK & KeyValue);
115 
116   if (FileExplorerDisplayUnknown == CallbackData->FeDisplayContext) {
117     //
118     // First in, display file system.
119     //
120     BOpt_FreeMenu (&FsOptionMenu);
121     BOpt_FindFileSystem (CallbackData);
122     CreateMenuStringToken (CallbackData, CallbackData->FeHiiHandle, &FsOptionMenu);
123 
124     UpdateFileExplorePage (CallbackData, &FsOptionMenu);
125 
126     CallbackData->FeDisplayContext = FileExplorerDisplayFileSystem;
127   } else {
128     if (FileExplorerDisplayFileSystem == CallbackData->FeDisplayContext) {
129       NewMenuEntry = BOpt_GetMenuEntry (&FsOptionMenu, FileOptionMask);
130     } else if (FileExplorerDisplayDirectory == CallbackData->FeDisplayContext) {
131       NewMenuEntry = BOpt_GetMenuEntry (&DirectoryMenu, FileOptionMask);
132     }
133 
134     NewFileContext                  = (BM_FILE_CONTEXT *) NewMenuEntry->VariableContext;
135 
136     if (NewFileContext->IsDir ) {
137       CallbackData->FeDisplayContext = FileExplorerDisplayDirectory;
138 
139       RemoveEntryList (&NewMenuEntry->Link);
140       BOpt_FreeMenu (&DirectoryMenu);
141       Status = BOpt_FindFiles (CallbackData, NewMenuEntry);
142        if (EFI_ERROR (Status)) {
143          ExitFileExplorer = TRUE;
144          goto exit;
145        }
146       CreateMenuStringToken (CallbackData, CallbackData->FeHiiHandle, &DirectoryMenu);
147       BOpt_DestroyMenuEntry (NewMenuEntry);
148 
149       UpdateFileExplorePage (CallbackData, &DirectoryMenu);
150 
151     } else {
152       switch (CallbackData->FeCurrentState) {
153       case FileExplorerStateBootFromFile:
154         //
155         // Restore to original mode before  launching boot option.
156         //
157         BdsSetConsoleMode (FALSE);
158 
159         //
160         // Here boot from file
161         //
162         BootThisFile (NewFileContext);
163         //
164         // Set proper video resolution and text mode for setup.
165         //
166         BdsSetConsoleMode (TRUE);
167         ExitFileExplorer = TRUE;
168         break;
169 
170       case FileExplorerStateAddBootOption:
171       case FileExplorerStateAddDriverOptionState:
172         if (FileExplorerStateAddBootOption == CallbackData->FeCurrentState) {
173           FormId = FORM_BOOT_ADD_DESCRIPTION_ID;
174           if (!CallbackData->FeFakeNvData.BootOptionChanged) {
175             ZeroMem (CallbackData->FeFakeNvData.BootOptionalData, sizeof (CallbackData->FeFakeNvData.BootOptionalData));
176             ZeroMem (CallbackData->FeFakeNvData.BootDescriptionData, sizeof (CallbackData->FeFakeNvData.BootDescriptionData));
177           }
178         } else {
179           FormId = FORM_DRIVER_ADD_FILE_DESCRIPTION_ID;
180           if (!CallbackData->FeFakeNvData.DriverOptionChanged) {
181             ZeroMem (CallbackData->FeFakeNvData.DriverOptionalData, sizeof (CallbackData->FeFakeNvData.DriverOptionalData));
182             ZeroMem (CallbackData->FeFakeNvData.DriverDescriptionData, sizeof (CallbackData->FeFakeNvData.DriverDescriptionData));
183           }
184         }
185 
186         CallbackData->MenuEntry = NewMenuEntry;
187         CallbackData->LoadContext->FilePathList = ((BM_FILE_CONTEXT *) (CallbackData->MenuEntry->VariableContext))->DevicePath;
188 
189         //
190         // Create Subtitle op-code for the display string of the option.
191         //
192         RefreshUpdateData ();
193         mStartLabel->Number = FormId;
194 
195         HiiCreateSubTitleOpCode (
196           mStartOpCodeHandle,
197           NewMenuEntry->DisplayStringToken,
198           0,
199           0,
200           0
201           );
202 
203         HiiUpdateForm (
204           CallbackData->FeHiiHandle,
205           &gFileExploreFormSetGuid,
206           FormId,
207           mStartOpCodeHandle, // Label FormId
208           mEndOpCodeHandle    // LABEL_END
209           );
210         break;
211 
212       default:
213         break;
214       }
215     }
216   }
217   exit:
218   return ExitFileExplorer;
219 }
220 
221 /**
222   This function applies changes in a driver's configuration.
223   Input is a Configuration, which has the routing data for this
224   driver followed by name / value configuration pairs. The driver
225   must apply those pairs to its configurable storage. If the
226   driver's configuration is stored in a linear block of data
227   and the driver's name / value pairs are in <BlockConfig>
228   format, it may use the ConfigToBlock helper function (above) to
229   simplify the job. Currently not implemented.
230 
231   @param[in]  This                Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
232   @param[in]  Configuration       A null-terminated Unicode string in
233                                   <ConfigString> format.
234   @param[out] Progress            A pointer to a string filled in with the
235                                   offset of the most recent '&' before the
236                                   first failing name / value pair (or the
237                                   beginn ing of the string if the failure
238                                   is in the first name / value pair) or
239                                   the terminating NULL if all was
240                                   successful.
241 
242   @retval EFI_SUCCESS             The results have been distributed or are
243                                   awaiting distribution.
244   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the
245                                   parts of the results that must be
246                                   stored awaiting possible future
247                                   protocols.
248   @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the
249                                   Results parameter would result
250                                   in this type of error.
251   @retval EFI_NOT_FOUND           Target for the specified routing data
252                                   was not found.
253 **/
254 EFI_STATUS
255 EFIAPI
FileExplorerRouteConfig(IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL * This,IN CONST EFI_STRING Configuration,OUT EFI_STRING * Progress)256 FileExplorerRouteConfig (
257   IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
258   IN CONST EFI_STRING                     Configuration,
259   OUT EFI_STRING                          *Progress
260   )
261 {
262   EFI_STATUS                      Status;
263   UINTN                           BufferSize;
264   EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting;
265   FILE_EXPLORER_NV_DATA           *FeData;
266   BMM_CALLBACK_DATA               *Private;
267 
268   if (Progress == NULL) {
269     return EFI_INVALID_PARAMETER;
270   }
271   *Progress = Configuration;
272 
273   if (Configuration == NULL) {
274     return EFI_INVALID_PARAMETER;
275   }
276 
277   //
278   // Check routing data in <ConfigHdr>.
279   // Note: there is no name for Name/Value storage, only GUID will be checked
280   //
281   if (!HiiIsConfigHdrMatch (Configuration, &gFileExploreFormSetGuid, mFileExplorerStorageName)) {
282     return EFI_NOT_FOUND;
283   }
284 
285   Status = gBS->LocateProtocol (
286                   &gEfiHiiConfigRoutingProtocolGuid,
287                   NULL,
288                   (VOID**) &ConfigRouting
289                   );
290   if (EFI_ERROR (Status)) {
291     return Status;
292   }
293 
294   Private = FE_CALLBACK_DATA_FROM_THIS (This);
295   //
296   // Get Buffer Storage data from EFI variable
297   //
298   BufferSize = sizeof (FILE_EXPLORER_NV_DATA );
299   FeData = &Private->FeFakeNvData;
300 
301   //
302   // Convert <ConfigResp> to buffer data by helper function ConfigToBlock()
303   //
304   Status = ConfigRouting->ConfigToBlock (
305                             ConfigRouting,
306                             Configuration,
307                             (UINT8 *) FeData,
308                             &BufferSize,
309                             Progress
310                             );
311   ASSERT_EFI_ERROR (Status);
312 
313   if (FeData->BootDescriptionData[0] != 0x00 || FeData->BootOptionalData[0] != 0x00) {
314     Status = Var_UpdateBootOption (Private, FeData);
315     if (EFI_ERROR (Status)) {
316       return Status;
317     }
318 
319     BOpt_GetBootOptions (Private);
320     CreateMenuStringToken (Private, Private->FeHiiHandle, &BootOptionMenu);
321   }
322 
323   if (FeData->DriverDescriptionData[0] != 0x00 || FeData->DriverOptionalData[0] != 0x00) {
324     Status = Var_UpdateDriverOption (
325               Private,
326               Private->FeHiiHandle,
327               FeData->DriverDescriptionData,
328               FeData->DriverOptionalData,
329               FeData->ForceReconnect
330               );
331     if (EFI_ERROR (Status)) {
332       return Status;
333     }
334 
335     BOpt_GetDriverOptions (Private);
336     CreateMenuStringToken (Private, Private->FeHiiHandle, &DriverOptionMenu);
337   }
338 
339   return EFI_SUCCESS;
340 }
341 
342 /**
343   This function processes the results of changes in configuration.
344   When user select a interactive opcode, this callback will be triggered.
345   Based on the Question(QuestionId) that triggers the callback, the corresponding
346   actions is performed. It handles:
347 
348   1) the addition of boot option.
349   2) the addition of driver option.
350   3) exit from file browser
351   4) update of file content if a dir is selected.
352   5) boot the file if a file is selected in "boot from file"
353 
354 
355   @param This            Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
356   @param Action          Specifies the type of action taken by the browser.
357   @param QuestionId      A unique value which is sent to the original exporting driver
358                          so that it can identify the type of data to expect.
359   @param Type            The type of value for the question.
360   @param Value           A pointer to the data being sent to the original exporting driver.
361   @param ActionRequest   On return, points to the action requested by the callback function.
362 
363   @retval  EFI_SUCCESS           The callback successfully handled the action.
364   @retval  EFI_OUT_OF_RESOURCES  Not enough storage is available to hold the variable and its data.
365   @retval  EFI_DEVICE_ERROR      The variable could not be saved.
366   @retval  EFI_UNSUPPORTED       The specified Action is not supported by the callback.
367   @retval  EFI_INVALID_PARAMETER If parameter Value or ActionRequest is NULL.
368 **/
369 EFI_STATUS
370 EFIAPI
FileExplorerCallback(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)371 FileExplorerCallback (
372   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
373   IN  EFI_BROWSER_ACTION                     Action,
374   IN  EFI_QUESTION_ID                        QuestionId,
375   IN  UINT8                                  Type,
376   IN  EFI_IFR_TYPE_VALUE                     *Value,
377   OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
378   )
379 {
380   BMM_CALLBACK_DATA     *Private;
381   FILE_EXPLORER_NV_DATA *NvRamMap;
382   EFI_STATUS            Status;
383 
384   if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) {
385     //
386     // All other action return unsupported.
387     //
388     return EFI_UNSUPPORTED;
389   }
390 
391   Status         = EFI_SUCCESS;
392   Private        = FE_CALLBACK_DATA_FROM_THIS (This);
393 
394   //
395   // Retrieve uncommitted data from Form Browser
396   //
397   NvRamMap = &Private->FeFakeNvData;
398   HiiGetBrowserData (&gFileExploreFormSetGuid, mFileExplorerStorageName, sizeof (FILE_EXPLORER_NV_DATA), (UINT8 *) NvRamMap);
399 
400   if (Action == EFI_BROWSER_ACTION_CHANGED) {
401     if ((Value == NULL) || (ActionRequest == NULL)) {
402       return EFI_INVALID_PARAMETER;
403     }
404 
405     if (QuestionId == KEY_VALUE_SAVE_AND_EXIT_BOOT) {
406       NvRamMap->BootOptionChanged = FALSE;
407       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
408     } else if (QuestionId == KEY_VALUE_SAVE_AND_EXIT_DRIVER) {
409       NvRamMap->DriverOptionChanged = FALSE;
410       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
411     } else if (QuestionId == KEY_VALUE_NO_SAVE_AND_EXIT_DRIVER) {
412       //
413       // Discard changes and exit formset
414       //
415       NvRamMap->DriverOptionalData[0]     = 0x0000;
416       NvRamMap->DriverDescriptionData[0]  = 0x0000;
417       NvRamMap->DriverOptionChanged = FALSE;
418       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
419     } else if (QuestionId == KEY_VALUE_NO_SAVE_AND_EXIT_BOOT) {
420       //
421       // Discard changes and exit formset
422       //
423       NvRamMap->BootOptionalData[0]     = 0x0000;
424       NvRamMap->BootDescriptionData[0]  = 0x0000;
425       NvRamMap->BootOptionChanged = FALSE;
426       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
427     } else if (QuestionId == KEY_VALUE_BOOT_DESCRIPTION || QuestionId == KEY_VALUE_BOOT_OPTION) {
428       NvRamMap->BootOptionChanged = TRUE;
429     } else if (QuestionId == KEY_VALUE_DRIVER_DESCRIPTION || QuestionId == KEY_VALUE_DRIVER_OPTION) {
430       NvRamMap->DriverOptionChanged = TRUE;
431     } else if (QuestionId < FILE_OPTION_OFFSET) {
432       //
433       // Exit File Explorer formset
434       //
435       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
436     } else if (QuestionId >= FILE_OPTION_OFFSET && QuestionId < FILE_OPTION_GOTO_OFFSET) {
437       //
438       // Update forms may return TRUE or FALSE, need to check here.
439       //
440       if (UpdateFileExplorer (Private, QuestionId)) {
441         *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT;
442       }
443     }
444   } else if (Action == EFI_BROWSER_ACTION_CHANGING) {
445     if (Value == NULL) {
446       return EFI_INVALID_PARAMETER;
447     }
448 
449     if (QuestionId >= FILE_OPTION_GOTO_OFFSET) {
450       //
451       // function will always return FALSE, no need to check here.
452       //
453       UpdateFileExplorer (Private, QuestionId);
454     }
455   }
456 
457   //
458   // Pass changed uncommitted data back to Form Browser
459   //
460   HiiSetBrowserData (&gFileExploreFormSetGuid, mFileExplorerStorageName, sizeof (FILE_EXPLORER_NV_DATA), (UINT8 *) NvRamMap, NULL);
461 
462   return Status;
463 }
464