| /** @file |
| Copyright (c) 2007, Intel Corporation |
| All rights reserved. This program and the accompanying materials |
| are licensed and made available under the terms and conditions of the BSD License |
| which accompanies this distribution. The full text of the license may be found at |
| http://opensource.org/licenses/bsd-license.php |
| |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. |
| |
| Module Name: |
| |
| Setup.c |
| |
| Abstract: |
| |
| Entry and initialization module for the browser. |
| |
| |
| **/ |
| |
| #include "Setup.h" |
| #include "Ui.h" |
| |
| |
| SETUP_DRIVER_PRIVATE_DATA mPrivateData = { |
| SETUP_DRIVER_SIGNATURE, |
| NULL, |
| { |
| SendForm, |
| BrowserCallback |
| }, |
| { |
| UnicodeVSPrint |
| } |
| }; |
| |
| EFI_HII_DATABASE_PROTOCOL *mHiiDatabase; |
| EFI_HII_STRING_PROTOCOL *mHiiString; |
| EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting; |
| |
| BANNER_DATA *BannerData; |
| EFI_HII_HANDLE FrontPageHandle; |
| UINTN gClassOfVfr; |
| UINTN gFunctionKeySetting; |
| BOOLEAN gResetRequired; |
| BOOLEAN gNvUpdateRequired; |
| EFI_HII_HANDLE gHiiHandle; |
| BOOLEAN gFirstIn; |
| UINT16 gDirection; |
| EFI_SCREEN_DESCRIPTOR gScreenDimensions; |
| BOOLEAN gUpArrow; |
| BOOLEAN gDownArrow; |
| |
| // |
| // Browser Global Strings |
| // |
| CHAR16 *gFunctionOneString; |
| CHAR16 *gFunctionTwoString; |
| CHAR16 *gFunctionNineString; |
| CHAR16 *gFunctionTenString; |
| CHAR16 *gEnterString; |
| CHAR16 *gEnterCommitString; |
| CHAR16 *gEscapeString; |
| CHAR16 *gSaveFailed; |
| CHAR16 *gMoveHighlight; |
| CHAR16 *gMakeSelection; |
| CHAR16 *gDecNumericInput; |
| CHAR16 *gHexNumericInput; |
| CHAR16 *gToggleCheckBox; |
| CHAR16 *gPromptForData; |
| CHAR16 *gPromptForPassword; |
| CHAR16 *gPromptForNewPassword; |
| CHAR16 *gConfirmPassword; |
| CHAR16 *gConfirmError; |
| CHAR16 *gPassowordInvalid; |
| CHAR16 *gPressEnter; |
| CHAR16 *gEmptyString; |
| CHAR16 *gAreYouSure; |
| CHAR16 *gYesResponse; |
| CHAR16 *gNoResponse; |
| CHAR16 *gMiniString; |
| CHAR16 *gPlusString; |
| CHAR16 *gMinusString; |
| CHAR16 *gAdjustNumber; |
| |
| CHAR16 gPromptBlockWidth; |
| CHAR16 gOptionBlockWidth; |
| CHAR16 gHelpBlockWidth; |
| |
| EFI_GUID gZeroGuid = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; |
| EFI_GUID gSetupBrowserGuid = { |
| 0xab368524, 0xb60c, 0x495b, 0xa0, 0x9, 0x12, 0xe8, 0x5b, 0x1a, 0xea, 0x32 |
| }; |
| |
| FUNCTIION_KEY_SETTING gFunctionKeySettingTable[] = { |
| // |
| // Boot Manager |
| // |
| { |
| { |
| 0x847bc3fe, |
| 0xb974, |
| 0x446d, |
| 0x94, |
| 0x49, |
| 0x5a, |
| 0xd5, |
| 0x41, |
| 0x2e, |
| 0x99, |
| 0x3b |
| }, |
| NONE_FUNCTION_KEY_SETTING |
| }, |
| // |
| // Device Manager |
| // |
| { |
| { |
| 0x3ebfa8e6, |
| 0x511d, |
| 0x4b5b, |
| 0xa9, |
| 0x5f, |
| 0xfb, |
| 0x38, |
| 0x26, |
| 0xf, |
| 0x1c, |
| 0x27 |
| }, |
| NONE_FUNCTION_KEY_SETTING |
| }, |
| // |
| // BMM FormSet. |
| // |
| { |
| { |
| 0x642237c7, |
| 0x35d4, |
| 0x472d, |
| 0x83, |
| 0x65, |
| 0x12, |
| 0xe0, |
| 0xcc, |
| 0xf2, |
| 0x7a, |
| 0x22 |
| }, |
| NONE_FUNCTION_KEY_SETTING |
| }, |
| // |
| // BMM File Explorer FormSet. |
| // |
| { |
| { |
| 0x1f2d63e1, |
| 0xfebd, |
| 0x4dc7, |
| 0x9c, |
| 0xc5, |
| 0xba, |
| 0x2b, |
| 0x1c, |
| 0xef, |
| 0x9c, |
| 0x5b |
| }, |
| NONE_FUNCTION_KEY_SETTING |
| }, |
| }; |
| |
| //@MT: EFI_DRIVER_ENTRY_POINT (InitializeSetup) |
| |
| 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 |
| ) |
| /*++ |
| |
| Routine Description: |
| This is the routine which an external caller uses to direct the browser |
| where to obtain it's information. |
| |
| Arguments: |
| This - The Form Browser protocol instanse. |
| Handles - A pointer to an array of Handles. If HandleCount > 1 we |
| display a list of the formsets for the handles specified. |
| HandleCount - The number of Handles specified in Handle. |
| 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. |
| 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. |
| ScreenDimenions - This allows the browser to be called so that it occupies a |
| portion of the physical screen instead of dynamically determining the screen dimensions. |
| ActionRequest - Points to the action recommended by the form. |
| |
| Returns: |
| EFI_SUCCESS - The function completed successfully. |
| EFI_INVALID_PARAMETER - One of the parameters has an invalid value. |
| EFI_NOT_FOUND - No valid forms could be found to display. |
| |
| --*/ |
| { |
| EFI_STATUS Status; |
| UI_MENU_SELECTION *Selection; |
| UINTN Index; |
| FORM_BROWSER_FORMSET *FormSet; |
| |
| Status = EFI_SUCCESS; |
| ZeroMem (&gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); |
| |
| // |
| // Seed the dimensions in the global |
| // |
| gST->ConOut->QueryMode ( |
| gST->ConOut, |
| gST->ConOut->Mode->Mode, |
| &gScreenDimensions.RightColumn, |
| &gScreenDimensions.BottomRow |
| ); |
| |
| if (ScreenDimensions != NULL) { |
| // |
| // Check local dimension vs. global dimension. |
| // |
| if ((gScreenDimensions.RightColumn < ScreenDimensions->RightColumn) || |
| (gScreenDimensions.BottomRow < ScreenDimensions->BottomRow) |
| ) { |
| return EFI_INVALID_PARAMETER; |
| } else { |
| // |
| // Local dimension validation. |
| // |
| if ((ScreenDimensions->RightColumn > ScreenDimensions->LeftColumn) && |
| (ScreenDimensions->BottomRow > ScreenDimensions->TopRow) && |
| ((ScreenDimensions->RightColumn - ScreenDimensions->LeftColumn) > 2) && |
| ( |
| (ScreenDimensions->BottomRow - ScreenDimensions->TopRow) > STATUS_BAR_HEIGHT + |
| SCROLL_ARROW_HEIGHT * |
| 2 + |
| FRONT_PAGE_HEADER_HEIGHT + |
| FOOTER_HEIGHT + |
| 1 |
| ) |
| ) { |
| CopyMem (&gScreenDimensions, (VOID *) ScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); |
| } else { |
| return EFI_INVALID_PARAMETER; |
| } |
| } |
| } |
| |
| gOptionBlockWidth = (CHAR16) ((gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn) / 3); |
| gHelpBlockWidth = gOptionBlockWidth; |
| gPromptBlockWidth = gOptionBlockWidth; |
| |
| // |
| // Initialize the strings for the browser, upon exit of the browser, the strings will be freed |
| // |
| InitializeBrowserStrings (); |
| |
| gFunctionKeySetting = DEFAULT_FUNCTION_KEY_SETTING; |
| gClassOfVfr = EFI_SETUP_APPLICATION_SUBCLASS; |
| |
| // |
| // Ensure we are in Text mode |
| // |
| if (gFirstIn) { |
| gFirstIn = FALSE; |
| gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); |
| DisableQuietBoot (); |
| } |
| |
| 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; |
| } |
| |
| do { |
| FormSet = AllocateZeroPool (sizeof (FORM_BROWSER_FORMSET)); |
| ASSERT (FormSet != NULL); |
| |
| // |
| // Initialize internal data structures of FormSet |
| // |
| Status = InitializeFormSet (Selection->Handle, &Selection->FormSetGuid, FormSet); |
| if (EFI_ERROR (Status)) { |
| DestroyFormSet (FormSet); |
| break; |
| } |
| Selection->FormSet = FormSet; |
| |
| // |
| // Initialize current settings of Questions in this FormSet |
| // |
| Status = InitializeCurrentSetting (FormSet); |
| if (EFI_ERROR (Status)) { |
| DestroyFormSet (FormSet); |
| break; |
| } |
| |
| // |
| // Display this formset |
| // |
| gCurrentSelection = Selection; |
| |
| Status = SetupBrowser (Selection); |
| |
| gCurrentSelection = NULL; |
| DestroyFormSet (FormSet); |
| |
| if (EFI_ERROR (Status)) { |
| break; |
| } |
| |
| } while (Selection->Action == UI_ACTION_REFRESH_FORMSET); |
| |
| gBS->FreePool (Selection); |
| } |
| |
| if (ActionRequest != NULL) { |
| *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE; |
| if (gResetRequired) { |
| *ActionRequest = EFI_BROWSER_ACTION_REQUEST_RESET; |
| } |
| } |
| |
| FreeBrowserStrings (); |
| |
| gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); |
| gST->ConOut->ClearScreen (gST->ConOut); |
| |
| return Status; |
| } |
| |
| |
| /** |
| This function is called by a callback handler to retrieve uncommitted state |
| data from the browser. |
| |
| @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; |
| FORMSET_STORAGE *Storage; |
| FORM_BROWSER_FORMSET *FormSet; |
| BOOLEAN Found; |
| CHAR16 *ConfigResp; |
| CHAR16 *StrPtr; |
| UINTN BufferSize; |
| UINTN TmpSize; |
| |
| if (ResultsDataSize == NULL || ResultsData == NULL) { |
| return EFI_INVALID_PARAMETER; |
| } |
| |
| if (gCurrentSelection == NULL) { |
| return EFI_NOT_READY; |
| } |
| |
| Storage = NULL; |
| ConfigResp = NULL; |
| FormSet = gCurrentSelection->FormSet; |
| |
| // |
| // Find target storage |
| // |
| Link = GetFirstNode (&FormSet->StorageListHead); |
| if (IsNull (&FormSet->StorageListHead, Link)) { |
| return EFI_UNSUPPORTED; |
| } |
| |
| if (VariableGuid != NULL) { |
| // |
| // Try to find target storage |
| // |
| Found = FALSE; |
| while (!IsNull (&FormSet->StorageListHead, Link)) { |
| Storage = FORMSET_STORAGE_FROM_LINK (Link); |
| Link = GetNextNode (&FormSet->StorageListHead, Link); |
| |
| if (CompareGuid (&Storage->Guid, (EFI_GUID *) VariableGuid)) { |
| if (Storage->Type == EFI_HII_VARSTORE_BUFFER) { |
| // |
| // Buffer storage require both GUID and Name |
| // |
| if (VariableName == NULL) { |
| return EFI_NOT_FOUND; |
| } |
| |
| if (StrCmp (Storage->Name, (CHAR16 *) VariableName) != 0) { |
| continue; |
| } |
| } |
| Found = TRUE; |
| break; |
| } |
| } |
| |
| if (!Found) { |
| return EFI_NOT_FOUND; |
| } |
| } else { |
| // |
| // GUID/Name is not specified, take the first storage in FormSet |
| // |
| Storage = FORMSET_STORAGE_FROM_LINK (Link); |
| } |
| |
| if (RetrieveData) { |
| // |
| // Skip if there is no RequestElement |
| // |
| if (Storage->ElementCount == 0) { |
| return EFI_SUCCESS; |
| } |
| |
| // |
| // Generate <ConfigResp> |
| // |
| Status = StorageToConfigResp (Storage, &ConfigResp); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| // |
| // Skip <ConfigHdr> and '&' to point to <ConfigBody> |
| // |
| StrPtr = ConfigResp + StrLen (Storage->ConfigHdr) + 1; |
| |
| BufferSize = StrSize (StrPtr); |
| if (*ResultsDataSize < BufferSize) { |
| *ResultsDataSize = BufferSize; |
| |
| gBS->FreePool (ConfigResp); |
| return EFI_BUFFER_TOO_SMALL; |
| } |
| |
| *ResultsDataSize = BufferSize; |
| CopyMem (ResultsData, StrPtr, BufferSize); |
| |
| gBS->FreePool (ConfigResp); |
| } else { |
| // |
| // Prepare <ConfigResp> |
| // |
| TmpSize = StrLen (ResultsData); |
| BufferSize = (TmpSize + StrLen (Storage->ConfigHdr) + 2) * sizeof (CHAR16); |
| ConfigResp = AllocateZeroPool (BufferSize); |
| ASSERT (ConfigResp != NULL); |
| |
| StrCpy (ConfigResp, Storage->ConfigHdr); |
| StrCat (ConfigResp, L"&"); |
| StrCat (ConfigResp, ResultsData); |
| |
| // |
| // Update Browser uncommited data |
| // |
| Status = ConfigRespToStorage (Storage, ConfigResp); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Initialize Setup |
| |
| @param entry EFI_IMAGE_ENTRY_POINT) |
| |
| @retval EFI_SUCCESS Setup loaded. |
| @retval other Setup Error |
| |
| **/ |
| EFI_STATUS |
| EFIAPI |
| InitializeSetup ( |
| IN EFI_HANDLE ImageHandle, |
| IN EFI_SYSTEM_TABLE *SystemTable |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_HANDLE HiiDriverHandle; |
| EFI_HII_PACKAGE_LIST_HEADER *PackageList; |
| |
| //@MT: EfiInitializeDriverLib (ImageHandle, SystemTable); |
| |
| // |
| // Locate required Hii relative protocols |
| // |
| Status = gBS->LocateProtocol ( |
| &gEfiHiiDatabaseProtocolGuid, |
| NULL, |
| (VOID **) &mHiiDatabase |
| ); |
| ASSERT_EFI_ERROR (Status); |
| |
| Status = gBS->LocateProtocol ( |
| &gEfiHiiStringProtocolGuid, |
| NULL, |
| (VOID **) &mHiiString |
| ); |
| ASSERT_EFI_ERROR (Status); |
| |
| Status = gBS->LocateProtocol ( |
| &gEfiHiiConfigRoutingProtocolGuid, |
| NULL, |
| (VOID **) &mHiiConfigRouting |
| ); |
| ASSERT_EFI_ERROR (Status); |
| |
| // |
| // Publish our HII data |
| // |
| Status = HiiLibCreateHiiDriverHandle (&HiiDriverHandle); |
| ASSERT_EFI_ERROR (Status); |
| |
| PackageList = PreparePackageList (1, &gSetupBrowserGuid, SetupBrowserStrings); |
| ASSERT (PackageList != NULL); |
| Status = mHiiDatabase->NewPackageList ( |
| mHiiDatabase, |
| PackageList, |
| HiiDriverHandle, |
| &gHiiHandle |
| ); |
| ASSERT_EFI_ERROR (Status); |
| |
| // |
| // Initialize Driver private data |
| // |
| gFirstIn = TRUE; |
| BannerData = AllocateZeroPool (sizeof (BANNER_DATA)); |
| ASSERT (BannerData != NULL); |
| |
| // |
| // Install FormBrowser2 protocol |
| // |
| mPrivateData.Handle = NULL; |
| Status = gBS->InstallProtocolInterface ( |
| &mPrivateData.Handle, |
| &gEfiFormBrowser2ProtocolGuid, |
| EFI_NATIVE_INTERFACE, |
| &mPrivateData.FormBrowser2 |
| ); |
| ASSERT_EFI_ERROR (Status); |
| |
| // |
| // Install Print protocol |
| // |
| Status = gBS->InstallProtocolInterface ( |
| &mPrivateData.Handle, |
| &gEfiPrintProtocolGuid, |
| EFI_NATIVE_INTERFACE, |
| &mPrivateData.Print |
| ); |
| |
| return Status; |
| } |
| |
| |
| /** |
| 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; |
| EFI_STATUS Status; |
| |
| StringId = 0; |
| Status = IfrLibNewString (HiiHandle, &StringId, String); |
| ASSERT_EFI_ERROR (Status); |
| |
| 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; |
| return IfrLibSetString (HiiHandle, StringId, &NullChar); |
| } |
| |
| |
| /** |
| 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_STATUS Status; |
| CHAR16 *String; |
| UINTN BufferLength; |
| |
| // |
| // Set default string size assumption at no more than 256 bytes |
| // |
| BufferLength = 0x100; |
| String = AllocateZeroPool (BufferLength); |
| ASSERT (String != NULL); |
| |
| Status = IfrLibGetString (HiiHandle, Token, String, &BufferLength); |
| |
| if (Status == EFI_BUFFER_TOO_SMALL) { |
| gBS->FreePool (String); |
| String = AllocateZeroPool (BufferLength); |
| ASSERT (String != NULL); |
| |
| Status = IfrLibGetString (HiiHandle, Token, String, &BufferLength); |
| } |
| ASSERT_EFI_ERROR (Status); |
| |
| return String; |
| } |
| |
| |
| /** |
| Allocate new memory and then copy the Unicode string Source to Destination. |
| |
| @param Dest Location to copy string |
| @param Src String to copy |
| |
| @return NONE |
| |
| **/ |
| VOID |
| NewStringCpy ( |
| IN OUT CHAR16 **Dest, |
| IN CHAR16 *Src |
| ) |
| { |
| SafeFreePool (*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. |
| |
| @return NONE |
| |
| **/ |
| VOID |
| NewStringCat ( |
| IN OUT CHAR16 **Dest, |
| IN CHAR16 *Src |
| ) |
| { |
| CHAR16 *NewString; |
| UINTN TmpSize; |
| |
| if (*Dest == NULL) { |
| NewStringCpy (Dest, Src); |
| return; |
| } |
| |
| TmpSize = StrSize (*Dest); |
| NewString = AllocateZeroPool (TmpSize + StrSize (Src) - 1); |
| ASSERT (NewString != NULL); |
| |
| StrCpy (NewString, *Dest); |
| StrCat (NewString, Src); |
| |
| gBS->FreePool (*Dest); |
| *Dest = NewString; |
| } |
| |
| |
| /** |
| Synchronize Storage's Edit copy to Shadow copy. |
| |
| @param Storage The Storage to be synchronized. |
| |
| @return NONE |
| |
| **/ |
| VOID |
| SynchronizeStorage ( |
| IN FORMSET_STORAGE *Storage |
| ) |
| { |
| LIST_ENTRY *Link; |
| NAME_VALUE_NODE *Node; |
| |
| switch (Storage->Type) { |
| case EFI_HII_VARSTORE_BUFFER: |
| CopyMem (Storage->Buffer, Storage->EditBuffer, Storage->Size); |
| break; |
| |
| case EFI_HII_VARSTORE_NAME_VALUE: |
| Link = GetFirstNode (&Storage->NameValueListHead); |
| while (!IsNull (&Storage->NameValueListHead, Link)) { |
| Node = NAME_VALUE_NODE_FROM_LINK (Link); |
| |
| NewStringCpy (&Node->Value, Node->EditValue); |
| |
| Link = GetNextNode (&Storage->NameValueListHead, Link); |
| } |
| break; |
| |
| case EFI_HII_VARSTORE_EFI_VARIABLE: |
| default: |
| break; |
| } |
| } |
| |
| |
| /** |
| Get Value for given Name from a NameValue Storage. |
| |
| @param Storage The NameValue Storage. |
| @param Name The Name. |
| @param Value The retured Value. |
| |
| @retval EFI_SUCCESS Value found for given Name. |
| @retval EFI_NOT_FOUND No such Name found in NameValue storage. |
| |
| **/ |
| EFI_STATUS |
| GetValueByName ( |
| IN FORMSET_STORAGE *Storage, |
| IN CHAR16 *Name, |
| IN OUT CHAR16 **Value |
| ) |
| { |
| LIST_ENTRY *Link; |
| NAME_VALUE_NODE *Node; |
| |
| *Value = NULL; |
| |
| Link = GetFirstNode (&Storage->NameValueListHead); |
| while (!IsNull (&Storage->NameValueListHead, Link)) { |
| Node = NAME_VALUE_NODE_FROM_LINK (Link); |
| |
| if (StrCmp (Name, Node->Name) == 0) { |
| NewStringCpy (Value, Node->EditValue); |
| 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. |
| |
| @retval EFI_SUCCESS Value found for given Name. |
| @retval EFI_NOT_FOUND No such Name found in NameValue storage. |
| |
| **/ |
| EFI_STATUS |
| SetValueByName ( |
| IN FORMSET_STORAGE *Storage, |
| IN CHAR16 *Name, |
| IN CHAR16 *Value |
| ) |
| { |
| LIST_ENTRY *Link; |
| NAME_VALUE_NODE *Node; |
| |
| Link = GetFirstNode (&Storage->NameValueListHead); |
| while (!IsNull (&Storage->NameValueListHead, Link)) { |
| Node = NAME_VALUE_NODE_FROM_LINK (Link); |
| |
| if (StrCmp (Name, Node->Name) == 0) { |
| SafeFreePool (Node->EditValue); |
| Node->EditValue = AllocateCopyPool (StrSize (Value), Value); |
| ASSERT (Node->EditValue != NULL); |
| return EFI_SUCCESS; |
| } |
| |
| Link = GetNextNode (&Storage->NameValueListHead, Link); |
| } |
| |
| return EFI_NOT_FOUND; |
| } |
| |
| |
| /** |
| Convert setting of Buffer Storage or NameValue Storage to <ConfigResp>. |
| |
| @param Storage The Storage to be conveted. |
| @param ConfigResp The returned <ConfigResp>. |
| |
| @retval EFI_SUCCESS Convert success. |
| @retval EFI_INVALID_PARAMETER Incorrect storage type. |
| |
| **/ |
| EFI_STATUS |
| StorageToConfigResp ( |
| IN FORMSET_STORAGE *Storage, |
| IN CHAR16 **ConfigResp |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_STRING Progress; |
| LIST_ENTRY *Link; |
| NAME_VALUE_NODE *Node; |
| |
| Status = EFI_SUCCESS; |
| |
| switch (Storage->Type) { |
| case EFI_HII_VARSTORE_BUFFER: |
| Status = mHiiConfigRouting->BlockToConfig ( |
| mHiiConfigRouting, |
| Storage->ConfigRequest, |
| Storage->EditBuffer, |
| Storage->Size, |
| ConfigResp, |
| &Progress |
| ); |
| break; |
| |
| case EFI_HII_VARSTORE_NAME_VALUE: |
| *ConfigResp = NULL; |
| NewStringCat (ConfigResp, Storage->ConfigHdr); |
| |
| Link = GetFirstNode (&Storage->NameValueListHead); |
| while (!IsNull (&Storage->NameValueListHead, Link)) { |
| Node = NAME_VALUE_NODE_FROM_LINK (Link); |
| |
| NewStringCat (ConfigResp, L"&"); |
| NewStringCat (ConfigResp, Node->Name); |
| NewStringCat (ConfigResp, L"="); |
| NewStringCat (ConfigResp, Node->EditValue); |
| |
| Link = GetNextNode (&Storage->NameValueListHead, Link); |
| } |
| break; |
| |
| case EFI_HII_VARSTORE_EFI_VARIABLE: |
| default: |
| Status = EFI_INVALID_PARAMETER; |
| break; |
| } |
| |
| return Status; |
| } |
| |
| |
| /** |
| Convert <ConfigResp> to settings in Buffer Storage or NameValue Storage. |
| |
| @param Storage The Storage to receive the settings. |
| @param ConfigResp The <ConfigResp> to be converted. |
| |
| @retval EFI_SUCCESS Convert success. |
| @retval EFI_INVALID_PARAMETER Incorrect storage type. |
| |
| **/ |
| EFI_STATUS |
| ConfigRespToStorage ( |
| IN FORMSET_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: |
| BufferSize = Storage->Size; |
| Status = mHiiConfigRouting->ConfigToBlock ( |
| mHiiConfigRouting, |
| ConfigResp, |
| Storage->EditBuffer, |
| &BufferSize, |
| &Progress |
| ); |
| break; |
| |
| case EFI_HII_VARSTORE_NAME_VALUE: |
| 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); |
| } |
| break; |
| |
| case EFI_HII_VARSTORE_EFI_VARIABLE: |
| default: |
| Status = EFI_INVALID_PARAMETER; |
| break; |
| } |
| |
| return Status; |
| } |
| |
| |
| /** |
| Get Question's current Value. |
| |
| @param FormSet FormSet data structure. |
| @param Form Form data structure. |
| @param Question Question to be initialized. |
| @param Cached TRUE: get from Edit copy FALSE: get from original |
| Storage |
| |
| @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 BOOLEAN Cached |
| ) |
| { |
| EFI_STATUS Status; |
| BOOLEAN Enabled; |
| BOOLEAN Pending; |
| UINT8 *Dst; |
| UINTN StorageWidth; |
| EFI_TIME EfiTime; |
| FORMSET_STORAGE *Storage; |
| EFI_IFR_TYPE_VALUE *QuestionValue; |
| CHAR16 *ConfigRequest; |
| CHAR16 *Progress; |
| CHAR16 *Result; |
| CHAR16 *Value; |
| UINTN Length; |
| BOOLEAN IsBufferStorage; |
| BOOLEAN IsString; |
| |
| Status = EFI_SUCCESS; |
| |
| // |
| // Statement don't have storage, skip them |
| // |
| if (Question->QuestionId == 0) { |
| return Status; |
| } |
| |
| // |
| // Question value is provided by an Expression, evaluate it |
| // |
| if (Question->ValueExpression != NULL) { |
| Status = EvaluateExpression (FormSet, Form, Question->ValueExpression); |
| if (!EFI_ERROR (Status)) { |
| CopyMem (&Question->HiiValue, &Question->ValueExpression->Result, sizeof (EFI_HII_VALUE)); |
| } |
| 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) { |
| 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; |
| } |
| |
| IsBufferStorage = (BOOLEAN) ((Storage->Type == EFI_HII_VARSTORE_BUFFER) ? TRUE : FALSE); |
| IsString = (BOOLEAN) ((Question->HiiValue.Type == EFI_IFR_TYPE_STRING) ? TRUE : FALSE); |
| if (Cached) { |
| if (IsBufferStorage) { |
| // |
| // Copy from storage Edit buffer |
| // |
| CopyMem (Dst, Storage->EditBuffer + Question->VarStoreInfo.VarOffset, StorageWidth); |
| } else { |
| Status = GetValueByName (Storage, Question->VariableName, &Value); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| if (IsString) { |
| StrCpy ((CHAR16 *) Dst, Value); |
| } else { |
| Status = R8_HexStringToBuf (Dst, &StorageWidth, Value, NULL); |
| } |
| |
| gBS->FreePool (Value); |
| } |
| } else { |
| // |
| // Request current settings from Configuration Driver |
| // |
| if (FormSet->ConfigAccess == NULL) { |
| return EFI_NOT_FOUND; |
| } |
| |
| // |
| // <ConfigRequest> ::= <ConfigHdr> + <BlockName> || |
| // <ConfigHdr> + "&" + <VariableName> |
| // |
| if (IsBufferStorage) { |
| Length = StrLen (Storage->ConfigHdr); |
| Length += StrLen (Question->BlockName); |
| } else { |
| Length = StrLen (Storage->ConfigHdr); |
| Length += StrLen (Question->VariableName) + 1; |
| } |
| ConfigRequest = AllocateZeroPool ((Length + 1) * sizeof (CHAR16)); |
| ASSERT (ConfigRequest != NULL); |
| |
| StrCpy (ConfigRequest, Storage->ConfigHdr); |
| if (IsBufferStorage) { |
| StrCat (ConfigRequest, Question->BlockName); |
| } else { |
| StrCat (ConfigRequest, L"&"); |
| StrCat (ConfigRequest, Question->VariableName); |
| } |
| |
| Status = FormSet->ConfigAccess->ExtractConfig ( |
| FormSet->ConfigAccess, |
| ConfigRequest, |
| &Progress, |
| &Result |
| ); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| // |
| // Skip <ConfigRequest> |
| // |
| Value = Result + Length; |
| if (IsBufferStorage) { |
| // |
| // Skip "&VALUE" |
| // |
| Value = Value + 6; |
| } |
| if (*Value != '=') { |
| gBS->FreePool (Result); |
| return EFI_NOT_FOUND; |
| } |
| // |
| // Skip '=', point to value |
| // |
| Value = Value + 1; |
| if (!IsBufferStorage && IsString) { |
| StrCpy ((CHAR16 *) Dst, Value); |
| } else { |
| Status = R8_HexStringToBuf (Dst, &StorageWidth, Value, NULL); |
| if (EFI_ERROR (Status)) { |
| gBS->FreePool (Result); |
| return Status; |
| } |
| } |
| |
| // |
| // Synchronize Edit Buffer |
| // |
| if (IsBufferStorage) { |
| CopyMem (Storage->EditBuffer + Question->VarStoreInfo.VarOffset, Dst, StorageWidth); |
| } else { |
| SetValueByName (Storage, Question->VariableName, Value); |
| } |
| gBS->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 Cached TRUE: set to Edit copy FALSE: set to original |
| Storage |
| |
| @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 BOOLEAN Cached |
| ) |
| { |
| EFI_STATUS Status; |
| BOOLEAN Enabled; |
| BOOLEAN Pending; |
| UINT8 *Src; |
| EFI_TIME EfiTime; |
| UINTN BufferLen; |
| UINTN StorageWidth; |
| FORMSET_STORAGE *Storage; |
| EFI_IFR_TYPE_VALUE *QuestionValue; |
| CHAR16 *ConfigResp; |
| CHAR16 *Progress; |
| CHAR16 *Value; |
| UINTN Length; |
| BOOLEAN IsBufferStorage; |
| BOOLEAN IsString; |
| |
| Status = EFI_SUCCESS; |
| |
| // |
| // Statement don't have storage, skip them |
| // |
| if (Question->QuestionId == 0) { |
| return Status; |
| } |
| |
| // |
| // If Question value is provided by an Expression, then it is read only |
| // |
| if (Question->ValueExpression != NULL) { |
| 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; |
| } |
| |
| IsBufferStorage = (BOOLEAN) ((Storage->Type == EFI_HII_VARSTORE_BUFFER) ? TRUE : FALSE); |
| IsString = (BOOLEAN) ((Question->HiiValue.Type == EFI_IFR_TYPE_STRING) ? TRUE : FALSE); |
| if (IsBufferStorage) { |
| // |
| // Copy to storage edit buffer |
| // |
| CopyMem (Storage->EditBuffer + Question->VarStoreInfo.VarOffset, Src, StorageWidth); |
| } else { |
| if (IsString) { |
| Value = NULL; |
| NewStringCpy (&Value, (CHAR16 *) Src); |
| } else { |
| BufferLen = (StorageWidth * 2 + 1) * sizeof (CHAR16); |
| Value = AllocateZeroPool (BufferLen); |
| ASSERT (Value != NULL); |
| R8_BufToHexString (Value, &BufferLen, Src, StorageWidth); |
| } |
| |
| Status = SetValueByName (Storage, Question->VariableName, Value); |
| gBS->FreePool (Value); |
| } |
| |
| if (!Cached) { |
| // |
| // <ConfigResp> ::= <ConfigHdr> + <BlockName> + "&VALUE=" + "<HexCh>StorageWidth * 2" || |
| // <ConfigHdr> + "&" + <VariableName> + "=" + "<HexCh>StorageWidth * 2" |
| // |
| if (IsBufferStorage) { |
| Length = StrLen (Question->BlockName) + 7; |
| } else { |
| Length = StrLen (Question->VariableName) + 2; |
| } |
| if (!IsBufferStorage && IsString) { |
| Length += StrLen ((CHAR16 *) Src); |
| } else { |
| Length += (StorageWidth * 2); |
| } |
| ConfigResp = AllocateZeroPool ((StrLen (Storage->ConfigHdr) + Length + 1) * sizeof (CHAR16)); |
| ASSERT (ConfigResp != NULL); |
| |
| StrCpy (ConfigResp, Storage->ConfigHdr); |
| if (IsBufferStorage) { |
| StrCat (ConfigResp, Question->BlockName); |
| StrCat (ConfigResp, L"&VALUE="); |
| } else { |
| StrCat (ConfigResp, L"&"); |
| StrCat (ConfigResp, Question->VariableName); |
| StrCat (ConfigResp, L"="); |
| } |
| |
| Value = ConfigResp + StrLen (ConfigResp); |
| if (!IsBufferStorage && IsString) { |
| StrCpy (Value, (CHAR16 *) Src); |
| } else { |
| BufferLen = (StorageWidth * 2 + 1) * sizeof (CHAR16); |
| R8_BufToHexString (Value, &BufferLen, Src, StorageWidth); |
| } |
| |
| // |
| // Submit Question Value to Configuration Driver |
| // |
| if (FormSet->ConfigAccess != NULL) { |
| Status = FormSet->ConfigAccess->RouteConfig ( |
| FormSet->ConfigAccess, |
| ConfigResp, |
| &Progress |
| ); |
| if (EFI_ERROR (Status)) { |
| gBS->FreePool (ConfigResp); |
| return Status; |
| } |
| } |
| gBS->FreePool (ConfigResp); |
| |
| // |
| // Synchronize shadow Buffer |
| // |
| SynchronizeStorage (Storage); |
| } |
| |
| return Status; |
| } |
| |
| |
| /** |
| Perform inconsistent 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: InConsistent or 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; |
| EFI_STRING PopUp; |
| EFI_INPUT_KEY Key; |
| FORM_EXPRESSION *Expression; |
| |
| if (Type == EFI_HII_EXPRESSION_INCONSISTENT_IF) { |
| ListHead = &Question->InconsistentListHead; |
| } else if (Type == EFI_HII_EXPRESSION_NO_SUBMIT_IF) { |
| ListHead = &Question->NoSubmitListHead; |
| } else { |
| 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 (Expression->Result.Value.b) { |
| // |
| // Condition meet, show up error message |
| // |
| if (Expression->Error != 0) { |
| PopUp = GetToken (Expression->Error, FormSet->HiiHandle); |
| do { |
| CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, PopUp, gPressEnter, gEmptyString); |
| } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); |
| gBS->FreePool (PopUp); |
| } |
| |
| return EFI_NOT_READY; |
| } |
| |
| Link = GetNextNode (ListHead, Link); |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Perform NoSubmit check for a Form. |
| |
| @param FormSet FormSet data structure. |
| @param Form Form data structure. |
| |
| @retval EFI_SUCCESS Form validation pass. |
| @retval other Form validation failed. |
| |
| **/ |
| EFI_STATUS |
| NoSubmitCheck ( |
| 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); |
| |
| Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_NO_SUBMIT_IF); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| Link = GetNextNode (&Form->StatementListHead, Link); |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Submit a Form. |
| |
| @param FormSet FormSet data structure. |
| @param Form Form data structure. |
| |
| @retval EFI_SUCCESS The function completed successfully. |
| |
| **/ |
| EFI_STATUS |
| SubmitForm ( |
| IN FORM_BROWSER_FORMSET *FormSet, |
| IN FORM_BROWSER_FORM *Form |
| ) |
| { |
| EFI_STATUS Status; |
| LIST_ENTRY *Link; |
| EFI_STRING ConfigResp; |
| EFI_STRING Progress; |
| FORMSET_STORAGE *Storage; |
| |
| // |
| // Validate the Form by NoSubmit check |
| // |
| Status = NoSubmitCheck (FormSet, Form); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| // |
| // Submit 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->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { |
| continue; |
| } |
| |
| // |
| // Skip if there is no RequestElement |
| // |
| if (Storage->ElementCount == 0) { |
| continue; |
| } |
| |
| // |
| // Prepare <ConfigResp> |
| // |
| Status = StorageToConfigResp (Storage, &ConfigResp); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| // |
| // Send <ConfigResp> to Configuration Driver |
| // |
| if (FormSet->ConfigAccess != NULL) { |
| Status = FormSet->ConfigAccess->RouteConfig ( |
| FormSet->ConfigAccess, |
| ConfigResp, |
| &Progress |
| ); |
| if (EFI_ERROR (Status)) { |
| gBS->FreePool (ConfigResp); |
| return Status; |
| } |
| } |
| gBS->FreePool (ConfigResp); |
| |
| // |
| // Config success, update storage shadow Buffer |
| // |
| SynchronizeStorage (Storage); |
| } |
| |
| gNvUpdateRequired = FALSE; |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Reset Question to its default value. |
| |
| @param FormSet FormSet data structure. |
| @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; |
| |
| Status = EFI_SUCCESS; |
| |
| // |
| // Statement don't have storage, skip them |
| // |
| if (Question->QuestionId == 0) { |
| return Status; |
| } |
| |
| // |
| // There are three ways to specify default value for a Question: |
| // 1, use nested EFI_IFR_DEFAULT (highest priority) |
| // 2, set flags of EFI_ONE_OF_OPTION (provide Standard and Manufacturing default) |
| // 3, set flags of EFI_IFR_CHECKBOX (provide Standard and Manufacturing default) (lowest priority) |
| // |
| HiiValue = &Question->HiiValue; |
| |
| // |
| // 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; |
| } |
| |
| CopyMem (HiiValue, &Default->ValueExpression->Result, sizeof (EFI_HII_VALUE)); |
| } else { |
| // |
| // Default value is embedded in EFI_IFR_DEFAULT |
| // |
| CopyMem (HiiValue, &Default->Value, sizeof (EFI_HII_VALUE)); |
| } |
| |
| 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); |
| |
| if (((DefaultId == EFI_HII_DEFAULT_CLASS_STANDARD) && (Option->Flags & EFI_IFR_OPTION_DEFAULT)) || |
| ((DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) && (Option->Flags & EFI_IFR_OPTION_DEFAULT_MFG)) |
| ) { |
| CopyMem (HiiValue, &Option->Value, sizeof (EFI_HII_VALUE)); |
| |
| return EFI_SUCCESS; |
| } |
| |
| Link = GetNextNode (&Question->OptionListHead, Link); |
| } |
| } |
| } |
| |
| // |
| // 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)) || |
| ((DefaultId == EFI_HII_DEFAULT_CLASS_MANUFACTURING) && (Question->Flags & EFI_IFR_CHECKBOX_DEFAULT_MFG)) |
| ) { |
| HiiValue->Value.b = TRUE; |
| } else { |
| HiiValue->Value.b = FALSE; |
| } |
| |
| return EFI_SUCCESS; |
| } |
| } |
| |
| // |
| // For Questions without default |
| // |
| switch (Question->Operand) { |
| case EFI_IFR_NUMERIC_OP: |
| // |
| // Take minimal value as numeric's default value |
| // |
| HiiValue->Value.u64 = Question->Minimum; |
| break; |
| |
| case EFI_IFR_ONE_OF_OP: |
| // |
| // Take first oneof option as oneof's default value |
| // |
| Link = GetFirstNode (&Question->OptionListHead); |
| if (!IsNull (&Question->OptionListHead, Link)) { |
| Option = QUESTION_OPTION_FROM_LINK (Link); |
| CopyMem (HiiValue, &Option->Value, sizeof (EFI_HII_VALUE)); |
| } |
| 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)) { |
| Option = QUESTION_OPTION_FROM_LINK (Link); |
| |
| Question->BufferValue[Index] = Option->Value.Value.u8; |
| |
| Index++; |
| if (Index >= Question->MaxContainers) { |
| break; |
| } |
| |
| Link = GetNextNode (&Question->OptionListHead, Link); |
| } |
| break; |
| |
| default: |
| Status = EFI_NOT_FOUND; |
| break; |
| } |
| |
| return Status; |
| } |
| |
| |
| /** |
| Reset Questions in a Form to their default value. |
| |
| @param FormSet FormSet data structure. |
| @param Form The Form which to be reset. |
| @param DefaultId The Class of the default. |
| |
| @retval EFI_SUCCESS The function completed successfully. |
| |
| **/ |
| EFI_STATUS |
| ExtractFormDefault ( |
| IN FORM_BROWSER_FORMSET *FormSet, |
| IN FORM_BROWSER_FORM *Form, |
| IN UINT16 DefaultId |
| ) |
| { |
| 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); |
| Link = GetNextNode (&Form->StatementListHead, Link); |
| |
| // |
| // Reset Question to its default value |
| // |
| 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, TRUE); |
| } |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Initialize Question's Edit copy from Storage. |
| |
| @param FormSet FormSet data structure. |
| @param Form Form data structure. |
| |
| @retval EFI_SUCCESS The function completed successfully. |
| |
| **/ |
| EFI_STATUS |
| LoadFormConfig ( |
| 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 |
| // |
| Status = GetQuestionValue (FormSet, Form, Question, TRUE); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| Link = GetNextNode (&Form->StatementListHead, Link); |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Fill storage's edit copy with settings requested from Configuration Driver. |
| |
| @param FormSet FormSet data structure. |
| @param Storage Buffer Storage. |
| |
| @retval EFI_SUCCESS The function completed successfully. |
| |
| **/ |
| EFI_STATUS |
| LoadStorage ( |
| IN FORM_BROWSER_FORMSET *FormSet, |
| IN FORMSET_STORAGE *Storage |
| ) |
| { |
| EFI_STATUS Status; |
| EFI_STRING Progress; |
| EFI_STRING Result; |
| CHAR16 *StrPtr; |
| |
| if (Storage->Type == EFI_HII_VARSTORE_EFI_VARIABLE) { |
| return EFI_SUCCESS; |
| } |
| |
| if (FormSet->ConfigAccess == NULL) { |
| return EFI_NOT_FOUND; |
| } |
| |
| if (Storage->ElementCount == 0) { |
| // |
| // Skip if there is no RequestElement |
| // |
| return EFI_SUCCESS; |
| } |
| |
| // |
| // Request current settings from Configuration Driver |
| // |
| Status = FormSet->ConfigAccess->ExtractConfig ( |
| FormSet->ConfigAccess, |
| Storage->ConfigRequest, |
| &Progress, |
| &Result |
| ); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| // |
| // Convert Result from <ConfigAltResp> to <ConfigResp> |
| // |
| StrPtr = StrStr (Result, L"ALTCFG"); |
| if (StrPtr != NULL) { |
| *StrPtr = L'\0'; |
| } |
| |
| Status = ConfigRespToStorage (Storage, Result); |
| gBS->FreePool (Result); |
| return Status; |
| } |
| |
| |
| /** |
| Get current setting of Questions. |
| |
| @param FormSet FormSet data structure. |
| |
| @retval EFI_SUCCESS The function completed successfully. |
| |
| **/ |
| EFI_STATUS |
| InitializeCurrentSetting ( |
| IN OUT FORM_BROWSER_FORMSET *FormSet |
| ) |
| { |
| LIST_ENTRY *Link; |
| FORMSET_STORAGE *Storage; |
| FORM_BROWSER_FORM *Form; |
| EFI_STATUS Status; |
| |
| // |
| // Extract default from IFR binary |
| // |
| Link = GetFirstNode (&FormSet->FormListHead); |
| while (!IsNull (&FormSet->FormListHead, Link)) { |
| Form = FORM_BROWSER_FORM_FROM_LINK (Link); |
| |
| Status = ExtractFormDefault (FormSet, Form, EFI_HII_DEFAULT_CLASS_STANDARD); |
| |
| Link = GetNextNode (&FormSet->FormListHead, Link); |
| } |
| |
| // |
| // Request current settings from Configuration Driver |
| // |
| Link = GetFirstNode (&FormSet->StorageListHead); |
| while (!IsNull (&FormSet->StorageListHead, Link)) { |
| Storage = FORMSET_STORAGE_FROM_LINK (Link); |
| |
| Status = LoadStorage (FormSet, Storage); |
| |
| // |
| // Now Edit Buffer is filled with default values(lower priority) and current |
| // settings(higher priority), sychronize it to shadow Buffer |
| // |
| if (!EFI_ERROR (Status)) { |
| SynchronizeStorage (Storage); |
| } |
| |
| Link = GetNextNode (&FormSet->StorageListHead, Link); |
| } |
| |
| return EFI_SUCCESS; |
| } |
| |
| |
| /** |
| Fetch the Ifr binary data of a FormSet. |
| |
| @param Handle PackageList Handle |
| @param FormSetGuid GUID of a formset. If not specified (NULL or zero |
| GUID), take the first FormSet found in package |
| list. |
| @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; |
| BOOLEAN ReturnDefault; |
| UINT32 PackageListLength; |
| EFI_HII_PACKAGE_HEADER PackageHeader; |
| |
| OpCodeData = NULL; |
| Package = NULL; |
| ZeroMem (&PackageHeader, sizeof (EFI_HII_PACKAGE_HEADER));; |
| |
| // |
| // if FormSetGuid is NULL or zero GUID, return first FormSet in the package list |
| // |
| if (FormSetGuid == NULL || CompareGuid (FormSetGuid, &gZeroGuid)) { |
| ReturnDefault = TRUE; |
| } else { |
| ReturnDefault = FALSE; |
| } |
| |
| // |
| // 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; |
| } |
| |
| // |
| // Get Form package from this HII package List |
| // |
| Offset = sizeof (EFI_HII_PACKAGE_LIST_HEADER); |
| Offset2 = 0; |
| CopyMem (&PackageListLength, &HiiPackageList->PackageLength, sizeof (UINT32)); |
| |
| while (Offset < PackageListLength) { |
| Package = ((UINT8 *) HiiPackageList) + Offset; |
| CopyMem (&PackageHeader, Package, sizeof (EFI_HII_PACKAGE_HEADER)); |
| |
| if (PackageHeader.Type == EFI_HII_PACKAGE_FORM) { |
| // |
| // 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) { |
| // |
| // Check whether return default FormSet |
| // |
| if (ReturnDefault) { |
| break; |
| } |
| |
| // |
| // FormSet GUID is specified, check it |
| // |
| if (CompareGuid (FormSetGuid, (EFI_GUID *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) { |
| 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 |
| // |
| gBS->FreePool (HiiPackageList); |
| return EFI_NOT_FOUND; |
| } |
| |
| if (ReturnDefault && FormSetGuid != NULL) { |
| // |
| // Return the default 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); |
| |
| gBS->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 GUID of a formset. If not specified (NULL or zero |
| GUID), take the first FormSet found in package |
| list. |
| @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; |
| UINT16 Index; |
| |
| Status = GetIfrBinaryData (Handle, FormSetGuid, &FormSet->IfrBinaryLength, &FormSet->IfrBinaryData); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| FormSet->HiiHandle = Handle; |
| CopyMem (&FormSet->Guid, FormSetGuid, sizeof (EFI_GUID)); |
| |
| // |
| // 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); |
| if (EFI_ERROR (Status)) { |
| return Status; |
| } |
| |
| gClassOfVfr = FormSet->SubClass; |
| if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) { |
| FrontPageHandle = FormSet->HiiHandle; |
| } |
| |
| // |
| // Match GUID to find out the function key setting. If match fail, use the default setting. |
| // |
| for (Index = 0; Index < sizeof (gFunctionKeySettingTable) / sizeof (FUNCTIION_KEY_SETTING); Index++) { |
| if (CompareGuid (&FormSet->Guid, &(gFunctionKeySettingTable[Index].FormSetGuid))) { |
| // |
| // Update the function key setting. |
| // |
| gFunctionKeySetting = gFunctionKeySettingTable[Index].KeySetting; |
| // |
| // Function key prompt can not be displayed if the function key has been disabled. |
| // |
| if ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE) { |
| gFunctionOneString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); |
| } |
| |
| if ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO) { |
| gFunctionTwoString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); |
| } |
| |
| if ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE) { |
| gFunctionNineString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); |
| } |
| |
| if ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN) { |
| gFunctionTenString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle); |
| } |
| } |
| } |
| |
| return Status; |
| } |