/** @file Legacy Boot Maintenance UI implementation. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
(C) Copyright 2018 Hewlett Packard Enterprise Development LP
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "LegacyBootMaintUi.h" LEGACY_BOOT_OPTION_CALLBACK_DATA *mLegacyBootOptionPrivate = NULL; EFI_GUID mLegacyBootOptionGuid = LEGACY_BOOT_OPTION_FORMSET_GUID; CHAR16 mLegacyBootStorageName[] = L"LegacyBootData"; BBS_TYPE mBbsType[] = {BBS_FLOPPY, BBS_HARDDISK, BBS_CDROM, BBS_EMBED_NETWORK, BBS_BEV_DEVICE, BBS_UNKNOWN}; BOOLEAN mFirstEnterLegacyForm = FALSE; /// /// Legacy FD Info from LegacyBios.GetBbsInfo() /// LEGACY_MENU_OPTION LegacyFDMenu = { LEGACY_MENU_OPTION_SIGNATURE, {NULL}, 0 }; /// /// Legacy HD Info from LegacyBios.GetBbsInfo() /// LEGACY_MENU_OPTION LegacyHDMenu = { LEGACY_MENU_OPTION_SIGNATURE, {NULL}, 0 }; /// /// Legacy CD Info from LegacyBios.GetBbsInfo() /// LEGACY_MENU_OPTION LegacyCDMenu = { LEGACY_MENU_OPTION_SIGNATURE, {NULL}, 0 }; /// /// Legacy NET Info from LegacyBios.GetBbsInfo() /// LEGACY_MENU_OPTION LegacyNETMenu = { LEGACY_MENU_OPTION_SIGNATURE, {NULL}, 0 }; /// /// Legacy NET Info from LegacyBios.GetBbsInfo() /// LEGACY_MENU_OPTION LegacyBEVMenu = { LEGACY_MENU_OPTION_SIGNATURE, {NULL}, 0 }; VOID *mLegacyStartOpCodeHandle = NULL; VOID *mLegacyEndOpCodeHandle = NULL; EFI_IFR_GUID_LABEL *mLegacyStartLabel = NULL; EFI_IFR_GUID_LABEL *mLegacyEndLabel = NULL; HII_VENDOR_DEVICE_PATH mLegacyBootOptionHiiVendorDevicePath = { { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { (UINT8) (sizeof (VENDOR_DEVICE_PATH)), (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) } }, { 0x6bc75598, 0x89b4, 0x483d, { 0x91, 0x60, 0x7f, 0x46, 0x9a, 0x96, 0x35, 0x31 } } }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { (UINT8) (END_DEVICE_PATH_LENGTH), (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) } } }; /** Build the LegacyFDMenu LegacyHDMenu LegacyCDMenu according to LegacyBios.GetBbsInfo(). **/ VOID GetLegacyOptions ( VOID ); /** Base on the L"LegacyDevOrder" variable to build the current order data. **/ VOID GetLegacyOptionsOrder ( VOID ); /** Re-order the Boot Option according to the DevOrder. The routine re-orders the Boot Option in BootOption array according to the order specified by DevOrder. @param DevOrder Pointer to buffer containing the BBS Index, high 8-bit value 0xFF indicating a disabled boot option @param DevOrderCount Count of the BBS Index @param EnBootOption Callee allocated buffer containing the enabled Boot Option Numbers @param EnBootOptionCount Count of the enabled Boot Option Numbers @param DisBootOption Callee allocated buffer containing the disabled Boot Option Numbers @param DisBootOptionCount Count of the disabled Boot Option Numbers @return EFI_SUCCESS The function completed successfully. @retval other Contain some error, details see the status return by gRT->SetVariable. **/ EFI_STATUS OrderLegacyBootOption4SameType ( UINT16 *DevOrder, UINTN DevOrderCount, UINT16 **EnBootOption, UINTN *EnBootOptionCount, UINT16 **DisBootOption, UINTN *DisBootOptionCount ) { EFI_STATUS Status; UINT16 *NewBootOption; UINT16 *BootOrder; UINTN BootOrderSize; UINTN Index; UINTN StartPosition; EFI_BOOT_MANAGER_LOAD_OPTION BootOption; CHAR16 OptionName[sizeof ("Boot####")]; UINT16 *BbsIndexArray; UINT16 *DeviceTypeArray; GetEfiGlobalVariable2 (L"BootOrder", (VOID **) &BootOrder, &BootOrderSize); ASSERT (BootOrder != NULL); BbsIndexArray = AllocatePool (BootOrderSize); DeviceTypeArray = AllocatePool (BootOrderSize); *EnBootOption = AllocatePool (BootOrderSize); *DisBootOption = AllocatePool (BootOrderSize); *DisBootOptionCount = 0; *EnBootOptionCount = 0; Index = 0; Status = EFI_SUCCESS; ASSERT (BbsIndexArray != NULL); ASSERT (DeviceTypeArray != NULL); ASSERT (*EnBootOption != NULL); ASSERT (*DisBootOption != NULL); for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", BootOrder[Index]); Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption); ASSERT_EFI_ERROR (Status); if ((DevicePathType (BootOption.FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption.FilePath) == BBS_BBS_DP)) { // // Legacy Boot Option // ASSERT (BootOption.OptionalDataSize == sizeof (LEGACY_BOOT_OPTION_BBS_DATA)); DeviceTypeArray[Index] = ((BBS_BBS_DEVICE_PATH *) BootOption.FilePath)->DeviceType; BbsIndexArray [Index] = ((LEGACY_BOOT_OPTION_BBS_DATA *) BootOption.OptionalData)->BbsIndex; } else { DeviceTypeArray[Index] = BBS_TYPE_UNKNOWN; BbsIndexArray [Index] = 0xFFFF; } EfiBootManagerFreeLoadOption (&BootOption); } // // Record the corresponding Boot Option Numbers according to the DevOrder // Record the EnBootOption and DisBootOption according to the DevOrder // StartPosition = BootOrderSize / sizeof (UINT16); NewBootOption = AllocatePool (DevOrderCount * sizeof (UINT16)); ASSERT (NewBootOption != NULL); while (DevOrderCount-- != 0) { for (Index = 0; Index < BootOrderSize / sizeof (UINT16); Index++) { if (BbsIndexArray[Index] == (DevOrder[DevOrderCount] & 0xFF)) { StartPosition = MIN (StartPosition, Index); NewBootOption[DevOrderCount] = BootOrder[Index]; if ((DevOrder[DevOrderCount] & 0xFF00) == 0xFF00) { (*DisBootOption)[*DisBootOptionCount] = BootOrder[Index]; (*DisBootOptionCount)++; } else { (*EnBootOption)[*EnBootOptionCount] = BootOrder[Index]; (*EnBootOptionCount)++; } break; } } } // // Overwrite the old BootOption // CopyMem (&BootOrder[StartPosition], NewBootOption, (*DisBootOptionCount + *EnBootOptionCount) * sizeof (UINT16)); Status = gRT->SetVariable ( L"BootOrder", &gEfiGlobalVariableGuid, VAR_FLAG, BootOrderSize, BootOrder ); FreePool (NewBootOption); FreePool (DeviceTypeArray); FreePool (BbsIndexArray); return Status; } /** Update the legacy BBS boot option. L"LegacyDevOrder" and gEfiLegacyDevOrderVariableGuid EFI Variable is updated with the new Legacy Boot order. The EFI Variable of "Boot####" and gEfiGlobalVariableGuid is also updated. @param NVMapData The data for legacy BBS boot. @return EFI_SUCCESS The function completed successfully. @retval EFI_NOT_FOUND If L"LegacyDevOrder" and gEfiLegacyDevOrderVariableGuid EFI Variable can not be found. @retval EFI_OUT_OF_RESOURCES Fail to allocate memory resource @retval other Contain some error, details see the status return by gRT->SetVariable. **/ EFI_STATUS UpdateBBSOption ( IN LEGACY_BOOT_NV_DATA *NVMapData ) { UINTN Index; UINTN Index2; UINTN CurrentType; VOID *BootOptionVar; CHAR16 VarName[100]; UINTN OptionSize; EFI_STATUS Status; UINT32 *Attribute; LEGACY_MENU_OPTION *OptionMenu; UINT16 *LegacyDev; UINT16 *InitialLegacyDev; UINT8 *VarData; UINTN VarSize; LEGACY_DEV_ORDER_ENTRY *DevOrder; UINT8 *OriginalPtr; UINT8 *DisMap; UINTN Pos; UINTN Bit; UINT16 *NewOrder; UINT16 Tmp; UINT16 *EnBootOption; UINTN EnBootOptionCount; UINT16 *DisBootOption; UINTN DisBootOptionCount; UINTN BufferSize; DisMap = NULL; NewOrder = NULL; CurrentType = 0; EnBootOption = NULL; DisBootOption = NULL; DisMap = mLegacyBootOptionPrivate->MaintainMapData->DisableMap; Status = EFI_SUCCESS; // // Update the Variable "LegacyDevOrder" // GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &VarData, &VarSize); if (VarData == NULL) { return EFI_NOT_FOUND; } OriginalPtr = VarData; while (mBbsType[CurrentType] != BBS_UNKNOWN) { switch (mBbsType[CurrentType]) { case BBS_FLOPPY: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyFDMenu; LegacyDev = NVMapData->LegacyFD; InitialLegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyFD; BufferSize = sizeof (NVMapData->LegacyFD); break; case BBS_HARDDISK: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyHDMenu; LegacyDev = NVMapData->LegacyHD; InitialLegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyHD; BufferSize = sizeof (NVMapData->LegacyHD); break; case BBS_CDROM: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyCDMenu; LegacyDev = NVMapData->LegacyCD; InitialLegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyCD; BufferSize = sizeof (NVMapData->LegacyCD); break; case BBS_EMBED_NETWORK: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyNETMenu; LegacyDev = NVMapData->LegacyNET; InitialLegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyNET; BufferSize = sizeof (NVMapData->LegacyNET); break; default: ASSERT (mBbsType[CurrentType] == BBS_BEV_DEVICE); OptionMenu = (LEGACY_MENU_OPTION *) &LegacyBEVMenu; LegacyDev = NVMapData->LegacyBEV; InitialLegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyBEV; BufferSize = sizeof (NVMapData->LegacyBEV); break; } // // Check whether has value changed. // if (CompareMem (LegacyDev, InitialLegacyDev, BufferSize) == 0) { CurrentType++; continue; } DevOrder = (LEGACY_DEV_ORDER_ENTRY *) OriginalPtr; while (VarData < OriginalPtr + VarSize) { if (DevOrder->BbsType == mBbsType[CurrentType]) { break; } VarData += sizeof (BBS_TYPE) + DevOrder->Length; DevOrder = (LEGACY_DEV_ORDER_ENTRY *) VarData; } if (VarData >= OriginalPtr + VarSize) { FreePool (OriginalPtr); return EFI_NOT_FOUND; } NewOrder = AllocateZeroPool (DevOrder->Length - sizeof (DevOrder->Length)); if (NewOrder == NULL) { FreePool (OriginalPtr); return EFI_OUT_OF_RESOURCES; } for (Index = 0; Index < OptionMenu->MenuNumber; Index++) { if (0xFF == LegacyDev[Index]) { break; } NewOrder[Index] = LegacyDev[Index]; } // // Only the enable/disable state of each boot device with same device type can be changed, // so we can count on the index information in DevOrder. // DisMap bit array is the only reliable source to check a device's en/dis state, // so we use DisMap to set en/dis state of each item in NewOrder array // for (Index2 = 0; Index2 < OptionMenu->MenuNumber; Index2++) { Tmp = (UINT16) (DevOrder->Data[Index2] & 0xFF); Pos = Tmp / 8; Bit = 7 - (Tmp % 8); if ((DisMap[Pos] & (1 << Bit)) != 0) { NewOrder[Index] = (UINT16) (0xFF00 | Tmp); Index++; } } CopyMem ( DevOrder->Data, NewOrder, DevOrder->Length - sizeof (DevOrder->Length) ); FreePool (NewOrder); // // Update BootOrder and Boot####.Attribute // // 1. Re-order the Option Number in BootOrder according to Legacy Dev Order // ASSERT (OptionMenu->MenuNumber == DevOrder->Length / sizeof (UINT16) - 1); Status = OrderLegacyBootOption4SameType ( DevOrder->Data, DevOrder->Length / sizeof (UINT16) - 1, &EnBootOption, &EnBootOptionCount, &DisBootOption, &DisBootOptionCount ); if (EFI_ERROR(Status)) { goto Fail; } // // 2. Deactivate the DisBootOption and activate the EnBootOption // for (Index = 0; Index < DisBootOptionCount; Index++) { UnicodeSPrint (VarName, sizeof (VarName), L"Boot%04x", DisBootOption[Index]); GetEfiGlobalVariable2 (VarName, (VOID **) &BootOptionVar, &OptionSize); if (BootOptionVar != NULL) { Attribute = (UINT32 *) BootOptionVar; *Attribute &= ~LOAD_OPTION_ACTIVE; Status = gRT->SetVariable ( VarName, &gEfiGlobalVariableGuid, VAR_FLAG, OptionSize, BootOptionVar ); FreePool (BootOptionVar); } } for (Index = 0; Index < EnBootOptionCount; Index++) { UnicodeSPrint (VarName, sizeof (VarName), L"Boot%04x", EnBootOption[Index]); GetEfiGlobalVariable2 (VarName, (VOID **) &BootOptionVar, &OptionSize); if (BootOptionVar != NULL) { Attribute = (UINT32 *) BootOptionVar; *Attribute |= LOAD_OPTION_ACTIVE; Status = gRT->SetVariable ( VarName, &gEfiGlobalVariableGuid, VAR_FLAG, OptionSize, BootOptionVar ); FreePool (BootOptionVar); } } FreePool (EnBootOption); FreePool (DisBootOption); CurrentType++; } Status = gRT->SetVariable ( VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, VarSize, OriginalPtr ); Fail: if (EnBootOption != NULL) { FreePool (EnBootOption); } if (DisBootOption != NULL) { FreePool (DisBootOption); } FreePool (OriginalPtr); return Status; } /** This function allows a caller to extract the current configuration for one or more named elements from the target driver. @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param Request A null-terminated Unicode string in format. @param Progress On return, points to a character in the Request string. Points to the string's null terminator if request was successful. Points to the most recent '&' before the first failing name/value pair (or the beginning of the string if the failure is in the first name/value pair) if the request was not successful. @param Results A null-terminated Unicode string in format which has all values filled in for the names in the Request string. String to be allocated by the called function. @retval EFI_SUCCESS The Results is filled with the requested values. @retval EFI_OUT_OF_RESOURCES Not enough memory to store the results. @retval EFI_INVALID_PARAMETER Request is illegal syntax, or unknown name. @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. **/ EFI_STATUS EFIAPI LegacyBootOptionExtractConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Request, OUT EFI_STRING *Progress, OUT EFI_STRING *Results ) { if (Progress == NULL || Results == NULL) { return EFI_INVALID_PARAMETER; } *Progress = Request; return EFI_NOT_FOUND; } /** This function processes the results of changes in configuration. @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param Configuration A null-terminated Unicode string in format. @param Progress A pointer to a string filled in with the offset of the most recent '&' before the first failing name/value pair (or the beginning of the string if the failure is in the first name/value pair) or the terminating NULL if all was successful. @retval EFI_SUCCESS The Results is processed successfully. @retval EFI_INVALID_PARAMETER Configuration is NULL. @retval EFI_NOT_FOUND Routing data doesn't match any storage in this driver. **/ EFI_STATUS EFIAPI LegacyBootOptionRouteConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Configuration, OUT EFI_STRING *Progress ) { EFI_STATUS Status; EFI_HII_CONFIG_ROUTING_PROTOCOL *ConfigRouting; LEGACY_BOOT_NV_DATA *CurrentNVMapData; UINTN BufferSize; if (Configuration == NULL || Progress == NULL) { return EFI_INVALID_PARAMETER; } *Progress = Configuration; // // Check routing data in . // Note: there is no name for Name/Value storage, only GUID will be checked // if (!HiiIsConfigHdrMatch (Configuration, &mLegacyBootOptionGuid, mLegacyBootStorageName)) { return EFI_NOT_FOUND; } Status = gBS->LocateProtocol ( &gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **) &ConfigRouting ); if (EFI_ERROR (Status)) { return Status; } // // Convert to buffer data by helper function ConfigToBlock() // CurrentNVMapData = &mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData; Status = ConfigRouting->ConfigToBlock ( ConfigRouting, Configuration, (UINT8 *) CurrentNVMapData, &BufferSize, Progress ); ASSERT_EFI_ERROR (Status); Status = UpdateBBSOption (CurrentNVMapData); return Status; } /** Refresh the global UpdateData structure. **/ VOID RefreshLegacyUpdateData ( VOID ) { // // Free current updated date // if (mLegacyStartOpCodeHandle != NULL) { HiiFreeOpCodeHandle (mLegacyStartOpCodeHandle); } if (mLegacyEndOpCodeHandle != NULL) { HiiFreeOpCodeHandle (mLegacyEndOpCodeHandle); } // // Create new OpCode Handle // mLegacyStartOpCodeHandle = HiiAllocateOpCodeHandle (); mLegacyEndOpCodeHandle = HiiAllocateOpCodeHandle (); // // Create Hii Extend Label OpCode as the start opcode // mLegacyStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( mLegacyStartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL) ); mLegacyStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; mLegacyStartLabel->Number = FORM_BOOT_LEGACY_DEVICE_ID; // // Create Hii Extend Label OpCode as the start opcode // mLegacyEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode ( mLegacyEndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL) ); mLegacyEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; mLegacyEndLabel->Number = FORM_BOOT_LEGACY_LABEL_END; } /** Get the Menu Entry from the list in Menu Entry List. If MenuNumber is great or equal to the number of Menu Entry in the list, then ASSERT. @param MenuOption The Menu Entry List to read the menu entry. @param MenuNumber The index of Menu Entry. @return The Menu Entry. **/ LEGACY_MENU_ENTRY * GetMenuEntry ( LEGACY_MENU_OPTION *MenuOption, UINTN MenuNumber ) { LEGACY_MENU_ENTRY *NewMenuEntry; UINTN Index; LIST_ENTRY *List; ASSERT (MenuNumber < MenuOption->MenuNumber); List = MenuOption->Head.ForwardLink; for (Index = 0; Index < MenuNumber; Index++) { List = List->ForwardLink; } NewMenuEntry = CR (List, LEGACY_MENU_ENTRY, Link, LEGACY_MENU_ENTRY_SIGNATURE); return NewMenuEntry; } /** Create string tokens for a menu from its help strings and display strings @param HiiHandle Hii Handle of the package to be updated. @param MenuOption The Menu whose string tokens need to be created **/ VOID CreateLegacyMenuStringToken ( IN EFI_HII_HANDLE HiiHandle, IN LEGACY_MENU_OPTION *MenuOption ) { LEGACY_MENU_ENTRY *NewMenuEntry; UINTN Index; for (Index = 0; Index < MenuOption->MenuNumber; Index++) { NewMenuEntry = GetMenuEntry (MenuOption, Index); NewMenuEntry->DisplayStringToken = HiiSetString ( HiiHandle, 0, NewMenuEntry->DisplayString, NULL ); if (NULL == NewMenuEntry->HelpString) { NewMenuEntry->HelpStringToken = NewMenuEntry->DisplayStringToken; } else { NewMenuEntry->HelpStringToken = HiiSetString ( HiiHandle, 0, NewMenuEntry->HelpString, NULL ); } } } /** Create a dynamic page so that Legacy Device boot order can be set for specified device type. @param UpdatePageId The form ID. It also specifies the legacy device type. **/ VOID UpdateLegacyDeviceOrderPage ( IN UINT16 UpdatePageId ) { LEGACY_MENU_OPTION *OptionMenu; LEGACY_MENU_ENTRY *NewMenuEntry; EFI_STRING_ID StrRef; EFI_STRING_ID StrRefHelp; UINT16 *Default; UINT16 Index; UINT16 Key; CHAR16 String[100]; CHAR16 *TypeStr; CHAR16 *TypeStrHelp; CHAR16 *FormTitle; VOID *OptionsOpCodeHandle; VOID *DefaultOpCodeHandle; Key = 0; StrRef = 0; StrRefHelp = 0; OptionMenu = NULL; TypeStr = NULL; TypeStrHelp = NULL; Default = NULL; RefreshLegacyUpdateData(); // // Create oneof option list // switch (UpdatePageId) { case FORM_FLOPPY_BOOT_ID: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyFDMenu; Key = (UINT16) LEGACY_FD_QUESTION_ID; TypeStr = STR_FLOPPY; TypeStrHelp = STR_FLOPPY_HELP; FormTitle = STR_FLOPPY_TITLE; Default = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyFD; break; case FORM_HARDDISK_BOOT_ID: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyHDMenu; Key = (UINT16) LEGACY_HD_QUESTION_ID; TypeStr = STR_HARDDISK; TypeStrHelp = STR_HARDDISK_HELP; FormTitle = STR_HARDDISK_TITLE; Default = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyHD; break; case FORM_CDROM_BOOT_ID: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyCDMenu; Key = (UINT16) LEGACY_CD_QUESTION_ID; TypeStr = STR_CDROM; TypeStrHelp = STR_CDROM_HELP; FormTitle = STR_CDROM_TITLE; Default = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyCD; break; case FORM_NET_BOOT_ID: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyNETMenu; Key = (UINT16) LEGACY_NET_QUESTION_ID; TypeStr = STR_NET; TypeStrHelp = STR_NET_HELP; FormTitle = STR_NET_TITLE; Default = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyNET; break; case FORM_BEV_BOOT_ID: OptionMenu = (LEGACY_MENU_OPTION *) &LegacyBEVMenu; Key = (UINT16) LEGACY_BEV_QUESTION_ID; TypeStr = STR_BEV; TypeStrHelp = STR_BEV_HELP; FormTitle = STR_BEV_TITLE; Default = mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData.LegacyBEV; break; default: DEBUG ((DEBUG_ERROR, "Invalid command ID for updating page!\n")); return; } HiiSetString (mLegacyBootOptionPrivate->HiiHandle, STRING_TOKEN(STR_ORDER_CHANGE_PROMPT), FormTitle, NULL); CreateLegacyMenuStringToken (mLegacyBootOptionPrivate->HiiHandle, OptionMenu); OptionsOpCodeHandle = HiiAllocateOpCodeHandle (); ASSERT (OptionsOpCodeHandle != NULL); for (Index = 0; Index < OptionMenu->MenuNumber; Index++) { NewMenuEntry = GetMenuEntry (OptionMenu, Index); // // Create OneOf for each legacy device // HiiCreateOneOfOptionOpCode ( OptionsOpCodeHandle, NewMenuEntry->DisplayStringToken, 0, EFI_IFR_TYPE_NUM_SIZE_16, ((LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext)->BbsIndex ); } // // Create OneOf for item "Disabled" // HiiCreateOneOfOptionOpCode ( OptionsOpCodeHandle, STRING_TOKEN (STR_DISABLE_LEGACY_DEVICE), 0, EFI_IFR_TYPE_NUM_SIZE_16, 0xFF ); // // Create oneof tag here for FD/HD/CD #1 #2 // for (Index = 0; Index < OptionMenu->MenuNumber; Index++) { DefaultOpCodeHandle = HiiAllocateOpCodeHandle (); ASSERT (DefaultOpCodeHandle != NULL); HiiCreateDefaultOpCode ( DefaultOpCodeHandle, EFI_HII_DEFAULT_CLASS_STANDARD, EFI_IFR_TYPE_NUM_SIZE_16, *Default++ ); // // Create the string for oneof tag // UnicodeSPrint (String, sizeof (String), TypeStr, Index); StrRef = HiiSetString (mLegacyBootOptionPrivate->HiiHandle, 0, String, NULL); UnicodeSPrint (String, sizeof (String), TypeStrHelp, Index); StrRefHelp = HiiSetString (mLegacyBootOptionPrivate->HiiHandle, 0, String, NULL); HiiCreateOneOfOpCode ( mLegacyStartOpCodeHandle, (EFI_QUESTION_ID) (Key + Index), VARSTORE_ID_LEGACY_BOOT, (UINT16) (Key + Index * 2 - CONFIG_OPTION_OFFSET), StrRef, StrRefHelp, EFI_IFR_FLAG_CALLBACK, EFI_IFR_NUMERIC_SIZE_2, OptionsOpCodeHandle, DefaultOpCodeHandle //NULL // ); HiiFreeOpCodeHandle (DefaultOpCodeHandle); } HiiUpdateForm ( mLegacyBootOptionPrivate->HiiHandle, &mLegacyBootOptionGuid, LEGACY_ORDER_CHANGE_FORM_ID, mLegacyStartOpCodeHandle, mLegacyEndOpCodeHandle ); HiiFreeOpCodeHandle (OptionsOpCodeHandle); } /** Adjust question value when one question value has been changed. @param QuestionId The question id for the value changed question. @param Value The value for the changed question. **/ VOID AdjustOptionValue ( IN UINT16 QuestionId, IN EFI_IFR_TYPE_VALUE *Value ) { UINTN Number; UINT16 *Default; LEGACY_BOOT_NV_DATA *CurrentNVMap; UINT16 *CurrentVal; UINTN Index; UINTN Index2; UINTN Index3; UINTN NewValuePos; UINTN OldValue; UINTN NewValue; UINT8 *DisMap; UINTN Pos; UINTN Bit; Number = 0; CurrentVal = 0; Default = NULL; NewValue = 0; NewValuePos = 0; OldValue = 0; // // Update Select FD/HD/CD/NET/BEV Order Form // ASSERT ((QuestionId >= LEGACY_FD_QUESTION_ID) && (QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER)); CurrentNVMap = &mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData; HiiGetBrowserData (&mLegacyBootOptionGuid, mLegacyBootStorageName, sizeof (LEGACY_BOOT_NV_DATA), (UINT8 *) CurrentNVMap); DisMap = mLegacyBootOptionPrivate->MaintainMapData->DisableMap; if (QuestionId >= LEGACY_FD_QUESTION_ID && QuestionId < LEGACY_FD_QUESTION_ID + MAX_MENU_NUMBER) { Number = (UINT16) LegacyFDMenu.MenuNumber; CurrentVal = CurrentNVMap->LegacyFD; Default = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyFD; } else if (QuestionId >= LEGACY_HD_QUESTION_ID && QuestionId < LEGACY_HD_QUESTION_ID + MAX_MENU_NUMBER) { Number = (UINT16) LegacyHDMenu.MenuNumber; CurrentVal = CurrentNVMap->LegacyHD; Default = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyHD; } else if (QuestionId >= LEGACY_CD_QUESTION_ID && QuestionId < LEGACY_CD_QUESTION_ID + MAX_MENU_NUMBER) { Number = (UINT16) LegacyCDMenu.MenuNumber; CurrentVal = CurrentNVMap->LegacyCD; Default = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyCD; } else if (QuestionId >= LEGACY_NET_QUESTION_ID && QuestionId < LEGACY_NET_QUESTION_ID + MAX_MENU_NUMBER) { Number = (UINT16) LegacyNETMenu.MenuNumber; CurrentVal = CurrentNVMap->LegacyNET; Default = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyNET; } else if (QuestionId >= LEGACY_BEV_QUESTION_ID && QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER) { Number = (UINT16) LegacyBEVMenu.MenuNumber; CurrentVal = CurrentNVMap->LegacyBEV; Default = mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData.LegacyBEV; } // // First, find the different position // if there is change, it should be only one // for (Index = 0; Index < Number; Index++) { if (CurrentVal[Index] != Default[Index]) { OldValue = Default[Index]; NewValue = CurrentVal[Index]; break; } } if (Index != Number) { // // there is change, now process // if (0xFF == NewValue) { // // This item will be disable // Just move the items behind this forward to overlap it // Pos = OldValue / 8; Bit = 7 - (OldValue % 8); DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit)); for (Index2 = Index; Index2 < Number - 1; Index2++) { CurrentVal[Index2] = CurrentVal[Index2 + 1]; } CurrentVal[Index2] = 0xFF; } else { for (Index2 = 0; Index2 < Number; Index2++) { if (Index2 == Index) { continue; } if (Default[Index2] == NewValue) { // // If NewValue is in OldLegacyDev array // remember its old position // NewValuePos = Index2; break; } } if (Index2 != Number) { // // We will change current item to an existing item // (It's hard to describe here, please read code, it's like a cycle-moving) // for (Index2 = NewValuePos; Index2 != Index;) { if (NewValuePos < Index) { CurrentVal[Index2] = Default[Index2 + 1]; Index2++; } else { CurrentVal[Index2] = Default[Index2 - 1]; Index2--; } } } else { // // If NewValue is not in OldlegacyDev array, we are changing to a disabled item // so we should modify DisMap to reflect the change // Pos = NewValue / 8; Bit = 7 - (NewValue % 8); DisMap[Pos] = (UINT8) (DisMap[Pos] & (~ (UINT8) (1 << Bit))); if (0xFF != OldValue) { // // Because NewValue is a item that was disabled before // so after changing the OldValue should be disabled // actually we are doing a swap of enable-disable states of two items // Pos = OldValue / 8; Bit = 7 - (OldValue % 8); DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit)); } } } // // To prevent DISABLE appears in the middle of the list // we should perform a re-ordering // Index3 = Index; Index = 0; while (Index < Number) { if (0xFF != CurrentVal[Index]) { Index++; continue; } Index2 = Index; Index2++; while (Index2 < Number) { if (0xFF != CurrentVal[Index2]) { break; } Index2++; } if (Index2 < Number) { CurrentVal[Index] = CurrentVal[Index2]; CurrentVal[Index2] = 0xFF; } Index++; } // // Return correct question value. // Value->u16 = CurrentVal[Index3]; CopyMem (Default, CurrentVal, sizeof (UINT16) * Number); } // // Pass changed uncommitted data back to Form Browser // HiiSetBrowserData (&mLegacyBootOptionGuid, mLegacyBootStorageName, sizeof (LEGACY_BOOT_NV_DATA), (UINT8 *) CurrentNVMap, NULL); } /** This call back function is registered with Boot Manager formset. When user selects a boot option, this call back function will be triggered. The boot option is saved for later processing. @param This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param Action Specifies the type of action taken by the browser. @param QuestionId A unique value which is sent to the original exporting driver so that it can identify the type of data to expect. @param Type The type of value for the question. @param Value A pointer to the data being sent to the original exporting driver. @param ActionRequest On return, points to the action requested by the callback function. @retval EFI_SUCCESS The callback successfully handled the action. @retval EFI_INVALID_PARAMETER The setup browser call this function with invalid parameters. **/ EFI_STATUS EFIAPI LegacyBootOptionCallback ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN EFI_BROWSER_ACTION Action, IN EFI_QUESTION_ID QuestionId, IN UINT8 Type, IN EFI_IFR_TYPE_VALUE *Value, OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest ) { if (Action != EFI_BROWSER_ACTION_CHANGED && Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_FORM_OPEN) { // // Do nothing for other UEFI Action. Only do call back when data is changed or the form is open. // return EFI_UNSUPPORTED; } if ((Value == NULL) || (ActionRequest == NULL)) { return EFI_INVALID_PARAMETER; } if (Action == EFI_BROWSER_ACTION_FORM_OPEN) { if (QuestionId == FORM_FLOPPY_BOOT_ID) { if (!mFirstEnterLegacyForm) { // // The legacyBootMaintUiLib depends on the LegacyBootManagerLib to realize its functionality. // We need to do the legacy boot options related actions after the LegacyBootManagerLib has been initialized. // Opening the legacy menus is the appropriate time that the LegacyBootManagerLib has already been initialized. // mFirstEnterLegacyForm = TRUE; GetLegacyOptions (); GetLegacyOptionsOrder (); } } } if (Action == EFI_BROWSER_ACTION_CHANGING) { switch (QuestionId) { case FORM_FLOPPY_BOOT_ID: case FORM_HARDDISK_BOOT_ID: case FORM_CDROM_BOOT_ID: case FORM_NET_BOOT_ID: case FORM_BEV_BOOT_ID: UpdateLegacyDeviceOrderPage (QuestionId); break; default: break; } } else if (Action == EFI_BROWSER_ACTION_CHANGED) { if ((Value == NULL) || (ActionRequest == NULL)) { return EFI_INVALID_PARAMETER; } if ((QuestionId >= LEGACY_FD_QUESTION_ID) && (QuestionId < LEGACY_BEV_QUESTION_ID + MAX_MENU_NUMBER)) { AdjustOptionValue(QuestionId, Value); } } return EFI_SUCCESS; } /** Create a menu entry by given menu type. @param MenuType The Menu type to be created. @retval NULL If failed to create the menu. @return the new menu entry. **/ LEGACY_MENU_ENTRY * CreateMenuEntry ( VOID ) { LEGACY_MENU_ENTRY *MenuEntry; // // Create new menu entry // MenuEntry = AllocateZeroPool (sizeof (LEGACY_MENU_ENTRY)); if (MenuEntry == NULL) { return NULL; } MenuEntry->VariableContext = AllocateZeroPool (sizeof (LEGACY_DEVICE_CONTEXT)); if (MenuEntry->VariableContext == NULL) { FreePool (MenuEntry); return NULL; } MenuEntry->Signature = LEGACY_MENU_ENTRY_SIGNATURE; return MenuEntry; } /** Base on the L"LegacyDevOrder" variable to build the current order data. **/ VOID GetLegacyOptionsOrder ( VOID ) { UINTN VarSize; UINT8 *VarData; UINT8 *VarTmp; LEGACY_DEV_ORDER_ENTRY *DevOrder; UINT16 *LegacyDev; UINTN Index; LEGACY_MENU_OPTION *OptionMenu; UINT16 VarDevOrder; UINTN Pos; UINTN Bit; UINT8 *DisMap; UINTN TotalLength; LegacyDev = NULL; OptionMenu = NULL; DisMap = ZeroMem (mLegacyBootOptionPrivate->MaintainMapData->DisableMap, sizeof (mLegacyBootOptionPrivate->MaintainMapData->DisableMap)); // // Get Device Order from variable // GetVariable2 (VAR_LEGACY_DEV_ORDER, &gEfiLegacyDevOrderVariableGuid, (VOID **) &VarData, &VarSize); VarTmp = VarData; if (NULL != VarData) { DevOrder = (LEGACY_DEV_ORDER_ENTRY *) VarData; while (VarData < VarTmp + VarSize) { switch (DevOrder->BbsType) { case BBS_FLOPPY: LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyFD; OptionMenu = &LegacyFDMenu; break; case BBS_HARDDISK: LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyHD; OptionMenu = &LegacyHDMenu; break; case BBS_CDROM: LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyCD; OptionMenu = &LegacyCDMenu; break; case BBS_EMBED_NETWORK: LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyNET; OptionMenu = &LegacyNETMenu; break; case BBS_BEV_DEVICE: LegacyDev = mLegacyBootOptionPrivate->MaintainMapData->InitialNvData.LegacyBEV; OptionMenu = &LegacyBEVMenu; break; case BBS_UNKNOWN: default: ASSERT (FALSE); DEBUG ((DEBUG_ERROR, "Unsupported device type found!\n")); break; } // // Create oneof tag here for FD/HD/CD #1 #2 // for (Index = 0; Index < OptionMenu->MenuNumber; Index++) { TotalLength = sizeof (BBS_TYPE) + sizeof (UINT16) + Index * sizeof (UINT16); VarDevOrder = *(UINT16 *) ((UINT8 *) DevOrder + TotalLength); if (0xFF00 == (VarDevOrder & 0xFF00)) { LegacyDev[Index] = 0xFF; Pos = (VarDevOrder & 0xFF) / 8; Bit = 7 - ((VarDevOrder & 0xFF) % 8); DisMap[Pos] = (UINT8) (DisMap[Pos] | (UINT8) (1 << Bit)); } else { LegacyDev[Index] = VarDevOrder & 0xFF; } } VarData ++; VarData += *(UINT16 *) VarData; DevOrder = (LEGACY_DEV_ORDER_ENTRY *) VarData; } } CopyMem (&mLegacyBootOptionPrivate->MaintainMapData->LastTimeNvData, &mLegacyBootOptionPrivate->MaintainMapData->InitialNvData, sizeof (LEGACY_BOOT_NV_DATA)); CopyMem (&mLegacyBootOptionPrivate->MaintainMapData->CurrentNvData, &mLegacyBootOptionPrivate->MaintainMapData->InitialNvData, sizeof (LEGACY_BOOT_NV_DATA)); } /** Build the LegacyFDMenu LegacyHDMenu LegacyCDMenu according to LegacyBios.GetBbsInfo(). **/ VOID GetLegacyOptions ( VOID ) { LEGACY_MENU_ENTRY *NewMenuEntry; LEGACY_DEVICE_CONTEXT *NewLegacyDevContext; EFI_BOOT_MANAGER_LOAD_OPTION *BootOption; UINTN BootOptionCount; UINT16 Index; UINTN FDNum; UINTN HDNum; UINTN CDNum; UINTN NETNum; UINTN BEVNum; // // Initialize Bbs Table Context from BBS info data // InitializeListHead (&LegacyFDMenu.Head); InitializeListHead (&LegacyHDMenu.Head); InitializeListHead (&LegacyCDMenu.Head); InitializeListHead (&LegacyNETMenu.Head); InitializeListHead (&LegacyBEVMenu.Head); FDNum = 0; HDNum = 0; CDNum = 0; NETNum = 0; BEVNum = 0; EfiBootManagerConnectAll (); // // for better user experience // 1. User changes HD configuration (e.g.: unplug HDD), here we have a chance to remove the HDD boot option // 2. User enables/disables UEFI PXE, here we have a chance to add/remove EFI Network boot option // EfiBootManagerRefreshAllBootOption (); BootOption = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); for (Index = 0; Index < BootOptionCount; Index++) { if ((DevicePathType (BootOption[Index].FilePath) != BBS_DEVICE_PATH) || (DevicePathSubType (BootOption[Index].FilePath) != BBS_BBS_DP) ) { continue; } ASSERT (BootOption[Index].OptionalDataSize == sizeof (LEGACY_BOOT_OPTION_BBS_DATA)); NewMenuEntry = CreateMenuEntry (); ASSERT (NewMenuEntry != NULL); NewLegacyDevContext = (LEGACY_DEVICE_CONTEXT *) NewMenuEntry->VariableContext; NewLegacyDevContext->BbsIndex = ((LEGACY_BOOT_OPTION_BBS_DATA *) BootOption[Index].OptionalData)->BbsIndex; NewLegacyDevContext->Description = AllocateCopyPool (StrSize (BootOption[Index].Description), BootOption[Index].Description); ASSERT (NewLegacyDevContext->Description != NULL); NewMenuEntry->DisplayString = NewLegacyDevContext->Description; NewMenuEntry->HelpString = NULL; switch (((BBS_BBS_DEVICE_PATH *) BootOption[Index].FilePath)->DeviceType) { case BBS_TYPE_FLOPPY: InsertTailList (&LegacyFDMenu.Head, &NewMenuEntry->Link); FDNum++; break; case BBS_TYPE_HARDDRIVE: InsertTailList (&LegacyHDMenu.Head, &NewMenuEntry->Link); HDNum++; break; case BBS_TYPE_CDROM: InsertTailList (&LegacyCDMenu.Head, &NewMenuEntry->Link); CDNum++; break; case BBS_TYPE_EMBEDDED_NETWORK: InsertTailList (&LegacyNETMenu.Head, &NewMenuEntry->Link); NETNum++; break; case BBS_TYPE_BEV: InsertTailList (&LegacyBEVMenu.Head, &NewMenuEntry->Link); BEVNum++; break; } } EfiBootManagerFreeLoadOptions (BootOption, BootOptionCount); LegacyFDMenu.MenuNumber = FDNum; LegacyHDMenu.MenuNumber = HDNum; LegacyCDMenu.MenuNumber = CDNum; LegacyNETMenu.MenuNumber = NETNum; LegacyBEVMenu.MenuNumber = BEVNum; } /** Install Boot Manager Menu driver. @param ImageHandle The image handle. @param SystemTable The system table. @retval EFI_SUCCESS Install Boot manager menu success. @retval Other Return error status. **/ EFI_STATUS EFIAPI LegacyBootMaintUiLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_LEGACY_BIOS_PROTOCOL *LegacyBios; LEGACY_BOOT_OPTION_CALLBACK_DATA *LegacyBootOptionData; Status = gBS->LocateProtocol (&gEfiLegacyBiosProtocolGuid, NULL, (VOID **) &LegacyBios); if (!EFI_ERROR (Status)) { // // Create LegacyBootOptionData structures for Driver Callback // LegacyBootOptionData = AllocateZeroPool (sizeof (LEGACY_BOOT_OPTION_CALLBACK_DATA)); ASSERT (LegacyBootOptionData != NULL); LegacyBootOptionData->MaintainMapData = AllocateZeroPool (sizeof (LEGACY_BOOT_MAINTAIN_DATA)); ASSERT (LegacyBootOptionData->MaintainMapData != NULL); LegacyBootOptionData->ConfigAccess.ExtractConfig = LegacyBootOptionExtractConfig; LegacyBootOptionData->ConfigAccess.RouteConfig = LegacyBootOptionRouteConfig; LegacyBootOptionData->ConfigAccess.Callback = LegacyBootOptionCallback; // // Install Device Path Protocol and Config Access protocol to driver handle // Status = gBS->InstallMultipleProtocolInterfaces ( &LegacyBootOptionData->DriverHandle, &gEfiDevicePathProtocolGuid, &mLegacyBootOptionHiiVendorDevicePath, &gEfiHiiConfigAccessProtocolGuid, &LegacyBootOptionData->ConfigAccess, NULL ); ASSERT_EFI_ERROR (Status); // // Publish our HII data // LegacyBootOptionData->HiiHandle = HiiAddPackages ( &mLegacyBootOptionGuid, LegacyBootOptionData->DriverHandle, LegacyBootMaintUiVfrBin, LegacyBootMaintUiLibStrings, NULL ); ASSERT (LegacyBootOptionData->HiiHandle != NULL); mLegacyBootOptionPrivate = LegacyBootOptionData; } return EFI_SUCCESS; } /** Destructor of Customized Display Library Instance. @param ImageHandle The firmware allocated handle for the EFI image. @param SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The destructor completed successfully. @retval Other value The destructor did not complete successfully. **/ EFI_STATUS EFIAPI LegacyBootMaintUiLibDestructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; if (mLegacyBootOptionPrivate != NULL && mLegacyBootOptionPrivate->DriverHandle != NULL) { Status = gBS->UninstallMultipleProtocolInterfaces ( mLegacyBootOptionPrivate->DriverHandle, &gEfiDevicePathProtocolGuid, &mLegacyBootOptionHiiVendorDevicePath, &gEfiHiiConfigAccessProtocolGuid, &mLegacyBootOptionPrivate->ConfigAccess, NULL ); ASSERT_EFI_ERROR (Status); HiiRemovePackages (mLegacyBootOptionPrivate->HiiHandle); FreePool (mLegacyBootOptionPrivate->MaintainMapData); FreePool (mLegacyBootOptionPrivate); } return EFI_SUCCESS; }