From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../MdeModulePkg/Universal/SetupBrowserDxe/Setup.c | 6675 ++++++++++++++++++++ 1 file changed, 6675 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c (limited to 'roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c') diff --git a/roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c b/roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c new file mode 100644 index 000000000..f936a4b8e --- /dev/null +++ b/roms/edk2/MdeModulePkg/Universal/SetupBrowserDxe/Setup.c @@ -0,0 +1,6675 @@ +/** @file +Entry and initialization module for the browser. + +Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.
+(C) Copyright 2020 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "Setup.h" + +SETUP_DRIVER_PRIVATE_DATA mPrivateData = { + SETUP_DRIVER_SIGNATURE, + NULL, + { + SendForm, + BrowserCallback + }, + { + SetScope, + RegisterHotKey, + RegiserExitHandler, + SaveReminder + }, + { + BROWSER_EXTENSION2_VERSION_1_1, + SetScope, + RegisterHotKey, + RegiserExitHandler, + IsBrowserDataModified, + ExecuteAction, + {NULL,NULL}, + {NULL,NULL}, + IsResetRequired + } +}; + +EFI_HII_DATABASE_PROTOCOL *mHiiDatabase; +EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting; +EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL *mPathFromText; +EDKII_FORM_DISPLAY_ENGINE_PROTOCOL *mFormDisplay; + +UINTN gBrowserContextCount = 0; +LIST_ENTRY gBrowserContextList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserContextList); +LIST_ENTRY gBrowserFormSetList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserFormSetList); +LIST_ENTRY gBrowserHotKeyList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserHotKeyList); +LIST_ENTRY gBrowserStorageList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserStorageList); +LIST_ENTRY gBrowserSaveFailFormSetList = INITIALIZE_LIST_HEAD_VARIABLE (gBrowserSaveFailFormSetList); + +BOOLEAN mSystemSubmit = FALSE; +BOOLEAN gResetRequiredFormLevel; +BOOLEAN gResetRequiredSystemLevel = FALSE; +BOOLEAN gExitRequired; +BOOLEAN gFlagReconnect; +BOOLEAN gCallbackReconnect; +BROWSER_SETTING_SCOPE gBrowserSettingScope = FormSetLevel; +BOOLEAN mBrowserScopeFirstSet = TRUE; +EXIT_HANDLER ExitHandlerFunction = NULL; +FORM_BROWSER_FORMSET *mSystemLevelFormSet; + +// +// Browser Global Strings +// +CHAR16 *gEmptyString; +CHAR16 *mUnknownString = L"!"; + +extern EFI_GUID mCurrentFormSetGuid; +extern EFI_HII_HANDLE mCurrentHiiHandle; +extern UINT16 mCurrentFormId; +extern FORM_DISPLAY_ENGINE_FORM gDisplayFormData; +extern BOOLEAN mDynamicFormUpdated; + +/** + Create a menu with specified formset GUID and form ID, and add it as a child + of the given parent menu. + + @param HiiHandle Hii handle related to this formset. + @param FormSetGuid The Formset Guid of menu to be added. + @param FormId The Form ID of menu to be added. + @param QuestionId The question id of this menu to be added. + + @return A pointer to the newly added menu or NULL if memory is insufficient. + +**/ +FORM_ENTRY_INFO * +UiAddMenuList ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid, + IN UINT16 FormId, + IN UINT16 QuestionId + ) +{ + FORM_ENTRY_INFO *MenuList; + + MenuList = AllocateZeroPool (sizeof (FORM_ENTRY_INFO)); + if (MenuList == NULL) { + return NULL; + } + + MenuList->Signature = FORM_ENTRY_INFO_SIGNATURE; + + MenuList->HiiHandle = HiiHandle; + CopyMem (&MenuList->FormSetGuid, FormSetGuid, sizeof (EFI_GUID)); + MenuList->FormId = FormId; + MenuList->QuestionId = QuestionId; + + // + // If parent is not specified, it is the root Form of a Formset + // + InsertTailList (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &MenuList->Link); + + return MenuList; +} + +/** + Return the form id for the input hiihandle and formset. + + @param HiiHandle HiiHandle for FormSet. + @param FormSetGuid The Formset GUID of the menu to search. + + @return First form's id for this form set. + +**/ +EFI_FORM_ID +GetFirstFormId ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_FORM *Form; + + Link = GetFirstNode (&gCurrentSelection->FormSet->FormListHead); + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + + return Form->FormId; +} + +/** + Search Menu with given FormSetGuid and FormId in all cached menu list. + + @param HiiHandle HiiHandle for FormSet. + @param FormSetGuid The Formset GUID of the menu to search. + @param FormId The Form ID of menu to search. + + @return A pointer to menu found or NULL if not found. + +**/ +FORM_ENTRY_INFO * +UiFindMenuList ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_GUID *FormSetGuid, + IN UINT16 FormId + ) +{ + LIST_ENTRY *Link; + FORM_ENTRY_INFO *MenuList; + FORM_ENTRY_INFO *RetMenu; + EFI_FORM_ID FirstFormId; + + RetMenu = NULL; + + Link = GetFirstNode (&mPrivateData.FormBrowserEx2.FormViewHistoryHead); + while (!IsNull (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, Link)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (Link); + Link = GetNextNode (&mPrivateData.FormBrowserEx2.FormViewHistoryHead, Link); + + // + // If already find the menu, free the menus behind it. + // + if (RetMenu != NULL) { + RemoveEntryList (&MenuList->Link); + FreePool (MenuList); + continue; + } + + // + // Find the same FromSet. + // + if (MenuList->HiiHandle == HiiHandle) { + if (IsZeroGuid (&MenuList->FormSetGuid)) { + // + // FormSetGuid is not specified. + // + RetMenu = MenuList; + } else if (CompareGuid (&MenuList->FormSetGuid, FormSetGuid)) { + if (MenuList->FormId == FormId) { + RetMenu = MenuList; + } else if (FormId == 0 || MenuList->FormId == 0 ) { + FirstFormId = GetFirstFormId (HiiHandle, FormSetGuid); + if ((FormId == 0 && FirstFormId == MenuList->FormId) || (MenuList->FormId ==0 && FirstFormId == FormId)) { + RetMenu = MenuList; + } + } + } + } + } + + return RetMenu; +} + +/** + Find parent menu for current menu. + + @param CurrentMenu Current Menu + @param SettingLevel Whether find parent menu in Form Level or Formset level. + In form level, just find the parent menu; + In formset level, find the parent menu which has different + formset guid value. + + @retval The parent menu for current menu. +**/ +FORM_ENTRY_INFO * +UiFindParentMenu ( + IN FORM_ENTRY_INFO *CurrentMenu, + IN BROWSER_SETTING_SCOPE SettingLevel + ) +{ + FORM_ENTRY_INFO *ParentMenu; + LIST_ENTRY *Link; + + ASSERT (SettingLevel == FormLevel || SettingLevel == FormSetLevel); + + if (CurrentMenu == NULL) { + return NULL; + } + + ParentMenu = NULL; + Link = &CurrentMenu->Link; + + while (Link->BackLink != &mPrivateData.FormBrowserEx2.FormViewHistoryHead) { + ParentMenu = FORM_ENTRY_INFO_FROM_LINK (Link->BackLink); + + if (SettingLevel == FormLevel) { + // + // For FormLevel, just find the parent menu, return. + // + break; + } + + if (!CompareGuid (&CurrentMenu->FormSetGuid, &ParentMenu->FormSetGuid)) { + // + // For SystemLevel, must find the menu which has different formset. + // + break; + } + + Link = Link->BackLink; + } + + // + // Not find the parent menu, just return NULL. + // + if (Link->BackLink == &mPrivateData.FormBrowserEx2.FormViewHistoryHead) { + return NULL; + } + + return ParentMenu; +} + +/** + Free Menu list linked list. + + @param MenuListHead One Menu list point in the menu list. + +**/ +VOID +UiFreeMenuList ( + LIST_ENTRY *MenuListHead + ) +{ + FORM_ENTRY_INFO *MenuList; + + while (!IsListEmpty (MenuListHead)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (MenuListHead->ForwardLink); + RemoveEntryList (&MenuList->Link); + + FreePool (MenuList); + } +} + +/** + Copy current Menu list to the new menu list. + + @param NewMenuListHead New create Menu list. + @param CurrentMenuListHead Current Menu list. + +**/ +VOID +UiCopyMenuList ( + OUT LIST_ENTRY *NewMenuListHead, + IN LIST_ENTRY *CurrentMenuListHead + ) +{ + LIST_ENTRY *Link; + FORM_ENTRY_INFO *MenuList; + FORM_ENTRY_INFO *NewMenuEntry; + + // + // If new menu list not empty, free it first. + // + UiFreeMenuList (NewMenuListHead); + + Link = GetFirstNode (CurrentMenuListHead); + while (!IsNull (CurrentMenuListHead, Link)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (Link); + Link = GetNextNode (CurrentMenuListHead, Link); + + NewMenuEntry = AllocateZeroPool (sizeof (FORM_ENTRY_INFO)); + ASSERT (NewMenuEntry != NULL); + NewMenuEntry->Signature = FORM_ENTRY_INFO_SIGNATURE; + NewMenuEntry->HiiHandle = MenuList->HiiHandle; + CopyMem (&NewMenuEntry->FormSetGuid, &MenuList->FormSetGuid, sizeof (EFI_GUID)); + NewMenuEntry->FormId = MenuList->FormId; + NewMenuEntry->QuestionId = MenuList->QuestionId; + + InsertTailList (NewMenuListHead, &NewMenuEntry->Link); + } +} + +/** + Load all hii formset to the browser. + +**/ +VOID +LoadAllHiiFormset ( + VOID + ) +{ + FORM_BROWSER_FORMSET *LocalFormSet; + EFI_HII_HANDLE *HiiHandles; + UINTN Index; + EFI_GUID ZeroGuid; + EFI_STATUS Status; + FORM_BROWSER_FORMSET *OldFormset; + + OldFormset = mSystemLevelFormSet; + + // + // Get all the Hii handles + // + HiiHandles = HiiGetHiiHandles (NULL); + ASSERT (HiiHandles != NULL); + + // + // Search for formset of each class type + // + for (Index = 0; HiiHandles[Index] != NULL; Index++) { + // + // Check HiiHandles[Index] does exist in global maintain list. + // + if (GetFormSetFromHiiHandle (HiiHandles[Index]) != NULL) { + continue; + } + + // + // Initilize FormSet Setting + // + LocalFormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); + ASSERT (LocalFormSet != NULL); + mSystemLevelFormSet = LocalFormSet; + + ZeroMem (&ZeroGuid, sizeof (ZeroGuid)); + Status = InitializeFormSet (HiiHandles[Index], &ZeroGuid, LocalFormSet); + if (EFI_ERROR (Status) || IsListEmpty (&LocalFormSet->FormListHead)) { + DestroyFormSet (LocalFormSet); + continue; + } + InitializeCurrentSetting (LocalFormSet); + + // + // Initilize Questions' Value + // + Status = LoadFormSetConfig (NULL, LocalFormSet); + if (EFI_ERROR (Status)) { + DestroyFormSet (LocalFormSet); + continue; + } + } + + // + // Free resources, and restore gOldFormSet and gClassOfVfr + // + FreePool (HiiHandles); + + mSystemLevelFormSet = OldFormset; +} + +/** + Pop up the error info. + + @param BrowserStatus The input browser status. + @param HiiHandle The Hiihandle for this opcode. + @param OpCode The opcode use to get the erro info and timeout value. + @param ErrorString Error string used by BROWSER_NO_SUBMIT_IF. + +**/ +UINT32 +PopupErrorMessage ( + IN UINT32 BrowserStatus, + IN EFI_HII_HANDLE HiiHandle, + IN EFI_IFR_OP_HEADER *OpCode, OPTIONAL + IN CHAR16 *ErrorString + ) +{ + FORM_DISPLAY_ENGINE_STATEMENT *Statement; + USER_INPUT UserInputData; + + Statement = NULL; + + if (OpCode != NULL) { + Statement = AllocateZeroPool (sizeof(FORM_DISPLAY_ENGINE_STATEMENT)); + ASSERT (Statement != NULL); + Statement->OpCode = OpCode; + gDisplayFormData.HighLightedStatement = Statement; + } + + // + // Used to compatible with old display engine. + // New display engine not use this field. + // + gDisplayFormData.ErrorString = ErrorString; + gDisplayFormData.BrowserStatus = BrowserStatus; + + if (HiiHandle != NULL) { + gDisplayFormData.HiiHandle = HiiHandle; + } + + mFormDisplay->FormDisplay (&gDisplayFormData, &UserInputData); + + gDisplayFormData.BrowserStatus = BROWSER_SUCCESS; + gDisplayFormData.ErrorString = NULL; + + if (OpCode != NULL) { + FreePool (Statement); + } + + return UserInputData.Action; +} + +/** + This is the routine which an external caller uses to direct the browser + where to obtain it's information. + + + @param This The Form Browser protocol instanse. + @param Handles A pointer to an array of Handles. If HandleCount > 1 we + display a list of the formsets for the handles specified. + @param HandleCount The number of Handles specified in Handle. + @param FormSetGuid This field points to the EFI_GUID which must match the Guid + field in the EFI_IFR_FORM_SET op-code for the specified + forms-based package. If FormSetGuid is NULL, then this + function will display the first found forms package. + @param FormId This field specifies which EFI_IFR_FORM to render as the first + displayable page. If this field has a value of 0x0000, then + the forms browser will render the specified forms in their encoded order. + @param ScreenDimensions Points to recommended form dimensions, including any non-content area, in + characters. + @param ActionRequest Points to the action recommended by the form. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. + @retval EFI_NOT_FOUND No valid forms could be found to display. + +**/ +EFI_STATUS +EFIAPI +SendForm ( + IN CONST EFI_FORM_BROWSER2_PROTOCOL *This, + IN EFI_HII_HANDLE *Handles, + IN UINTN HandleCount, + IN EFI_GUID *FormSetGuid, OPTIONAL + IN UINT16 FormId, OPTIONAL + IN CONST EFI_SCREEN_DESCRIPTOR *ScreenDimensions, OPTIONAL + OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest OPTIONAL + ) +{ + EFI_STATUS Status; + UI_MENU_SELECTION *Selection; + UINTN Index; + FORM_BROWSER_FORMSET *FormSet; + FORM_ENTRY_INFO *MenuList; + BOOLEAN RetVal; + + // + // If EDKII_FORM_DISPLAY_ENGINE_PROTOCOL not found, return EFI_UNSUPPORTED. + // + if (mFormDisplay == NULL) { + DEBUG ((DEBUG_ERROR, "Fatal Error! EDKII_FORM_DISPLAY_ENGINE_PROTOCOL not found!")); + return EFI_UNSUPPORTED; + } + + // + // Save globals used by SendForm() + // + SaveBrowserContext (); + + gFlagReconnect = FALSE; + gResetRequiredFormLevel = FALSE; + gExitRequired = FALSE; + gCallbackReconnect = FALSE; + Status = EFI_SUCCESS; + gEmptyString = L""; + gDisplayFormData.ScreenDimensions = (EFI_SCREEN_DESCRIPTOR *) ScreenDimensions; + + for (Index = 0; Index < HandleCount; Index++) { + Selection = AllocateZeroPool (sizeof (UI_MENU_SELECTION)); + ASSERT (Selection != NULL); + + Selection->Handle = Handles[Index]; + if (FormSetGuid != NULL) { + CopyMem (&Selection->FormSetGuid, FormSetGuid, sizeof (EFI_GUID)); + Selection->FormId = FormId; + } else { + CopyMem (&Selection->FormSetGuid, &gEfiHiiPlatformSetupFormsetGuid, sizeof (EFI_GUID)); + } + + do { + FormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); + ASSERT (FormSet != NULL); + + // + // Validate the HiiHandle + // if validate failed, find the first validate parent HiiHandle. + // + if (!ValidateHiiHandle(Selection->Handle)) { + FindNextMenu (Selection, FormSetLevel); + } + + // + // Initialize internal data structures of FormSet + // + Status = InitializeFormSet (Selection->Handle, &Selection->FormSetGuid, FormSet); + if (EFI_ERROR (Status) || IsListEmpty (&FormSet->FormListHead)) { + DestroyFormSet (FormSet); + break; + } + Selection->FormSet = FormSet; + mSystemLevelFormSet = FormSet; + mDynamicFormUpdated = FALSE; + + // + // Display this formset + // + gCurrentSelection = Selection; + + Status = SetupBrowser (Selection); + + gCurrentSelection = NULL; + mSystemLevelFormSet = NULL; + + // + // If callback update form dynamically, it's not exiting of the formset for user so system do not reconnect driver hanlde + // this time. + // + if (!mDynamicFormUpdated && (gFlagReconnect || gCallbackReconnect)) { + RetVal = ReconnectController (FormSet->DriverHandle); + if (!RetVal) { + PopupErrorMessage(BROWSER_RECONNECT_FAIL, NULL, NULL, NULL); + } + gFlagReconnect = FALSE; + gCallbackReconnect = FALSE; + } + + // + // If no data is changed, don't need to save current FormSet into the maintain list. + // + if (!IsNvUpdateRequiredForFormSet (FormSet)) { + CleanBrowserStorage(FormSet); + RemoveEntryList (&FormSet->Link); + DestroyFormSet (FormSet); + } + + if (EFI_ERROR (Status)) { + break; + } + } while (Selection->Action == UI_ACTION_REFRESH_FORMSET); + + FreePool (Selection); + } + + if (ActionRequest != NULL) { + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; + if (gResetRequiredFormLevel) { + *ActionRequest = EFI_BROWSER_ACTION_REQUEST_RESET; + } + } + + mFormDisplay->ExitDisplay(); + + // + // Clear the menu history data. + // + while (!IsListEmpty (&mPrivateData.FormBrowserEx2.FormViewHistoryHead)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (mPrivateData.FormBrowserEx2.FormViewHistoryHead.ForwardLink); + RemoveEntryList (&MenuList->Link); + FreePool (MenuList); + } + + // + // Restore globals used by SendForm() + // + RestoreBrowserContext (); + + return Status; +} + +/** + Get or set data to the storage. + + @param ResultsDataSize The size of the buffer associatedwith ResultsData. + @param ResultsData A string returned from an IFR browser or + equivalent. The results string will have no + routing information in them. + @param RetrieveData A BOOLEAN field which allows an agent to retrieve + (if RetrieveData = TRUE) data from the uncommitted + browser state information or set (if RetrieveData + = FALSE) data in the uncommitted browser state + information. + @param Storage The pointer to the storage. + + @retval EFI_SUCCESS The results have been distributed or are awaiting + distribution. + +**/ +EFI_STATUS +ProcessStorage ( + IN OUT UINTN *ResultsDataSize, + IN OUT EFI_STRING *ResultsData, + IN BOOLEAN RetrieveData, + IN BROWSER_STORAGE *Storage + ) +{ + CHAR16 *ConfigResp; + EFI_STATUS Status; + CHAR16 *StrPtr; + UINTN BufferSize; + UINTN TmpSize; + UINTN MaxLen; + FORMSET_STORAGE *BrowserStorage; + + if (RetrieveData) { + // + // Generate + // + Status = StorageToConfigResp (Storage, &ConfigResp, Storage->ConfigRequest, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Skip and '&' to point to when first copy the configbody. + // Also need to consider add "\0" at first time. + // + StrPtr = StrStr (ConfigResp, L"PATH"); + ASSERT (StrPtr != NULL); + StrPtr = StrStr (StrPtr, L"&"); + StrPtr += 1; + BufferSize = StrSize (StrPtr); + + // + // Copy the data if the input buffer is bigger enough. + // + if (*ResultsDataSize >= BufferSize) { + StrCpyS (*ResultsData, *ResultsDataSize / sizeof (CHAR16), StrPtr); + } + + *ResultsDataSize = BufferSize; + FreePool (ConfigResp); + } else { + // + // Prepare + // + BrowserStorage = GetFstStgFromBrsStg (Storage); + ASSERT (BrowserStorage != NULL); + TmpSize = StrLen (*ResultsData); + BufferSize = (TmpSize + StrLen (BrowserStorage->ConfigHdr) + 2) * sizeof (CHAR16); + MaxLen = BufferSize / sizeof (CHAR16); + ConfigResp = AllocateZeroPool (BufferSize); + ASSERT (ConfigResp != NULL); + + StrCpyS (ConfigResp, MaxLen, BrowserStorage->ConfigHdr); + StrCatS (ConfigResp, MaxLen, L"&"); + StrCatS (ConfigResp, MaxLen, *ResultsData); + + // + // Update Browser uncommited data + // + Status = ConfigRespToStorage (Storage, ConfigResp); + FreePool (ConfigResp); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} + +/** + This routine called this service in the browser to retrieve or set certain uncommitted + state information that resides in the open formsets. + + @param This A pointer to the EFI_FORM_BROWSER2_PROTOCOL + instance. + @param ResultsDataSize A pointer to the size of the buffer associated + with ResultsData. + @param ResultsData A string returned from an IFR browser or + equivalent. The results string will have no + routing information in them. + @param RetrieveData A BOOLEAN field which allows an agent to retrieve + (if RetrieveData = TRUE) data from the uncommitted + browser state information or set (if RetrieveData + = FALSE) data in the uncommitted browser state + information. + @param VariableGuid An optional field to indicate the target variable + GUID name to use. + @param VariableName An optional field to indicate the target + human-readable variable name. + + @retval EFI_SUCCESS The results have been distributed or are awaiting + distribution. + @retval EFI_BUFFER_TOO_SMALL The ResultsDataSize specified was too small to + contain the results data. + +**/ +EFI_STATUS +EFIAPI +BrowserCallback ( + IN CONST EFI_FORM_BROWSER2_PROTOCOL *This, + IN OUT UINTN *ResultsDataSize, + IN OUT EFI_STRING ResultsData, + IN BOOLEAN RetrieveData, + IN CONST EFI_GUID *VariableGuid, OPTIONAL + IN CONST CHAR16 *VariableName OPTIONAL + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + BROWSER_STORAGE *Storage; + FORMSET_STORAGE *FormsetStorage; + UINTN TotalSize; + BOOLEAN Found; + + if (ResultsDataSize == NULL || ResultsData == NULL) { + return EFI_INVALID_PARAMETER; + } + + TotalSize = *ResultsDataSize; + Storage = NULL; + Found = FALSE; + Status = EFI_SUCCESS; + + if (VariableGuid != NULL) { + // + // Try to find target storage in the current formset. + // + Link = GetFirstNode (&gBrowserStorageList); + while (!IsNull (&gBrowserStorageList, Link)) { + Storage = BROWSER_STORAGE_FROM_LINK (Link); + Link = GetNextNode (&gBrowserStorageList, Link); + // + // Check the current storage. + // + if (!CompareGuid (&Storage->Guid, (EFI_GUID *) VariableGuid)) { + continue; + } + + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { + // + // Buffer storage require both GUID and Name + // + if (VariableName == NULL) { + return EFI_NOT_FOUND; + } + + if (StrCmp (Storage->Name, (CHAR16 *) VariableName) != 0) { + continue; + } + } + + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE || + Storage->Type == EFI_HII_VARSTORE_BUFFER) { + if (mSystemLevelFormSet == NULL || mSystemLevelFormSet->HiiHandle == NULL) { + return EFI_NOT_FOUND; + } + + if (Storage->HiiHandle != mSystemLevelFormSet->HiiHandle) { + continue; + } + } + + Status = ProcessStorage (&TotalSize, &ResultsData, RetrieveData, Storage); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { + ConfigRequestAdjust (Storage, ResultsData, TRUE); + } + + // + // Different formsets may have same varstore, so here just set the flag + // not exit the circle. + // + Found = TRUE; + break; + } + + if (!Found) { + return EFI_NOT_FOUND; + } + } else { + // + // GUID/Name is not specified, take the first storage in FormSet + // + if (mSystemLevelFormSet == NULL) { + return EFI_NOT_READY; + } + + // + // Generate + // + Link = GetFirstNode (&mSystemLevelFormSet->StorageListHead); + if (IsNull (&mSystemLevelFormSet->StorageListHead, Link)) { + return EFI_UNSUPPORTED; + } + + FormsetStorage = FORMSET_STORAGE_FROM_LINK (Link); + + Status = ProcessStorage (&TotalSize, &ResultsData, RetrieveData, FormsetStorage->BrowserStorage); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (RetrieveData) { + Status = TotalSize <= *ResultsDataSize ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL; + *ResultsDataSize = TotalSize; + } + + return Status; + +} + + +/** + Callback function for SimpleTextInEx protocol install events + + @param Event the event that is signaled. + @param Context not used here. + +**/ +VOID +EFIAPI +FormDisplayCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + if (mFormDisplay != NULL) { + return; + } + + gBS->LocateProtocol ( + &gEdkiiFormDisplayEngineProtocolGuid, + NULL, + (VOID **) &mFormDisplay + ); +} + +/** + Initialize Setup Browser driver. + + @param ImageHandle The image handle. + @param SystemTable The system table. + + @retval EFI_SUCCESS The Setup Browser module is initialized correctly.. + @return Other value if failed to initialize the Setup Browser module. + +**/ +EFI_STATUS +EFIAPI +InitializeSetup ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + VOID *Registration; + + // + // Locate required Hii relative protocols + // + Status = gBS->LocateProtocol ( + &gEfiHiiDatabaseProtocolGuid, + NULL, + (VOID **) &mHiiDatabase + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->LocateProtocol ( + &gEfiHiiConfigRoutingProtocolGuid, + NULL, + (VOID **) &mHiiConfigRouting + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->LocateProtocol ( + &gEfiDevicePathFromTextProtocolGuid, + NULL, + (VOID **) &mPathFromText + ); + + // + // Install FormBrowser2 protocol + // + mPrivateData.Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &mPrivateData.Handle, + &gEfiFormBrowser2ProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivateData.FormBrowser2 + ); + ASSERT_EFI_ERROR (Status); + + // + // Install FormBrowserEx2 protocol + // + InitializeListHead (&mPrivateData.FormBrowserEx2.FormViewHistoryHead); + InitializeListHead (&mPrivateData.FormBrowserEx2.OverrideQestListHead); + mPrivateData.Handle = NULL; + Status = gBS->InstallProtocolInterface ( + &mPrivateData.Handle, + &gEdkiiFormBrowserEx2ProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivateData.FormBrowserEx2 + ); + ASSERT_EFI_ERROR (Status); + + Status = gBS->InstallProtocolInterface ( + &mPrivateData.Handle, + &gEdkiiFormBrowserExProtocolGuid, + EFI_NATIVE_INTERFACE, + &mPrivateData.FormBrowserEx + ); + ASSERT_EFI_ERROR (Status); + + InitializeDisplayFormData (); + + Status = gBS->LocateProtocol ( + &gEdkiiFormDisplayEngineProtocolGuid, + NULL, + (VOID **) &mFormDisplay + ); + + if (EFI_ERROR (Status)) { + EfiCreateProtocolNotifyEvent ( + &gEdkiiFormDisplayEngineProtocolGuid, + TPL_CALLBACK, + FormDisplayCallback, + NULL, + &Registration + ); + } + + return EFI_SUCCESS; +} + + +/** + Create a new string in HII Package List. + + @param String The String to be added + @param HiiHandle The package list in the HII database to insert the + specified string. + + @return The output string. + +**/ +EFI_STRING_ID +NewString ( + IN CHAR16 *String, + IN EFI_HII_HANDLE HiiHandle + ) +{ + EFI_STRING_ID StringId; + + StringId = HiiSetString (HiiHandle, 0, String, NULL); + ASSERT (StringId != 0); + + return StringId; +} + + +/** + Delete a string from HII Package List. + + @param StringId Id of the string in HII database. + @param HiiHandle The HII package list handle. + + @retval EFI_SUCCESS The string was deleted successfully. + +**/ +EFI_STATUS +DeleteString ( + IN EFI_STRING_ID StringId, + IN EFI_HII_HANDLE HiiHandle + ) +{ + CHAR16 NullChar; + + NullChar = CHAR_NULL; + HiiSetString (HiiHandle, StringId, &NullChar, NULL); + return EFI_SUCCESS; +} + + +/** + Get the string based on the StringId and HII Package List Handle. + + @param Token The String's ID. + @param HiiHandle The package list in the HII database to search for + the specified string. + + @return The output string. + +**/ +CHAR16 * +GetToken ( + IN EFI_STRING_ID Token, + IN EFI_HII_HANDLE HiiHandle + ) +{ + EFI_STRING String; + + if (HiiHandle == NULL) { + return NULL; + } + + String = HiiGetString (HiiHandle, Token, NULL); + if (String == NULL) { + String = AllocateCopyPool (StrSize (mUnknownString), mUnknownString); + ASSERT (String != NULL); + } + return (CHAR16 *) String; +} + + +/** + Allocate new memory and then copy the Unicode string Source to Destination. + + @param Dest Location to copy string + @param Src String to copy + +**/ +VOID +NewStringCpy ( + IN OUT CHAR16 **Dest, + IN CHAR16 *Src + ) +{ + if (*Dest != NULL) { + FreePool (*Dest); + } + *Dest = AllocateCopyPool (StrSize (Src), Src); + ASSERT (*Dest != NULL); +} + + +/** + Allocate new memory and concatinate Source on the end of Destination. + + @param Dest String to added to the end of. + @param Src String to concatinate. + +**/ +VOID +NewStringCat ( + IN OUT CHAR16 **Dest, + IN CHAR16 *Src + ) +{ + CHAR16 *NewString; + UINTN MaxLen; + + if (*Dest == NULL) { + NewStringCpy (Dest, Src); + return; + } + + MaxLen = ( StrSize (*Dest) + StrSize (Src) - 1) / sizeof (CHAR16); + NewString = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + ASSERT (NewString != NULL); + + StrCpyS (NewString, MaxLen, *Dest); + StrCatS (NewString, MaxLen, Src); + + FreePool (*Dest); + *Dest = NewString; +} + +/** + Get Value for given Name from a NameValue Storage. + + @param Storage The NameValue Storage. + @param Name The Name. + @param Value The retured Value. + @param GetValueFrom Where to get source value, from EditValue or Value. + + @retval EFI_SUCCESS Value found for given Name. + @retval EFI_NOT_FOUND No such Name found in NameValue storage. + +**/ +EFI_STATUS +GetValueByName ( + IN BROWSER_STORAGE *Storage, + IN CHAR16 *Name, + IN OUT CHAR16 **Value, + IN GET_SET_QUESTION_VALUE_WITH GetValueFrom + ) +{ + LIST_ENTRY *Link; + NAME_VALUE_NODE *Node; + + if (GetValueFrom != GetSetValueWithEditBuffer && GetValueFrom != GetSetValueWithBuffer) { + return EFI_INVALID_PARAMETER; + } + + *Value = NULL; + + Link = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + + if (StrCmp (Name, Node->Name) == 0) { + if (GetValueFrom == GetSetValueWithEditBuffer) { + NewStringCpy (Value, Node->EditValue); + } else { + NewStringCpy (Value, Node->Value); + } + return EFI_SUCCESS; + } + + Link = GetNextNode (&Storage->NameValueListHead, Link); + } + + return EFI_NOT_FOUND; +} + + +/** + Set Value of given Name in a NameValue Storage. + + @param Storage The NameValue Storage. + @param Name The Name. + @param Value The Value to set. + @param SetValueTo Whether update editValue or Value. + @param ReturnNode The node use the input name. + + @retval EFI_SUCCESS Value found for given Name. + @retval EFI_NOT_FOUND No such Name found in NameValue storage. + +**/ +EFI_STATUS +SetValueByName ( + IN BROWSER_STORAGE *Storage, + IN CHAR16 *Name, + IN CHAR16 *Value, + IN GET_SET_QUESTION_VALUE_WITH SetValueTo, + OUT NAME_VALUE_NODE **ReturnNode + ) +{ + LIST_ENTRY *Link; + NAME_VALUE_NODE *Node; + CHAR16 *Buffer; + + if (SetValueTo != GetSetValueWithEditBuffer && SetValueTo != GetSetValueWithBuffer) { + return EFI_INVALID_PARAMETER; + } + + Link = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + + if (StrCmp (Name, Node->Name) == 0) { + if (SetValueTo == GetSetValueWithEditBuffer) { + Buffer = Node->EditValue; + } else { + Buffer = Node->Value; + } + if (Buffer != NULL) { + FreePool (Buffer); + } + Buffer = AllocateCopyPool (StrSize (Value), Value); + ASSERT (Buffer != NULL); + if (SetValueTo == GetSetValueWithEditBuffer) { + Node->EditValue = Buffer; + } else { + Node->Value = Buffer; + } + + if (ReturnNode != NULL) { + *ReturnNode = Node; + } + + return EFI_SUCCESS; + } + + Link = GetNextNode (&Storage->NameValueListHead, Link); + } + + return EFI_NOT_FOUND; +} + + +/** + Convert setting of Buffer Storage or NameValue Storage to . + + @param Storage The Storage to be conveted. + @param ConfigResp The returned . + @param ConfigRequest The ConfigRequest string. + @param GetEditBuf Get the data from editbuffer or buffer. + + @retval EFI_SUCCESS Convert success. + @retval EFI_INVALID_PARAMETER Incorrect storage type. + +**/ +EFI_STATUS +StorageToConfigResp ( + IN BROWSER_STORAGE *Storage, + IN CHAR16 **ConfigResp, + IN CHAR16 *ConfigRequest, + IN BOOLEAN GetEditBuf + ) +{ + EFI_STATUS Status; + EFI_STRING Progress; + LIST_ENTRY *Link; + NAME_VALUE_NODE *Node; + UINT8 *SourceBuf; + FORMSET_STORAGE *FormsetStorage; + + Status = EFI_SUCCESS; + + switch (Storage->Type) { + case EFI_HII_VARSTORE_BUFFER: + case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: + SourceBuf = GetEditBuf ? Storage->EditBuffer : Storage->Buffer; + Status = mHiiConfigRouting->BlockToConfig ( + mHiiConfigRouting, + ConfigRequest, + SourceBuf, + Storage->Size, + ConfigResp, + &Progress + ); + break; + + case EFI_HII_VARSTORE_NAME_VALUE: + *ConfigResp = NULL; + FormsetStorage = GetFstStgFromBrsStg(Storage); + ASSERT (FormsetStorage != NULL); + NewStringCat (ConfigResp, FormsetStorage->ConfigHdr); + + Link = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + + if (StrStr (ConfigRequest, Node->Name) != NULL) { + NewStringCat (ConfigResp, L"&"); + NewStringCat (ConfigResp, Node->Name); + NewStringCat (ConfigResp, L"="); + if (GetEditBuf) { + NewStringCat (ConfigResp, Node->EditValue); + } else { + NewStringCat (ConfigResp, Node->Value); + } + } + Link = GetNextNode (&Storage->NameValueListHead, Link); + } + break; + + case EFI_HII_VARSTORE_EFI_VARIABLE: + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + return Status; +} + + +/** + Convert to settings in Buffer Storage or NameValue Storage. + + @param Storage The Storage to receive the settings. + @param ConfigResp The to be converted. + + @retval EFI_SUCCESS Convert success. + @retval EFI_INVALID_PARAMETER Incorrect storage type. + +**/ +EFI_STATUS +ConfigRespToStorage ( + IN BROWSER_STORAGE *Storage, + IN CHAR16 *ConfigResp + ) +{ + EFI_STATUS Status; + EFI_STRING Progress; + UINTN BufferSize; + CHAR16 *StrPtr; + CHAR16 *Name; + CHAR16 *Value; + + Status = EFI_SUCCESS; + + switch (Storage->Type) { + case EFI_HII_VARSTORE_BUFFER: + case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: + BufferSize = Storage->Size; + Status = mHiiConfigRouting->ConfigToBlock ( + mHiiConfigRouting, + ConfigResp, + Storage->EditBuffer, + &BufferSize, + &Progress + ); + break; + + case EFI_HII_VARSTORE_NAME_VALUE: + StrPtr = StrStr (ConfigResp, L"PATH"); + if (StrPtr == NULL) { + break; + } + StrPtr = StrStr (ConfigResp, L"&"); + while (StrPtr != NULL) { + // + // Skip '&' + // + StrPtr = StrPtr + 1; + Name = StrPtr; + StrPtr = StrStr (StrPtr, L"="); + if (StrPtr == NULL) { + break; + } + *StrPtr = 0; + + // + // Skip '=' + // + StrPtr = StrPtr + 1; + Value = StrPtr; + StrPtr = StrStr (StrPtr, L"&"); + if (StrPtr != NULL) { + *StrPtr = 0; + } + SetValueByName (Storage, Name, Value, GetSetValueWithEditBuffer, NULL); + } + break; + + case EFI_HII_VARSTORE_EFI_VARIABLE: + default: + Status = EFI_INVALID_PARAMETER; + break; + } + + return Status; +} + +/** + Get bit field value from the buffer and then set the value for the question. + Note: Data type UINT32 can cover all the bit field value. + + @param Question The question refer to bit field. + @param Buffer Point to the buffer which the question value get from. + +**/ +VOID +GetBitsQuestionValue ( + IN FORM_BROWSER_STATEMENT *Question, + IN UINT8 *Buffer + ) +{ + UINTN StartBit; + UINTN EndBit; + UINT32 RetVal; + UINT32 BufferValue; + + StartBit = Question->BitVarOffset % 8; + EndBit = StartBit + Question->BitStorageWidth - 1; + + CopyMem ((UINT8 *) &BufferValue, Buffer, Question->StorageWidth); + + RetVal = BitFieldRead32 (BufferValue, StartBit, EndBit); + + // + // Set question value. + // Note: Since Question with BufferValue (orderedlist, password, string)are not supported to refer bit field. + // Only oneof/checkbox/oneof can support bit field.So we can copy the value to the Hiivalue of Question directly. + // + CopyMem ((UINT8 *) &Question->HiiValue.Value, (UINT8 *) &RetVal, Question->StorageWidth); +} + +/** + Set bit field value to the buffer. + Note: Data type UINT32 can cover all the bit field value. + + @param Question The question refer to bit field. + @param Buffer Point to the buffer which the question value set to. + @param Value The bit field value need to set. + +**/ +VOID +SetBitsQuestionValue ( + IN FORM_BROWSER_STATEMENT *Question, + IN OUT UINT8 *Buffer, + IN UINT32 Value + ) +{ + UINT32 Operand; + UINTN StartBit; + UINTN EndBit; + UINT32 RetVal; + + StartBit = Question->BitVarOffset % 8; + EndBit = StartBit + Question->BitStorageWidth - 1; + + CopyMem ((UINT8*) &Operand, Buffer, Question->StorageWidth); + + RetVal = BitFieldWrite32 (Operand, StartBit, EndBit, Value); + + CopyMem (Buffer, (UINT8*) &RetVal, Question->StorageWidth); +} + +/** + Convert the buffer value to HiiValue. + + @param Question The question. + @param Value Unicode buffer save the question value. + + @retval Status whether convert the value success. + +**/ +EFI_STATUS +BufferToValue ( + IN OUT FORM_BROWSER_STATEMENT *Question, + IN CHAR16 *Value + ) +{ + CHAR16 *StringPtr; + BOOLEAN IsBufferStorage; + CHAR16 *DstBuf; + CHAR16 TempChar; + UINTN LengthStr; + UINT8 *Dst; + CHAR16 TemStr[5]; + UINTN Index; + UINT8 DigitUint8; + BOOLEAN IsString; + UINTN Length; + EFI_STATUS Status; + UINT8 *Buffer; + + Buffer = NULL; + + IsString = (BOOLEAN) ((Question->HiiValue.Type == EFI_IFR_TYPE_STRING) ? TRUE : FALSE); + if (Question->Storage->Type == EFI_HII_VARSTORE_BUFFER || + Question->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { + IsBufferStorage = TRUE; + } else { + IsBufferStorage = FALSE; + } + + // + // Question Value is provided by Buffer Storage or NameValue Storage + // + if (Question->BufferValue != NULL) { + // + // This Question is password or orderedlist + // + Dst = Question->BufferValue; + } else { + // + // Other type of Questions + // + if (Question->QuestionReferToBitField) { + Buffer = (UINT8 *)AllocateZeroPool (Question->StorageWidth); + if (Buffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + Dst = Buffer; + } else { + Dst = (UINT8 *) &Question->HiiValue.Value; + } + } + + // + // Temp cut at the end of this section, end with '\0' or '&'. + // + StringPtr = Value; + while (*StringPtr != L'\0' && *StringPtr != L'&') { + StringPtr++; + } + TempChar = *StringPtr; + *StringPtr = L'\0'; + + LengthStr = StrLen (Value); + + // + // Value points to a Unicode hexadecimal string, we need to convert the string to the value with CHAR16/UINT8...type. + // When generating the Value string, we follow this rule: 1 byte -> 2 Unicode characters (for string: 2 byte(CHAR16) ->4 Unicode characters). + // So the maximum value string length of a question is : Question->StorageWidth * 2. + // If the value string length > Question->StorageWidth * 2, only set the string length as Question->StorageWidth * 2, then convert. + // + if (LengthStr > (UINTN) Question->StorageWidth * 2) { + Length = (UINTN) Question->StorageWidth * 2; + } else { + Length = LengthStr; + } + + Status = EFI_SUCCESS; + if (!IsBufferStorage && IsString) { + // + // Convert Config String to Unicode String, e.g "0041004200430044" => "ABCD" + // Add string tail char L'\0' into Length + // + DstBuf = (CHAR16 *) Dst; + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0; Index < Length; Index += 4) { + StrnCpyS (TemStr, sizeof (TemStr) / sizeof (CHAR16), Value + Index, 4); + DstBuf[Index/4] = (CHAR16) StrHexToUint64 (TemStr); + } + // + // Add tailing L'\0' character + // + DstBuf[Index/4] = L'\0'; + } else { + ZeroMem (TemStr, sizeof (TemStr)); + for (Index = 0; Index < Length; Index ++) { + TemStr[0] = Value[LengthStr - Index - 1]; + DigitUint8 = (UINT8) StrHexToUint64 (TemStr); + if ((Index & 1) == 0) { + Dst [Index/2] = DigitUint8; + } else { + Dst [Index/2] = (UINT8) ((DigitUint8 << 4) + Dst [Index/2]); + } + } + } + + *StringPtr = TempChar; + + if (Buffer != NULL && Question->QuestionReferToBitField) { + GetBitsQuestionValue (Question, Buffer); + FreePool (Buffer); + } + + return Status; +} + +/** + Get Question's current Value. + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param Question Question to be initialized. + @param GetValueFrom Where to get value, may from editbuffer, buffer or hii driver. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +GetQuestionValue ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_BROWSER_STATEMENT *Question, + IN GET_SET_QUESTION_VALUE_WITH GetValueFrom + ) +{ + EFI_STATUS Status; + BOOLEAN Enabled; + BOOLEAN Pending; + UINT8 *Dst; + UINTN StorageWidth; + EFI_TIME EfiTime; + BROWSER_STORAGE *Storage; + FORMSET_STORAGE *FormsetStorage; + EFI_IFR_TYPE_VALUE *QuestionValue; + CHAR16 *ConfigRequest; + CHAR16 *Progress; + CHAR16 *Result; + CHAR16 *Value; + UINTN Length; + BOOLEAN IsBufferStorage; + UINTN MaxLen; + + Status = EFI_SUCCESS; + Value = NULL; + Result = NULL; + + if (GetValueFrom >= GetSetValueWithMax) { + return EFI_INVALID_PARAMETER; + } + + // + // Question value is provided by an Expression, evaluate it + // + if (Question->ValueExpression != NULL) { + Status = EvaluateExpression (FormSet, Form, Question->ValueExpression); + if (!EFI_ERROR (Status)) { + if (Question->ValueExpression->Result.Type == EFI_IFR_TYPE_BUFFER) { + ASSERT (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER && Question->HiiValue.Buffer != NULL); + if (Question->StorageWidth > Question->ValueExpression->Result.BufferLen) { + CopyMem (Question->HiiValue.Buffer, Question->ValueExpression->Result.Buffer, Question->ValueExpression->Result.BufferLen); + Question->HiiValue.BufferLen = Question->ValueExpression->Result.BufferLen; + } else { + CopyMem (Question->HiiValue.Buffer, Question->ValueExpression->Result.Buffer, Question->StorageWidth); + Question->HiiValue.BufferLen = Question->StorageWidth; + } + FreePool (Question->ValueExpression->Result.Buffer); + } + Question->HiiValue.Type = Question->ValueExpression->Result.Type; + CopyMem (&Question->HiiValue.Value, &Question->ValueExpression->Result.Value, sizeof (EFI_IFR_TYPE_VALUE)); + } + return Status; + } + + // + // Get question value by read expression. + // + if (Question->ReadExpression != NULL && Form->FormType == STANDARD_MAP_FORM_TYPE) { + Status = EvaluateExpression (FormSet, Form, Question->ReadExpression); + if (!EFI_ERROR (Status) && + ((Question->ReadExpression->Result.Type < EFI_IFR_TYPE_OTHER) || (Question->ReadExpression->Result.Type == EFI_IFR_TYPE_BUFFER))) { + // + // Only update question value to the valid result. + // + if (Question->ReadExpression->Result.Type == EFI_IFR_TYPE_BUFFER) { + ASSERT (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER && Question->HiiValue.Buffer != NULL); + if (Question->StorageWidth > Question->ReadExpression->Result.BufferLen) { + CopyMem (Question->HiiValue.Buffer, Question->ReadExpression->Result.Buffer, Question->ReadExpression->Result.BufferLen); + Question->HiiValue.BufferLen = Question->ReadExpression->Result.BufferLen; + } else { + CopyMem (Question->HiiValue.Buffer, Question->ReadExpression->Result.Buffer, Question->StorageWidth); + Question->HiiValue.BufferLen = Question->StorageWidth; + } + FreePool (Question->ReadExpression->Result.Buffer); + } + Question->HiiValue.Type = Question->ReadExpression->Result.Type; + CopyMem (&Question->HiiValue.Value, &Question->ReadExpression->Result.Value, sizeof (EFI_IFR_TYPE_VALUE)); + return EFI_SUCCESS; + } + } + + // + // Question value is provided by RTC + // + Storage = Question->Storage; + QuestionValue = &Question->HiiValue.Value; + if (Storage == NULL) { + // + // It's a Question without storage, or RTC date/time + // + if (Question->Operand == EFI_IFR_DATE_OP || Question->Operand == EFI_IFR_TIME_OP) { + // + // Date and time define the same Flags bit + // + switch (Question->Flags & EFI_QF_DATE_STORAGE) { + case QF_DATE_STORAGE_TIME: + Status = gRT->GetTime (&EfiTime, NULL); + break; + + case QF_DATE_STORAGE_WAKEUP: + Status = gRT->GetWakeupTime (&Enabled, &Pending, &EfiTime); + break; + + case QF_DATE_STORAGE_NORMAL: + default: + // + // For date/time without storage + // + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + if (Question->Operand == EFI_IFR_DATE_OP){ + QuestionValue->date.Year = 0xff; + QuestionValue->date.Month = 0xff; + QuestionValue->date.Day = 0xff; + } else { + QuestionValue->time.Hour = 0xff; + QuestionValue->time.Minute = 0xff; + QuestionValue->time.Second = 0xff; + } + return EFI_SUCCESS; + } + + if (Question->Operand == EFI_IFR_DATE_OP) { + QuestionValue->date.Year = EfiTime.Year; + QuestionValue->date.Month = EfiTime.Month; + QuestionValue->date.Day = EfiTime.Day; + } else { + QuestionValue->time.Hour = EfiTime.Hour; + QuestionValue->time.Minute = EfiTime.Minute; + QuestionValue->time.Second = EfiTime.Second; + } + } + + return EFI_SUCCESS; + } + + // + // Question value is provided by EFI variable + // + StorageWidth = Question->StorageWidth; + if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + if (Question->BufferValue != NULL) { + Dst = Question->BufferValue; + } else { + Dst = (UINT8 *) QuestionValue; + } + + Status = gRT->GetVariable ( + Question->VariableName, + &Storage->Guid, + NULL, + &StorageWidth, + Dst + ); + // + // Always return success, even this EFI variable doesn't exist + // + return EFI_SUCCESS; + } + + // + // Question Value is provided by Buffer Storage or NameValue Storage + // + if (Question->BufferValue != NULL) { + // + // This Question is password or orderedlist + // + Dst = Question->BufferValue; + } else { + // + // Other type of Questions + // + Dst = (UINT8 *) &Question->HiiValue.Value; + } + + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { + IsBufferStorage = TRUE; + } else { + IsBufferStorage = FALSE; + } + if (GetValueFrom == GetSetValueWithEditBuffer || GetValueFrom == GetSetValueWithBuffer ) { + if (IsBufferStorage) { + if (GetValueFrom == GetSetValueWithEditBuffer) { + // + // Copy from storage Edit buffer + // If the Question refer to bit filed, get the value in the related bit filed. + // + if (Question->QuestionReferToBitField) { + GetBitsQuestionValue (Question, Storage->EditBuffer + Question->VarStoreInfo.VarOffset); + } else { + CopyMem (Dst, Storage->EditBuffer + Question->VarStoreInfo.VarOffset, StorageWidth); + } + } else { + // + // Copy from storage Edit buffer + // If the Question refer to bit filed, get the value in the related bit filed. + // + if (Question->QuestionReferToBitField) { + GetBitsQuestionValue (Question, Storage->Buffer + Question->VarStoreInfo.VarOffset); + } else { + CopyMem (Dst, Storage->Buffer + Question->VarStoreInfo.VarOffset, StorageWidth); + } + } + } else { + Value = NULL; + Status = GetValueByName (Storage, Question->VariableName, &Value, GetValueFrom); + if (EFI_ERROR (Status)) { + return Status; + } + + ASSERT (Value != NULL); + Status = BufferToValue (Question, Value); + FreePool (Value); + } + } else { + FormsetStorage = GetFstStgFromVarId(FormSet, Question->VarStoreId); + ASSERT (FormsetStorage != NULL); + // + // ::= + || + // + "&" + + // + if (IsBufferStorage) { + Length = StrLen (FormsetStorage->ConfigHdr); + Length += StrLen (Question->BlockName); + } else { + Length = StrLen (FormsetStorage->ConfigHdr); + Length += StrLen (Question->VariableName) + 1; + } + // Allocate buffer include '\0' + MaxLen = Length + 1; + ConfigRequest = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + ASSERT (ConfigRequest != NULL); + + StrCpyS (ConfigRequest, MaxLen, FormsetStorage->ConfigHdr); + if (IsBufferStorage) { + StrCatS (ConfigRequest, MaxLen, Question->BlockName); + } else { + StrCatS (ConfigRequest, MaxLen, L"&"); + StrCatS (ConfigRequest, MaxLen, Question->VariableName); + } + + // + // Request current settings from Configuration Driver + // + Status = mHiiConfigRouting->ExtractConfig ( + mHiiConfigRouting, + ConfigRequest, + &Progress, + &Result + ); + FreePool (ConfigRequest); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Skip + // + if (IsBufferStorage) { + Value = StrStr (Result, L"&VALUE"); + if (Value == NULL) { + FreePool (Result); + return EFI_NOT_FOUND; + } + // + // Skip "&VALUE" + // + Value = Value + 6; + } else { + Value = Result + Length; + } + if (*Value != '=') { + FreePool (Result); + return EFI_NOT_FOUND; + } + // + // Skip '=', point to value + // + Value = Value + 1; + + Status = BufferToValue (Question, Value); + if (EFI_ERROR (Status)) { + FreePool (Result); + return Status; + } + + // + // Synchronize Edit Buffer + // + if (IsBufferStorage) { + CopyMem (Storage->EditBuffer + Question->VarStoreInfo.VarOffset, Dst, StorageWidth); + } else { + SetValueByName (Storage, Question->VariableName, Value, GetSetValueWithEditBuffer, NULL); + } + + if (Result != NULL) { + FreePool (Result); + } + } + + return Status; +} + + +/** + Save Question Value to edit copy(cached) or Storage(uncached). + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param Question Pointer to the Question. + @param SetValueTo Update the question value to editbuffer , buffer or hii driver. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +SetQuestionValue ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_BROWSER_STATEMENT *Question, + IN GET_SET_QUESTION_VALUE_WITH SetValueTo + ) +{ + EFI_STATUS Status; + BOOLEAN Enabled; + BOOLEAN Pending; + UINT8 *Src; + EFI_TIME EfiTime; + UINTN BufferLen; + UINTN StorageWidth; + BROWSER_STORAGE *Storage; + FORMSET_STORAGE *FormsetStorage; + EFI_IFR_TYPE_VALUE *QuestionValue; + CHAR16 *ConfigResp; + CHAR16 *Progress; + CHAR16 *Value; + UINTN Length; + BOOLEAN IsBufferStorage; + BOOLEAN IsString; + UINT8 *TemBuffer; + CHAR16 *TemName; + CHAR16 *TemString; + UINTN Index; + NAME_VALUE_NODE *Node; + UINTN MaxLen; + + Status = EFI_SUCCESS; + Node = NULL; + + if (SetValueTo >= GetSetValueWithMax) { + return EFI_INVALID_PARAMETER; + } + + // + // If Question value is provided by an Expression, then it is read only + // + if (Question->ValueExpression != NULL) { + return Status; + } + + // + // Before set question value, evaluate its write expression. + // + if (Question->WriteExpression != NULL && Form->FormType == STANDARD_MAP_FORM_TYPE) { + Status = EvaluateExpression (FormSet, Form, Question->WriteExpression); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Question value is provided by RTC + // + Storage = Question->Storage; + QuestionValue = &Question->HiiValue.Value; + if (Storage == NULL) { + // + // It's a Question without storage, or RTC date/time + // + if (Question->Operand == EFI_IFR_DATE_OP || Question->Operand == EFI_IFR_TIME_OP) { + // + // Date and time define the same Flags bit + // + switch (Question->Flags & EFI_QF_DATE_STORAGE) { + case QF_DATE_STORAGE_TIME: + Status = gRT->GetTime (&EfiTime, NULL); + break; + + case QF_DATE_STORAGE_WAKEUP: + Status = gRT->GetWakeupTime (&Enabled, &Pending, &EfiTime); + break; + + case QF_DATE_STORAGE_NORMAL: + default: + // + // For date/time without storage + // + return EFI_SUCCESS; + } + + if (EFI_ERROR (Status)) { + return Status; + } + + if (Question->Operand == EFI_IFR_DATE_OP) { + EfiTime.Year = QuestionValue->date.Year; + EfiTime.Month = QuestionValue->date.Month; + EfiTime.Day = QuestionValue->date.Day; + } else { + EfiTime.Hour = QuestionValue->time.Hour; + EfiTime.Minute = QuestionValue->time.Minute; + EfiTime.Second = QuestionValue->time.Second; + } + + if ((Question->Flags & EFI_QF_DATE_STORAGE) == QF_DATE_STORAGE_TIME) { + Status = gRT->SetTime (&EfiTime); + } else { + Status = gRT->SetWakeupTime (TRUE, &EfiTime); + } + } + + return Status; + } + + // + // Question value is provided by EFI variable + // + StorageWidth = Question->StorageWidth; + if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + if (Question->BufferValue != NULL) { + Src = Question->BufferValue; + } else { + Src = (UINT8 *) QuestionValue; + } + + Status = gRT->SetVariable ( + Question->VariableName, + &Storage->Guid, + Storage->Attributes, + StorageWidth, + Src + ); + return Status; + } + + // + // Question Value is provided by Buffer Storage or NameValue Storage + // + if (Question->BufferValue != NULL) { + Src = Question->BufferValue; + } else { + Src = (UINT8 *) &Question->HiiValue.Value; + } + + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { + IsBufferStorage = TRUE; + } else { + IsBufferStorage = FALSE; + } + IsString = (BOOLEAN) ((Question->HiiValue.Type == EFI_IFR_TYPE_STRING) ? TRUE : FALSE); + + if (SetValueTo == GetSetValueWithEditBuffer || SetValueTo == GetSetValueWithBuffer) { + if (IsBufferStorage) { + if (SetValueTo == GetSetValueWithEditBuffer) { + // + // Copy to storage edit buffer + // If the Question refer to bit filed, copy the value in related bit filed to storage edit buffer. + // + if (Question->QuestionReferToBitField) { + SetBitsQuestionValue (Question, Storage->EditBuffer + Question->VarStoreInfo.VarOffset, (UINT32)(*Src)); + } else { + CopyMem (Storage->EditBuffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); + } + } else if (SetValueTo == GetSetValueWithBuffer) { + // + // Copy to storage buffer + // If the Question refer to bit filed, copy the value in related bit filed to storage buffer. + // + if (Question->QuestionReferToBitField) { + SetBitsQuestionValue (Question, Storage->Buffer + Question->VarStoreInfo.VarOffset, (UINT32)(*Src)); + } else { + CopyMem (Storage->Buffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); + } + } + } else { + if (IsString) { + // + // Allocate enough string buffer. + // + Value = NULL; + BufferLen = ((StrLen ((CHAR16 *) Src) * 4) + 1) * sizeof (CHAR16); + Value = AllocateZeroPool (BufferLen); + ASSERT (Value != NULL); + // + // Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044" + // + TemName = (CHAR16 *) Src; + TemString = Value; + for (; *TemName != L'\0'; TemName++) { + UnicodeValueToStringS ( + TemString, + BufferLen - ((UINTN)TemString - (UINTN)Value), + PREFIX_ZERO | RADIX_HEX, + *TemName, + 4 + ); + TemString += StrnLenS (TemString, (BufferLen - ((UINTN)TemString - (UINTN)Value)) / sizeof (CHAR16)); + } + } else { + BufferLen = StorageWidth * 2 + 1; + Value = AllocateZeroPool (BufferLen * sizeof (CHAR16)); + ASSERT (Value != NULL); + // + // Convert Buffer to Hex String + // + TemBuffer = Src + StorageWidth - 1; + TemString = Value; + for (Index = 0; Index < StorageWidth; Index ++, TemBuffer --) { + UnicodeValueToStringS ( + TemString, + BufferLen * sizeof (CHAR16) - ((UINTN)TemString - (UINTN)Value), + PREFIX_ZERO | RADIX_HEX, + *TemBuffer, + 2 + ); + TemString += StrnLenS (TemString, BufferLen - ((UINTN)TemString - (UINTN)Value) / sizeof (CHAR16)); + } + } + + Status = SetValueByName (Storage, Question->VariableName, Value, SetValueTo, &Node); + FreePool (Value); + if (EFI_ERROR (Status)) { + return Status; + } + } + } else if (SetValueTo == GetSetValueWithHiiDriver) { + // + // ::= + + "&VALUE=" + "StorageWidth * 2" || + // + "&" + + "=" + "" + // + if (IsBufferStorage) { + Length = StrLen (Question->BlockName) + 7; + } else { + Length = StrLen (Question->VariableName) + 2; + } + if (!IsBufferStorage && IsString) { + Length += (StrLen ((CHAR16 *) Src) * 4); + } else { + Length += (StorageWidth * 2); + } + FormsetStorage = GetFstStgFromVarId(FormSet, Question->VarStoreId); + ASSERT (FormsetStorage != NULL); + MaxLen = StrLen (FormsetStorage->ConfigHdr) + Length + 1; + ConfigResp = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + ASSERT (ConfigResp != NULL); + + StrCpyS (ConfigResp, MaxLen, FormsetStorage->ConfigHdr); + if (IsBufferStorage) { + StrCatS (ConfigResp, MaxLen, Question->BlockName); + StrCatS (ConfigResp, MaxLen, L"&VALUE="); + } else { + StrCatS (ConfigResp, MaxLen, L"&"); + StrCatS (ConfigResp, MaxLen, Question->VariableName); + StrCatS (ConfigResp, MaxLen, L"="); + } + + Value = ConfigResp + StrLen (ConfigResp); + + if (!IsBufferStorage && IsString) { + // + // Convert Unicode String to Config String, e.g. "ABCD" => "0041004200430044" + // + TemName = (CHAR16 *) Src; + TemString = Value; + for (; *TemName != L'\0'; TemName++) { + UnicodeValueToStringS ( + TemString, + MaxLen * sizeof (CHAR16) - ((UINTN)TemString - (UINTN)ConfigResp), + PREFIX_ZERO | RADIX_HEX, + *TemName, + 4 + ); + TemString += StrnLenS (TemString, MaxLen - ((UINTN)TemString - (UINTN)ConfigResp) / sizeof (CHAR16)); + } + } else { + // + // Convert Buffer to Hex String + // + TemBuffer = Src + StorageWidth - 1; + TemString = Value; + for (Index = 0; Index < StorageWidth; Index ++, TemBuffer --) { + UnicodeValueToStringS ( + TemString, + MaxLen * sizeof (CHAR16) - ((UINTN)TemString - (UINTN)ConfigResp), + PREFIX_ZERO | RADIX_HEX, + *TemBuffer, + 2 + ); + TemString += StrnLenS (TemString, MaxLen - ((UINTN)TemString - (UINTN)ConfigResp) / sizeof (CHAR16)); + } + } + + // + // Convert to lower char. + // + for (TemString = Value; *Value != L'\0'; Value++) { + if (*Value >= L'A' && *Value <= L'Z') { + *Value = (CHAR16) (*Value - L'A' + L'a'); + } + } + + // + // Submit Question Value to Configuration Driver + // + Status = mHiiConfigRouting->RouteConfig ( + mHiiConfigRouting, + ConfigResp, + &Progress + ); + if (EFI_ERROR (Status)) { + FreePool (ConfigResp); + return Status; + } + FreePool (ConfigResp); + + // + // Sync storage, from editbuffer to buffer. + // + CopyMem (Storage->Buffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); + } + + return Status; +} + + +/** + Perform nosubmitif check for a Form. + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param Question The Question to be validated. + @param Type Validation type: NoSubmit + + @retval EFI_SUCCESS Form validation pass. + @retval other Form validation failed. + +**/ +EFI_STATUS +ValidateQuestion ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN FORM_BROWSER_STATEMENT *Question, + IN UINTN Type + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + LIST_ENTRY *ListHead; + FORM_EXPRESSION *Expression; + UINT32 BrowserStatus; + CHAR16 *ErrorStr; + + BrowserStatus = BROWSER_SUCCESS; + ErrorStr = NULL; + + switch (Type) { + case EFI_HII_EXPRESSION_INCONSISTENT_IF: + ListHead = &Question->InconsistentListHead; + break; + + case EFI_HII_EXPRESSION_WARNING_IF: + ListHead = &Question->WarningListHead; + break; + + case EFI_HII_EXPRESSION_NO_SUBMIT_IF: + ListHead = &Question->NoSubmitListHead; + break; + + default: + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Link = GetFirstNode (ListHead); + while (!IsNull (ListHead, Link)) { + Expression = FORM_EXPRESSION_FROM_LINK (Link); + + // + // Evaluate the expression + // + Status = EvaluateExpression (FormSet, Form, Expression); + if (EFI_ERROR (Status)) { + return Status; + } + + if (IsTrue (&Expression->Result)) { + switch (Type) { + case EFI_HII_EXPRESSION_INCONSISTENT_IF: + BrowserStatus = BROWSER_INCONSISTENT_IF; + break; + + case EFI_HII_EXPRESSION_WARNING_IF: + BrowserStatus = BROWSER_WARNING_IF; + break; + + case EFI_HII_EXPRESSION_NO_SUBMIT_IF: + BrowserStatus = BROWSER_NO_SUBMIT_IF; + // + // This code only used to compatible with old display engine, + // New display engine will not use this field. + // + if (Expression->Error != 0) { + ErrorStr = GetToken (Expression->Error, FormSet->HiiHandle); + } + break; + + default: + ASSERT (FALSE); + break; + } + + if (!((Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF) && mSystemSubmit)) { + // + // If in system submit process and for no_submit_if check, not popup this error message. + // Will process this fail again later in not system submit process. + // + PopupErrorMessage(BrowserStatus, FormSet->HiiHandle, Expression->OpCode, ErrorStr); + } + + if (ErrorStr != NULL) { + FreePool (ErrorStr); + } + + if (Type == EFI_HII_EXPRESSION_WARNING_IF) { + return EFI_SUCCESS; + } else { + return EFI_NOT_READY; + } + } + + Link = GetNextNode (ListHead, Link); + } + + return EFI_SUCCESS; +} + +/** + Perform question check. + + If one question has more than one check, process form high priority to low. + Only one error info will be popup. + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param Question The Question to be validated. + + @retval EFI_SUCCESS Form validation pass. + @retval other Form validation failed. + +**/ +EFI_STATUS +ValueChangedValidation ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN FORM_BROWSER_STATEMENT *Question + ) +{ + EFI_STATUS Status; + + Status = EFI_SUCCESS; + + // + // Do the inconsistentif check. + // + if (!IsListEmpty (&Question->InconsistentListHead)) { + Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Do the warningif check. + // + if (!IsListEmpty (&Question->WarningListHead)) { + Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_WARNING_IF); + } + + return Status; +} + +/** + Perform NoSubmit check for each Form in FormSet. + + @param FormSet FormSet data structure. + @param CurrentForm Current input form data structure. + @param Statement The statement for this check. + + @retval EFI_SUCCESS Form validation pass. + @retval other Form validation failed. + +**/ +EFI_STATUS +NoSubmitCheck ( + IN FORM_BROWSER_FORMSET *FormSet, + IN OUT FORM_BROWSER_FORM **CurrentForm, + OUT FORM_BROWSER_STATEMENT **Statement + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + FORM_BROWSER_FORM *Form; + LIST_ENTRY *LinkForm; + + LinkForm = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, LinkForm)) { + Form = FORM_BROWSER_FORM_FROM_LINK (LinkForm); + LinkForm = GetNextNode (&FormSet->FormListHead, LinkForm); + + if (*CurrentForm != NULL && *CurrentForm != Form) { + continue; + } + + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_NO_SUBMIT_IF); + if (EFI_ERROR (Status)) { + if (*CurrentForm == NULL) { + *CurrentForm = Form; + } + if (Statement != NULL) { + *Statement = Question; + } + return Status; + } + + Link = GetNextNode (&Form->StatementListHead, Link); + } + } + + return EFI_SUCCESS; +} + +/** + Fill storage's edit copy with settings requested from Configuration Driver. + + @param Storage The storage which need to sync. + @param ConfigRequest The config request string which used to sync storage. + @param SyncOrRestore Sync the buffer to editbuffer or Restore the + editbuffer to buffer + if TRUE, copy the editbuffer to the buffer. + if FALSE, copy the buffer to the editbuffer. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +SynchronizeStorage ( + OUT BROWSER_STORAGE *Storage, + IN CHAR16 *ConfigRequest, + IN BOOLEAN SyncOrRestore + ) +{ + EFI_STATUS Status; + EFI_STRING Progress; + EFI_STRING Result; + UINTN BufferSize; + LIST_ENTRY *Link; + NAME_VALUE_NODE *Node; + UINT8 *Src; + UINT8 *Dst; + + Status = EFI_SUCCESS; + Result = NULL; + + if (Storage->Type == EFI_HII_VARSTORE_BUFFER || + (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { + BufferSize = Storage->Size; + + if (SyncOrRestore) { + Src = Storage->EditBuffer; + Dst = Storage->Buffer; + } else { + Src = Storage->Buffer; + Dst = Storage->EditBuffer; + } + + if (ConfigRequest != NULL) { + Status = mHiiConfigRouting->BlockToConfig( + mHiiConfigRouting, + ConfigRequest, + Src, + BufferSize, + &Result, + &Progress + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = mHiiConfigRouting->ConfigToBlock ( + mHiiConfigRouting, + Result, + Dst, + &BufferSize, + &Progress + ); + if (Result != NULL) { + FreePool (Result); + } + } else { + CopyMem (Dst, Src, BufferSize); + } + } else if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + Link = GetFirstNode (&Storage->NameValueListHead); + while (!IsNull (&Storage->NameValueListHead, Link)) { + Node = NAME_VALUE_NODE_FROM_LINK (Link); + + if ((ConfigRequest != NULL && StrStr (ConfigRequest, Node->Name) != NULL) || + (ConfigRequest == NULL)) { + if (SyncOrRestore) { + NewStringCpy (&Node->Value, Node->EditValue); + } else { + NewStringCpy (&Node->EditValue, Node->Value); + } + } + + Link = GetNextNode (&Storage->NameValueListHead, Link); + } + } + + return Status; +} + +/** + When discard the question value, call the callback function with Changed type + to inform the hii driver. + + @param FormSet FormSet data structure. + @param Form Form data structure. + +**/ +VOID +SendDiscardInfoToDriver ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + EFI_IFR_TYPE_VALUE *TypeValue; + EFI_BROWSER_ACTION_REQUEST ActionRequest; + + if (FormSet->ConfigAccess == NULL) { + return; + } + + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&Form->StatementListHead, Link); + + if (Question->Storage == NULL || Question->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != EFI_IFR_FLAG_CALLBACK) { + continue; + } + + if (Question->Operand == EFI_IFR_PASSWORD_OP) { + continue; + } + + if (!Question->ValueChanged) { + continue; + } + + // + // Restore the question value before call the CHANGED callback type. + // + GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); + + if (Question->Operand == EFI_IFR_STRING_OP){ + HiiSetString (FormSet->HiiHandle, Question->HiiValue.Value.string, (CHAR16*)Question->BufferValue, NULL); + } + + if (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER) { + TypeValue = (EFI_IFR_TYPE_VALUE *) Question->BufferValue; + } else { + TypeValue = &Question->HiiValue.Value; + } + + ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; + FormSet->ConfigAccess->Callback ( + FormSet->ConfigAccess, + EFI_BROWSER_ACTION_CHANGED, + Question->QuestionId, + Question->HiiValue.Type, + TypeValue, + &ActionRequest + ); + } +} + +/** + When submit the question value, call the callback function with Submitted type + to inform the hii driver. + + @param FormSet FormSet data structure. + @param Form Form data structure. + +**/ +VOID +SubmitCallbackForForm ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + EFI_IFR_TYPE_VALUE *TypeValue; + EFI_BROWSER_ACTION_REQUEST ActionRequest; + + if (FormSet->ConfigAccess == NULL) { + return; + } + + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&Form->StatementListHead, Link); + + if (Question->Storage == NULL || Question->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != EFI_IFR_FLAG_CALLBACK) { + continue; + } + + if (Question->Operand == EFI_IFR_PASSWORD_OP) { + continue; + } + + if (Question->HiiValue.Type == EFI_IFR_TYPE_BUFFER) { + TypeValue = (EFI_IFR_TYPE_VALUE *) Question->BufferValue; + } else { + TypeValue = &Question->HiiValue.Value; + } + + ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; + FormSet->ConfigAccess->Callback ( + FormSet->ConfigAccess, + EFI_BROWSER_ACTION_SUBMITTED, + Question->QuestionId, + Question->HiiValue.Type, + TypeValue, + &ActionRequest + ); + } +} + +/** + When value set Success, call the submit callback function. + + @param FormSet FormSet data structure. + @param Form Form data structure. + +**/ +VOID +SubmitCallback ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + FORM_BROWSER_FORM *CurrentForm; + LIST_ENTRY *Link; + + if (Form != NULL) { + SubmitCallbackForForm(FormSet, Form); + return; + } + + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + CurrentForm = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&FormSet->FormListHead, Link); + + SubmitCallbackForForm(FormSet, CurrentForm); + } +} + +/** + Validate the HiiHandle. + + @param HiiHandle The input HiiHandle which need to validate. + + @retval TRUE The handle is validate. + @retval FALSE The handle is invalidate. + +**/ +BOOLEAN +ValidateHiiHandle ( + EFI_HII_HANDLE HiiHandle + ) +{ + EFI_HII_HANDLE *HiiHandles; + UINTN Index; + BOOLEAN Find; + + if (HiiHandle == NULL) { + return FALSE; + } + + Find = FALSE; + + HiiHandles = HiiGetHiiHandles (NULL); + ASSERT (HiiHandles != NULL); + + for (Index = 0; HiiHandles[Index] != NULL; Index++) { + if (HiiHandles[Index] == HiiHandle) { + Find = TRUE; + break; + } + } + + FreePool (HiiHandles); + + return Find; +} + +/** + Validate the FormSet. If the formset is not validate, remove it from the list. + + @param FormSet The input FormSet which need to validate. + + @retval TRUE The handle is validate. + @retval FALSE The handle is invalidate. + +**/ +BOOLEAN +ValidateFormSet ( + FORM_BROWSER_FORMSET *FormSet + ) +{ + BOOLEAN Find; + + ASSERT (FormSet != NULL); + + Find = ValidateHiiHandle(FormSet->HiiHandle); + // + // Should not remove the formset which is being used. + // + if (!Find && (FormSet != gCurrentSelection->FormSet)) { + CleanBrowserStorage(FormSet); + RemoveEntryList (&FormSet->Link); + DestroyFormSet (FormSet); + } + + return Find; +} +/** + Check whether need to enable the reset flag in form level. + Also clean all ValueChanged flag in question. + + @param SetFlag Whether need to set the Reset Flag. + @param FormSet FormSet data structure. + @param Form Form data structure. + +**/ +VOID +UpdateFlagForForm ( + IN BOOLEAN SetFlag, + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + BOOLEAN OldValue; + + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&Form->StatementListHead, Link); + + if (!Question->ValueChanged) { + continue; + } + + OldValue = Question->ValueChanged; + + // + // Compare the buffer and editbuffer data to see whether the data has been saved. + // + Question->ValueChanged = IsQuestionValueChanged(FormSet, Form, Question, GetSetValueWithBothBuffer); + + // + // Only the changed data has been saved, then need to set the reset flag. + // + if (SetFlag && OldValue && !Question->ValueChanged) { + if ((Question->QuestionFlags & EFI_IFR_FLAG_RESET_REQUIRED) != 0) { + gResetRequiredFormLevel = TRUE; + gResetRequiredSystemLevel = TRUE; + } + + if ((Question->QuestionFlags & EFI_IFR_FLAG_RECONNECT_REQUIRED) != 0) { + gFlagReconnect = TRUE; + } + } + } +} + +/** + Check whether need to enable the reset flag. + Also clean ValueChanged flag for all statements. + + Form level or formset level, only one. + + @param SetFlag Whether need to set the Reset Flag. + @param FormSet FormSet data structure. + @param Form Form data structure. + +**/ +VOID +ValueChangeResetFlagUpdate ( + IN BOOLEAN SetFlag, + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + FORM_BROWSER_FORM *CurrentForm; + LIST_ENTRY *Link; + + if (Form != NULL) { + UpdateFlagForForm(SetFlag, FormSet, Form); + return; + } + + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + CurrentForm = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&FormSet->FormListHead, Link); + + UpdateFlagForForm(SetFlag, FormSet, CurrentForm); + } +} + +/** + Base on the return Progress string to find the form. + + Base on the first return Offset/Width (Name) string to find the form + which keep this string. + + @param FormSet FormSet data structure. + @param Storage Storage which has this Progress string. + @param Progress The Progress string which has the first fail string. + @param RetForm The return form for this progress string. + @param RetQuestion The return question for the error progress string. + + @retval TRUE Find the error form and statement for this error progress string. + @retval FALSE Not find the error form. + +**/ +BOOLEAN +FindQuestionFromProgress ( + IN FORM_BROWSER_FORMSET *FormSet, + IN BROWSER_STORAGE *Storage, + IN EFI_STRING Progress, + OUT FORM_BROWSER_FORM **RetForm, + OUT FORM_BROWSER_STATEMENT **RetQuestion + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *LinkStorage; + LIST_ENTRY *LinkStatement; + FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; + FORM_BROWSER_FORM *Form; + EFI_STRING EndStr; + FORM_BROWSER_STATEMENT *Statement; + + ASSERT ((*Progress == '&') || (*Progress == 'G')); + + ConfigInfo = NULL; + *RetForm = NULL; + *RetQuestion = NULL; + + // + // Skip the first "&" or the ConfigHdr part. + // + if (*Progress == '&') { + Progress++; + } else { + // + // Prepare the "NAME" or "OFFSET=0x####&WIDTH=0x####" string. + // + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + // + // For Name/Value type, Skip the ConfigHdr part. + // + EndStr = StrStr (Progress, L"PATH="); + ASSERT (EndStr != NULL); + while (*EndStr != '&') { + EndStr++; + } + + *EndStr = '\0'; + } else { + // + // For Buffer type, Skip the ConfigHdr part. + // + EndStr = StrStr (Progress, L"&OFFSET="); + ASSERT (EndStr != NULL); + *EndStr = '\0'; + } + + Progress = EndStr + 1; + } + + // + // Prepare the "NAME" or "OFFSET=0x####&WIDTH=0x####" string. + // + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + // + // For Name/Value type, the data is "&Fred=16&George=16&Ron=12" formset, + // here, just keep the "Fred" string. + // + EndStr = StrStr (Progress, L"="); + ASSERT (EndStr != NULL); + *EndStr = '\0'; + } else { + // + // For Buffer type, the data is "OFFSET=0x####&WIDTH=0x####&VALUE=0x####", + // here, just keep the "OFFSET=0x####&WIDTH=0x####" string. + // + EndStr = StrStr (Progress, L"&VALUE="); + ASSERT (EndStr != NULL); + *EndStr = '\0'; + } + + // + // Search in the form list. + // + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&FormSet->FormListHead, Link); + + // + // Search in the ConfigReqeust list in this form. + // + LinkStorage = GetFirstNode (&Form->ConfigRequestHead); + while (!IsNull (&Form->ConfigRequestHead, LinkStorage)) { + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (LinkStorage); + LinkStorage = GetNextNode (&Form->ConfigRequestHead, LinkStorage); + + if (Storage != ConfigInfo->Storage) { + continue; + } + + if (StrStr (ConfigInfo->ConfigRequest, Progress) != NULL) { + // + // Find the OffsetWidth string in this form. + // + *RetForm = Form; + break; + } + } + + if (*RetForm != NULL) { + LinkStatement = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, LinkStatement)) { + Statement = FORM_BROWSER_STATEMENT_FROM_LINK (LinkStatement); + LinkStatement = GetNextNode (&Form->StatementListHead, LinkStatement); + + if (Statement->BlockName != NULL && StrStr (Statement->BlockName, Progress) != NULL) { + *RetQuestion = Statement; + break; + } + + if (Statement->VariableName != NULL && StrStr (Statement->VariableName, Progress) != NULL) { + *RetQuestion = Statement; + break; + } + } + } + + if (*RetForm != NULL) { + break; + } + } + + // + // restore the OffsetWidth string to the original format. + // + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + *EndStr = '='; + } else { + *EndStr = '&'; + } + + return (BOOLEAN) (*RetForm != NULL); +} + +/** + Base on the return Progress string to get the SyncConfigRequest and RestoreConfigRequest + for form and formset. + + @param Storage Storage which has this Progress string. + @param ConfigRequest The ConfigRequest string. + @param Progress The Progress string which has the first fail string. + @param RestoreConfigRequest Return the RestoreConfigRequest string. + @param SyncConfigRequest Return the SyncConfigRequest string. + +**/ +VOID +GetSyncRestoreConfigRequest( + IN BROWSER_STORAGE *Storage, + IN EFI_STRING ConfigRequest, + IN EFI_STRING Progress, + OUT EFI_STRING *RestoreConfigRequest, + OUT EFI_STRING *SyncConfigRequest + ) +{ + EFI_STRING EndStr; + EFI_STRING ConfigHdrEndStr; + EFI_STRING ElementStr; + UINTN TotalSize; + UINTN RestoreEleSize; + UINTN SyncSize; + + ASSERT ((*Progress == L'&') || (*Progress == L'G')); + // + // If the Progress starts with ConfigHdr, means the failure is in the first name / value pair. + // Need to restore all the fields in the ConfigRequest. + // + if (*Progress == L'G') { + *RestoreConfigRequest = AllocateCopyPool (StrSize (ConfigRequest), ConfigRequest); + ASSERT (*RestoreConfigRequest != NULL); + return; + } + + // + // Find the first fail "NAME" or "OFFSET=0x####&WIDTH=0x####" string. + // + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + // + // For Name/Value type, the data is "&Fred=16&George=16&Ron=12" formset, + // here, just keep the "Fred" string. + // + EndStr = StrStr (Progress, L"="); + ASSERT (EndStr != NULL); + *EndStr = L'\0'; + // + // Find the ConfigHdr in ConfigRequest. + // + ConfigHdrEndStr = StrStr (ConfigRequest, L"PATH="); + ASSERT (ConfigHdrEndStr != NULL); + while (*ConfigHdrEndStr != L'&') { + ConfigHdrEndStr++; + } + } else { + // + // For Buffer type, the data is "OFFSET=0x####&WIDTH=0x####&VALUE=0x####", + // here, just keep the "OFFSET=0x####&WIDTH=0x####" string. + // + EndStr = StrStr (Progress, L"&VALUE="); + ASSERT (EndStr != NULL); + *EndStr = L'\0'; + // + // Find the ConfigHdr in ConfigRequest. + // + ConfigHdrEndStr = StrStr (ConfigRequest, L"&OFFSET="); + } + // + // Find the first fail pair in the ConfigRequest. + // + ElementStr = StrStr (ConfigRequest, Progress); + ASSERT (ElementStr != NULL); + // + // To get the RestoreConfigRequest. + // + RestoreEleSize = StrSize (ElementStr); + TotalSize = (ConfigHdrEndStr - ConfigRequest) * sizeof (CHAR16) + RestoreEleSize + sizeof (CHAR16); + *RestoreConfigRequest = AllocateZeroPool (TotalSize); + ASSERT (*RestoreConfigRequest != NULL); + StrnCpyS (*RestoreConfigRequest, TotalSize / sizeof (CHAR16), ConfigRequest, ConfigHdrEndStr - ConfigRequest); + StrCatS (*RestoreConfigRequest, TotalSize / sizeof (CHAR16), ElementStr); + // + // To get the SyncConfigRequest. + // + SyncSize = StrSize (ConfigRequest) - RestoreEleSize + sizeof (CHAR16); + *SyncConfigRequest = AllocateZeroPool (SyncSize); + ASSERT (*SyncConfigRequest != NULL); + StrnCpyS (*SyncConfigRequest, SyncSize / sizeof (CHAR16), ConfigRequest, SyncSize / sizeof (CHAR16) - 1); + + // + // restore the Progress string to the original format. + // + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + *EndStr = L'='; + } else { + *EndStr = L'&'; + } +} + +/** + Popup an save error info and get user input. + + @param TitleId The form title id. + @param HiiHandle The hii handle for this package. + + @retval UINT32 The user select option for the save fail. + BROWSER_ACTION_DISCARD or BROWSER_ACTION_JUMP_TO_FORMSET +**/ +UINT32 +ConfirmSaveFail ( + IN EFI_STRING_ID TitleId, + IN EFI_HII_HANDLE HiiHandle + ) +{ + CHAR16 *FormTitle; + CHAR16 *StringBuffer; + UINT32 RetVal; + + FormTitle = GetToken (TitleId, HiiHandle); + + StringBuffer = AllocateZeroPool (256 * sizeof (CHAR16)); + ASSERT (StringBuffer != NULL); + + UnicodeSPrint ( + StringBuffer, + 24 * sizeof (CHAR16) + StrSize (FormTitle), + L"Submit Fail For Form: %s.", + FormTitle + ); + + RetVal = PopupErrorMessage(BROWSER_SUBMIT_FAIL, NULL, NULL, StringBuffer); + + FreePool (StringBuffer); + FreePool (FormTitle); + + return RetVal; +} + +/** + Popup an NO_SUBMIT_IF error info and get user input. + + @param TitleId The form title id. + @param HiiHandle The hii handle for this package. + + @retval UINT32 The user select option for the save fail. + BROWSER_ACTION_DISCARD or BROWSER_ACTION_JUMP_TO_FORMSET +**/ +UINT32 +ConfirmNoSubmitFail ( + IN EFI_STRING_ID TitleId, + IN EFI_HII_HANDLE HiiHandle + ) +{ + CHAR16 *FormTitle; + CHAR16 *StringBuffer; + UINT32 RetVal; + + FormTitle = GetToken (TitleId, HiiHandle); + + StringBuffer = AllocateZeroPool (256 * sizeof (CHAR16)); + ASSERT (StringBuffer != NULL); + + UnicodeSPrint ( + StringBuffer, + 24 * sizeof (CHAR16) + StrSize (FormTitle), + L"NO_SUBMIT_IF error For Form: %s.", + FormTitle + ); + + RetVal = PopupErrorMessage(BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF, NULL, NULL, StringBuffer); + + FreePool (StringBuffer); + FreePool (FormTitle); + + return RetVal; +} + +/** + Discard data based on the input setting scope (Form, FormSet or System). + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param SettingScope Setting Scope for Discard action. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_UNSUPPORTED Unsupport SettingScope. + +**/ +EFI_STATUS +DiscardForm ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN BROWSER_SETTING_SCOPE SettingScope + ) +{ + LIST_ENTRY *Link; + FORMSET_STORAGE *Storage; + FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; + FORM_BROWSER_FORMSET *LocalFormSet; + FORM_BROWSER_FORMSET *OldFormSet; + + // + // Check the supported setting level. + // + if (SettingScope >= MaxLevel) { + return EFI_UNSUPPORTED; + } + + if (SettingScope == FormLevel && IsNvUpdateRequiredForForm (Form)) { + ConfigInfo = NULL; + Link = GetFirstNode (&Form->ConfigRequestHead); + while (!IsNull (&Form->ConfigRequestHead, Link)) { + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); + Link = GetNextNode (&Form->ConfigRequestHead, Link); + + if (ConfigInfo->Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + // + // Skip if there is no RequestElement + // + if (ConfigInfo->ElementCount == 0) { + continue; + } + + // + // Prepare + // + SynchronizeStorage(ConfigInfo->Storage, ConfigInfo->ConfigRequest, FALSE); + + // + // Call callback with Changed type to inform the driver. + // + SendDiscardInfoToDriver (FormSet, Form); + } + + ValueChangeResetFlagUpdate (FALSE, FormSet, Form); + } else if (SettingScope == FormSetLevel && IsNvUpdateRequiredForFormSet (FormSet)) { + + // + // Discard Buffer storage or Name/Value storage + // + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + Storage = FORMSET_STORAGE_FROM_LINK (Link); + Link = GetNextNode (&FormSet->StorageListHead, Link); + + if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + // + // Skip if there is no RequestElement + // + if (Storage->ElementCount == 0) { + continue; + } + + SynchronizeStorage(Storage->BrowserStorage, Storage->ConfigRequest, FALSE); + } + + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&FormSet->FormListHead, Link); + + // + // Call callback with Changed type to inform the driver. + // + SendDiscardInfoToDriver (FormSet, Form); + } + + ValueChangeResetFlagUpdate(FALSE, FormSet, NULL); + } else if (SettingScope == SystemLevel) { + // + // System Level Discard. + // + OldFormSet = mSystemLevelFormSet; + + // + // Discard changed value for each FormSet in the maintain list. + // + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + Link = GetNextNode (&gBrowserFormSetList, Link); + if (!ValidateFormSet(LocalFormSet)) { + continue; + } + + mSystemLevelFormSet = LocalFormSet; + + DiscardForm (LocalFormSet, NULL, FormSetLevel); + if (!IsHiiHandleInBrowserContext (LocalFormSet->HiiHandle)) { + // + // Remove maintain backup list after discard except for the current using FormSet. + // + CleanBrowserStorage(LocalFormSet); + RemoveEntryList (&LocalFormSet->Link); + DestroyFormSet (LocalFormSet); + } + } + + mSystemLevelFormSet = OldFormSet; + } + + return EFI_SUCCESS; +} + +/** + Submit data for a form. + + @param FormSet FormSet data structure. + @param Form Form data structure. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_UNSUPPORTED Unsupport SettingScope. + +**/ +EFI_STATUS +SubmitForForm ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + EFI_STRING ConfigResp; + EFI_STRING Progress; + BROWSER_STORAGE *Storage; + FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; + BOOLEAN SubmitFormFail; + + SubmitFormFail = FALSE; + + if (!IsNvUpdateRequiredForForm (Form)) { + return EFI_SUCCESS; + } + + Status = NoSubmitCheck (FormSet, &Form, NULL); + if (EFI_ERROR (Status)) { + return Status; + } + + Link = GetFirstNode (&Form->ConfigRequestHead); + while (!IsNull (&Form->ConfigRequestHead, Link)) { + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); + Link = GetNextNode (&Form->ConfigRequestHead, Link); + + Storage = ConfigInfo->Storage; + if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + // + // Skip if there is no RequestElement + // + if (ConfigInfo->ElementCount == 0) { + continue; + } + + // + // 1. Prepare + // + Status = StorageToConfigResp (ConfigInfo->Storage, &ConfigResp, ConfigInfo->ConfigRequest, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // 2. Set value to hii config routine protocol. + // + Status = mHiiConfigRouting->RouteConfig ( + mHiiConfigRouting, + ConfigResp, + &Progress + ); + + if (EFI_ERROR (Status)) { + // + // Submit fail, to get the RestoreConfigRequest and SyncConfigRequest. + // + SubmitFormFail = TRUE; + GetSyncRestoreConfigRequest (ConfigInfo->Storage, ConfigInfo->ConfigRequest, Progress, &ConfigInfo->RestoreConfigRequest, &ConfigInfo->SyncConfigRequest); + InsertTailList (&gBrowserSaveFailFormSetList, &ConfigInfo->SaveFailLink); + FreePool (ConfigResp); + continue; + } + + FreePool (ConfigResp); + // + // 3. Config success, update storage shadow Buffer, only update the data belong to this form. + // + SynchronizeStorage (ConfigInfo->Storage, ConfigInfo->ConfigRequest, TRUE); + } + + // + // 4. Process the save failed storage. + // + if (!IsListEmpty (&gBrowserSaveFailFormSetList)) { + if (ConfirmSaveFail (Form->FormTitle, FormSet->HiiHandle) == BROWSER_ACTION_DISCARD) { + Link = GetFirstNode (&gBrowserSaveFailFormSetList); + while (!IsNull (&gBrowserSaveFailFormSetList, Link)) { + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_SAVE_FAIL_LINK (Link); + Link = GetNextNode (&gBrowserSaveFailFormSetList, Link); + // + // Process the submit fail question, base on the RestoreConfigRequest to restore the EditBuffer + // base on the SyncConfigRequest to Sync the buffer. + // + SynchronizeStorage (ConfigInfo->Storage, ConfigInfo->RestoreConfigRequest, FALSE); + FreePool (ConfigInfo->RestoreConfigRequest); + ConfigInfo->RestoreConfigRequest = NULL; + if (ConfigInfo->SyncConfigRequest != NULL) { + SynchronizeStorage(ConfigInfo->Storage, ConfigInfo->SyncConfigRequest, TRUE); + FreePool (ConfigInfo->SyncConfigRequest); + ConfigInfo->SyncConfigRequest = NULL; + } + + Status = EFI_SUCCESS; + } + SendDiscardInfoToDriver (FormSet,Form); + } else { + Status = EFI_UNSUPPORTED; + } + + // + // Free Form save fail list. + // + while (!IsListEmpty (&gBrowserSaveFailFormSetList)) { + Link = GetFirstNode (&gBrowserSaveFailFormSetList); + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_SAVE_FAIL_LINK (Link); + RemoveEntryList (&ConfigInfo->SaveFailLink); + } + } + + // + // 5. Update the NV flag. + // + ValueChangeResetFlagUpdate(TRUE, FormSet, Form); + + // + // 6 Call callback with Submitted type to inform the driver. + // + if (!SubmitFormFail) { + SubmitCallback (FormSet, Form); + } + + return Status; +} + +/** + Submit data for a formset. + + @param FormSet FormSet data structure. + @param SkipProcessFail Whether skip to process the save failed storage. + If submit formset is called when do system level save, + set this value to true and process the failed formset + together. + if submit formset is called when do formset level save, + set the value to false and process the failed storage + right after process all storages for this formset. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_UNSUPPORTED Unsupport SettingScope. + +**/ +EFI_STATUS +SubmitForFormSet ( + IN FORM_BROWSER_FORMSET *FormSet, + IN BOOLEAN SkipProcessFail + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + EFI_STRING ConfigResp; + EFI_STRING Progress; + BROWSER_STORAGE *Storage; + FORMSET_STORAGE *FormSetStorage; + FORM_BROWSER_FORM *Form; + BOOLEAN HasInserted; + FORM_BROWSER_STATEMENT *Question; + BOOLEAN SubmitFormSetFail; + BOOLEAN DiscardChange; + + HasInserted = FALSE; + SubmitFormSetFail = FALSE; + DiscardChange = FALSE; + + if (!IsNvUpdateRequiredForFormSet (FormSet)) { + return EFI_SUCCESS; + } + + Form = NULL; + Status = NoSubmitCheck (FormSet, &Form, &Question); + if (EFI_ERROR (Status)) { + if (SkipProcessFail) { + // + // Process NO_SUBMIT check first, so insert it at head. + // + FormSet->SaveFailForm = Form; + FormSet->SaveFailStatement = Question; + InsertHeadList (&gBrowserSaveFailFormSetList, &FormSet->SaveFailLink); + } + + return Status; + } + + Form = NULL; + Question = NULL; + // + // Submit Buffer storage or Name/Value storage + // + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); + Storage = FormSetStorage->BrowserStorage; + Link = GetNextNode (&FormSet->StorageListHead, Link); + + if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + // + // Skip if there is no RequestElement + // + if (FormSetStorage->ElementCount == 0) { + continue; + } + + // + // 1. Prepare + // + Status = StorageToConfigResp (Storage, &ConfigResp, FormSetStorage->ConfigRequest, TRUE); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // 2. Send to Routine config Protocol. + // + Status = mHiiConfigRouting->RouteConfig ( + mHiiConfigRouting, + ConfigResp, + &Progress + ); + if (EFI_ERROR (Status)) { + // + // Submit fail, to get the RestoreConfigRequest and SyncConfigRequest. + // + SubmitFormSetFail = TRUE; + GetSyncRestoreConfigRequest (FormSetStorage->BrowserStorage, FormSetStorage->ConfigRequest, Progress, &FormSetStorage->RestoreConfigRequest, &FormSetStorage->SyncConfigRequest); + InsertTailList (&FormSet->SaveFailStorageListHead, &FormSetStorage->SaveFailLink); + if (!HasInserted) { + // + // Call submit formset for system level, save the formset info + // and process later. + // + FindQuestionFromProgress(FormSet, Storage, Progress, &Form, &Question); + ASSERT (Form != NULL && Question != NULL); + FormSet->SaveFailForm = Form; + FormSet->SaveFailStatement = Question; + if (SkipProcessFail) { + InsertTailList (&gBrowserSaveFailFormSetList, &FormSet->SaveFailLink); + } + HasInserted = TRUE; + } + + FreePool (ConfigResp); + continue; + } + + FreePool (ConfigResp); + // + // 3. Config success, update storage shadow Buffer + // + SynchronizeStorage (Storage, FormSetStorage->ConfigRequest, TRUE); + } + + // + // 4. Has save fail storage need to handle. + // + if (Form != NULL) { + if (!SkipProcessFail) { + // + // If not in system level, just handl the save failed storage here. + // + if (ConfirmSaveFail (Form->FormTitle, FormSet->HiiHandle) == BROWSER_ACTION_DISCARD) { + DiscardChange = TRUE; + Link = GetFirstNode (&FormSet->SaveFailStorageListHead); + while (!IsNull (&FormSet->SaveFailStorageListHead, Link)) { + FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (Link); + Storage = FormSetStorage->BrowserStorage; + Link = GetNextNode (&FormSet->SaveFailStorageListHead, Link); + // + // Process the submit fail question, base on the RestoreConfigRequest to restore the EditBuffer + // base on the SyncConfigRequest to Sync the buffer. + // + SynchronizeStorage (FormSetStorage->BrowserStorage, FormSetStorage->RestoreConfigRequest, FALSE); + FreePool (FormSetStorage->RestoreConfigRequest); + FormSetStorage->RestoreConfigRequest = NULL; + if (FormSetStorage->SyncConfigRequest != NULL) { + SynchronizeStorage(FormSetStorage->BrowserStorage, FormSetStorage->SyncConfigRequest, TRUE); + FreePool (FormSetStorage->SyncConfigRequest); + FormSetStorage->SyncConfigRequest = NULL; + } + + Status = EFI_SUCCESS; + } + } else { + UiCopyMenuList(&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &Form->FormViewListHead); + + gCurrentSelection->Action = UI_ACTION_REFRESH_FORMSET; + gCurrentSelection->Handle = FormSet->HiiHandle; + CopyGuid (&gCurrentSelection->FormSetGuid, &FormSet->Guid); + gCurrentSelection->FormId = Form->FormId; + gCurrentSelection->QuestionId = Question->QuestionId; + + Status = EFI_UNSUPPORTED; + } + + // + // Free FormSet save fail list. + // + while (!IsListEmpty (&FormSet->SaveFailStorageListHead)) { + Link = GetFirstNode (&FormSet->SaveFailStorageListHead); + FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (Link); + RemoveEntryList (&FormSetStorage->SaveFailLink); + } + } else { + // + // If in system level, just return error and handle the failed formset later. + // + Status = EFI_UNSUPPORTED; + } + } + + // + // If user discard the change, send the discard info to driver. + // + if (DiscardChange) { + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&FormSet->FormListHead, Link); + // + // Call callback with Changed type to inform the driver. + // + SendDiscardInfoToDriver (FormSet, Form); + } + } + + // + // 5. Update the NV flag. + // + ValueChangeResetFlagUpdate(TRUE, FormSet, NULL); + + // + // 6. Call callback with Submitted type to inform the driver. + // + if (!SubmitFormSetFail) { + SubmitCallback (FormSet, NULL); + } + + return Status; +} + +/** + Submit data for all formsets. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_UNSUPPORTED Unsupport SettingScope. + +**/ +EFI_STATUS +SubmitForSystem ( + VOID + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + LIST_ENTRY *FormLink; + LIST_ENTRY *StorageLink; + FORMSET_STORAGE *FormSetStorage; + FORM_BROWSER_FORM *Form; + FORM_BROWSER_FORMSET *LocalFormSet; + UINT32 UserSelection; + FORM_BROWSER_STATEMENT *Question; + + mSystemSubmit = TRUE; + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + Link = GetNextNode (&gBrowserFormSetList, Link); + if (!ValidateFormSet(LocalFormSet)) { + continue; + } + + Status = SubmitForFormSet (LocalFormSet, TRUE); + if (EFI_ERROR (Status)) { + continue; + } + + // + // Remove maintain backup list after save except for the current using FormSet. + // + if (!IsHiiHandleInBrowserContext (LocalFormSet->HiiHandle)) { + CleanBrowserStorage(LocalFormSet); + RemoveEntryList (&LocalFormSet->Link); + DestroyFormSet (LocalFormSet); + } + } + mSystemSubmit = FALSE; + + Status = EFI_SUCCESS; + + // + // Process the save failed formsets. + // + Link = GetFirstNode (&gBrowserSaveFailFormSetList); + while (!IsNull (&gBrowserSaveFailFormSetList, Link)) { + LocalFormSet = FORM_BROWSER_FORMSET_FROM_SAVE_FAIL_LINK (Link); + Link = GetNextNode (&gBrowserSaveFailFormSetList, Link); + + if (!ValidateFormSet(LocalFormSet)) { + continue; + } + + Form = LocalFormSet->SaveFailForm; + Question= LocalFormSet->SaveFailStatement; + + // + // Confirm with user, get user input. + // + if (IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { + // + // NULL for SaveFailStorageListHead means error caused by NO_SUBMIT_IF check. + // + UserSelection = ConfirmNoSubmitFail (Form->FormTitle, LocalFormSet->HiiHandle); + } else { + UserSelection = ConfirmSaveFail (Form->FormTitle, LocalFormSet->HiiHandle); + } + + if (UserSelection == BROWSER_ACTION_DISCARD) { + if (IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { + StorageLink = GetFirstNode (&LocalFormSet->StorageListHead); + while (!IsNull (&LocalFormSet->StorageListHead, StorageLink)) { + FormSetStorage = FORMSET_STORAGE_FROM_LINK (StorageLink); + StorageLink = GetNextNode (&LocalFormSet->StorageListHead, StorageLink); + + SynchronizeStorage(FormSetStorage->BrowserStorage, FormSetStorage->ConfigRequest, FALSE); + } + } else { + StorageLink = GetFirstNode (&LocalFormSet->SaveFailStorageListHead); + while (!IsNull (&LocalFormSet->SaveFailStorageListHead, StorageLink)) { + FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (StorageLink); + StorageLink = GetNextNode (&LocalFormSet->SaveFailStorageListHead, StorageLink); + // + // Process the submit fail question, base on the RestoreConfigRequest to restore the EditBuffer + // base on the SyncConfigRequest to Sync the buffer. + // + SynchronizeStorage (FormSetStorage->BrowserStorage, FormSetStorage->RestoreConfigRequest, FALSE); + FreePool (FormSetStorage->RestoreConfigRequest); + FormSetStorage->RestoreConfigRequest = NULL; + if ( FormSetStorage->SyncConfigRequest != NULL) { + SynchronizeStorage (FormSetStorage->BrowserStorage, FormSetStorage->SyncConfigRequest, TRUE); + FreePool (FormSetStorage->SyncConfigRequest); + FormSetStorage->SyncConfigRequest = NULL; + } + } + } + + FormLink = GetFirstNode (&LocalFormSet->FormListHead); + while (!IsNull (&LocalFormSet->FormListHead, FormLink)) { + Form = FORM_BROWSER_FORM_FROM_LINK (FormLink); + FormLink = GetNextNode (&LocalFormSet->FormListHead, FormLink); + // + // Call callback with Changed type to inform the driver. + // + SendDiscardInfoToDriver (LocalFormSet, Form); + } + + if (!IsHiiHandleInBrowserContext (LocalFormSet->HiiHandle)) { + CleanBrowserStorage(LocalFormSet); + RemoveEntryList (&LocalFormSet->Link); + RemoveEntryList (&LocalFormSet->SaveFailLink); + DestroyFormSet (LocalFormSet); + } else { + ValueChangeResetFlagUpdate(FALSE, LocalFormSet, NULL); + } + } else { + if (IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { + NoSubmitCheck (LocalFormSet, &Form, &Question); + } + + UiCopyMenuList(&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &Form->FormViewListHead); + + gCurrentSelection->Action = UI_ACTION_REFRESH_FORMSET; + gCurrentSelection->Handle = LocalFormSet->HiiHandle; + CopyGuid (&gCurrentSelection->FormSetGuid, &LocalFormSet->Guid); + gCurrentSelection->FormId = Form->FormId; + gCurrentSelection->QuestionId = Question->QuestionId; + + Status = EFI_UNSUPPORTED; + break; + } + } + + // + // Clean the list which will not process. + // + while (!IsListEmpty (&gBrowserSaveFailFormSetList)) { + Link = GetFirstNode (&gBrowserSaveFailFormSetList); + LocalFormSet = FORM_BROWSER_FORMSET_FROM_SAVE_FAIL_LINK (Link); + RemoveEntryList (&LocalFormSet->SaveFailLink); + + while (!IsListEmpty (&LocalFormSet->SaveFailStorageListHead)) { + StorageLink = GetFirstNode (&LocalFormSet->SaveFailStorageListHead); + FormSetStorage = FORMSET_STORAGE_FROM_SAVE_FAIL_LINK (StorageLink); + RemoveEntryList (&FormSetStorage->SaveFailLink); + } + } + + return Status; +} + +/** + Submit data based on the input Setting level (Form, FormSet or System). + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param SettingScope Setting Scope for Submit action. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_UNSUPPORTED Unsupport SettingScope. + +**/ +EFI_STATUS +SubmitForm ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN BROWSER_SETTING_SCOPE SettingScope + ) +{ + EFI_STATUS Status; + + switch (SettingScope) { + case FormLevel: + Status = SubmitForForm(FormSet, Form); + break; + + case FormSetLevel: + Status = SubmitForFormSet (FormSet, FALSE); + break; + + case SystemLevel: + Status = SubmitForSystem (); + break; + + default: + Status = EFI_UNSUPPORTED; + break; + } + + return Status; +} + +/** + Converts the unicode character of the string from uppercase to lowercase. + This is a internal function. + + @param ConfigString String to be converted + +**/ +VOID +EFIAPI +HiiToLower ( + IN EFI_STRING ConfigString + ) +{ + EFI_STRING String; + BOOLEAN Lower; + + ASSERT (ConfigString != NULL); + + // + // Convert all hex digits in range [A-F] in the configuration header to [a-f] + // + for (String = ConfigString, Lower = FALSE; *String != L'\0'; String++) { + if (*String == L'=') { + Lower = TRUE; + } else if (*String == L'&') { + Lower = FALSE; + } else if (Lower && *String >= L'A' && *String <= L'F') { + *String = (CHAR16) (*String - L'A' + L'a'); + } + } +} + +/** + Find the point in the ConfigResp string for this question. + + @param Question The question. + @param ConfigResp Get ConfigResp string. + + @retval point to the offset where is for this question. + +**/ +CHAR16 * +GetOffsetFromConfigResp ( + IN FORM_BROWSER_STATEMENT *Question, + IN CHAR16 *ConfigResp + ) +{ + CHAR16 *RequestElement; + CHAR16 *BlockData; + + // + // Type is EFI_HII_VARSTORE_NAME_VALUE. + // + if (Question->Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + RequestElement = StrStr (ConfigResp, Question->VariableName); + if (RequestElement != NULL) { + // + // Skip the "VariableName=" field. + // + RequestElement += StrLen (Question->VariableName) + 1; + } + + return RequestElement; + } + + // + // Type is EFI_HII_VARSTORE_EFI_VARIABLE or EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER + // + + // + // Convert all hex digits in ConfigResp to lower case before searching. + // + HiiToLower (ConfigResp); + + // + // 1. Directly use Question->BlockName to find. + // + RequestElement = StrStr (ConfigResp, Question->BlockName); + if (RequestElement != NULL) { + // + // Skip the "Question->BlockName&VALUE=" field. + // + RequestElement += StrLen (Question->BlockName) + StrLen (L"&VALUE="); + return RequestElement; + } + + // + // 2. Change all hex digits in Question->BlockName to lower and compare again. + // + BlockData = AllocateCopyPool (StrSize(Question->BlockName), Question->BlockName); + ASSERT (BlockData != NULL); + HiiToLower (BlockData); + RequestElement = StrStr (ConfigResp, BlockData); + FreePool (BlockData); + + if (RequestElement != NULL) { + // + // Skip the "Question->BlockName&VALUE=" field. + // + RequestElement += StrLen (Question->BlockName) + StrLen (L"&VALUE="); + } + + return RequestElement; +} + +/** + Get Question default value from AltCfg string. + + @param FormSet The form set. + @param Form The form + @param Question The question. + + @retval EFI_SUCCESS Question is reset to default value. + +**/ +EFI_STATUS +GetDefaultValueFromAltCfg ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_BROWSER_STATEMENT *Question + ) +{ + BROWSER_STORAGE *Storage; + FORMSET_STORAGE *FormSetStorage; + CHAR16 *ConfigResp; + CHAR16 *Value; + LIST_ENTRY *Link; + FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; + + Storage = Question->Storage; + if ((Storage == NULL) || (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE)) { + return EFI_NOT_FOUND; + } + + // + // Try to get AltCfg string from form. If not found it, then + // try to get it from formset. + // + ConfigResp = NULL; + Link = GetFirstNode (&Form->ConfigRequestHead); + while (!IsNull (&Form->ConfigRequestHead, Link)) { + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); + Link = GetNextNode (&Form->ConfigRequestHead, Link); + + if (Storage == ConfigInfo->Storage) { + ConfigResp = ConfigInfo->ConfigAltResp; + break; + } + } + + if (ConfigResp == NULL) { + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); + Link = GetNextNode (&FormSet->StorageListHead, Link); + + if (Storage == FormSetStorage->BrowserStorage) { + ConfigResp = FormSetStorage->ConfigAltResp; + break; + } + } + } + + if (ConfigResp == NULL) { + return EFI_NOT_FOUND; + } + + Value = GetOffsetFromConfigResp (Question, ConfigResp); + if (Value == NULL) { + return EFI_NOT_FOUND; + } + + return BufferToValue (Question, Value); +} + +/** + Get default Id value used for browser. + + @param DefaultId The default id value used by hii. + + @retval Browser used default value. + +**/ +INTN +GetDefaultIdForCallBack ( + UINTN DefaultId + ) +{ + if (DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) { + return EFI_BROWSER_ACTION_DEFAULT_STANDARD; + } else if (DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) { + return EFI_BROWSER_ACTION_DEFAULT_MANUFACTURING; + } else if (DefaultId == EFI_HII_DEFAULT_CLASS_SAFE) { + return EFI_BROWSER_ACTION_DEFAULT_SAFE; + } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN + 0x1000) { + return EFI_BROWSER_ACTION_DEFAULT_PLATFORM + DefaultId - EFI_HII_DEFAULT_CLASS_PLATFORM_BEGIN; + } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN + 0x1000) { + return EFI_BROWSER_ACTION_DEFAULT_HARDWARE + DefaultId - EFI_HII_DEFAULT_CLASS_HARDWARE_BEGIN; + } else if (DefaultId >= EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN && DefaultId < EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN + 0x1000) { + return EFI_BROWSER_ACTION_DEFAULT_FIRMWARE + DefaultId - EFI_HII_DEFAULT_CLASS_FIRMWARE_BEGIN; + } else { + return -1; + } +} + + + +/** + Return data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + + @retval Value The data to be returned + +**/ +UINT64 +GetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index + ) +{ + UINT64 Data; + + ASSERT (Array != NULL); + + Data = 0; + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + Data = (UINT64) *(((UINT8 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + Data = (UINT64) *(((UINT16 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + Data = (UINT64) *(((UINT32 *) Array) + Index); + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + Data = (UINT64) *(((UINT64 *) Array) + Index); + break; + + default: + break; + } + + return Data; +} + + +/** + Set value of a data element in an Array by its Index. + + @param Array The data array. + @param Type Type of the data in this array. + @param Index Zero based index for data in this array. + @param Value The value to be set. + +**/ +VOID +SetArrayData ( + IN VOID *Array, + IN UINT8 Type, + IN UINTN Index, + IN UINT64 Value + ) +{ + + ASSERT (Array != NULL); + + switch (Type) { + case EFI_IFR_TYPE_NUM_SIZE_8: + *(((UINT8 *) Array) + Index) = (UINT8) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_16: + *(((UINT16 *) Array) + Index) = (UINT16) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_32: + *(((UINT32 *) Array) + Index) = (UINT32) Value; + break; + + case EFI_IFR_TYPE_NUM_SIZE_64: + *(((UINT64 *) Array) + Index) = (UINT64) Value; + break; + + default: + break; + } +} + +/** + Search an Option of a Question by its value. + + @param Question The Question + @param OptionValue Value for Option to be searched. + + @retval Pointer Pointer to the found Option. + @retval NULL Option not found. + +**/ +QUESTION_OPTION * +ValueToOption ( + IN FORM_BROWSER_STATEMENT *Question, + IN EFI_HII_VALUE *OptionValue + ) +{ + LIST_ENTRY *Link; + QUESTION_OPTION *Option; + INTN Result; + + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = QUESTION_OPTION_FROM_LINK (Link); + + if ((CompareHiiValue (&Option->Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { + // + // Check the suppressif condition, only a valid option can be return. + // + if ((Option->SuppressExpression == NULL) || + ((EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse))) { + return Option; + } + } + + Link = GetNextNode (&Question->OptionListHead, Link); + } + + return NULL; +} + + +/** + Reset Question to its default value. + + @param FormSet The form set. + @param Form The form. + @param Question The question. + @param DefaultId The Class of the default. + + @retval EFI_SUCCESS Question is reset to default value. + +**/ +EFI_STATUS +GetQuestionDefault ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN FORM_BROWSER_STATEMENT *Question, + IN UINT16 DefaultId + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + QUESTION_DEFAULT *Default; + QUESTION_OPTION *Option; + EFI_HII_VALUE *HiiValue; + UINT8 Index; + EFI_STRING StrValue; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; + EFI_BROWSER_ACTION_REQUEST ActionRequest; + INTN Action; + CHAR16 *NewString; + EFI_IFR_TYPE_VALUE *TypeValue; + UINT16 OriginalDefaultId; + FORMSET_DEFAULTSTORE *DefaultStore; + LIST_ENTRY *DefaultLink; + + Status = EFI_NOT_FOUND; + StrValue = NULL; + OriginalDefaultId = DefaultId; + DefaultLink = GetFirstNode (&FormSet->DefaultStoreListHead); + + // + // Statement don't have storage, skip them + // + if (Question->QuestionId == 0) { + return Status; + } + + // + // There are Five ways to specify default value for a Question: + // 1, use call back function (highest priority) + // 2, use ExtractConfig function + // 3, use nested EFI_IFR_DEFAULT + // 4, set flags of EFI_ONE_OF_OPTION (provide Standard and Manufacturing default) + // 5, set flags of EFI_IFR_CHECKBOX (provide Standard and Manufacturing default) (lowest priority) + // +ReGetDefault: + HiiValue = &Question->HiiValue; + TypeValue = &HiiValue->Value; + if (HiiValue->Type == EFI_IFR_TYPE_BUFFER) { + // + // For orderedlist, need to pass the BufferValue to Callback function. + // + TypeValue = (EFI_IFR_TYPE_VALUE *) Question->BufferValue; + } + + // + // Get Question defaut value from call back function. + // + ConfigAccess = FormSet->ConfigAccess; + Action = GetDefaultIdForCallBack (DefaultId); + if ((Action > 0) && ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) && (ConfigAccess != NULL)) { + ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; + Status = ConfigAccess->Callback ( + ConfigAccess, + Action, + Question->QuestionId, + HiiValue->Type, + TypeValue, + &ActionRequest + ); + if (!EFI_ERROR (Status)) { + if (HiiValue->Type == EFI_IFR_TYPE_STRING) { + NewString = GetToken (Question->HiiValue.Value.string, FormSet->HiiHandle); + ASSERT (NewString != NULL); + + ASSERT (StrLen (NewString) * sizeof (CHAR16) <= Question->StorageWidth); + if (StrLen (NewString) * sizeof (CHAR16) <= Question->StorageWidth) { + ZeroMem (Question->BufferValue, Question->StorageWidth); + CopyMem (Question->BufferValue, NewString, StrSize (NewString)); + } else { + CopyMem (Question->BufferValue, NewString, Question->StorageWidth); + } + + FreePool (NewString); + } + return Status; + } + } + + // + // Get default value from altcfg string. + // + if (ConfigAccess != NULL) { + Status = GetDefaultValueFromAltCfg(FormSet, Form, Question); + if (!EFI_ERROR (Status)) { + return Status; + } + } + + // + // EFI_IFR_DEFAULT has highest priority + // + if (!IsListEmpty (&Question->DefaultListHead)) { + Link = GetFirstNode (&Question->DefaultListHead); + while (!IsNull (&Question->DefaultListHead, Link)) { + Default = QUESTION_DEFAULT_FROM_LINK (Link); + + if (Default->DefaultId == DefaultId) { + if (Default->ValueExpression != NULL) { + // + // Default is provided by an Expression, evaluate it + // + Status = EvaluateExpression (FormSet, Form, Default->ValueExpression); + if (EFI_ERROR (Status)) { + return Status; + } + + if (Default->ValueExpression->Result.Type == EFI_IFR_TYPE_BUFFER) { + ASSERT (HiiValue->Type == EFI_IFR_TYPE_BUFFER && Question->BufferValue != NULL); + if (Question->StorageWidth > Default->ValueExpression->Result.BufferLen) { + CopyMem (Question->HiiValue.Buffer, Default->ValueExpression->Result.Buffer, Default->ValueExpression->Result.BufferLen); + Question->HiiValue.BufferLen = Default->ValueExpression->Result.BufferLen; + } else { + CopyMem (Question->HiiValue.Buffer, Default->ValueExpression->Result.Buffer, Question->StorageWidth); + Question->HiiValue.BufferLen = Question->StorageWidth; + } + FreePool (Default->ValueExpression->Result.Buffer); + } + HiiValue->Type = Default->ValueExpression->Result.Type; + CopyMem (&HiiValue->Value, &Default->ValueExpression->Result.Value, sizeof (EFI_IFR_TYPE_VALUE)); + } else { + // + // Default value is embedded in EFI_IFR_DEFAULT + // + if (Default->Value.Type == EFI_IFR_TYPE_BUFFER) { + ASSERT (HiiValue->Buffer != NULL); + CopyMem (HiiValue->Buffer, Default->Value.Buffer, Default->Value.BufferLen); + } else { + CopyMem (HiiValue, &Default->Value, sizeof (EFI_HII_VALUE)); + } + } + + if (HiiValue->Type == EFI_IFR_TYPE_STRING) { + StrValue = HiiGetString (FormSet->HiiHandle, HiiValue->Value.string, NULL); + if (StrValue == NULL) { + return EFI_NOT_FOUND; + } + if (Question->StorageWidth > StrSize (StrValue)) { + ZeroMem (Question->BufferValue, Question->StorageWidth); + CopyMem (Question->BufferValue, StrValue, StrSize (StrValue)); + } else { + CopyMem (Question->BufferValue, StrValue, Question->StorageWidth); + } + } + + return EFI_SUCCESS; + } + + Link = GetNextNode (&Question->DefaultListHead, Link); + } + } + + // + // EFI_ONE_OF_OPTION + // + if ((Question->Operand == EFI_IFR_ONE_OF_OP) && !IsListEmpty (&Question->OptionListHead)) { + if (DefaultId <= EFI_HII_DEFAULT_CLASS_MANUFACTURING) { + // + // OneOfOption could only provide Standard and Manufacturing default + // + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + if ((Option->SuppressExpression != NULL) && + EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) { + continue; + } + + if (((DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) && ((Option->Flags & EFI_IFR_OPTION_DEFAULT) != 0)) || + ((DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) && ((Option->Flags & EFI_IFR_OPTION_DEFAULT_MFG) != 0)) + ) { + CopyMem (HiiValue, &Option->Value, sizeof (EFI_HII_VALUE)); + + return EFI_SUCCESS; + } + } + } + } + + // + // EFI_IFR_CHECKBOX - lowest priority + // + if (Question->Operand == EFI_IFR_CHECKBOX_OP) { + if (DefaultId <= EFI_HII_DEFAULT_CLASS_MANUFACTURING) { + // + // Checkbox could only provide Standard and Manufacturing default + // + if (((DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) && ((Question->Flags & EFI_IFR_CHECKBOX_DEFAULT) != 0)) || + ((DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) && ((Question->Flags & EFI_IFR_CHECKBOX_DEFAULT_MFG) != 0)) + ) { + HiiValue->Value.b = TRUE; + } + + return EFI_SUCCESS; + } + } + + // + // For question without default value for current default Id, we try to re-get the default value form other default id in the DefaultStoreList. + // If get, will exit the function, if not, will choose next default id in the DefaultStoreList. + // The default id in DefaultStoreList are in ascending order to make sure choose the smallest default id every time. + // + while (!IsNull(&FormSet->DefaultStoreListHead, DefaultLink)) { + DefaultStore = FORMSET_DEFAULTSTORE_FROM_LINK(DefaultLink); + DefaultLink = GetNextNode (&FormSet->DefaultStoreListHead,DefaultLink); + DefaultId = DefaultStore->DefaultId; + if (DefaultId == OriginalDefaultId) { + continue; + } + goto ReGetDefault; + } + + // + // For Questions without default value for all the default id in the DefaultStoreList. + // + Status = EFI_NOT_FOUND; + switch (Question->Operand) { + case EFI_IFR_CHECKBOX_OP: + HiiValue->Value.b = FALSE; + Status = EFI_SUCCESS; + break; + + case EFI_IFR_NUMERIC_OP: + // + // Take minimum value as numeric default value + // + if ((Question->Flags & EFI_IFR_DISPLAY) == 0) { + // + // In EFI_IFR_DISPLAY_INT_DEC type, should check value with int* type. + // + switch (Question->Flags & EFI_IFR_NUMERIC_SIZE) { + case EFI_IFR_NUMERIC_SIZE_1: + if (((INT8) HiiValue->Value.u8 < (INT8) Question->Minimum) || ((INT8) HiiValue->Value.u8 > (INT8) Question->Maximum)) { + HiiValue->Value.u8 = (UINT8) Question->Minimum; + Status = EFI_SUCCESS; + } + break; + case EFI_IFR_NUMERIC_SIZE_2: + if (((INT16) HiiValue->Value.u16 < (INT16) Question->Minimum) || ((INT16) HiiValue->Value.u16 > (INT16) Question->Maximum)) { + HiiValue->Value.u16 = (UINT16) Question->Minimum; + Status = EFI_SUCCESS; + } + break; + case EFI_IFR_NUMERIC_SIZE_4: + if (((INT32) HiiValue->Value.u32 < (INT32) Question->Minimum) || ((INT32) HiiValue->Value.u32 > (INT32) Question->Maximum)) { + HiiValue->Value.u32 = (UINT32) Question->Minimum; + Status = EFI_SUCCESS; + } + break; + case EFI_IFR_NUMERIC_SIZE_8: + if (((INT64) HiiValue->Value.u64 < (INT64) Question->Minimum) || ((INT64) HiiValue->Value.u64 > (INT64) Question->Maximum)) { + HiiValue->Value.u64 = Question->Minimum; + Status = EFI_SUCCESS; + } + break; + default: + break; + } + } else { + if ((HiiValue->Value.u64 < Question->Minimum) || (HiiValue->Value.u64 > Question->Maximum)) { + HiiValue->Value.u64 = Question->Minimum; + Status = EFI_SUCCESS; + } + } + break; + + case EFI_IFR_ONE_OF_OP: + // + // Take first oneof option as oneof's default value + // + if (ValueToOption (Question, HiiValue) == NULL) { + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Option = QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + if ((Option->SuppressExpression != NULL) && + EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) { + continue; + } + + CopyMem (HiiValue, &Option->Value, sizeof (EFI_HII_VALUE)); + Status = EFI_SUCCESS; + break; + } + } + break; + + case EFI_IFR_ORDERED_LIST_OP: + // + // Take option sequence in IFR as ordered list's default value + // + Index = 0; + Link = GetFirstNode (&Question->OptionListHead); + while (!IsNull (&Question->OptionListHead, Link)) { + Status = EFI_SUCCESS; + Option = QUESTION_OPTION_FROM_LINK (Link); + Link = GetNextNode (&Question->OptionListHead, Link); + + if ((Option->SuppressExpression != NULL) && + EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) { + continue; + } + + SetArrayData (Question->BufferValue, Question->ValueType, Index, Option->Value.Value.u64); + + Index++; + if (Index >= Question->MaxContainers) { + break; + } + } + break; + + default: + break; + } + + return Status; +} + +/** + Get AltCfg string for current form. + + @param FormSet Form data structure. + @param Form Form data structure. + @param DefaultId The Class of the default. + @param BrowserStorage The input request storage for the questions. + +**/ +VOID +ExtractAltCfgForForm ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN UINT16 DefaultId, + IN BROWSER_STORAGE *BrowserStorage + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + CHAR16 *ConfigResp; + CHAR16 *Progress; + CHAR16 *Result; + BROWSER_STORAGE *Storage; + FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; + FORMSET_STORAGE *FormSetStorage; + + // + // Check whether has get AltCfg string for this formset. + // If yes, no need to get AltCfg for form. + // + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); + Storage = FormSetStorage->BrowserStorage; + Link = GetNextNode (&FormSet->StorageListHead, Link); + if (BrowserStorage != NULL && BrowserStorage != Storage) { + continue; + } + + if (Storage->Type != EFI_HII_VARSTORE_EFI_VARIABLE && + FormSetStorage->ElementCount != 0 && + FormSetStorage->HasCallAltCfg) { + return; + } + } + + // + // Get AltCfg string for each form. + // + Link = GetFirstNode (&Form->ConfigRequestHead); + while (!IsNull (&Form->ConfigRequestHead, Link)) { + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); + Link = GetNextNode (&Form->ConfigRequestHead, Link); + + Storage = ConfigInfo->Storage; + if (BrowserStorage != NULL && BrowserStorage != Storage) { + continue; + } + + if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + // + // 1. Skip if there is no RequestElement + // + if (ConfigInfo->ElementCount == 0) { + continue; + } + + // + // 2. Get value through hii config routine protocol. + // + Status = mHiiConfigRouting->ExtractConfig ( + mHiiConfigRouting, + ConfigInfo->ConfigRequest, + &Progress, + &Result + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // 3. Call ConfigRouting GetAltCfg(ConfigRoute, , Guid, Name, DevicePath, AltCfgId, AltCfgResp) + // Get the default configuration string according to the default ID. + // + Status = mHiiConfigRouting->GetAltConfig ( + mHiiConfigRouting, + Result, + &Storage->Guid, + Storage->Name, + NULL, + &DefaultId, // it can be NULL to get the current setting. + &ConfigResp + ); + FreePool (Result); + if (EFI_ERROR (Status)) { + continue; + } + + ConfigInfo->ConfigAltResp = ConfigResp; + } +} + +/** + Clean AltCfg string for current form. + + @param Form Form data structure. + +**/ +VOID +CleanAltCfgForForm ( + IN FORM_BROWSER_FORM *Form + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_CONFIG_REQUEST *ConfigInfo; + + Link = GetFirstNode (&Form->ConfigRequestHead); + while (!IsNull (&Form->ConfigRequestHead, Link)) { + ConfigInfo = FORM_BROWSER_CONFIG_REQUEST_FROM_LINK (Link); + Link = GetNextNode (&Form->ConfigRequestHead, Link); + + if (ConfigInfo->ConfigAltResp != NULL) { + FreePool (ConfigInfo->ConfigAltResp); + ConfigInfo->ConfigAltResp = NULL; + } + } +} + +/** + Get AltCfg string for current formset. + + @param FormSet Form data structure. + @param DefaultId The Class of the default. + @param BrowserStorage The input request storage for the questions. + +**/ +VOID +ExtractAltCfgForFormSet ( + IN FORM_BROWSER_FORMSET *FormSet, + IN UINT16 DefaultId, + IN BROWSER_STORAGE *BrowserStorage + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + CHAR16 *ConfigResp; + CHAR16 *Progress; + CHAR16 *Result; + BROWSER_STORAGE *Storage; + FORMSET_STORAGE *FormSetStorage; + + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); + Storage = FormSetStorage->BrowserStorage; + Link = GetNextNode (&FormSet->StorageListHead, Link); + + if (BrowserStorage != NULL && BrowserStorage != Storage) { + continue; + } + + if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { + continue; + } + + // + // 1. Skip if there is no RequestElement + // + if (FormSetStorage->ElementCount == 0) { + continue; + } + + FormSetStorage->HasCallAltCfg = TRUE; + + // + // 2. Get value through hii config routine protocol. + // + Status = mHiiConfigRouting->ExtractConfig ( + mHiiConfigRouting, + FormSetStorage->ConfigRequest, + &Progress, + &Result + ); + if (EFI_ERROR (Status)) { + continue; + } + + // + // 3. Call ConfigRouting GetAltCfg(ConfigRoute, , Guid, Name, DevicePath, AltCfgId, AltCfgResp) + // Get the default configuration string according to the default ID. + // + Status = mHiiConfigRouting->GetAltConfig ( + mHiiConfigRouting, + Result, + &Storage->Guid, + Storage->Name, + NULL, + &DefaultId, // it can be NULL to get the current setting. + &ConfigResp + ); + + FreePool (Result); + if (EFI_ERROR (Status)) { + continue; + } + + FormSetStorage->ConfigAltResp = ConfigResp; + } + +} + +/** + Clean AltCfg string for current formset. + + @param FormSet Form data structure. + +**/ +VOID +CleanAltCfgForFormSet ( + IN FORM_BROWSER_FORMSET *FormSet + ) +{ + LIST_ENTRY *Link; + FORMSET_STORAGE *FormSetStorage; + + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + FormSetStorage = FORMSET_STORAGE_FROM_LINK (Link); + Link = GetNextNode (&FormSet->StorageListHead, Link); + + if (FormSetStorage->ConfigAltResp != NULL) { + FreePool (FormSetStorage->ConfigAltResp); + FormSetStorage->ConfigAltResp = NULL; + } + + FormSetStorage->HasCallAltCfg = FALSE; + } +} + +/** + Reset Questions to their initial value or default value in a Form, Formset or System. + + GetDefaultValueScope parameter decides which questions will reset + to its default value. + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param DefaultId The Class of the default. + @param SettingScope Setting Scope for Default action. + @param GetDefaultValueScope Get default value scope. + @param Storage Get default value only for this storage. + @param RetrieveValueFirst Whether call the retrieve call back to + get the initial value before get default + value. + @param SkipGetAltCfg Whether skip the get altcfg string process. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_UNSUPPORTED Unsupport SettingScope. + +**/ +EFI_STATUS +ExtractDefault ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN UINT16 DefaultId, + IN BROWSER_SETTING_SCOPE SettingScope, + IN BROWSER_GET_DEFAULT_VALUE GetDefaultValueScope, + IN BROWSER_STORAGE *Storage OPTIONAL, + IN BOOLEAN RetrieveValueFirst, + IN BOOLEAN SkipGetAltCfg + ) +{ + EFI_STATUS Status; + LIST_ENTRY *FormLink; + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + FORM_BROWSER_FORMSET *LocalFormSet; + FORM_BROWSER_FORMSET *OldFormSet; + + Status = EFI_SUCCESS; + + // + // Check the supported setting level. + // + if (SettingScope >= MaxLevel || GetDefaultValueScope >= GetDefaultForMax) { + return EFI_UNSUPPORTED; + } + + if (GetDefaultValueScope == GetDefaultForStorage && Storage == NULL) { + return EFI_UNSUPPORTED; + } + + if (SettingScope == FormLevel) { + // + // Prepare the AltCfg String for form. + // + if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { + ExtractAltCfgForForm (FormSet, Form, DefaultId, Storage); + } + + // + // Extract Form default + // + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + Link = GetNextNode (&Form->StatementListHead, Link); + + // + // If get default value only for this storage, check the storage first. + // + if ((GetDefaultValueScope == GetDefaultForStorage) && (Question->Storage != Storage)) { + continue; + } + + // + // If get default value only for no storage question, just skip the question which has storage. + // + if ((GetDefaultValueScope == GetDefaultForNoStorage) && (Question->Storage != NULL)) { + continue; + } + + // + // If Question is disabled, don't reset it to default + // + if (Question->Expression != NULL) { + if (EvaluateExpressionList(Question->Expression, TRUE, FormSet, Form) == ExpressDisable) { + continue; + } + } + + if (RetrieveValueFirst) { + // + // Call the Retrieve call back to get the initial question value. + // + Status = ProcessRetrieveForQuestion(FormSet->ConfigAccess, Question, FormSet); + } + + // + // If not request to get the initial value or get initial value fail, then get default value. + // + if (!RetrieveValueFirst || EFI_ERROR (Status)) { + Status = GetQuestionDefault (FormSet, Form, Question, DefaultId); + if (EFI_ERROR (Status)) { + continue; + } + } + + // + // Synchronize Buffer storage's Edit buffer + // + if ((Question->Storage != NULL) && + (Question->Storage->Type != EFI_HII_VARSTORE_EFI_VARIABLE)) { + SetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); + } + } + + // + // Clean the AltCfg String. + // + if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { + CleanAltCfgForForm(Form); + } + } else if (SettingScope == FormSetLevel) { + // + // Prepare the AltCfg String for formset. + // + if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { + ExtractAltCfgForFormSet (FormSet, DefaultId, Storage); + } + + FormLink = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, FormLink)) { + Form = FORM_BROWSER_FORM_FROM_LINK (FormLink); + ExtractDefault (FormSet, Form, DefaultId, FormLevel, GetDefaultValueScope, Storage, RetrieveValueFirst, SkipGetAltCfg); + FormLink = GetNextNode (&FormSet->FormListHead, FormLink); + } + + // + // Clean the AltCfg String. + // + if (!SkipGetAltCfg && (GetDefaultValueScope != GetDefaultForNoStorage)) { + CleanAltCfgForFormSet (FormSet); + } + } else if (SettingScope == SystemLevel) { + // + // Preload all Hii formset. + // + LoadAllHiiFormset(); + + OldFormSet = mSystemLevelFormSet; + + // + // Set Default Value for each FormSet in the maintain list. + // + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + Link = GetNextNode (&gBrowserFormSetList, Link); + if (!ValidateFormSet(LocalFormSet)) { + continue; + } + + mSystemLevelFormSet = LocalFormSet; + + ExtractDefault (LocalFormSet, NULL, DefaultId, FormSetLevel, GetDefaultValueScope, Storage, RetrieveValueFirst, SkipGetAltCfg); + } + + mSystemLevelFormSet = OldFormSet; + } + + return EFI_SUCCESS; +} + + +/** + Validate whether this question's value has changed. + + @param FormSet FormSet data structure. + @param Form Form data structure. + @param Question Question to be initialized. + @param GetValueFrom Where to get value, may from editbuffer, buffer or hii driver. + + @retval TRUE Question's value has changed. + @retval FALSE Question's value has not changed + +**/ +BOOLEAN +IsQuestionValueChanged ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form, + IN OUT FORM_BROWSER_STATEMENT *Question, + IN GET_SET_QUESTION_VALUE_WITH GetValueFrom + ) +{ + EFI_HII_VALUE BackUpValue; + CHAR8 *BackUpBuffer; + EFI_HII_VALUE BackUpValue2; + CHAR8 *BackUpBuffer2; + EFI_STATUS Status; + BOOLEAN ValueChanged; + UINTN BufferWidth; + + // + // For quetion without storage, always mark it as data not changed. + // + if (Question->Storage == NULL && Question->Operand != EFI_IFR_TIME_OP && Question->Operand != EFI_IFR_DATE_OP) { + return FALSE; + } + + BackUpBuffer = NULL; + BackUpBuffer2 = NULL; + ValueChanged = FALSE; + + switch (Question->Operand) { + case EFI_IFR_ORDERED_LIST_OP: + BufferWidth = Question->StorageWidth; + BackUpBuffer = AllocateCopyPool (BufferWidth, Question->BufferValue); + ASSERT (BackUpBuffer != NULL); + break; + + case EFI_IFR_STRING_OP: + case EFI_IFR_PASSWORD_OP: + BufferWidth = (UINTN) Question->Maximum * sizeof (CHAR16); + BackUpBuffer = AllocateCopyPool (BufferWidth, Question->BufferValue); + ASSERT (BackUpBuffer != NULL); + break; + + default: + BufferWidth = 0; + break; + } + CopyMem (&BackUpValue, &Question->HiiValue, sizeof (EFI_HII_VALUE)); + + if (GetValueFrom == GetSetValueWithBothBuffer) { + Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); + ASSERT_EFI_ERROR(Status); + + switch (Question->Operand) { + case EFI_IFR_ORDERED_LIST_OP: + BufferWidth = Question->StorageWidth; + BackUpBuffer2 = AllocateCopyPool (BufferWidth, Question->BufferValue); + ASSERT (BackUpBuffer2 != NULL); + break; + + case EFI_IFR_STRING_OP: + case EFI_IFR_PASSWORD_OP: + BufferWidth = (UINTN) Question->Maximum * sizeof (CHAR16); + BackUpBuffer2 = AllocateCopyPool (BufferWidth, Question->BufferValue); + ASSERT (BackUpBuffer2 != NULL); + break; + + default: + BufferWidth = 0; + break; + } + CopyMem (&BackUpValue2, &Question->HiiValue, sizeof (EFI_HII_VALUE)); + + Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithBuffer); + ASSERT_EFI_ERROR(Status); + + if (CompareMem (&BackUpValue2, &Question->HiiValue, sizeof (EFI_HII_VALUE)) != 0 || + CompareMem (BackUpBuffer2, Question->BufferValue, BufferWidth) != 0) { + ValueChanged = TRUE; + } + } else { + Status = GetQuestionValue (FormSet, Form, Question, GetValueFrom); + ASSERT_EFI_ERROR(Status); + + if (CompareMem (&BackUpValue, &Question->HiiValue, sizeof (EFI_HII_VALUE)) != 0 || + CompareMem (BackUpBuffer, Question->BufferValue, BufferWidth) != 0) { + ValueChanged = TRUE; + } + } + + CopyMem (&Question->HiiValue, &BackUpValue, sizeof (EFI_HII_VALUE)); + if (BackUpBuffer != NULL) { + CopyMem (Question->BufferValue, BackUpBuffer, BufferWidth); + FreePool (BackUpBuffer); + } + + if (BackUpBuffer2 != NULL) { + FreePool (BackUpBuffer2); + } + + Question->ValueChanged = ValueChanged; + + return ValueChanged; +} + +/** + Initialize Question's Edit copy from Storage. + + @param Selection Selection contains the information about + the Selection, form and formset to be displayed. + Selection action may be updated in retrieve callback. + If Selection is NULL, only initialize Question value. + @param FormSet FormSet data structure. + @param Form Form data structure. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +LoadFormConfig ( + IN OUT UI_MENU_SELECTION *Selection, + IN FORM_BROWSER_FORMSET *FormSet, + IN FORM_BROWSER_FORM *Form + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + FORM_BROWSER_STATEMENT *Question; + + Link = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, Link)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (Link); + + // + // Initialize local copy of Value for each Question + // + if (Question->Operand == EFI_IFR_PASSWORD_OP && (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK)== 0) { + Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithHiiDriver); + } else { + Status = GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer); + } + if (EFI_ERROR (Status)) { + return Status; + } + + if ((Question->Operand == EFI_IFR_STRING_OP) || (Question->Operand == EFI_IFR_PASSWORD_OP)) { + HiiSetString (FormSet->HiiHandle, Question->HiiValue.Value.string, (CHAR16*)Question->BufferValue, NULL); + } + + Link = GetNextNode (&Form->StatementListHead, Link); + } + + return EFI_SUCCESS; +} + +/** + Initialize Question's Edit copy from Storage for the whole Formset. + + @param Selection Selection contains the information about + the Selection, form and formset to be displayed. + Selection action may be updated in retrieve callback. + If Selection is NULL, only initialize Question value. + @param FormSet FormSet data structure. + + @retval EFI_SUCCESS The function completed successfully. + +**/ +EFI_STATUS +LoadFormSetConfig ( + IN OUT UI_MENU_SELECTION *Selection, + IN FORM_BROWSER_FORMSET *FormSet + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Link; + FORM_BROWSER_FORM *Form; + + Link = GetFirstNode (&FormSet->FormListHead); + while (!IsNull (&FormSet->FormListHead, Link)) { + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + + // + // Initialize local copy of Value for each Form + // + Status = LoadFormConfig (Selection, FormSet, Form); + if (EFI_ERROR (Status)) { + return Status; + } + + Link = GetNextNode (&FormSet->FormListHead, Link); + } + + // + // Finished question initialization. + // + FormSet->QuestionInited = TRUE; + + return EFI_SUCCESS; +} + +/** + Remove the Request element from the Config Request. + + @param Storage Pointer to the browser storage. + @param RequestElement The pointer to the Request element. + +**/ +VOID +RemoveElement ( + IN OUT BROWSER_STORAGE *Storage, + IN CHAR16 *RequestElement + ) +{ + CHAR16 *NewStr; + CHAR16 *DestStr; + + ASSERT (Storage->ConfigRequest != NULL && RequestElement != NULL); + + NewStr = StrStr (Storage->ConfigRequest, RequestElement); + + if (NewStr == NULL) { + return; + } + + // + // Remove this element from this ConfigRequest. + // + DestStr = NewStr; + NewStr += StrLen (RequestElement); + CopyMem (DestStr, NewStr, StrSize (NewStr)); + + Storage->SpareStrLen += StrLen (RequestElement); +} + +/** + Adjust config request in storage, remove the request elements existed in the input ConfigRequest. + + @param Storage Pointer to the formset storage. + @param ConfigRequest The pointer to the Request element. + +**/ +VOID +RemoveConfigRequest ( + FORMSET_STORAGE *Storage, + CHAR16 *ConfigRequest + ) +{ + CHAR16 *RequestElement; + CHAR16 *NextRequestElement; + CHAR16 *SearchKey; + + // + // No request element in it, just return. + // + if (ConfigRequest == NULL) { + return; + } + + if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + // + // "&Name1&Name2" section for EFI_HII_VARSTORE_NAME_VALUE storage + // + SearchKey = L"&"; + } else { + // + // "&OFFSET=####&WIDTH=####" section for EFI_HII_VARSTORE_BUFFER storage + // + SearchKey = L"&OFFSET"; + } + + // + // Find SearchKey storage + // + if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + RequestElement = StrStr (ConfigRequest, L"PATH"); + ASSERT (RequestElement != NULL); + RequestElement = StrStr (RequestElement, SearchKey); + } else { + RequestElement = StrStr (ConfigRequest, SearchKey); + } + + while (RequestElement != NULL) { + // + // +1 to avoid find header itself. + // + NextRequestElement = StrStr (RequestElement + 1, SearchKey); + + // + // The last Request element in configRequest string. + // + if (NextRequestElement != NULL) { + // + // Replace "&" with '\0'. + // + *NextRequestElement = L'\0'; + } + + RemoveElement (Storage->BrowserStorage, RequestElement); + + if (NextRequestElement != NULL) { + // + // Restore '&' with '\0' for later used. + // + *NextRequestElement = L'&'; + } + + RequestElement = NextRequestElement; + } + + // + // If no request element remain, just remove the ConfigRequest string. + // + if (StrCmp (Storage->BrowserStorage->ConfigRequest, Storage->ConfigHdr) == 0) { + FreePool (Storage->BrowserStorage->ConfigRequest); + Storage->BrowserStorage->ConfigRequest = NULL; + Storage->BrowserStorage->SpareStrLen = 0; + } +} + +/** + Base on the current formset info, clean the ConfigRequest string in browser storage. + + @param FormSet Pointer of the FormSet + +**/ +VOID +CleanBrowserStorage ( + IN OUT FORM_BROWSER_FORMSET *FormSet + ) +{ + LIST_ENTRY *Link; + FORMSET_STORAGE *Storage; + + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + Storage = FORMSET_STORAGE_FROM_LINK (Link); + Link = GetNextNode (&FormSet->StorageListHead, Link); + + if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER) { + if (Storage->ConfigRequest == NULL || Storage->BrowserStorage->ConfigRequest == NULL) { + continue; + } + + RemoveConfigRequest (Storage, Storage->ConfigRequest); + } else if (Storage->BrowserStorage->Type == EFI_HII_VARSTORE_BUFFER || + Storage->BrowserStorage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + if (Storage->BrowserStorage->ConfigRequest != NULL) { + FreePool (Storage->BrowserStorage->ConfigRequest); + Storage->BrowserStorage->ConfigRequest = NULL; + } + Storage->BrowserStorage->Initialized = FALSE; + } + } +} + +/** + Check whether current element in the ConfigReqeust string. + + @param BrowserStorage Storage which includes ConfigReqeust. + @param RequestElement New element need to check. + + @retval TRUE The Element is in the ConfigReqeust string. + @retval FALSE The Element not in the configReqeust String. + +**/ +BOOLEAN +ElementValidation ( + BROWSER_STORAGE *BrowserStorage, + CHAR16 *RequestElement + ) +{ + return StrStr (BrowserStorage->ConfigRequest, RequestElement) != NULL ? TRUE : FALSE; +} + +/** + Append the Request element to the Config Request. + + @param ConfigRequest Current ConfigRequest info. + @param SpareStrLen Current remain free buffer for config reqeust. + @param RequestElement New Request element. + +**/ +VOID +AppendConfigRequest ( + IN OUT CHAR16 **ConfigRequest, + IN OUT UINTN *SpareStrLen, + IN CHAR16 *RequestElement + ) +{ + CHAR16 *NewStr; + UINTN StringSize; + UINTN StrLength; + UINTN MaxLen; + + StrLength = StrLen (RequestElement); + StringSize = (*ConfigRequest != NULL) ? StrSize (*ConfigRequest) : sizeof (CHAR16); + MaxLen = StringSize / sizeof (CHAR16) + *SpareStrLen; + + // + // Append to + // + if (StrLength > *SpareStrLen) { + // + // Old String buffer is not sufficient for RequestElement, allocate a new one + // + MaxLen = StringSize / sizeof (CHAR16) + CONFIG_REQUEST_STRING_INCREMENTAL; + NewStr = AllocateZeroPool (MaxLen * sizeof (CHAR16)); + ASSERT (NewStr != NULL); + + if (*ConfigRequest != NULL) { + CopyMem (NewStr, *ConfigRequest, StringSize); + FreePool (*ConfigRequest); + } + *ConfigRequest = NewStr; + *SpareStrLen = CONFIG_REQUEST_STRING_INCREMENTAL; + } + + StrCatS (*ConfigRequest, MaxLen, RequestElement); + *SpareStrLen -= StrLength; +} + +/** + Adjust the config request info, remove the request elements which already in AllConfigRequest string. + + @param Storage Form set Storage. + @param Request The input request string. + @param RespString Whether the input is ConfigRequest or ConfigResp format. + + @retval TRUE Has element not covered by current used elements, need to continue to call ExtractConfig + @retval FALSE All elements covered by current used elements. + +**/ +BOOLEAN +ConfigRequestAdjust ( + IN BROWSER_STORAGE *Storage, + IN CHAR16 *Request, + IN BOOLEAN RespString + ) +{ + CHAR16 *RequestElement; + CHAR16 *NextRequestElement; + CHAR16 *NextElementBakup; + CHAR16 *SearchKey; + CHAR16 *ValueKey; + BOOLEAN RetVal; + CHAR16 *ConfigRequest; + + RetVal = FALSE; + NextElementBakup = NULL; + ValueKey = NULL; + + if (Request != NULL) { + ConfigRequest = Request; + } else { + ConfigRequest = Storage->ConfigRequest; + } + + if (Storage->ConfigRequest == NULL) { + Storage->ConfigRequest = AllocateCopyPool (StrSize (ConfigRequest), ConfigRequest); + return TRUE; + } + + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + // + // "&Name1&Name2" section for EFI_HII_VARSTORE_NAME_VALUE storage + // + SearchKey = L"&"; + } else { + // + // "&OFFSET=####&WIDTH=####" section for EFI_HII_VARSTORE_BUFFER storage + // + SearchKey = L"&OFFSET"; + ValueKey = L"&VALUE"; + } + + // + // Find SearchKey storage + // + if (Storage->Type == EFI_HII_VARSTORE_NAME_VALUE) { + RequestElement = StrStr (ConfigRequest, L"PATH"); + ASSERT (RequestElement != NULL); + RequestElement = StrStr (RequestElement, SearchKey); + } else { + RequestElement = StrStr (ConfigRequest, SearchKey); + } + + while (RequestElement != NULL) { + + // + // +1 to avoid find header itself. + // + NextRequestElement = StrStr (RequestElement + 1, SearchKey); + + // + // The last Request element in configRequest string. + // + if (NextRequestElement != NULL) { + if (RespString && (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { + NextElementBakup = NextRequestElement; + NextRequestElement = StrStr (RequestElement, ValueKey); + ASSERT (NextRequestElement != NULL); + } + // + // Replace "&" with '\0'. + // + *NextRequestElement = L'\0'; + } else { + if (RespString && (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { + NextElementBakup = NextRequestElement; + NextRequestElement = StrStr (RequestElement, ValueKey); + ASSERT (NextRequestElement != NULL); + // + // Replace "&" with '\0'. + // + *NextRequestElement = L'\0'; + } + } + + if (!ElementValidation (Storage, RequestElement)) { + // + // Add this element to the Storage->BrowserStorage->AllRequestElement. + // + AppendConfigRequest(&Storage->ConfigRequest, &Storage->SpareStrLen, RequestElement); + RetVal = TRUE; + } + + if (NextRequestElement != NULL) { + // + // Restore '&' with '\0' for later used. + // + *NextRequestElement = L'&'; + } + + if (RespString && (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER)) { + RequestElement = NextElementBakup; + } else { + RequestElement = NextRequestElement; + } + } + + return RetVal; +} + +/** + Fill storage's edit copy with settings requested from Configuration Driver. + + @param FormSet FormSet data structure. + @param Storage Buffer Storage. + +**/ +VOID +LoadStorage ( + IN FORM_BROWSER_FORMSET *FormSet, + IN FORMSET_STORAGE *Storage + ) +{ + EFI_STATUS Status; + EFI_STRING Progress; + EFI_STRING Result; + CHAR16 *StrPtr; + EFI_STRING ConfigRequest; + UINTN StrLen; + + ConfigRequest = NULL; + + switch (Storage->BrowserStorage->Type) { + case EFI_HII_VARSTORE_EFI_VARIABLE: + return; + + case EFI_HII_VARSTORE_EFI_VARIABLE_BUFFER: + if (Storage->BrowserStorage->ConfigRequest != NULL) { + ConfigRequestAdjust(Storage->BrowserStorage, Storage->ConfigRequest, FALSE); + return; + } + break; + + case EFI_HII_VARSTORE_BUFFER: + case EFI_HII_VARSTORE_NAME_VALUE: + // + // Skip if there is no RequestElement. + // + if (Storage->ElementCount == 0) { + return; + } + + // + // Just update the ConfigRequest, if storage already initialized. + // + if (Storage->BrowserStorage->Initialized) { + ConfigRequestAdjust(Storage->BrowserStorage, Storage->ConfigRequest, FALSE); + return; + } + + Storage->BrowserStorage->Initialized = TRUE; + break; + + default: + return; + } + + if (Storage->BrowserStorage->Type != EFI_HII_VARSTORE_NAME_VALUE) { + // + // Create the config request string to get all fields for this storage. + // Allocate and fill a buffer large enough to hold the template + // followed by "&OFFSET=0&WIDTH=WWWW"followed by a Null-terminator + // + StrLen = StrSize (Storage->ConfigHdr) + 20 * sizeof (CHAR16); + ConfigRequest = AllocateZeroPool (StrLen); + ASSERT (ConfigRequest != NULL); + UnicodeSPrint ( + ConfigRequest, + StrLen, + L"%s&OFFSET=0&WIDTH=%04x", + Storage->ConfigHdr, + Storage->BrowserStorage->Size); + } else { + ConfigRequest = Storage->ConfigRequest; + } + + // + // Request current settings from Configuration Driver + // + Status = mHiiConfigRouting->ExtractConfig ( + mHiiConfigRouting, + ConfigRequest, + &Progress, + &Result + ); + + // + // If get value fail, extract default from IFR binary + // + if (EFI_ERROR (Status)) { + ExtractDefault (FormSet, NULL, EFI_HII_DEFAULT_CLASS_STANDARD, FormSetLevel, GetDefaultForStorage, Storage->BrowserStorage, TRUE, TRUE); + } else { + // + // Convert Result from to + // + StrPtr = StrStr (Result, L"&GUID="); + if (StrPtr != NULL) { + *StrPtr = L'\0'; + } + + Status = ConfigRespToStorage (Storage->BrowserStorage, Result); + FreePool (Result); + } + + Storage->BrowserStorage->ConfigRequest = AllocateCopyPool (StrSize (Storage->ConfigRequest), Storage->ConfigRequest); + + // + // Input NULL for ConfigRequest field means sync all fields from editbuffer to buffer. + // + SynchronizeStorage(Storage->BrowserStorage, NULL, TRUE); + + if (Storage->BrowserStorage->Type != EFI_HII_VARSTORE_NAME_VALUE) { + if (ConfigRequest != NULL) { + FreePool (ConfigRequest); + } + } +} + +/** + Get Value changed status from old question. + + @param NewFormSet FormSet data structure. + @param OldQuestion Old question which has value changed. + +**/ +VOID +SyncStatusForQuestion ( + IN OUT FORM_BROWSER_FORMSET *NewFormSet, + IN FORM_BROWSER_STATEMENT *OldQuestion + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *QuestionLink; + FORM_BROWSER_FORM *Form; + FORM_BROWSER_STATEMENT *Question; + + // + // For each form in one formset. + // + Link = GetFirstNode (&NewFormSet->FormListHead); + while (!IsNull (&NewFormSet->FormListHead, Link)) { + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&NewFormSet->FormListHead, Link); + + // + // for each question in one form. + // + QuestionLink = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, QuestionLink)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (QuestionLink); + QuestionLink = GetNextNode (&Form->StatementListHead, QuestionLink); + + if (Question->QuestionId == OldQuestion->QuestionId) { + Question->ValueChanged = TRUE; + return; + } + } + } +} + +/** + Get Value changed status from old formset. + + @param NewFormSet FormSet data structure. + @param OldFormSet FormSet data structure. + +**/ +VOID +SyncStatusForFormSet ( + IN OUT FORM_BROWSER_FORMSET *NewFormSet, + IN FORM_BROWSER_FORMSET *OldFormSet + ) +{ + LIST_ENTRY *Link; + LIST_ENTRY *QuestionLink; + FORM_BROWSER_FORM *Form; + FORM_BROWSER_STATEMENT *Question; + + // + // For each form in one formset. + // + Link = GetFirstNode (&OldFormSet->FormListHead); + while (!IsNull (&OldFormSet->FormListHead, Link)) { + Form = FORM_BROWSER_FORM_FROM_LINK (Link); + Link = GetNextNode (&OldFormSet->FormListHead, Link); + + // + // for each question in one form. + // + QuestionLink = GetFirstNode (&Form->StatementListHead); + while (!IsNull (&Form->StatementListHead, QuestionLink)) { + Question = FORM_BROWSER_STATEMENT_FROM_LINK (QuestionLink); + QuestionLink = GetNextNode (&Form->StatementListHead, QuestionLink); + + if (!Question->ValueChanged) { + continue; + } + + // + // Find the same question in new formset and update the value changed flag. + // + SyncStatusForQuestion (NewFormSet, Question); + } + } +} + +/** + Get current setting of Questions. + + @param FormSet FormSet data structure. + +**/ +VOID +InitializeCurrentSetting ( + IN OUT FORM_BROWSER_FORMSET *FormSet + ) +{ + LIST_ENTRY *Link; + FORMSET_STORAGE *Storage; + FORM_BROWSER_FORMSET *OldFormSet; + + // + // Try to find pre FormSet in the maintain backup list. + // If old formset != NULL, destroy this formset. Add new formset to gBrowserFormSetList. + // + OldFormSet = GetFormSetFromHiiHandle (FormSet->HiiHandle); + if (OldFormSet != NULL) { + SyncStatusForFormSet (FormSet, OldFormSet); + RemoveEntryList (&OldFormSet->Link); + DestroyFormSet (OldFormSet); + } + InsertTailList (&gBrowserFormSetList, &FormSet->Link); + + // + // Extract default from IFR binary for no storage questions. + // + ExtractDefault (FormSet, NULL, EFI_HII_DEFAULT_CLASS_STANDARD, FormSetLevel, GetDefaultForNoStorage, NULL, TRUE, FALSE); + + // + // Request current settings from Configuration Driver + // + Link = GetFirstNode (&FormSet->StorageListHead); + while (!IsNull (&FormSet->StorageListHead, Link)) { + Storage = FORMSET_STORAGE_FROM_LINK (Link); + + LoadStorage (FormSet, Storage); + + Link = GetNextNode (&FormSet->StorageListHead, Link); + } +} + + +/** + Fetch the Ifr binary data of a FormSet. + + @param Handle PackageList Handle + @param FormSetGuid On input, GUID or class GUID of a formset. If not + specified (NULL or zero GUID), take the first + FormSet with class GUID EFI_HII_PLATFORM_SETUP_FORMSET_GUID + found in package list. + On output, GUID of the formset found(if not NULL). + @param BinaryLength The length of the FormSet IFR binary. + @param BinaryData The buffer designed to receive the FormSet. + + @retval EFI_SUCCESS Buffer filled with the requested FormSet. + BufferLength was updated. + @retval EFI_INVALID_PARAMETER The handle is unknown. + @retval EFI_NOT_FOUND A form or FormSet on the requested handle cannot + be found with the requested FormId. + +**/ +EFI_STATUS +GetIfrBinaryData ( + IN EFI_HII_HANDLE Handle, + IN OUT EFI_GUID *FormSetGuid, + OUT UINTN *BinaryLength, + OUT UINT8 **BinaryData + ) +{ + EFI_STATUS Status; + EFI_HII_PACKAGE_LIST_HEADER *HiiPackageList; + UINTN BufferSize; + UINT8 *Package; + UINT8 *OpCodeData; + UINT32 Offset; + UINT32 Offset2; + UINT32 PackageListLength; + EFI_HII_PACKAGE_HEADER PackageHeader; + UINT8 Index; + UINT8 NumberOfClassGuid; + BOOLEAN ClassGuidMatch; + EFI_GUID *ClassGuid; + EFI_GUID *ComparingGuid; + + OpCodeData = NULL; + Package = NULL; + ZeroMem (&PackageHeader, sizeof (EFI_HII_PACKAGE_HEADER)); + + // + // if FormSetGuid is NULL or zero GUID, return first Setup FormSet in the package list + // + if (FormSetGuid == NULL) { + ComparingGuid = &gZeroGuid; + } else { + ComparingGuid = FormSetGuid; + } + + // + // Get HII PackageList + // + BufferSize = 0; + HiiPackageList = NULL; + Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, Handle, &BufferSize, HiiPackageList); + if (Status == EFI_BUFFER_TOO_SMALL) { + HiiPackageList = AllocatePool (BufferSize); + ASSERT (HiiPackageList != NULL); + + Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, Handle, &BufferSize, HiiPackageList); + } + if (EFI_ERROR (Status)) { + return Status; + } + ASSERT (HiiPackageList != NULL); + + // + // Get Form package from this HII package List + // + Offset = sizeof (EFI_HII_PACKAGE_LIST_HEADER); + Offset2 = 0; + CopyMem (&PackageListLength, &HiiPackageList->PackageLength, sizeof (UINT32)); + + ClassGuidMatch = FALSE; + while (Offset < PackageListLength) { + Package = ((UINT8 *) HiiPackageList) + Offset; + CopyMem (&PackageHeader, Package, sizeof (EFI_HII_PACKAGE_HEADER)); + + if (PackageHeader.Type == EFI_HII_PACKAGE_FORMS) { + // + // Search FormSet in this Form Package + // + Offset2 = sizeof (EFI_HII_PACKAGE_HEADER); + while (Offset2 < PackageHeader.Length) { + OpCodeData = Package + Offset2; + + if (((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode == EFI_IFR_FORM_SET_OP) { + // + // Try to compare against formset GUID + // + if (IsZeroGuid (ComparingGuid) || + CompareGuid (ComparingGuid, (EFI_GUID *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) { + break; + } + + if (((EFI_IFR_OP_HEADER *) OpCodeData)->Length > OFFSET_OF (EFI_IFR_FORM_SET, Flags)) { + // + // Try to compare against formset class GUID + // + NumberOfClassGuid = (UINT8) (((EFI_IFR_FORM_SET *) OpCodeData)->Flags & 0x3); + ClassGuid = (EFI_GUID *) (OpCodeData + sizeof (EFI_IFR_FORM_SET)); + for (Index = 0; Index < NumberOfClassGuid; Index++) { + if (CompareGuid (ComparingGuid, ClassGuid + Index)) { + ClassGuidMatch = TRUE; + break; + } + } + if (ClassGuidMatch) { + break; + } + } else if (ComparingGuid == &gEfiHiiPlatformSetupFormsetGuid) { + ClassGuidMatch = TRUE; + break; + } + } + + Offset2 += ((EFI_IFR_OP_HEADER *) OpCodeData)->Length; + } + + if (Offset2 < PackageHeader.Length) { + // + // Target formset found + // + break; + } + } + + Offset += PackageHeader.Length; + } + + if (Offset >= PackageListLength) { + // + // Form package not found in this Package List + // + FreePool (HiiPackageList); + return EFI_NOT_FOUND; + } + + if (FormSetGuid != NULL) { + // + // Return the FormSet GUID + // + CopyMem (FormSetGuid, &((EFI_IFR_FORM_SET *) OpCodeData)->Guid, sizeof (EFI_GUID)); + } + + // + // To determine the length of a whole FormSet IFR binary, one have to parse all the Opcodes + // in this FormSet; So, here just simply copy the data from start of a FormSet to the end + // of the Form Package. + // + *BinaryLength = PackageHeader.Length - Offset2; + *BinaryData = AllocateCopyPool (*BinaryLength, OpCodeData); + + FreePool (HiiPackageList); + + if (*BinaryData == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + + +/** + Initialize the internal data structure of a FormSet. + + @param Handle PackageList Handle + @param FormSetGuid On input, GUID or class GUID of a formset. If not + specified (NULL or zero GUID), take the first + FormSet with class GUID EFI_HII_PLATFORM_SETUP_FORMSET_GUID + found in package list. + On output, GUID of the formset found(if not NULL). + @param FormSet FormSet data structure. + + @retval EFI_SUCCESS The function completed successfully. + @retval EFI_NOT_FOUND The specified FormSet could not be found. + +**/ +EFI_STATUS +InitializeFormSet ( + IN EFI_HII_HANDLE Handle, + IN OUT EFI_GUID *FormSetGuid, + OUT FORM_BROWSER_FORMSET *FormSet + ) +{ + EFI_STATUS Status; + EFI_HANDLE DriverHandle; + + Status = GetIfrBinaryData (Handle, FormSetGuid, &FormSet->IfrBinaryLength, &FormSet->IfrBinaryData); + if (EFI_ERROR (Status)) { + return Status; + } + + FormSet->Signature = FORM_BROWSER_FORMSET_SIGNATURE; + FormSet->HiiHandle = Handle; + CopyMem (&FormSet->Guid, FormSetGuid, sizeof (EFI_GUID)); + FormSet->QuestionInited = FALSE; + + // + // Retrieve ConfigAccess Protocol associated with this HiiPackageList + // + Status = mHiiDatabase->GetPackageListHandle (mHiiDatabase, Handle, &DriverHandle); + if (EFI_ERROR (Status)) { + return Status; + } + FormSet->DriverHandle = DriverHandle; + Status = gBS->HandleProtocol ( + DriverHandle, + &gEfiHiiConfigAccessProtocolGuid, + (VOID **) &FormSet->ConfigAccess + ); + if (EFI_ERROR (Status)) { + // + // Configuration Driver don't attach ConfigAccess protocol to its HII package + // list, then there will be no configuration action required + // + FormSet->ConfigAccess = NULL; + } + + // + // Parse the IFR binary OpCodes + // + Status = ParseOpCodes (FormSet); + + return Status; +} + + +/** + Save globals used by previous call to SendForm(). SendForm() may be called from + HiiConfigAccess.Callback(), this will cause SendForm() be reentried. + So, save globals of previous call to SendForm() and restore them upon exit. + +**/ +VOID +SaveBrowserContext ( + VOID + ) +{ + BROWSER_CONTEXT *Context; + FORM_ENTRY_INFO *MenuList; + FORM_BROWSER_FORMSET *FormSet; + + gBrowserContextCount++; + if (gBrowserContextCount == 1) { + // + // This is not reentry of SendForm(), no context to save + // + return; + } + + Context = AllocatePool (sizeof (BROWSER_CONTEXT)); + ASSERT (Context != NULL); + + Context->Signature = BROWSER_CONTEXT_SIGNATURE; + + // + // Save FormBrowser context + // + Context->Selection = gCurrentSelection; + Context->ResetRequired = gResetRequiredFormLevel; + Context->FlagReconnect = gFlagReconnect; + Context->CallbackReconnect = gCallbackReconnect; + Context->ExitRequired = gExitRequired; + Context->HiiHandle = mCurrentHiiHandle; + Context->FormId = mCurrentFormId; + CopyGuid (&Context->FormSetGuid, &mCurrentFormSetGuid); + Context->SystemLevelFormSet = mSystemLevelFormSet; + Context->CurFakeQestId = mCurFakeQestId; + Context->HiiPackageListUpdated = mHiiPackageListUpdated; + Context->FinishRetrieveCall = mFinishRetrieveCall; + + // + // Save the menu history data. + // + InitializeListHead(&Context->FormHistoryList); + while (!IsListEmpty (&mPrivateData.FormBrowserEx2.FormViewHistoryHead)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (mPrivateData.FormBrowserEx2.FormViewHistoryHead.ForwardLink); + RemoveEntryList (&MenuList->Link); + + InsertTailList(&Context->FormHistoryList, &MenuList->Link); + } + + // + // Save formset list. + // + InitializeListHead(&Context->FormSetList); + while (!IsListEmpty (&gBrowserFormSetList)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (gBrowserFormSetList.ForwardLink); + RemoveEntryList (&FormSet->Link); + + InsertTailList(&Context->FormSetList, &FormSet->Link); + } + + // + // Insert to FormBrowser context list + // + InsertHeadList (&gBrowserContextList, &Context->Link); +} + + +/** + Restore globals used by previous call to SendForm(). + +**/ +VOID +RestoreBrowserContext ( + VOID + ) +{ + LIST_ENTRY *Link; + BROWSER_CONTEXT *Context; + FORM_ENTRY_INFO *MenuList; + FORM_BROWSER_FORMSET *FormSet; + + ASSERT (gBrowserContextCount != 0); + gBrowserContextCount--; + if (gBrowserContextCount == 0) { + // + // This is not reentry of SendForm(), no context to restore + // + return; + } + + ASSERT (!IsListEmpty (&gBrowserContextList)); + + Link = GetFirstNode (&gBrowserContextList); + Context = BROWSER_CONTEXT_FROM_LINK (Link); + + // + // Restore FormBrowser context + // + gCurrentSelection = Context->Selection; + gResetRequiredFormLevel = Context->ResetRequired; + gFlagReconnect = Context->FlagReconnect; + gCallbackReconnect = Context->CallbackReconnect; + gExitRequired = Context->ExitRequired; + mCurrentHiiHandle = Context->HiiHandle; + mCurrentFormId = Context->FormId; + CopyGuid (&mCurrentFormSetGuid, &Context->FormSetGuid); + mSystemLevelFormSet = Context->SystemLevelFormSet; + mCurFakeQestId = Context->CurFakeQestId; + mHiiPackageListUpdated = Context->HiiPackageListUpdated; + mFinishRetrieveCall = Context->FinishRetrieveCall; + + // + // Restore the menu history data. + // + while (!IsListEmpty (&Context->FormHistoryList)) { + MenuList = FORM_ENTRY_INFO_FROM_LINK (Context->FormHistoryList.ForwardLink); + RemoveEntryList (&MenuList->Link); + + InsertTailList(&mPrivateData.FormBrowserEx2.FormViewHistoryHead, &MenuList->Link); + } + + // + // Restore the Formset data. + // + while (!IsListEmpty (&Context->FormSetList)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Context->FormSetList.ForwardLink); + RemoveEntryList (&FormSet->Link); + + InsertTailList(&gBrowserFormSetList, &FormSet->Link); + } + + // + // Remove from FormBrowser context list + // + RemoveEntryList (&Context->Link); + gBS->FreePool (Context); +} + +/** + Find the matched FormSet context in the backup maintain list based on HiiHandle. + + @param Handle The Hii Handle. + + @return the found FormSet context. If no found, NULL will return. + +**/ +FORM_BROWSER_FORMSET * +GetFormSetFromHiiHandle ( + EFI_HII_HANDLE Handle + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_FORMSET *FormSet; + + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + Link = GetNextNode (&gBrowserFormSetList, Link); + if (!ValidateFormSet(FormSet)) { + continue; + } + if (FormSet->HiiHandle == Handle) { + return FormSet; + } + } + + return NULL; +} + +/** + Check whether the input HII handle is the FormSet that is being used. + + @param Handle The Hii Handle. + + @retval TRUE HII handle is being used. + @retval FALSE HII handle is not being used. + +**/ +BOOLEAN +IsHiiHandleInBrowserContext ( + EFI_HII_HANDLE Handle + ) +{ + LIST_ENTRY *Link; + BROWSER_CONTEXT *Context; + + // + // HiiHandle is Current FormSet. + // + if (mCurrentHiiHandle == Handle) { + return TRUE; + } + + // + // Check whether HiiHandle is in BrowserContext. + // + Link = GetFirstNode (&gBrowserContextList); + while (!IsNull (&gBrowserContextList, Link)) { + Context = BROWSER_CONTEXT_FROM_LINK (Link); + if (Context->HiiHandle == Handle) { + // + // HiiHandle is in BrowserContext + // + return TRUE; + } + Link = GetNextNode (&gBrowserContextList, Link); + } + + return FALSE; +} + +/** + Perform Password check. + Passwork may be encrypted by driver that requires the specific check. + + @param Form Form where Password Statement is in. + @param Statement Password statement + @param PasswordString Password string to be checked. It may be NULL. + NULL means to restore password. + "" string can be used to checked whether old password does exist. + + @return Status Status of Password check. +**/ +EFI_STATUS +EFIAPI +PasswordCheck ( + IN FORM_DISPLAY_ENGINE_FORM *Form, + IN FORM_DISPLAY_ENGINE_STATEMENT *Statement, + IN EFI_STRING PasswordString OPTIONAL + ) +{ + EFI_STATUS Status; + EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess; + EFI_BROWSER_ACTION_REQUEST ActionRequest; + EFI_IFR_TYPE_VALUE IfrTypeValue; + FORM_BROWSER_STATEMENT *Question; + + ConfigAccess = gCurrentSelection->FormSet->ConfigAccess; + Question = GetBrowserStatement(Statement); + ASSERT (Question != NULL); + + if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) == EFI_IFR_FLAG_CALLBACK) { + if (ConfigAccess == NULL) { + return EFI_UNSUPPORTED; + } + } else { + // + // If a password doesn't have the CALLBACK flag, browser will not handle it. + // + return EFI_UNSUPPORTED; + } + + // + // Prepare password string in HII database + // + if (PasswordString != NULL) { + IfrTypeValue.string = NewString (PasswordString, gCurrentSelection->FormSet->HiiHandle); + } else { + IfrTypeValue.string = 0; + } + + // + // Send password to Configuration Driver for validation + // + Status = ConfigAccess->Callback ( + ConfigAccess, + EFI_BROWSER_ACTION_CHANGING, + Question->QuestionId, + Question->HiiValue.Type, + &IfrTypeValue, + &ActionRequest + ); + + // + // Remove password string from HII database + // + if (PasswordString != NULL) { + DeleteString (IfrTypeValue.string, gCurrentSelection->FormSet->HiiHandle); + } + + return Status; +} + +/** + Find the registered HotKey based on KeyData. + + @param[in] KeyData A pointer to a buffer that describes the keystroke + information for the hot key. + + @return The registered HotKey context. If no found, NULL will return. +**/ +BROWSER_HOT_KEY * +GetHotKeyFromRegisterList ( + IN EFI_INPUT_KEY *KeyData + ) +{ + LIST_ENTRY *Link; + BROWSER_HOT_KEY *HotKey; + + Link = GetFirstNode (&gBrowserHotKeyList); + while (!IsNull (&gBrowserHotKeyList, Link)) { + HotKey = BROWSER_HOT_KEY_FROM_LINK (Link); + if (HotKey->KeyData->ScanCode == KeyData->ScanCode) { + return HotKey; + } + Link = GetNextNode (&gBrowserHotKeyList, Link); + } + + return NULL; +} + +/** + Configure what scope the hot key will impact. + All hot keys have the same scope. The mixed hot keys with the different level are not supported. + If no scope is set, the default scope will be FormSet level. + After all registered hot keys are removed, previous Scope can reset to another level. + + @param[in] Scope Scope level to be set. + + @retval EFI_SUCCESS Scope is set correctly. + @retval EFI_INVALID_PARAMETER Scope is not the valid value specified in BROWSER_SETTING_SCOPE. + @retval EFI_UNSPPORTED Scope level is different from current one that the registered hot keys have. + +**/ +EFI_STATUS +EFIAPI +SetScope ( + IN BROWSER_SETTING_SCOPE Scope + ) +{ + if (Scope >= MaxLevel) { + return EFI_INVALID_PARAMETER; + } + + // + // When no hot key registered in system or on the first setting, + // Scope can be set. + // + if (mBrowserScopeFirstSet || IsListEmpty (&gBrowserHotKeyList)) { + gBrowserSettingScope = Scope; + mBrowserScopeFirstSet = FALSE; + } else if (Scope != gBrowserSettingScope) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Register the hot key with its browser action, or unregistered the hot key. + Only support hot key that is not printable character (control key, function key, etc.). + If the action value is zero, the hot key will be unregistered if it has been registered. + If the same hot key has been registered, the new action and help string will override the previous ones. + + @param[in] KeyData A pointer to a buffer that describes the keystroke + information for the hot key. Its type is EFI_INPUT_KEY to + be supported by all ConsoleIn devices. + @param[in] Action Action value that describes what action will be trigged when the hot key is pressed. + @param[in] DefaultId Specifies the type of defaults to retrieve, which is only for DEFAULT action. + @param[in] HelpString Help string that describes the hot key information. + Its value may be NULL for the unregistered hot key. + + @retval EFI_SUCCESS Hot key is registered or unregistered. + @retval EFI_INVALID_PARAMETER KeyData is NULL or HelpString is NULL on register. + @retval EFI_NOT_FOUND KeyData is not found to be unregistered. + @retval EFI_UNSUPPORTED Key represents a printable character. It is conflicted with Browser. + @retval EFI_ALREADY_STARTED Key already been registered for one hot key. +**/ +EFI_STATUS +EFIAPI +RegisterHotKey ( + IN EFI_INPUT_KEY *KeyData, + IN UINT32 Action, + IN UINT16 DefaultId, + IN EFI_STRING HelpString OPTIONAL + ) +{ + BROWSER_HOT_KEY *HotKey; + + // + // Check input parameters. + // + if (KeyData == NULL || KeyData->UnicodeChar != CHAR_NULL || + (Action != BROWSER_ACTION_UNREGISTER && HelpString == NULL)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check whether the input KeyData is in BrowserHotKeyList. + // + HotKey = GetHotKeyFromRegisterList (KeyData); + + // + // Unregister HotKey + // + if (Action == BROWSER_ACTION_UNREGISTER) { + if (HotKey != NULL) { + // + // The registered HotKey is found. + // Remove it from List, and free its resource. + // + RemoveEntryList (&HotKey->Link); + FreePool (HotKey->KeyData); + FreePool (HotKey->HelpString); + return EFI_SUCCESS; + } else { + // + // The registered HotKey is not found. + // + return EFI_NOT_FOUND; + } + } + + if (HotKey != NULL) { + return EFI_ALREADY_STARTED; + } + + // + // Create new Key, and add it into List. + // + HotKey = AllocateZeroPool (sizeof (BROWSER_HOT_KEY)); + ASSERT (HotKey != NULL); + HotKey->Signature = BROWSER_HOT_KEY_SIGNATURE; + HotKey->KeyData = AllocateCopyPool (sizeof (EFI_INPUT_KEY), KeyData); + InsertTailList (&gBrowserHotKeyList, &HotKey->Link); + + // + // Fill HotKey information. + // + HotKey->Action = Action; + HotKey->DefaultId = DefaultId; + if (HotKey->HelpString != NULL) { + FreePool (HotKey->HelpString); + } + HotKey->HelpString = AllocateCopyPool (StrSize (HelpString), HelpString); + + return EFI_SUCCESS; +} + +/** + Register Exit handler function. + When more than one handler function is registered, the latter one will override the previous one. + When NULL handler is specified, the previous Exit handler will be unregistered. + + @param[in] Handler Pointer to handler function. + +**/ +VOID +EFIAPI +RegiserExitHandler ( + IN EXIT_HANDLER Handler + ) +{ + ExitHandlerFunction = Handler; + return; +} + +/** + Check whether the browser data has been modified. + + @retval TRUE Browser data is modified. + @retval FALSE No browser data is modified. + +**/ +BOOLEAN +EFIAPI +IsBrowserDataModified ( + VOID + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_FORMSET *FormSet; + + switch (gBrowserSettingScope) { + case FormLevel: + if (gCurrentSelection == NULL) { + return FALSE; + } + return IsNvUpdateRequiredForForm (gCurrentSelection->Form); + + case FormSetLevel: + if (gCurrentSelection == NULL) { + return FALSE; + } + return IsNvUpdateRequiredForFormSet (gCurrentSelection->FormSet); + + case SystemLevel: + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + if (!ValidateFormSet(FormSet)) { + continue; + } + + if (IsNvUpdateRequiredForFormSet (FormSet)) { + return TRUE; + } + Link = GetNextNode (&gBrowserFormSetList, Link); + } + return FALSE; + + default: + return FALSE; + } +} + +/** + Execute the action requested by the Action parameter. + + @param[in] Action Execute the request action. + @param[in] DefaultId The default Id info when need to load default value. Only used when Action is BROWSER_ACTION_DEFAULT. + + @retval EFI_SUCCESS Execute the request action succss. + @retval EFI_INVALID_PARAMETER The input action value is invalid. + +**/ +EFI_STATUS +EFIAPI +ExecuteAction ( + IN UINT32 Action, + IN UINT16 DefaultId + ) +{ + EFI_STATUS Status; + FORM_BROWSER_FORMSET *FormSet; + FORM_BROWSER_FORM *Form; + + if (gBrowserSettingScope < SystemLevel && gCurrentSelection == NULL) { + return EFI_NOT_READY; + } + + Status = EFI_SUCCESS; + FormSet = NULL; + Form = NULL; + if (gBrowserSettingScope < SystemLevel) { + FormSet = gCurrentSelection->FormSet; + Form = gCurrentSelection->Form; + } + + // + // Executet the discard action. + // + if ((Action & BROWSER_ACTION_DISCARD) != 0) { + Status = DiscardForm (FormSet, Form, gBrowserSettingScope); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Executet the difault action. + // + if ((Action & BROWSER_ACTION_DEFAULT) != 0) { + Status = ExtractDefault (FormSet, Form, DefaultId, gBrowserSettingScope, GetDefaultForAll, NULL, FALSE, FALSE); + if (EFI_ERROR (Status)) { + return Status; + } + UpdateStatementStatus (FormSet, Form, gBrowserSettingScope); + } + + // + // Executet the submit action. + // + if ((Action & BROWSER_ACTION_SUBMIT) != 0) { + Status = SubmitForm (FormSet, Form, gBrowserSettingScope); + if (EFI_ERROR (Status)) { + return Status; + } + } + + // + // Executet the reset action. + // + if ((Action & BROWSER_ACTION_RESET) != 0) { + gResetRequiredFormLevel = TRUE; + gResetRequiredSystemLevel = TRUE; + } + + // + // Executet the exit action. + // + if ((Action & BROWSER_ACTION_EXIT) != 0) { + DiscardForm (FormSet, Form, gBrowserSettingScope); + if (gBrowserSettingScope == SystemLevel) { + if (ExitHandlerFunction != NULL) { + ExitHandlerFunction (); + } + } + + gExitRequired = TRUE; + } + + return Status; +} + +/** + Create reminder to let user to choose save or discard the changed browser data. + Caller can use it to actively check the changed browser data. + + @retval BROWSER_NO_CHANGES No browser data is changed. + @retval BROWSER_SAVE_CHANGES The changed browser data is saved. + @retval BROWSER_DISCARD_CHANGES The changed browser data is discard. + @retval BROWSER_KEEP_CURRENT Browser keep current changes. + +**/ +UINT32 +EFIAPI +SaveReminder ( + VOID + ) +{ + LIST_ENTRY *Link; + FORM_BROWSER_FORMSET *FormSet; + BOOLEAN IsDataChanged; + UINT32 DataSavedAction; + UINT32 ConfirmRet; + + DataSavedAction = BROWSER_NO_CHANGES; + IsDataChanged = FALSE; + Link = GetFirstNode (&gBrowserFormSetList); + while (!IsNull (&gBrowserFormSetList, Link)) { + FormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link); + Link = GetNextNode (&gBrowserFormSetList, Link); + if (!ValidateFormSet(FormSet)) { + continue; + } + if (IsNvUpdateRequiredForFormSet (FormSet)) { + IsDataChanged = TRUE; + break; + } + } + + // + // No data is changed. No save is required. + // + if (!IsDataChanged) { + return DataSavedAction; + } + + // + // If data is changed, prompt user to save or discard it. + // + do { + ConfirmRet = (UINT32) mFormDisplay->ConfirmDataChange(); + + if (ConfirmRet == BROWSER_ACTION_SUBMIT) { + SubmitForm (NULL, NULL, SystemLevel); + DataSavedAction = BROWSER_SAVE_CHANGES; + break; + } else if (ConfirmRet == BROWSER_ACTION_DISCARD) { + DiscardForm (NULL, NULL, SystemLevel); + DataSavedAction = BROWSER_DISCARD_CHANGES; + break; + } else if (ConfirmRet == BROWSER_ACTION_NONE) { + DataSavedAction = BROWSER_KEEP_CURRENT; + break; + } + } while (1); + + return DataSavedAction; +} + +/** + Check whether the Reset Required for the browser + + @retval TRUE Browser required to reset after exit. + @retval FALSE Browser not need to reset after exit. + +**/ +BOOLEAN +EFIAPI +IsResetRequired ( + VOID + ) +{ + return gResetRequiredSystemLevel; +} + -- cgit