/** @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;
}