/** @file This file contains source for MEBx Setup initialization and support. @copyright INTEL CONFIDENTIAL Copyright 2017 - 2021 Intel Corporation. The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material may contain trade secrets and proprietary and confidential information of Intel Corporation and its suppliers and licensors, and is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel's prior express written permission. No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. Unless otherwise agreed by Intel in writing, you may not remove or alter this notice or any other notice embedded in Materials by Intel or Intel's suppliers or licensors in any way. @par Specification Reference: **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /// /// Global variables /// #define MEBX_CFG_VARIABLE_NAME L"MebxCfg" // These variables definitions are autogenerated by build system extern UINT8 MebxSetupVfrBin[]; extern UINT8 MebxSetupStrings[]; MEBX_CONFIGURATION gMebxConfiguration = {0}; EFI_GUID gMebxFormSetGuid = MEBX_FORMSET_GUID; EFI_HII_HANDLE mMebxHiiHandle; EFI_HII_CONFIG_ROUTING_PROTOCOL *mHiiConfigRouting; EFI_HII_CONFIG_ACCESS_PROTOCOL gMebxConfigAccess; typedef struct { VENDOR_DEVICE_PATH VendorDevicePath; EFI_DEVICE_PATH_PROTOCOL End; } HII_VENDOR_DEVICE_PATH; HII_VENDOR_DEVICE_PATH mHiiVendorDevicePath = { { { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { (UINT8) (sizeof (VENDOR_DEVICE_PATH)), (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) } }, MEBX_FORMSET_GUID }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { (UINT8) (END_DEVICE_PATH_LENGTH), (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) } } }; #define NET_IP_ADDR_ALL_ZEROS(ip) ((ip) == (0x00000000)) #define NET_IP_ADDR_IN_ZERO_SUBNET(ip) (((ip) & 0xff000000) == 0x00000000) #define NET_IP_ADDR_IN_LOOPBACK_SUBNET(ip) (((ip) & 0xff000000) == 0x7f000000) #define NET_IP_ADDR_IN_CLASS_D(ip) (((ip) >= 0xe0000000) && ((ip) <= 0xefffffff)) #define NET_IP_ADDR_IN_CLASS_E(ip) (((ip) >= 0xf0000000) && ((ip) <= 0xffffffff)) #define NET_IP_ADDR_IN_MULTICAST_RANGE(ip) NET_IP_ADDR_IN_CLASS_D(ip) #define NET_IP_ADDR_IN_RESERVED_RANGE(ip) NET_IP_ADDR_IN_CLASS_E(ip) #define NET_IP_ADDR_NETMASK_VALID_RANGE(mask) (((mask) >= 0xc0000000) && ((mask) <= 0xFFFFFFFE)) #define NET_IP_ADDR_NO_HOST_ID(ip,mask) (((ip) & (mask)) == (ip)) #define NET_IP_ADDR_IS_BROADCAST(ip,mask) (((ip) | (mask))== 0xffffffff) #define SHOW_VAR_DIFF(Struct1, Struct2, Field) {\ if (Struct1->Field != Struct2->Field) {\ DEBUG ((DEBUG_INFO, "Updating: " #Field "=%x->%x\n", Struct1->Field, Struct2->Field));\ }\ } #define SHOW_STRING_DIFF(Struct1, Struct2, Field) {\ if (StrCmp (Struct1->Field, Struct2->Field)) {\ DEBUG ((DEBUG_INFO, "Updating: " #Field "=%s->%s\n", Struct1->Field, Struct2->Field));\ }\ } #define FLAG_OFFSET(x) ((OFFSET_OF (MEBX_CONFIGURATION, x) )) #define SET_IN_BROWSER(x,y) ((SetVariableInBrowser(&(x.y),FLAG_OFFSET(y),sizeof(x.y)))) #define MEBX_STATE_UNDEFINED 0xFF #define MAX_SINGLE_REQUEST_STRING_LENGTH 30 /** Promote next platform reset to Global type. Required after changing certain settings in MEBX. **/ VOID ScheduleGlobalReset ( VOID ); /** When called from a setup callback, changes value of one variable inside MebxConfig. This is a wrapper for HiiSetBrowserData that hides the complexity of preparing Request string. @param[in] Value new value for the variable @param[in] Offset offset of the variable inside MebxConfig structure @param[in] Size size in bytes of the variable **/ VOID SetVariableInBrowser ( IN VOID *Value, IN UINT32 Offset, IN UINT32 Size ) { CHAR16 RequestString[MAX_SINGLE_REQUEST_STRING_LENGTH]; UINT8 MebxSetup[sizeof (MEBX_CONFIGURATION)]; ASSERT (Offset + Size <= sizeof (MEBX_CONFIGURATION)); UnicodeSPrint (RequestString, MAX_SINGLE_REQUEST_STRING_LENGTH * sizeof (UINT16), L"&OFFSET=%x&WIDTH=%x", Offset, Size); CopyMem (&(MebxSetup[Offset]), Value, Size); HiiSetBrowserData (&gMebxFormSetGuid, MEBX_CFG_VARIABLE_NAME, sizeof (MEBX_CONFIGURATION), MebxSetup, RequestString); } /** When called from a setup callback, retrieves a value of one variable inside MebxConfig. This is a wrapper for HiiGetBrowserData that hides the complexity of preparing Request string. @param[in] Value new value for the variable @param[in] Offset offset of the variable inside MebxConfig structure @param[in] Size size in bytes of the variable **/ VOID GetVariableFromBrowser ( IN UINT32 Offset, IN UINT32 Size, OUT VOID *Value ) { UINT8 MebxSetup[sizeof (MEBX_CONFIGURATION)]; BOOLEAN Result; ASSERT (Offset + Size <= sizeof (MEBX_CONFIGURATION)); Result = HiiGetBrowserData (&gMebxFormSetGuid, MEBX_CFG_VARIABLE_NAME, sizeof (MEBX_CONFIGURATION), MebxSetup); DEBUG ((DEBUG_INFO, "GetVariableFromBrowser result %d\n", Result)); CopyMem (Value, &(MebxSetup[Offset]), Size); } /** When called from a setup callback, sets one exposure flag inside MebxConfig to new value @param[in] Offset Offset of the variable inside MebxConfig structure @param[in] Flag New value for the flag **/ VOID SetExposureFlag ( IN UINT32 Offset, IN UINT8 Flag ) { DEBUG ((DEBUG_INFO, "SetExposureFlag at %x to %x\n", Offset, Flag)); SetVariableInBrowser (&Flag, Offset ,1); } /** Shows differences between current and new configuration @param[in] Cfg1 Pointer to structure holding current MEBx configuration. @param[in] Cfg2 Pointer to structure holding new MEBx configuration. **/ VOID ShowConfigDiff ( IN MEBX_CONFIGURATION *Cfg1, IN MEBX_CONFIGURATION *Cfg2 ) { UINT8 HashNumber; DEBUG ((DEBUG_INFO, "\n\n***** DIFF ***** DIFF ***** DIFF ***** DIFF ***** DIFF *****\n")); SHOW_VAR_DIFF (Cfg1, Cfg2, ShowError); SHOW_VAR_DIFF (Cfg1, Cfg2, NetAccess); SHOW_VAR_DIFF (Cfg1, Cfg2, MeOnHostSlpStates); SHOW_VAR_DIFF (Cfg1, Cfg2, IdleTimeout); SHOW_VAR_DIFF (Cfg1, Cfg2, PwdPolicy); SHOW_VAR_DIFF (Cfg1, Cfg2, AmtState); SHOW_VAR_DIFF (Cfg1, Cfg2, SolEnable); SHOW_VAR_DIFF (Cfg1, Cfg2, KvmEnable); SHOW_VAR_DIFF (Cfg1, Cfg2, UserOptIn); SHOW_VAR_DIFF (Cfg1, Cfg2, RemoteItOptInConfig); SHOW_VAR_DIFF (Cfg1, Cfg2, StorageRedirEnable); SHOW_VAR_DIFF (Cfg1, Cfg2, StorageRedirEnable); SHOW_STRING_DIFF (Cfg1, Cfg2, HostDomainName); SHOW_VAR_DIFF(Cfg1, Cfg2, FqdnType); SHOW_VAR_DIFF(Cfg1, Cfg2, DynamicDnsUpdate); SHOW_VAR_DIFF(Cfg1, Cfg2, UpdateInterval); SHOW_VAR_DIFF(Cfg1, Cfg2, Ttl); SHOW_VAR_DIFF(Cfg1, Cfg2, DhcpMode); SHOW_STRING_DIFF (Cfg1, Cfg2, Ipv4Addr); SHOW_STRING_DIFF (Cfg1, Cfg2, Ipv4MaskAddr); SHOW_STRING_DIFF (Cfg1, Cfg2, Ipv4GatewayAddr); SHOW_STRING_DIFF (Cfg1, Cfg2, Ipv4PrefDnsAddr); SHOW_STRING_DIFF (Cfg1, Cfg2, Ipv4AltDnsAddr); SHOW_VAR_DIFF(Cfg1, Cfg2, ProvServerPort); SHOW_VAR_DIFF(Cfg1, Cfg2, TlsPpkRemoteConfig); SHOW_STRING_DIFF (Cfg1, Cfg2, PkiDnsSuffix); SHOW_VAR_DIFF(Cfg1, Cfg2, PrivacyLevel); SHOW_VAR_DIFF(Cfg1, Cfg2, LanLessPlatform); SHOW_VAR_DIFF(Cfg1, Cfg2, NumberOfPowerPkgs); SHOW_VAR_DIFF(Cfg1, Cfg2, MOffOverride); SHOW_VAR_DIFF(Cfg1, Cfg2, SouthClinkState); SHOW_VAR_DIFF(Cfg1, Cfg2, GlobalExposure); SHOW_VAR_DIFF(Cfg1, Cfg2, AmtStateExposure); SHOW_VAR_DIFF(Cfg1, Cfg2, AmtSubmenuExposure); SHOW_VAR_DIFF(Cfg1, Cfg2, KvmExposure); SHOW_VAR_DIFF(Cfg1, Cfg2, ProvisioningState); SHOW_VAR_DIFF(Cfg1, Cfg2, OptInConfigExposure); SHOW_VAR_DIFF(Cfg1, Cfg2, RemoteItOptInConfigExposure); SHOW_VAR_DIFF(Cfg1, Cfg2, AddrMaskInvalid); SHOW_VAR_DIFF(Cfg1, Cfg2, NoDefaultHashes); for (HashNumber = 0; HashNumber < MAX_HASH_ENTRIES; HashNumber++) { if (Cfg1->HashValid[HashNumber] != Cfg2->HashValid[HashNumber]) { DEBUG ((DEBUG_INFO, "HashValid=%d->%d\n",Cfg1->HashValid[HashNumber], Cfg2->HashValid[HashNumber])); } } DEBUG ((DEBUG_INFO, "***** END ***** END ***** END ***** END ***** END *****\n")); } /** Show current MEBx configuration. @param[in] Cfg Pointer to structure holding current MEBx configuration. **/ VOID ShowConfig ( IN MEBX_CONFIGURATION *Cfg ) { DEBUG ((DEBUG_INFO, "ShowError=%x\n",Cfg->ShowError)); DEBUG ((DEBUG_INFO, "NetAccess=%x\n",Cfg->NetAccess)); DEBUG ((DEBUG_INFO, "MeOnHostSlpStates=%x\n",Cfg->MeOnHostSlpStates)); DEBUG ((DEBUG_INFO, "IdleTimeout=%x\n",Cfg->IdleTimeout)); DEBUG ((DEBUG_INFO, "PwdPolicy=%x\n",Cfg->PwdPolicy)); DEBUG ((DEBUG_INFO, "AmtState=%x\n",Cfg->AmtState)); DEBUG ((DEBUG_INFO, "SolEnable=%x\n",Cfg->SolEnable)); DEBUG ((DEBUG_INFO, "StorageRedirEnable=%x\n",Cfg->StorageRedirEnable)); DEBUG ((DEBUG_INFO, "KvmEnable=%x\n",Cfg->KvmEnable)); DEBUG ((DEBUG_INFO, "UserOptIn=%x\n",Cfg->UserOptIn)); DEBUG ((DEBUG_INFO, "RemoteItOptInConfig=%x\n",Cfg->RemoteItOptInConfig)); DEBUG ((DEBUG_INFO, "HostDomainName=%s\n",Cfg->HostDomainName)); DEBUG ((DEBUG_INFO, "FqdnType=%x\n",Cfg->FqdnType)); DEBUG ((DEBUG_INFO, "DynamicDnsUpdate=%x\n",Cfg->DynamicDnsUpdate)); DEBUG ((DEBUG_INFO, "UpdateInterval=%x\n",Cfg->UpdateInterval)); DEBUG ((DEBUG_INFO, "Ttl=%x\n",Cfg->Ttl)); DEBUG ((DEBUG_INFO, "DhcpMode=%x\n",Cfg->DhcpMode)); DEBUG ((DEBUG_INFO, "Ipv4Addr=%s\n",Cfg->Ipv4Addr)); DEBUG ((DEBUG_INFO, "Ipv4MaskAddr=%s\n",Cfg->Ipv4MaskAddr)); DEBUG ((DEBUG_INFO, "Ipv4GatewayAddr=%s\n",Cfg->Ipv4GatewayAddr)); DEBUG ((DEBUG_INFO, "Ipv4PrefDnsAddr=%s\n",Cfg->Ipv4PrefDnsAddr)); DEBUG ((DEBUG_INFO, "Ipv4AltDnsAddr=%s\n",Cfg->Ipv4AltDnsAddr)); DEBUG ((DEBUG_INFO, "ProvServerPort=%x\n",Cfg->ProvServerPort)); DEBUG ((DEBUG_INFO, "TlsPpkRemoteConfig=%x\n",Cfg->TlsPpkRemoteConfig)); DEBUG ((DEBUG_INFO, "PkiDnsSuffix=%s\n",Cfg->PkiDnsSuffix)); DEBUG ((DEBUG_INFO, "PrivacyLevel=%x\n",Cfg->PrivacyLevel)); DEBUG ((DEBUG_INFO, "LanLessPlatform=%x\n",Cfg->LanLessPlatform)); DEBUG ((DEBUG_INFO, "NumberOfPowerPkgs=%x\n",Cfg->NumberOfPowerPkgs)); DEBUG ((DEBUG_INFO, "NoDefaultHashes=%x\n",Cfg->NoDefaultHashes)); DEBUG ((DEBUG_INFO, "MOffOverride=%x\n",Cfg->MOffOverride)); DEBUG ((DEBUG_INFO, "SouthClinkState=%x\n",Cfg->SouthClinkState)); DEBUG ((DEBUG_INFO, "GlobalExposure=%x\n",Cfg->GlobalExposure)); DEBUG ((DEBUG_INFO, "AmtStateExposure=%x\n",Cfg->AmtStateExposure)); DEBUG ((DEBUG_INFO, "AmtSubmenuExposure=%x\n",Cfg->AmtSubmenuExposure)); DEBUG ((DEBUG_INFO, "KvmExposure=%x\n",Cfg->KvmExposure)); DEBUG ((DEBUG_INFO, "ProvisioningState=%x\n",Cfg->ProvisioningState)); DEBUG ((DEBUG_INFO, "OptInConfigExposure=%x\n",Cfg->OptInConfigExposure)); DEBUG ((DEBUG_INFO, "RemoteItOptInConfigExposure=%x\n",Cfg->RemoteItOptInConfigExposure)); DEBUG ((DEBUG_INFO, "AddrMaskInvalid=%x\n",Cfg->AddrMaskInvalid)); DEBUG ((DEBUG_INFO, "FwVersionMajor=%x\n",Cfg->FwVersionMajor)); } /** Convert a NULL-terminad Unicode string to IPv4 address stored into 32 bits. @param[in] AddrIn Pointer to a Null-terminated Unicode string. @param[out] Erorr Error status: BAD_IP_ADDR - string isn't in the correct format INPUT_OK - address was translated successfully @return converted IPv4 address **/ UINT32 Ipv4UnicodeToInteger ( IN CHAR16 *AddrIn, OUT UINT32 *Error ) { RETURN_STATUS Status; IPv4_ADDRESS Ipv4; UINT32 UintAddress; UINT8 Prefix; Status = StrToIpv4Address (AddrIn, NULL, &Ipv4, &Prefix); if (RETURN_ERROR (Status) || (Prefix != MAX_UINT8)) { *Error = BAD_IP_ADDR; return 0; } UintAddress = (Ipv4.Addr[0] << 24) | (Ipv4.Addr[1] << 16) | (Ipv4.Addr[2] << 8) | Ipv4.Addr[3]; DEBUG ((DEBUG_INFO, "[MEBx] %a, %s -> 0x%08X\n", __FUNCTION__, AddrIn, UintAddress)); *Error = INPUT_OK; return UintAddress; } /** Checks if input string is a valid hostname Hostname is a series of labels separated by dots. Label may consist of letters, digits and hyphens A hyphen may not be the first or last character of label Hostname must be no longer than 255 characters. Each label must be no longer than 63. Last label may be empty - in other words hostname may end with a dot @param[in] Addr Host and Domain name to verify @return Error Code **/ UINT32 ValidateHostDomainName ( IN CHAR8 *Name ) { UINT32 Index; UINT32 LabelLength; UINT32 HostnameLength; BOOLEAN Dot; BOOLEAN Hyphen; Index = 0; Dot = TRUE; LabelLength = 0; HostnameLength = 0; Hyphen = FALSE; DEBUG ((DEBUG_INFO, "ValidateHostDomainName: %a\n", Name)); while (Name[Index] != '\0') { if (!((Name[Index] >= 'a' && Name[Index] <= 'z') || (Name[Index] >= 'A' && Name[Index] <= 'Z') || (Name[Index] >= '0' && Name[Index] <= '9') || (Name[Index] == '-' || Name[Index] == '.'))) { DEBUG ((DEBUG_ERROR, "HDN_INVALID_CHARACTER %d\n", Index)); return HDN_INVALID_CHARACTER; } if (Name[Index] == '.') { if (Dot) { DEBUG ((DEBUG_ERROR, "HDN_BAD_DOT %d\n", Index)); return HDN_BAD_DOT; } if (Hyphen) { DEBUG ((DEBUG_ERROR, "HDN_BAD_HYPHEN %d\n", Index)); return HDN_BAD_HYPHEN; } if (HostnameLength == 0) { HostnameLength = Index; } Dot = TRUE; Hyphen = FALSE; LabelLength = 0; } else if (Name[Index] == '-') { if (Hyphen || LabelLength == 0 || Dot) { DEBUG ((DEBUG_ERROR, "HDN_BAD_HYPHEN %d\n", Index)); return HDN_BAD_HYPHEN; } Dot = FALSE; Hyphen = TRUE; LabelLength++; } else { Dot = FALSE; Hyphen = FALSE; LabelLength++; } if (Index - HostnameLength > 191) { DEBUG ((DEBUG_ERROR, "HDN_DOMAIN_TOO_LONG %d\n", Index)); return HDN_DOMAIN_TOO_LONG; } if (LabelLength > 63) { DEBUG ((DEBUG_ERROR, "HDN_LABEL_TOO_LONG %d\n", Index)); return HDN_LABEL_TOO_LONG; } if (Index > 253) { DEBUG ((DEBUG_ERROR, "HDN_NAME_TOO_LONG %d\n", Index)); return HDN_NAME_TOO_LONG; } Index++; } if (Hyphen) { DEBUG ((DEBUG_ERROR, "HDN_BAD_HYPHEN %d\n", Index)); return HDN_BAD_HYPHEN; } return INPUT_OK; } /** IPv4 Mask must have some highest bits set, then remaining bits all clear. Zeroes and ones must not be interleaved @param[in] Addr IP Address to verify @retval TRUE Address is valid @retval FALSE Adresss is invalid. **/ BOOLEAN IsValidIpMask ( IN UINT32 Addr ) { if (!NET_IP_ADDR_NETMASK_VALID_RANGE (Addr)) { DEBUG ((DEBUG_ERROR, "%a - netmask 0x%08x is in invalid range\n", __FUNCTION__, Addr)); return FALSE; } DEBUG ((DEBUG_INFO, "IsCorrectIpMask (%x):%x,%x->%x\n",Addr, ~Addr, (~Addr)+1, (((~Addr) & ((~Addr)+1)) == 0) )); if (((~Addr) & ((~Addr)+1)) == 0) { return TRUE; } return FALSE; } /** Function verifies if Addr:Mask combination produces broadcast address (unmasked bits of addr == all zeroes or all ones) @param[in] Addr IP Address to verify @param[in] Mask IP Mask to verify @retval TRUE Address is unicast @retval FALSE Adresss is broadcast. **/ BOOLEAN IsUnicastIpAddrMask ( IN UINT32 Addr, IN UINT32 Mask ) { UINT32 Network; UINT32 Host; Network = Addr & Mask; Host = Addr & (~Mask); DEBUG ((DEBUG_INFO, "ValidateIpAddrMask Addr %08x Mask %08x Network %08x Host %08x\n", Addr, Mask, Network, Host)); if (NET_IP_ADDR_IS_BROADCAST (Addr, Mask) || NET_IP_ADDR_NO_HOST_ID (Addr, Mask)) { return FALSE; } return TRUE; } /** Function verifies if IPv4 address is valid. @param[in] IpAddr Ipv4 address to verify @retval TRUE Address is valid @retval FALSE Adresss is invalid. **/ BOOLEAN IsIpv4AddressValid ( IN UINT32 IpAddr ) { if (NET_IP_ADDR_ALL_ZEROS (IpAddr) || NET_IP_ADDR_IN_ZERO_SUBNET (IpAddr) || NET_IP_ADDR_IN_LOOPBACK_SUBNET (IpAddr) || NET_IP_ADDR_IN_MULTICAST_RANGE (IpAddr) || NET_IP_ADDR_IN_RESERVED_RANGE (IpAddr)) { return FALSE; } return TRUE; } /** Function verifies if Provisioning Server Address is valid, @param[in] ProvSrvAddr Provisioning server address to verify @retval TRUE Address is valid @retval FALSE Adresss is invalid. **/ BOOLEAN IsProvisioningServerAddressValid ( IN UINT32 ProvSrvAddr ) { /* this is ok (0.0.0.0) for Provisioning Server Address */ if (ProvSrvAddr == 0) { return TRUE; } if (((ProvSrvAddr & 0xFF) == 0) || NET_IP_ADDR_IN_ZERO_SUBNET (ProvSrvAddr) || NET_IP_ADDR_IN_LOOPBACK_SUBNET (ProvSrvAddr) || NET_IP_ADDR_IN_MULTICAST_RANGE (ProvSrvAddr) || NET_IP_ADDR_IN_RESERVED_RANGE (ProvSrvAddr)) { return FALSE; } return TRUE; } /** Callback function that parses incoming strings as IPv4 addresses or masks and verifies their correctness. Addres has limitation for first octet: it must not be 0, 7f or greater than e0 Mask must have some highest bits set, then remaining bits all clear. Zeroes and ones must not be interleaved Also, addr:mask combination must not produce broadcast address (unmasked bits of addr == all zeroes or all ones) If addr / mask is invalid by itself it will be immediately rejected. Invalid addr:mask combination will only cause a warning to appear @param[in] Str String Token to verify @param[in] QuestionId Key Value @retval EFI_SUCCESS Input has been validated. @retval EFI_UNSUPPORTED Incorrect input or string not found in HII database. **/ EFI_STATUS Ipv4Callback ( IN EFI_STRING_ID Str, IN EFI_QUESTION_ID QuestionId ) { CHAR16 *UniStr; UINT32 Error; UINT32 Ip; CHAR16 OtherString[17]; UINT32 Addr; UINT32 Mask; ZeroMem (OtherString, sizeof (OtherString)); UniStr = NULL; DEBUG ((DEBUG_INFO, "Ipv4Callback, 0x%X\n", QuestionId)); if (Str != 0) { UniStr = HiiGetString (mMebxHiiHandle, Str, NULL); } if (UniStr == NULL) { return EFI_UNSUPPORTED; } Ip = Ipv4UnicodeToInteger (UniStr, &Error); FreePool (UniStr); if (QuestionId == KEY_IPV4MASKADDR) { if (!IsValidIpMask (Ip)) { Error = BAD_IP_MASK; } } if (QuestionId == KEY_IPV4ADDR || QuestionId == KEY_IPV4GATEWAYADDR || QuestionId == KEY_IPV4PREFDNSADDR || QuestionId == KEY_IPV4ALTDNSADDR) { if (!IsIpv4AddressValid (Ip)) { Error = BAD_IP_ADDR; } } SetVariableInBrowser (&Error, FLAG_OFFSET (ShowError) ,1); DEBUG ((DEBUG_INFO, "CheckResult = %x\n", Error)); if (Error != INPUT_OK) { return EFI_UNSUPPORTED; } if (QuestionId == KEY_IPV4ADDR || QuestionId == KEY_IPV4MASKADDR) { if (QuestionId == KEY_IPV4ADDR) { Addr = Ip; GetVariableFromBrowser (FLAG_OFFSET (Ipv4MaskAddr), 32, &OtherString); Mask = Ipv4UnicodeToInteger (OtherString, &Error); } else { Mask = Ip; GetVariableFromBrowser (FLAG_OFFSET (Ipv4Addr), 32, &OtherString); Addr = Ipv4UnicodeToInteger (OtherString, &Error); } ASSERT (Error == 0); if (!IsUnicastIpAddrMask (Addr, Mask)) { Error = BAD_IP_ADDR_MASK_COMBO; } SetVariableInBrowser (&Error, FLAG_OFFSET (AddrMaskInvalid), 4); } return EFI_SUCCESS; } /** Checks if provided string is a valid hostname. If not, sets ShowError variable to indicate the kind of problem found in string. @param[in] Str String Token to verify @retval EFI_SUCCESS Input has been validated. @retval EFI_UNSUPPORTED Incorrect input or string not found in HII database. **/ EFI_STATUS FqdnCallback ( IN EFI_STRING_ID Str ) { CHAR8 Name[256]; CHAR16 *UniStr; UINT32 CheckResult; UniStr = NULL; ZeroMem (Name, sizeof (Name)); DEBUG ((DEBUG_INFO, "FqdnCallback\n")); if (Str != 0) { UniStr = HiiGetString (mMebxHiiHandle, Str, NULL); } if (UniStr == NULL) { return EFI_UNSUPPORTED; } UnicodeStrToAsciiStrS (UniStr, Name, sizeof (Name)); FreePool (UniStr); CheckResult = ValidateHostDomainName (Name); SetVariableInBrowser (&CheckResult, FLAG_OFFSET (ShowError) ,1); DEBUG ((DEBUG_INFO, "CheckResult = %x\n", CheckResult)); return EFI_SUCCESS; } /** Checks if provided string is a valid server address. If not, sets ShowError variable to indicate the kind of problem found in string. @param[in] Str String Token to verify @retval EFI_SUCCESS Input has been validated. @retval EFI_UNSUPPORTED Incorrect input or string not found in HII database. **/ EFI_STATUS ProvServerAddressCallback ( IN EFI_STRING_ID Str ) { CHAR8 Name[256]; CHAR16 *UniStr; UINT32 CheckResult; RETURN_STATUS Status; IPv4_ADDRESS Ipv4; IPv6_ADDRESS Ipv6; UINT8 Prefix; UINT32 Ipv4Int; UniStr = NULL; Prefix = 0; ZeroMem (&Ipv6, sizeof (Ipv6)); ZeroMem (&Ipv4, sizeof (Ipv4)); ZeroMem (Name, sizeof (Name)); DEBUG ((DEBUG_INFO, "[MEBx] %a - start\n", __FUNCTION__)); if (Str != 0) { UniStr = HiiGetString (mMebxHiiHandle, Str, NULL); } if (UniStr == NULL) { return EFI_UNSUPPORTED; } UnicodeStrToAsciiStrS (UniStr, Name, sizeof (Name)); Ipv4Int = Ipv4UnicodeToInteger (UniStr, &CheckResult); FreePool (UniStr); if (CheckResult == INPUT_OK) { if (!IsProvisioningServerAddressValid (Ipv4Int)) { CheckResult = BAD_IP_ADDR; } SetVariableInBrowser (&CheckResult, FLAG_OFFSET (ShowError) ,1); return EFI_SUCCESS; } Status = AsciiStrToIpv6Address (Name, NULL, &Ipv6, &Prefix); CheckResult = RETURN_ERROR (Status) || (Prefix != MAX_UINT8); if (CheckResult == INPUT_OK) { SetVariableInBrowser (&CheckResult, FLAG_OFFSET (ShowError) ,1); return EFI_SUCCESS; } CheckResult = ValidateHostDomainName (Name); if (CheckResult == INPUT_OK) { SetVariableInBrowser (&CheckResult, FLAG_OFFSET (ShowError) ,1); return EFI_SUCCESS; } CheckResult = 1; SetVariableInBrowser (&CheckResult, FLAG_OFFSET (ShowError) ,1); return EFI_UNSUPPORTED; } /** Callback funtion to be executed on ReadyToBoot event. @param[in] Event A pointer to the Event that triggered the callback. @param[in] Context A pointer to private data registered with the callback function. **/ VOID EFIAPI MebxExit ( IN EFI_EVENT Event, IN VOID *Context ) { MebxSetMebxState (AmtMebxStateExit); gBS->CloseEvent (Event); } /** Callback for Login and Change password setup items. It follows the logic explained in UEFI specification 2.6, paragraph 31.3.8.3.54, with certain exceptions. According to that logic, here are the meanings of return codes from this function: EFI_SUCCESS: user provided correct current password, browser should ask for new password EFI_UNSUPPORTED: unercoverable error EFI_NOT_READY: current password was incorrect EFI_ABORTED (or any error different that the 2 above): current password was correct and browser should not ask for password change There is a login attempt counter inside CSME. After 3 failed attempts it is impossible to login and computer must be reset. Whenever user interacts with Password item, browser first calls this callback with empty password. Such request is not sent to CSME to conserve the counter. Then browser calls this callback with password provided by user. If callback determines that password change is necassary (because it was called from Change password menu item or because CSME password is still the default) then it returns EFI_SUCCESS which indicates to browser that user should be asked for new password. In that case the current password is temporarily stored in a global variable, because it will be used when browser calls this function again with new password. @param[in] Str StringID where password is provided from browser. 0 if user provided invalid input @param[in] ChangePwd If TRUE, it means user wants to change password rather than log in @retval EFI_SUCCESS User provided correct current password, browser should ask for new password @retval EFI_UNSUPPORTED Unhandled error @retval EFI_NOT_READY Current password was incorrect @retval Others Current password was correct and browser should not ask for password change **/ EFI_STATUS PasswordCallback ( IN EFI_STRING_ID Str, IN BOOLEAN ChangePwd ) { static CHAR8 sOldPwd[MAX_PASSWORD_SIZE_TERMINATED] = {0}; static BOOLEAN sChangingPwd = FALSE; EFI_STATUS Status; CHAR8 Password[MAX_PASSWORD_SIZE_TERMINATED]; CHAR16 *UniStr; BOOLEAN AttemptsExceeded; UINT32 PasswordModified; UINT32 PwdPolicy; DEBUG ((DEBUG_INFO, "%a (%d)\n", __FUNCTION__, sChangingPwd)); UniStr = NULL; if (Str != 0) { UniStr = HiiGetString (mMebxHiiHandle, Str, NULL); HiiSetString (mMebxHiiHandle, Str, L"", NULL); } if (UniStr == NULL) { sChangingPwd = FALSE; ZeroMem (sOldPwd, MAX_PASSWORD_SIZE_TERMINATED); return EFI_UNSUPPORTED; } Status = UnicodeStrToAsciiStrS (UniStr, Password, sizeof (Password)); ZeroMem (UniStr, StrSize (UniStr)); FreePool (UniStr); if (EFI_ERROR (Status)) { sChangingPwd = FALSE; ZeroMem (sOldPwd, MAX_PASSWORD_SIZE_TERMINATED); return EFI_UNSUPPORTED; } if (!sChangingPwd) { // attempt to login using provided password; skip it when the password is empty, to prevent exhausting the 3 attempts counter AttemptsExceeded = FALSE; if (AsciiStrLen (Password) != 0) { Status = HeciValidateMebxPassword ((UINT8) (AsciiStrLen (Password)), (UINT8 *) &Password, &AttemptsExceeded); } else { Status = EFI_NOT_READY; } if (EFI_ERROR (Status)) { ZeroMem (Password, MAX_PASSWORD_SIZE_TERMINATED); if (AttemptsExceeded) { SetExposureFlag (FLAG_OFFSET (GlobalExposure), SHOW_PWD_EXCEEDED); // hide password prompt and instead show text that attempts were exceeded ScheduleGlobalReset (); return EFI_ABORTED; } return EFI_NOT_READY; } else { // we're correctly logged in now gMebxConfiguration.GlobalExposure = SHOW_EVERYTHING; SetExposureFlag (FLAG_OFFSET (GlobalExposure), SHOW_EVERYTHING); if (gMebxConfiguration.PwdPolicy == 3) { if (gMebxConfiguration.AmtState == AmtEnabled) { MebxGetMebxPwdPolicy (&PwdPolicy); gMebxConfiguration.PwdPolicy = (UINT8)PwdPolicy; SetExposureFlag (FLAG_OFFSET (PwdPolicy), (UINT8)PwdPolicy); } } } // enforce pwd change if user asked for it or if the default password is still in use Status = HeciIsMebxPasswordModified (&PasswordModified); if (ChangePwd || !PasswordModified) { sChangingPwd = TRUE; CopyMem (sOldPwd, Password, AsciiStrLen (Password)); ZeroMem (Password, MAX_PASSWORD_SIZE_TERMINATED); return EFI_SUCCESS; } else { ZeroMem (Password, MAX_PASSWORD_SIZE_TERMINATED); return EFI_ABORTED; } } else { SetExposureFlag (FLAG_OFFSET (ShowError), INPUT_OK); Status = HeciModifyMebxPassword ((UINT8) (AsciiStrLen (sOldPwd)), (UINT8 *) &sOldPwd, (UINT8) (AsciiStrLen ((CHAR8*)&Password)), (UINT8*)&Password); sChangingPwd = FALSE; ZeroMem (sOldPwd, MAX_PASSWORD_SIZE_TERMINATED); ZeroMem (Password, MAX_PASSWORD_SIZE_TERMINATED); if (Status != 0) { SetExposureFlag (FLAG_OFFSET (ShowError), BAD_NEW_PWD); // if default password has not been modified it means we are on login screen. Show login only. if (!ChangePwd) { SetExposureFlag (FLAG_OFFSET (GlobalExposure), SHOW_LOGIN); } } return Status; } } /** Checks whether the given FormId is main MEBx menu or direct submenu of MEBx main menu. This is required in order correctly determine if MebxSetMebxState should be sent or not. Whenever user is in MEBx, MebxSetMebxState (AmtMebxStateEnter) should be the last issued AMTHI command of this type. The knowledge of direct submenus is needed, because EFI_BROWSER_ACTION_FORM_CLOSE is executed whenever user exits the menu or enters a submenu, causing to send MebxSetMebxState (AmtMebxStateExit) command. We need to resend MebxSetMebxState (AmtMebxStateEnter) to be sure that KVM is not supported at this time. @param[in] QuestionId A unique value which is sent to the original exporting driver so that it can identify the type of data to expect. @retval TRUE User has entered MEBx main menu or direct submenu. @retval FALSE User has entered any other menu. **/ BOOLEAN IsMebxFormId ( IN EFI_QUESTION_ID QuestionId ) { switch (QuestionId) { case FORM_ID_MEBX: case FORM_ID_AMT: case FORM_ID_OEM_DEBUG: return TRUE; default: return FALSE; } } /** See UEFI spec for the general meaning of Callback function. This function mostly just calls other, specific callback functions depending on question ID For Remote Assistance it also toggles between two strings to make impression that code didn't hang yet while it waits for remote connection @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Action Specifies the type of action taken by the browser. @param[in] QuestionId A unique value which is sent to the original exporting driver so that it can identify the type of data to expect. @param[in] Type The type of value for the question. @param[in] Value A pointer to the data being sent to the original exporting driver. @param[out] ActionRequest On return, points to the action requested by the callback function. @retval EFI_SUCCESS The callback successfully handled the action. @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data. @retval EFI_DEVICE_ERROR The variable could not be saved. @retval EFI_UNSUPPORTED The specified Action is not supported by the callback. **/ EFI_STATUS EFIAPI DriverCallback ( 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 ) { EFI_STATUS Status; EFI_EVENT Event; UINT8 CheckResult; static UINT8 CurrentMebxState = MEBX_STATE_UNDEFINED; if (Action == EFI_BROWSER_ACTION_FORM_OPEN && IsMebxFormId (QuestionId)) { // // When user opens MEBx submenu, remote admin shouldn't be able to connect to it through KVM. // Create a ReadyToBootEvent to be sure that Exit State is sent to CSME before OS. // if (CurrentMebxState == MEBX_STATE_UNDEFINED) { Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, MebxExit, NULL, &gMePlatformReadyToBootGuid, &Event); } if (CurrentMebxState != AmtMebxStateEnter) { Status = MebxSetMebxState (AmtMebxStateEnter); if (EFI_ERROR (Status) && (Status == EFI_ALREADY_STARTED)) { MebxDisplayError (KvmActiveSessionMsg); SetExposureFlag (FLAG_OFFSET (GlobalExposure), SHOW_REDIR_ACTIVE); return EFI_ACCESS_DENIED; } CurrentMebxState = AmtMebxStateEnter; } } else if (Action == EFI_BROWSER_ACTION_FORM_CLOSE && QuestionId == FORM_ID_MEBX) { // // When user exits MEBx submenu, remote admin should be able to connect to BIOS setup through KVM // MebxSetMebxState (AmtMebxStateExit); CurrentMebxState = AmtMebxStateExit; } if ((Action == EFI_BROWSER_ACTION_CHANGING) && (QuestionId == KEY_MEPWD || QuestionId == KEY_CHANGEMEPWD)) { return PasswordCallback (Value->string, (QuestionId == KEY_CHANGEMEPWD)); } if (QuestionId == KEY_HOSTDOMAINNAME || QuestionId == KEY_PKIDNSSUFFIX) { if (Action == EFI_BROWSER_ACTION_CHANGING) { return FqdnCallback (Value->string); } } if (QuestionId == KEY_PROVSERVERFQDNADDR && Action == EFI_BROWSER_ACTION_CHANGING) { return ProvServerAddressCallback (Value->string); } if (QuestionId == KEY_STARTCONFIG && Action == EFI_BROWSER_ACTION_CHANGING) { Status = MebxActivateRemoteConfiguration (&gMebxConfiguration.ProvisioningState); CheckResult = (UINT8) EFI_ERROR (Status); SetVariableInBrowser (&CheckResult, FLAG_OFFSET (ShowError) ,1); SET_IN_BROWSER (gMebxConfiguration, ProvisioningState); } if (QuestionId == KEY_HALTCONFIG && Action == EFI_BROWSER_ACTION_CHANGING) { Status = MebxStopRemoteConfiguration (&gMebxConfiguration.ProvisioningState); CheckResult = (UINT8) EFI_ERROR (Status); SetVariableInBrowser (&CheckResult, FLAG_OFFSET (ShowError) ,1); SET_IN_BROWSER (gMebxConfiguration, ProvisioningState); } if ((QuestionId == KEY_IPV4ADDR) || (QuestionId == KEY_IPV4MASKADDR) || (QuestionId == KEY_IPV4GATEWAYADDR) || (QuestionId == KEY_IPV4PREFDNSADDR) || (QuestionId == KEY_IPV4ALTDNSADDR)) { if (Action == EFI_BROWSER_ACTION_CHANGING) { return Ipv4Callback (Value->string, QuestionId); } } if (Action >= EFI_BROWSER_ACTION_DEFAULT_STANDARD) { return EFI_SUCCESS; } return EFI_UNSUPPORTED; } /** See UEFI spec for the general meaning of ExtractConfig function. MEBx is unusual in that it doesn't store configuration entered by user in EFI flash variables. Instead, all(*) config is retrieved from CSME firmware using HECI messages. "All" actually means all except Password Policy which can't be read unless user is logged in, which is not the case at the time ExtractCondig is executed. Instead Password Policy will be retrieved inside PasswordCallback. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Request A null-terminated Unicode string in format. @param[out] 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[out] 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 ExtractConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Request, OUT EFI_STRING *Progress, OUT EFI_STRING *Results ) { if ((NULL == This) || (NULL == Progress) || (NULL == Results)) { return EFI_INVALID_PARAMETER; } DEBUG ((DEBUG_INFO, "MEBx Setup Extract Config:\n")); MebxGetInfo (&gMebxConfiguration); ShowConfig (&gMebxConfiguration); // can't read PwdPolicy here, AmthiCfgGetMebxPwdChangePolicy message only works after login return mHiiConfigRouting->BlockToConfig ( mHiiConfigRouting, Request, (UINT8 *) &gMebxConfiguration, sizeof (MEBX_CONFIGURATION), Results, Progress ); } /** Promote next platform reset to Global type. Required after changing certain settings in MEBX. @param[in] ResetType UEFI defined reset type. @param[in] ResetStatus The status code for the reset. @param[in] DataSize The size of ResetData in bytes. @param[in] ResetData Optional element used to introduce a platform specific reset. The exact type of the reset is defined by the EFI_GUID that follows the Null-terminated Unicode string. **/ VOID EFIAPI ResetHook ( IN EFI_RESET_TYPE ResetType, IN EFI_STATUS ResetStatus, IN UINTN DataSize, IN VOID *ResetData OPTIONAL ) { PCH_RESET_DATA NewResetData; if (ResetType == EfiResetPlatformSpecific) { return; } DEBUG ((DEBUG_INFO, "MEBx requires Global Reset\n")); DataSize = sizeof (PCH_RESET_DATA); CopyMem (&NewResetData.Guid, &gPchGlobalResetGuid, sizeof (EFI_GUID)); StrCpyS (NewResetData.Description, PCH_RESET_DATA_STRING_MAX_LENGTH, PCH_PLATFORM_SPECIFIC_RESET_STRING); gRT->ResetSystem (EfiResetPlatformSpecific, EFI_SUCCESS, DataSize, &NewResetData); } /** Promote next platform reset to Global type. Required after changing certain settings in MEBX. **/ VOID ScheduleGlobalReset ( VOID ) { EDKII_PLATFORM_SPECIFIC_RESET_FILTER_PROTOCOL *ResetFilter; static BOOLEAN ResetAlreadyScheduled = FALSE; EFI_STATUS Status; if (ResetAlreadyScheduled) { return; } ResetAlreadyScheduled = TRUE; Status = gBS->LocateProtocol (&gEdkiiPlatformSpecificResetFilterProtocolGuid, NULL, (VOID **) &ResetFilter); if (!EFI_ERROR (Status)) { ResetFilter->RegisterResetNotify (ResetFilter, ResetHook); } } /** Address of provisioning server is provided by user as unicode string. This function checks if it was provided as IPv4 addr, IPv6 addr or host name. IPvN vs host address are sent in different fields of the same AMTHI message, so a decision must be taken which type of address it is and where to put it @param[in] Address Configuration Server Address @param[in] Port Configuration Server Port Value @retval EFI_SUCCESS Command succeeded @retval EFI_INVALID_PARAMETER NULL parameter @retval EFI_UNSUPPORTED Current ME mode doesn't support this function @retval EFI_DEVICE_ERROR HECI Device error, command aborts abnormally @retval EFI_TIMEOUT HECI does not return the buffer before timeout @retval EFI_BUFFER_TOO_SMALL Message Buffer is too small for the Acknowledge **/ EFI_STATUS UpdateProvisioningServer ( IN CHAR16 *Address, IN UINT16 Port ) { FQDN_ANSI_STRING Fqdn; UINT8 IpAddress[CFG_IPV6_ADDR_LEN_MAX + 1]; UINT32 Error; EFI_STATUS Status; IPv6_ADDRESS Ipv6; UINT8 Prefix; if (Address == NULL) { return EFI_INVALID_PARAMETER; } Prefix = 0; Error = 0; Ipv4UnicodeToInteger (Address, &Error); if (Error) { Status = StrToIpv6Address (Address, NULL, &Ipv6, &Prefix); Error = EFI_ERROR (Status) || (Prefix != MAX_UINT8); } // if Address could be converted to either IPv4 or IPv6 then send it in IP field of AMTHI message if (!Error) { UnicodeStrToAsciiStrS (Address, (CHAR8 *) &IpAddress, sizeof (IpAddress)); ZeroMem (&Fqdn, sizeof (Fqdn)); } else { UnicodeStrToAsciiStrS (Address, (CHAR8 *) &Fqdn.Buffer, sizeof (Fqdn.Buffer)); Fqdn.Length = (UINT16) AsciiStrLen ((CHAR8 *) &Fqdn.Buffer); ZeroMem (IpAddress, CFG_IPV6_ADDR_LEN_MAX); CopyMem (IpAddress, "0.0.0.0", 8); } return MebxSetConfigServerData (Port, IpAddress, &Fqdn); } /** Update current IPV4 TCPIP Parameters. The function adjusts input parameters to format supported by HECI message. @param[in] DhcpMode Mode of AMT DHCP operation @param[in] LocalAddr Local IP address that the AMT device uses @param[in] SubnetMask Mask for the local subnet @param[in] GatewayAddr IP address of the default gateway that the AMT device uses @param[in] PriDnsAddr IP Address of the Primary DNS @param[in] SecDnsAddr IP Address of the Secondary DNS @retval EFI_SUCCESS Command succeeded @retval EFI_INVALID_PARAMETER NULL parameter @retval EFI_UNSUPPORTED Current ME mode doesn't support this function @retval EFI_DEVICE_ERROR HECI Device error, command aborts abnormally @retval EFI_TIMEOUT HECI does not return the buffer before timeout @retval EFI_BUFFER_TOO_SMALL Message Buffer is too small for the Acknowledge **/ EFI_STATUS UpdateIpv4Params ( IN UINT8 DhcpMode, IN CHAR16 *LocalAddr, IN CHAR16 *SubnetMask, IN CHAR16 *GatewayAddr, IN CHAR16 *PriDnsAddr, IN CHAR16 *SecDnsAddr ) { IMB_TCPIP_PARAMS NewIpv4Cfg; UINT32 Error; if (LocalAddr == NULL || SubnetMask == NULL || GatewayAddr == NULL || PriDnsAddr == NULL || SecDnsAddr == NULL) { return EFI_INVALID_PARAMETER; } NewIpv4Cfg.DhcpMode = DhcpMode; NewIpv4Cfg.LocalAddr = Ipv4UnicodeToInteger (LocalAddr, &Error); NewIpv4Cfg.SubnetMask = Ipv4UnicodeToInteger (SubnetMask, &Error); NewIpv4Cfg.GatewayAddr = Ipv4UnicodeToInteger (GatewayAddr, &Error); NewIpv4Cfg.PriDnsAddr = Ipv4UnicodeToInteger (PriDnsAddr, &Error); NewIpv4Cfg.SecDnsAddr = Ipv4UnicodeToInteger (SecDnsAddr, &Error); // Domain name is always set to 0s NewIpv4Cfg.DomainName.Length = 0; ZeroMem (&NewIpv4Cfg.DomainName.Buffer, MAX_ASCII_STRING); return MebxSetIpv4Params (&NewIpv4Cfg); } /** Attempts to find given char inside string. If found, returns index of this char inside string. Otherwise, returns index that equals Length or points to null-terminator, whatever comes first @param[in] Value ASCII Value of the char to be found @param[in] String Pointer to the investigated string @param[in] Length Max string length @retval Position Index of the found character **/ UINT32 FindChar ( IN CHAR8 Value, IN CHAR8 *String, IN UINT32 Length ) { UINT32 Position; Position = 0; while (Position < Length && String[Position] != '\0' && String[Position] != Value) { Position++; } return Position; } /** Update current FQDN Data. @param[in] FqdnType Whether the domain name is shared with the Host or dedicated to ME @param[in] DynamicDnsUpdate Whether the Dynamic DNS Update Client in FW is enabled or not @param[in] UpdateInterval Defines the interval at which the FW DDNS Update client will send periodic updates for all the RRs registered by FW @param[in] Ttl The TTL value in seconds for RRs registered by FW @param[in] HostDomainName Host Domain name @retval EFI_SUCCESS Command succeeded @retval EFI_INVALID_PARAMETER NULL parameter @retval EFI_UNSUPPORTED Current ME mode doesn't support this function @retval EFI_DEVICE_ERROR HECI Device error, command aborts abnormally @retval EFI_TIMEOUT HECI does not return the buffer before timeout @retval EFI_BUFFER_TOO_SMALL Message Buffer is too small for the Acknowledge **/ EFI_STATUS UpdateFqdn ( IN UINT8 FqdnType, IN UINT8 DynamicDnsUpdate, IN UINT32 UpdateInterval, IN UINT32 Ttl, IN CHAR16 *HostDomainName ) { FQDN_DATA SendMe; if (HostDomainName == NULL) { return EFI_INVALID_PARAMETER; } ZeroMem (&SendMe, sizeof (SendMe)); SendMe.SharedFqdn = FqdnType; SendMe.DdnsUpdateEnabled = DynamicDnsUpdate; SendMe.DdnsPeriodicUpdateInterval = UpdateInterval; SendMe.DdnsTtl = Ttl; SendMe.Fqdn.Length = (UINT16)StrLen (HostDomainName); UnicodeStrToAsciiStrS (HostDomainName, (CHAR8 *) &SendMe.Fqdn.Buffer, MAX_STRING_LENGTH_FQDN); SendMe.HostNameLength = FindChar ('.', (CHAR8 *) &SendMe.Fqdn.Buffer, SendMe.Fqdn.Length); return MebxSetFqdn (&SendMe); } /** Update current PKI FQDN DNS Suffix. @attention Function is not allowed in POST_PROVISION State @param[in] NewPkiDns New Fqdn Pki Dns suffix @retval EFI_SUCCESS Command succeeded @retval EFI_INVALID_PARAMETER NULL parameter @retval EFI_UNSUPPORTED Current ME mode doesn't support this function @retval EFI_DEVICE_ERROR HECI Device error, command aborts abnormally @retval EFI_TIMEOUT HECI does not return the buffer before timeout @retval EFI_BUFFER_TOO_SMALL Message Buffer is too small for the Acknowledge **/ EFI_STATUS UpdatePkiFqdnSuffix ( IN CHAR16 *PkiDnsSuffix ) { FQDN_SUFFIX_ANSI_STRING Fqdn; if (PkiDnsSuffix == NULL) { return EFI_INVALID_PARAMETER; } ZeroMem (&Fqdn, sizeof (Fqdn)); Fqdn.Length = (UINT16)StrLen (PkiDnsSuffix); UnicodeStrToAsciiStrS (PkiDnsSuffix, (CHAR8 *) &Fqdn.Buffer, MAX_STRING_LENGTH_FQDN_SUFFIX); return MebxSetPkiFqdnSuffix (&Fqdn); } /** See UEFI spec for the general meaning of RouteConfig function. MEBx is unusual in that it doesn't store configuration entered by user in EFI flash variables. Instead, all config is passed to CSME firmware using HECI messages. Such messages take time, so as an optimization only those settings that did change will generate messages: we first check delta between copy of CSME config (stored in gMebxConfiguration) and the new config coming from browser (NewCfg) Disabling AMT prevents any other changes from being sent. Changing NetAccess resets most other settings so they must be retrieved from CSME and updated in browser. Some settings cannot be changed unless user is logged in. All those cases are handled in this function. @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. @param[in] Configuration A null-terminated Unicode string in format. @param[out] Progress A pointer to a string filled in with the offset of the most recent '&' before the first failing name/value pair (or the 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 RouteConfig ( IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, IN CONST EFI_STRING Configuration, OUT EFI_STRING *Progress ) { EFI_STATUS Status; UINTN BufferSize; MEBX_CONFIGURATION NewCfg; UINT32 AmthiStatus; BOOLEAN IsGlobalResetRequired; UINT32 HashNumber; DEBUG ((DEBUG_INFO, "MEBx Setup Route Config:\n")); DEBUG ((DEBUG_INFO, "%s\n", Configuration)); if ((NULL == This) || (NULL == Progress)) { return EFI_INVALID_PARAMETER; } IsGlobalResetRequired = FALSE; // Copy full buffer to make sure NewCfg uses full set of data even when Configuration string does not contain full struct CopyMem (&NewCfg, &gMebxConfiguration, sizeof (NewCfg)); BufferSize = sizeof (MEBX_CONFIGURATION); Status = mHiiConfigRouting->ConfigToBlock (mHiiConfigRouting, Configuration, (UINT8 *) &NewCfg, &BufferSize, Progress); if (gMebxConfiguration.GlobalExposure != SHOW_EVERYTHING || NewCfg.GlobalExposure != SHOW_EVERYTHING) { goto Exit; } // // Compare gMebxConfiguration with NewCfg and send Heci msg for every item that's different // ShowConfigDiff (&gMebxConfiguration, &NewCfg); if (NewCfg.AmtState != gMebxConfiguration.AmtState) { Status = MebxSetAmtState (NewCfg.AmtState); if (EFI_ERROR (Status)) { MebxDisplayError (MeSetAmtStateApiError); return Status; } IsGlobalResetRequired = TRUE; // // If AMT was disabled and user enables it, AMT menu will be inaccessible until reboot. // To change this behavior, one needs to retrieve whole AMT config here. // NewCfg.AmtStateExposure = 0; SetExposureFlag (FLAG_OFFSET (AmtStateExposure), 0); goto Exit; } if (NewCfg.TlsPpkRemoteConfig != gMebxConfiguration.TlsPpkRemoteConfig) { Status = MebxSetZeroTouchEnabled (NewCfg.TlsPpkRemoteConfig); if (NewCfg.TlsPpkRemoteConfig == 0 && NewCfg.ProvisioningState == 1) { // partial unprovisioning is a side effect of disabling ZTC during IN provisioning state NewCfg.NetAccess = MAX (NewCfg.NetAccess, 2); } if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetZtcApiError); } } if (StrCmp (NewCfg.PkiDnsSuffix, gMebxConfiguration.PkiDnsSuffix)) { Status = UpdatePkiFqdnSuffix (NewCfg.PkiDnsSuffix); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetPkiFqdnSuffixApiError); } } if (NewCfg.NetAccess != gMebxConfiguration.NetAccess) { switch (NewCfg.NetAccess) { case 0: MebxCompleteConfigurationRequest (&AmthiStatus); break; case 1: ASSERT (FALSE); break; case 2: MebxPerformUnprovisioning (UnprovisionTypePartial); break; case 3: MebxPerformUnprovisioning (UnprovisionTypeFull); break; } Status = MebxGetProvisioningState (&NewCfg.ProvisioningState); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiGetProvisionStateApiError); return Status; } SET_IN_BROWSER (NewCfg, ProvisioningState); if (NewCfg.NetAccess > 1) { //get new values for all fields because they might have changed NewCfg.NetAccess = 1; SET_IN_BROWSER (NewCfg, NetAccess); MebxGetInfo (&NewCfg); SET_IN_BROWSER (NewCfg, MeOnHostSlpStates); SET_IN_BROWSER (NewCfg, IdleTimeout); SET_IN_BROWSER (NewCfg, PwdPolicy); SET_IN_BROWSER (NewCfg, SolEnable); SET_IN_BROWSER (NewCfg, StorageRedirEnable); SET_IN_BROWSER (NewCfg, KvmEnable); SET_IN_BROWSER (NewCfg, UserOptIn); SET_IN_BROWSER (NewCfg, RemoteItOptInConfig); SET_IN_BROWSER (NewCfg, HostDomainName); SET_IN_BROWSER (NewCfg, FqdnType); SET_IN_BROWSER (NewCfg, DynamicDnsUpdate); SET_IN_BROWSER (NewCfg, UpdateInterval); SET_IN_BROWSER (NewCfg, Ttl); SET_IN_BROWSER (NewCfg, DhcpMode); SET_IN_BROWSER (NewCfg, Ipv4Addr); SET_IN_BROWSER (NewCfg, Ipv4MaskAddr); SET_IN_BROWSER (NewCfg, Ipv4GatewayAddr); SET_IN_BROWSER (NewCfg, Ipv4PrefDnsAddr); SET_IN_BROWSER (NewCfg, Ipv4AltDnsAddr); SET_IN_BROWSER (NewCfg, ProvServerAddr); SET_IN_BROWSER (NewCfg, ProvServerPort); SET_IN_BROWSER (NewCfg, TlsPpkRemoteConfig); SET_IN_BROWSER (NewCfg, PkiDnsSuffix); SET_IN_BROWSER (NewCfg, PrivacyLevel); SET_IN_BROWSER (NewCfg, LanLessPlatform); SET_IN_BROWSER (NewCfg, NumberOfPowerPkgs); SET_IN_BROWSER (NewCfg, NoDefaultHashes); SET_IN_BROWSER (NewCfg, HashValid); SET_IN_BROWSER (NewCfg, Pwd); SET_IN_BROWSER (NewCfg, MOffOverride); SET_IN_BROWSER (NewCfg, SouthClinkState); SET_IN_BROWSER (NewCfg, AmtStateExposure); SET_IN_BROWSER (NewCfg, AmtSubmenuExposure); SET_IN_BROWSER (NewCfg, KvmExposure); SET_IN_BROWSER (NewCfg, ProvisioningState); SET_IN_BROWSER (NewCfg, OptInConfigExposure); SET_IN_BROWSER (NewCfg, RemoteItOptInConfigExposure); SET_IN_BROWSER (NewCfg, GlobalExposure); SET_IN_BROWSER (NewCfg, SelectedHashType); goto Exit; } } if ((NewCfg.SolEnable != gMebxConfiguration.SolEnable) || (NewCfg.StorageRedirEnable != gMebxConfiguration.StorageRedirEnable)) { IsGlobalResetRequired = TRUE; Status = MebxSetSolStorageRedir (NewCfg.SolEnable, NewCfg.StorageRedirEnable); HeciSetInvocationCode (DATA_SYNC_CONFIRMATION); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetSolStorageRedirectionStateApiError); } } if (NewCfg.KvmEnable != gMebxConfiguration.KvmEnable) { IsGlobalResetRequired = TRUE; Status = MebxSetKvmEnabled (NewCfg.KvmEnable); HeciSetInvocationCode (DATA_SYNC_CONFIRMATION); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetKvmStateApiError); } } if (NewCfg.PwdPolicy != gMebxConfiguration.PwdPolicy) { MebxSetMebxPwdPolicy (NewCfg.PwdPolicy); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetPwdPolicyApiError); } } if (StrCmp (NewCfg.HostDomainName, gMebxConfiguration.HostDomainName) || (NewCfg.FqdnType != gMebxConfiguration.FqdnType) || (NewCfg.DynamicDnsUpdate != gMebxConfiguration.DynamicDnsUpdate) || (NewCfg.UpdateInterval != gMebxConfiguration.UpdateInterval) || (NewCfg.Ttl != gMebxConfiguration.Ttl)) { Status = UpdateFqdn ( NewCfg.FqdnType, NewCfg.DynamicDnsUpdate, NewCfg.UpdateInterval, NewCfg.Ttl, NewCfg.HostDomainName ); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetFqdnApiError); } } if ((NewCfg.RemoteItOptInConfig != gMebxConfiguration.RemoteItOptInConfig) || (NewCfg.UserOptIn != gMebxConfiguration.UserOptIn)) { MebxSetOptInState (NewCfg.UserOptIn, (UINT32)NewCfg.RemoteItOptInConfig); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetOptinStateApiError); } } if (NewCfg.IdleTimeout != gMebxConfiguration.IdleTimeout) { MebxSetIdleTimeout (NewCfg.IdleTimeout); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetIdleTimeoutApiError); } } if ((NewCfg.DhcpMode != gMebxConfiguration.DhcpMode) || StrCmp (NewCfg.Ipv4GatewayAddr, gMebxConfiguration.Ipv4GatewayAddr) || StrCmp (NewCfg.Ipv4Addr, gMebxConfiguration.Ipv4Addr) || StrCmp (NewCfg.Ipv4PrefDnsAddr, gMebxConfiguration.Ipv4PrefDnsAddr) || StrCmp (NewCfg.Ipv4AltDnsAddr, gMebxConfiguration.Ipv4AltDnsAddr) || StrCmp (NewCfg.Ipv4MaskAddr, gMebxConfiguration.Ipv4MaskAddr) ) { Status = UpdateIpv4Params ( NewCfg.DhcpMode, NewCfg.Ipv4Addr, NewCfg.Ipv4MaskAddr, NewCfg.Ipv4GatewayAddr, NewCfg.Ipv4PrefDnsAddr, NewCfg.Ipv4AltDnsAddr ); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetIpv4ParamsApiError); } } if (StrCmp (NewCfg.ProvServerAddr, gMebxConfiguration.ProvServerAddr) || NewCfg.ProvServerPort != gMebxConfiguration.ProvServerPort) { Status = UpdateProvisioningServer (NewCfg.ProvServerAddr, NewCfg.ProvServerPort); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetConfigServerApiError); } } if (NewCfg.MeOnHostSlpStates != gMebxConfiguration.MeOnHostSlpStates) { Status = MebxUpdatePowerPolicy (NewCfg.MeOnHostSlpStates); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiPowerPolicyApiError); } } if (NewCfg.MOffOverride != gMebxConfiguration.MOffOverride) { Status = HeciSetMoffOverrideState (NewCfg.MOffOverride); if (EFI_ERROR (Status)) { MebxDisplayError (MeSetMoffOvrdStateApiError); } IsGlobalResetRequired = TRUE; } if (NewCfg.SouthClinkState != gMebxConfiguration.SouthClinkState) { Status = HeciSetSouthClinkState (NewCfg.SouthClinkState); if (EFI_ERROR (Status)) { MebxDisplayError (MeSetSouthClinkStateApiError); } IsGlobalResetRequired = TRUE; } for (HashNumber = 0; HashNumber < MAX_HASH_ENTRIES; HashNumber++) { if (NewCfg.CertActive[HashNumber] != gMebxConfiguration.CertActive[HashNumber]) { Status = MebxSetCertificateHashState (&NewCfg.CertHandle[HashNumber], NewCfg.CertActive[HashNumber]); if (EFI_ERROR (Status)) { MebxDisplayError (AmthiSetHashStateApiError); } } } Exit: CopyMem (&gMebxConfiguration, &NewCfg, sizeof (MEBX_CONFIGURATION)); if (IsGlobalResetRequired) { ScheduleGlobalReset (); } return Status; } /** There are 2 marketing brands of AMT: "AMT" itself and "Standard Manageability". This function adjusts brand strings that will be displayed in setup browser **/ VOID ReplaceBrandStrings ( VOID ) { EFI_STRING Temp; Temp = HiiGetString (mMebxHiiHandle, STRING_TOKEN (STR_SUBMENU_STD_MANAGEABILITY), NULL); HiiSetString (mMebxHiiHandle, STRING_TOKEN (STR_SUBMENU_AMT), Temp, NULL); FreePool (Temp); Temp = HiiGetString (mMebxHiiHandle, STRING_TOKEN (STR_STD_MANAGEABILITY_DISABLED), NULL); HiiSetString (mMebxHiiHandle, STRING_TOKEN (STR_AMT_DISABLED), Temp, NULL); FreePool (Temp); Temp = HiiGetString (mMebxHiiHandle, STRING_TOKEN (STR_OPTION_STD_MANAGEABILITY_GLOBAL_STATE), NULL); HiiSetString (mMebxHiiHandle, STRING_TOKEN (STR_OPTION_AMT_GLOBAL_STATE), Temp, NULL); FreePool (Temp); } /** This function integrates MEBx with BIOS setup browser by publishing MEBx's formset and HII config access protocol. @param[in] PlatformBrand Platform Brand: @see PLATFORM_BRAND @retval EFI_SUCCESS Successfully published MEBx setup data @retval Others Error while publishing MEBx setup data **/ EFI_STATUS MebxSetupInit ( IN UINT32 PlatformBrand ) { EFI_STATUS Status; EFI_HANDLE DriverHandle; DEBUG ((DEBUG_INFO, "%a start\n", __FUNCTION__)); gMebxConfigAccess.ExtractConfig = ExtractConfig; gMebxConfigAccess.RouteConfig = RouteConfig; gMebxConfigAccess.Callback = DriverCallback; // // Locate required Hii protocols // Status = gBS->LocateProtocol ( &gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **) &mHiiConfigRouting ); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { return Status; } // // Install Device Path Protocol and Config Access protocol to driver handle. // DriverHandle = NULL; Status = gBS->InstallMultipleProtocolInterfaces ( &DriverHandle, &gEfiDevicePathProtocolGuid, &mHiiVendorDevicePath, &gEfiHiiConfigAccessProtocolGuid, &gMebxConfigAccess, NULL ); if (EFI_ERROR (Status)) { return Status; } DEBUG ((DEBUG_INFO, "%a Publishing HII Data\n", __FUNCTION__)); mMebxHiiHandle = HiiAddPackages ( &gMebxFormSetGuid, DriverHandle, MebxSetupStrings, MebxSetupVfrBin, NULL ); ASSERT (mMebxHiiHandle != NULL); if (mMebxHiiHandle == NULL) { return EFI_DEVICE_ERROR; } if (PlatformBrand == IntelStandardManageabilityBrand) { ReplaceBrandStrings (); } DEBUG ((DEBUG_INFO, "%a end\n", __FUNCTION__)); return Status; } /** Driver's entry point. Checks if MEBx is supported and publishes setup formset into setup browser. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS MEBx driver initialized successfully @retval EFI_UNSUPPORTED Feature is not supported by the FW @retval Others MEBx driver initialization failed **/ EFI_STATUS EFIAPI MebxSetupEntry ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE* SystemTable ) { ME_BIOS_PAYLOAD_HOB *MbpHob; DEBUG ((DEBUG_INFO, "%a invoked!\n", __FUNCTION__)); MbpHob = NULL; MbpHob = GetFirstGuidHob (&gMeBiosPayloadHobGuid); if (MbpHob != NULL) { if (MbpHob->MeBiosPayload.FwPlatType.RuleData.Fields.IntelMeFwImageType != IntelMeCorporateFw || MbpHob->MeBiosPayload.FwCapsSku.FwCapabilities.Fields.Amt != 1) { return EFI_UNSUPPORTED; } } else { DEBUG ((DEBUG_ERROR, "MBP HOB not found\n")); return EFI_UNSUPPORTED; } gMebxConfiguration.FwVersionMajor = (UINT16)(MbpHob->MeBiosPayload.FwVersionName.MajorVersion); return MebxSetupInit (MbpHob->MeBiosPayload.FwPlatType.RuleData.Fields.PlatformBrand); }