/** @file File explorer related functions. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "BootMaint.h" /** Update the File Explore page. @param CallbackData The BMM context data. @param MenuOption Pointer to menu options to display. **/ VOID UpdateFileExplorePage ( IN BMM_CALLBACK_DATA *CallbackData, BM_MENU_OPTION *MenuOption ) { UINTN Index; BM_MENU_ENTRY *NewMenuEntry; BM_FILE_CONTEXT *NewFileContext; EFI_FORM_ID FormId; NewMenuEntry = NULL; NewFileContext = NULL; FormId = 0; RefreshUpdateData (); mStartLabel->Number = FORM_FILE_EXPLORER_ID; for (Index = 0; Index < MenuOption->MenuNumber; Index++) { NewMenuEntry = BOpt_GetMenuEntry (MenuOption, Index); NewFileContext = (BM_FILE_CONTEXT *) NewMenuEntry->VariableContext; if (NewFileContext->IsBootLegacy) { continue; } if ((NewFileContext->IsDir) || (FileExplorerStateBootFromFile == CallbackData->FeCurrentState)) { // // Create Text opcode for directory, also create Text opcode for file in FileExplorerStateBootFromFile. // HiiCreateActionOpCode ( mStartOpCodeHandle, (UINT16) (FILE_OPTION_OFFSET + Index), NewMenuEntry->DisplayStringToken, STRING_TOKEN (STR_NULL_STRING), EFI_IFR_FLAG_CALLBACK, 0 ); } else { // // Create Goto opcode for file in FileExplorerStateAddBootOption or FileExplorerStateAddDriverOptionState. // if (FileExplorerStateAddBootOption == CallbackData->FeCurrentState) { FormId = FORM_BOOT_ADD_DESCRIPTION_ID; } else if (FileExplorerStateAddDriverOptionState == CallbackData->FeCurrentState) { FormId = FORM_DRIVER_ADD_FILE_DESCRIPTION_ID; } HiiCreateGotoOpCode ( mStartOpCodeHandle, FormId, NewMenuEntry->DisplayStringToken, STRING_TOKEN (STR_NULL_STRING), EFI_IFR_FLAG_CALLBACK, (UINT16) (FILE_OPTION_GOTO_OFFSET + Index) ); } } HiiUpdateForm ( CallbackData->FeHiiHandle, &gFileExploreFormSetGuid, FORM_FILE_EXPLORER_ID, mStartOpCodeHandle, // Label FORM_FILE_EXPLORER_ID mEndOpCodeHandle // LABEL_END ); } /** Update the file explower page with the refershed file system. @param CallbackData BMM context data @param KeyValue Key value to identify the type of data to expect. @retval TRUE Inform the caller to create a callback packet to exit file explorer. @retval FALSE Indicate that there is no need to exit file explorer. **/ BOOLEAN UpdateFileExplorer ( IN BMM_CALLBACK_DATA *CallbackData, IN UINT16 KeyValue ) { UINT16 FileOptionMask; BM_MENU_ENTRY *NewMenuEntry; BM_FILE_CONTEXT *NewFileContext; EFI_FORM_ID FormId; BOOLEAN ExitFileExplorer; EFI_STATUS Status; NewMenuEntry = NULL; NewFileContext = NULL; ExitFileExplorer = FALSE; FileOptionMask = (UINT16) (FILE_OPTION_MASK & KeyValue); if (FileExplorerDisplayUnknown == CallbackData->FeDisplayContext) { // // First in, display file system. // BOpt_FreeMenu (&FsOptionMenu); BOpt_FindFileSystem (CallbackData); CreateMenuStringToken (CallbackData, CallbackData->FeHiiHandle, &FsOptionMenu); UpdateFileExplorePage (CallbackData, &FsOptionMenu); CallbackData->FeDisplayContext = FileExplorerDisplayFileSystem; } else { if (FileExplorerDisplayFileSystem == CallbackData->FeDisplayContext) { NewMenuEntry = BOpt_GetMenuEntry (&FsOptionMenu, FileOptionMask); } else if (FileExplorerDisplayDirectory == CallbackData->FeDisplayContext) { NewMenuEntry = BOpt_GetMenuEntry (&DirectoryMenu, FileOptionMask); } NewFileContext = (BM_FILE_CONTEXT *) NewMenuEntry->VariableContext; if (NewFileContext->IsDir ) { CallbackData->FeDisplayContext = FileExplorerDisplayDirectory; RemoveEntryList (&NewMenuEntry->Link); BOpt_FreeMenu (&DirectoryMenu); Status = BOpt_FindFiles (CallbackData, NewMenuEntry); if (EFI_ERROR (Status)) { ExitFileExplorer = TRUE; goto exit; } CreateMenuStringToken (CallbackData, CallbackData->FeHiiHandle, &DirectoryMenu); BOpt_DestroyMenuEntry (NewMenuEntry); UpdateFileExplorePage (CallbackData, &DirectoryMenu); } else { switch (CallbackData->FeCurrentState) { case FileExplorerStateBootFromFile: // // Restore to original mode before launching boot option. // BdsSetConsoleMode (FALSE); // // Here boot from file // BootThisFile (NewFileContext); // // Set proper video resolution and text mode for setup. // BdsSetConsoleMode (TRUE); ExitFileExplorer = TRUE; break; case FileExplorerStateAddBootOption: case FileExplorerStateAddDriverOptionState: if (FileExplorerStateAddBootOption == CallbackData->FeCurrentState) { FormId = FORM_BOOT_ADD_DESCRIPTION_ID; if (!CallbackData->FeFakeNvData.BootOptionChanged) { ZeroMem (CallbackData->FeFakeNvData.BootOptionalData, sizeof (CallbackData->FeFakeNvData.BootOptionalData)); ZeroMem (CallbackData->FeFakeNvData.BootDescriptionData, sizeof (CallbackData->FeFakeNvData.BootDescriptionData)); } } else { FormId = FORM_DRIVER_ADD_FILE_DESCRIPTION_ID; if (!CallbackData->FeFakeNvData.DriverOptionChanged) { ZeroMem (CallbackData->FeFakeNvData.DriverOptionalData, sizeof (CallbackData->FeFakeNvData.DriverOptionalData)); ZeroMem (CallbackData->FeFakeNvData.DriverDescriptionData, sizeof (CallbackData->FeFakeNvData.DriverDescriptionData)); } } CallbackData->MenuEntry = NewMenuEntry; CallbackData->LoadContext->FilePathList = ((BM_FILE_CONTEXT *) (CallbackData->MenuEntry->VariableContext))->DevicePath; // // Create Subtitle op-code for the display string of the option. // RefreshUpdateData (); mStartLabel->Number = FormId; HiiCreateSubTitleOpCode ( mStartOpCodeHandle, NewMenuEntry->DisplayStringToken, 0, 0, 0 ); HiiUpdateForm ( CallbackData->FeHiiHandle, &gFileExploreFormSetGuid, FormId, mStartOpCodeHandle, // Label FormId mEndOpCodeHandle // LABEL_END ); break; default: break; } } } exit: return ExitFileExplorer; } /** This function applies changes in a driver's configuration. Input is a Configuration, which has the routing data for this driver followed by name / value configuration pairs. The driver must apply those pairs to its configurable storage. If the driver's configuration is stored in a linear block of data and the driver's name / value pairs are in format, it may use the ConfigToBlock helper function (above) to simplify the job. Currently not implemented. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Configuration A null-terminated Unicode string in format. @param[out] Progress A pointer to a string filled in with the offset of the most recent '&' before the first failing name / value pair (or the beginn ing of the string if the failure is in the first name / value pair) or the terminating NULL if all was successful. @retval EFI_SUCCESS The results have been distributed or are awaiting distribution. @retval EFI_OUT_OF_RESOURCES Not enough memory to store the parts of the results that must be stored awaiting possible future protocols. @retval EFI_INVALID_PARAMETERS Passing in a NULL for the Results parameter would result in this type of error. @retval EFI_NOT_FOUND Target for the specified routing data was not found. **/ EFI_STATUS EFIAPI FileExplorerRouteConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Configuration, OUT EFI_STRING *Progress ) { EFI_STATUS Status; UINTN BufferSize; EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting; FILE_EXPLORER_NV_DATA *FeData; BMM_CALLBACK_DATA *Private; if (Progress == NULL) { return EFI_INVALID_PARAMETER; } *Progress = Configuration; if (Configuration == NULL) { return EFI_INVALID_PARAMETER; } // // Check routing data in . // Note: there is no name for Name/Value storage, only GUID will be checked // if (!HiiIsConfigHdrMatch (Configuration, &gFileExploreFormSetGuid, mFileExplorerStorageName)) { return EFI_NOT_FOUND; } Status = gBS->LocateProtocol ( &gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID**) &ConfigRouting ); if (EFI_ERROR (Status)) { return Status; } Private = FE_CALLBACK_DATA_FROM_THIS (This); // // Get Buffer Storage data from EFI variable // BufferSize = sizeof (FILE_EXPLORER_NV_DATA ); FeData = &Private->FeFakeNvData; // // Convert to buffer data by helper function ConfigToBlock() // Status = ConfigRouting->ConfigToBlock ( ConfigRouting, Configuration, (UINT8 *) FeData, &BufferSize, Progress ); ASSERT_EFI_ERROR (Status); if (FeData->BootDescriptionData[0] != 0x00 || FeData->BootOptionalData[0] != 0x00) { Status = Var_UpdateBootOption (Private, FeData); if (EFI_ERROR (Status)) { return Status; } BOpt_GetBootOptions (Private); CreateMenuStringToken (Private, Private->FeHiiHandle, &BootOptionMenu); } if (FeData->DriverDescriptionData[0] != 0x00 || FeData->DriverOptionalData[0] != 0x00) { Status = Var_UpdateDriverOption ( Private, Private->FeHiiHandle, FeData->DriverDescriptionData, FeData->DriverOptionalData, FeData->ForceReconnect ); if (EFI_ERROR (Status)) { return Status; } BOpt_GetDriverOptions (Private); CreateMenuStringToken (Private, Private->FeHiiHandle, &DriverOptionMenu); } return EFI_SUCCESS; } /** This function processes the results of changes in configuration. When user select a interactive opcode, this callback will be triggered. Based on the Question(QuestionId) that triggers the callback, the corresponding actions is performed. It handles: 1) the addition of boot option. 2) the addition of driver option. 3) exit from file browser 4) update of file content if a dir is selected. 5) boot the file if a file is selected in "boot from file" @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param Action Specifies the type of action taken by the browser. @param QuestionId A unique value which is sent to the original exporting driver so that it can identify the type of data to expect. @param Type The type of value for the question. @param Value A pointer to the data being sent to the original exporting driver. @param ActionRequest On return, points to the action requested by the callback function. @retval EFI_SUCCESS The callback successfully handled the action. @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. @retval EFI_DEVICE_ERROR The variable could not be saved. @retval EFI_UNSUPPORTED The specified Action is not supported by the callback. @retval EFI_INVALID_PARAMETER If parameter Value or ActionRequest is NULL. **/ EFI_STATUS 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 ) { BMM_CALLBACK_DATA *Private; FILE_EXPLORER_NV_DATA *NvRamMap; EFI_STATUS Status; if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) { // // All other action return unsupported. // return EFI_UNSUPPORTED; } Status = EFI_SUCCESS; Private = FE_CALLBACK_DATA_FROM_THIS (This); // // Retrieve uncommitted data from Form Browser // NvRamMap = &Private->FeFakeNvData; HiiGetBrowserData (&gFileExploreFormSetGuid, mFileExplorerStorageName, sizeof (FILE_EXPLORER_NV_DATA), (UINT8 *) NvRamMap); if (Action == EFI_BROWSER_ACTION_CHANGED) { if ((Value == NULL) || (ActionRequest == NULL)) { return EFI_INVALID_PARAMETER; } if (QuestionId == KEY_VALUE_SAVE_AND_EXIT_BOOT) { NvRamMap->BootOptionChanged = FALSE; *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; } else if (QuestionId == KEY_VALUE_SAVE_AND_EXIT_DRIVER) { NvRamMap->DriverOptionChanged = FALSE; *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; } else if (QuestionId == KEY_VALUE_NO_SAVE_AND_EXIT_DRIVER) { // // Discard changes and exit formset // NvRamMap->DriverOptionalData[0] = 0x0000; NvRamMap->DriverDescriptionData[0] = 0x0000; NvRamMap->DriverOptionChanged = FALSE; *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; } else if (QuestionId == KEY_VALUE_NO_SAVE_AND_EXIT_BOOT) { // // Discard changes and exit formset // NvRamMap->BootOptionalData[0] = 0x0000; NvRamMap->BootDescriptionData[0] = 0x0000; NvRamMap->BootOptionChanged = FALSE; *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; } else if (QuestionId == KEY_VALUE_BOOT_DESCRIPTION || QuestionId == KEY_VALUE_BOOT_OPTION) { NvRamMap->BootOptionChanged = TRUE; } else if (QuestionId == KEY_VALUE_DRIVER_DESCRIPTION || QuestionId == KEY_VALUE_DRIVER_OPTION) { NvRamMap->DriverOptionChanged = TRUE; } else if (QuestionId < FILE_OPTION_OFFSET) { // // Exit File Explorer formset // *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; } else if (QuestionId >= FILE_OPTION_OFFSET && QuestionId < FILE_OPTION_GOTO_OFFSET) { // // Update forms may return TRUE or FALSE, need to check here. // if (UpdateFileExplorer (Private, QuestionId)) { *ActionRequest = EFI_BROWSER_ACTION_REQUEST_EXIT; } } } else if (Action == EFI_BROWSER_ACTION_CHANGING) { if (Value == NULL) { return EFI_INVALID_PARAMETER; } if (QuestionId >= FILE_OPTION_GOTO_OFFSET) { // // function will always return FALSE, no need to check here. // UpdateFileExplorer (Private, QuestionId); } } // // Pass changed uncommitted data back to Form Browser // HiiSetBrowserData (&gFileExploreFormSetGuid, mFileExplorerStorageName, sizeof (FILE_EXPLORER_NV_DATA), (UINT8 *) NvRamMap, NULL); return Status; }