/** @file | |
Helper functions for configuring or getting the parameters relating to iSCSI. | |
Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR> | |
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. | |
**/ | |
#include "IScsiImpl.h" | |
EFI_GUID mVendorGuid = ISCSI_CONFIG_GUID; | |
CHAR16 mVendorStorageName[] = L"ISCSI_CONFIG_IFR_NVDATA"; | |
BOOLEAN mIScsiDeviceListUpdated = FALSE; | |
UINTN mNumberOfIScsiDevices = 0; | |
ISCSI_FORM_CALLBACK_INFO *mCallbackInfo = NULL; | |
LIST_ENTRY mIScsiConfigFormList = { | |
&mIScsiConfigFormList, | |
&mIScsiConfigFormList | |
}; | |
HII_VENDOR_DEVICE_PATH mIScsiHiiVendorDevicePath = { | |
{ | |
{ | |
HARDWARE_DEVICE_PATH, | |
HW_VENDOR_DP, | |
{ | |
(UINT8) (sizeof (VENDOR_DEVICE_PATH)), | |
(UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8) | |
} | |
}, | |
// | |
// {49D7B73E-143D-4716-977B-C45F1CB038CC} | |
// | |
{ 0x49d7b73e, 0x143d, 0x4716, { 0x97, 0x7b, 0xc4, 0x5f, 0x1c, 0xb0, 0x38, 0xcc } } | |
}, | |
{ | |
END_DEVICE_PATH_TYPE, | |
END_ENTIRE_DEVICE_PATH_SUBTYPE, | |
{ | |
(UINT8) (END_DEVICE_PATH_LENGTH), | |
(UINT8) ((END_DEVICE_PATH_LENGTH) >> 8) | |
} | |
} | |
}; | |
/** | |
Convert the IPv4 address into a dotted string. | |
@param[in] Ip The IPv4 address. | |
@param[out] Str The dotted IP string. | |
**/ | |
VOID | |
IScsiIpToStr ( | |
IN EFI_IPv4_ADDRESS *Ip, | |
OUT CHAR16 *Str | |
) | |
{ | |
UnicodeSPrint ( Str, 2 * IP4_STR_MAX_SIZE, L"%d.%d.%d.%d", Ip->Addr[0], Ip->Addr[1], Ip->Addr[2], Ip->Addr[3]); | |
} | |
/** | |
Update the list of iSCSI devices the iSCSI driver is controlling. | |
@retval EFI_SUCCESS The callback successfully handled the action. | |
@retval Others Other errors as indicated. | |
**/ | |
EFI_STATUS | |
IScsiUpdateDeviceList ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
ISCSI_DEVICE_LIST *DeviceList; | |
UINTN DataSize; | |
UINTN NumHandles; | |
EFI_HANDLE *Handles; | |
UINTN HandleIndex; | |
UINTN Index; | |
UINTN LastDeviceIndex; | |
EFI_MAC_ADDRESS MacAddress; | |
UINTN HwAddressSize; | |
UINT16 VlanId; | |
ISCSI_MAC_INFO *CurMacInfo; | |
ISCSI_MAC_INFO TempMacInfo; | |
CHAR16 MacString[70]; | |
UINTN DeviceListSize; | |
// | |
// Dump all the handles the Managed Network Service Binding Protocol is installed on. | |
// | |
Status = gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiManagedNetworkServiceBindingProtocolGuid, | |
NULL, | |
&NumHandles, | |
&Handles | |
); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
DataSize = 0; | |
Status = gRT->GetVariable ( | |
L"iSCSIDeviceList", | |
&mVendorGuid, | |
NULL, | |
&DataSize, | |
NULL | |
); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
DeviceList = (ISCSI_DEVICE_LIST *) AllocatePool (DataSize); | |
gRT->GetVariable ( | |
L"iSCSIDeviceList", | |
&mVendorGuid, | |
NULL, | |
&DataSize, | |
DeviceList | |
); | |
LastDeviceIndex = 0; | |
for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) { | |
Status = NetLibGetMacAddress (Handles[HandleIndex], &MacAddress, &HwAddressSize); | |
ASSERT (Status == EFI_SUCCESS); | |
VlanId = NetLibGetVlanId (Handles[HandleIndex]); | |
for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) { | |
CurMacInfo = &DeviceList->MacInfo[Index]; | |
if ((CurMacInfo->Len == HwAddressSize) && | |
(CurMacInfo->VlanId == VlanId) && | |
(NET_MAC_EQUAL (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize)) | |
) { | |
// | |
// The previous configured NIC is still here. | |
// | |
if (Index != LastDeviceIndex) { | |
// | |
// Swap the current MAC address entry with the one indexed by | |
// LastDeviceIndex. | |
// | |
CopyMem (&TempMacInfo, CurMacInfo, sizeof (ISCSI_MAC_INFO)); | |
CopyMem (CurMacInfo, &DeviceList->MacInfo[LastDeviceIndex], sizeof (ISCSI_MAC_INFO)); | |
CopyMem (&DeviceList->MacInfo[LastDeviceIndex], &TempMacInfo, sizeof (ISCSI_MAC_INFO)); | |
} | |
LastDeviceIndex++; | |
} | |
} | |
if (LastDeviceIndex == DeviceList->NumDevice) { | |
break; | |
} | |
} | |
for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) { | |
// | |
// delete the variables | |
// | |
CurMacInfo = &DeviceList->MacInfo[Index]; | |
IScsiMacAddrToStr (&CurMacInfo->Mac, CurMacInfo->Len, CurMacInfo->VlanId, MacString); | |
gRT->SetVariable (MacString, &gEfiIScsiInitiatorNameProtocolGuid, 0, 0, NULL); | |
gRT->SetVariable (MacString, &mIScsiCHAPAuthInfoGuid, 0, 0, NULL); | |
} | |
FreePool (DeviceList); | |
} else if (Status != EFI_NOT_FOUND) { | |
FreePool (Handles); | |
return Status; | |
} | |
// | |
// Construct the new iSCSI device list. | |
// | |
DeviceListSize = sizeof (ISCSI_DEVICE_LIST) + (NumHandles - 1) * sizeof (ISCSI_MAC_INFO); | |
DeviceList = (ISCSI_DEVICE_LIST *) AllocatePool (DeviceListSize); | |
DeviceList->NumDevice = (UINT8) NumHandles; | |
for (Index = 0; Index < NumHandles; Index++) { | |
NetLibGetMacAddress (Handles[Index], &MacAddress, &HwAddressSize); | |
CurMacInfo = &DeviceList->MacInfo[Index]; | |
CopyMem (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize); | |
CurMacInfo->Len = (UINT8) HwAddressSize; | |
CurMacInfo->VlanId = NetLibGetVlanId (Handles[Index]); | |
} | |
gRT->SetVariable ( | |
L"iSCSIDeviceList", | |
&mVendorGuid, | |
ISCSI_CONFIG_VAR_ATTR, | |
DeviceListSize, | |
DeviceList | |
); | |
FreePool (DeviceList); | |
FreePool (Handles); | |
return Status; | |
} | |
/** | |
Get the iSCSI configuration form entry by the index of the goto opcode actived. | |
@param[in] Index The 0-based index of the goto opcode actived. | |
@return The iSCSI configuration form entry found. | |
**/ | |
ISCSI_CONFIG_FORM_ENTRY * | |
IScsiGetConfigFormEntryByIndex ( | |
IN UINT32 Index | |
) | |
{ | |
UINT32 CurrentIndex; | |
LIST_ENTRY *Entry; | |
ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry; | |
CurrentIndex = 0; | |
ConfigFormEntry = NULL; | |
NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) { | |
if (CurrentIndex == Index) { | |
ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link); | |
break; | |
} | |
CurrentIndex++; | |
} | |
return ConfigFormEntry; | |
} | |
/** | |
Convert the iSCSI configuration data into the IFR data. | |
@param[in] ConfigFormEntry The iSCSI configuration form entry. | |
@param[out] IfrNvData The IFR nv data. | |
**/ | |
VOID | |
IScsiConvertDeviceConfigDataToIfrNvData ( | |
IN ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry, | |
OUT ISCSI_CONFIG_IFR_NVDATA *IfrNvData | |
) | |
{ | |
ISCSI_SESSION_CONFIG_NVDATA *SessionConfigData; | |
ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData; | |
// | |
// Normal session configuration parameters. | |
// | |
SessionConfigData = &ConfigFormEntry->SessionConfigData; | |
IfrNvData->Enabled = SessionConfigData->Enabled; | |
IfrNvData->InitiatorInfoFromDhcp = SessionConfigData->InitiatorInfoFromDhcp; | |
IfrNvData->TargetInfoFromDhcp = SessionConfigData->TargetInfoFromDhcp; | |
IfrNvData->TargetPort = SessionConfigData->TargetPort; | |
IScsiIpToStr (&SessionConfigData->LocalIp, IfrNvData->LocalIp); | |
IScsiIpToStr (&SessionConfigData->SubnetMask, IfrNvData->SubnetMask); | |
IScsiIpToStr (&SessionConfigData->Gateway, IfrNvData->Gateway); | |
IScsiIpToStr (&SessionConfigData->TargetIp, IfrNvData->TargetIp); | |
IScsiAsciiStrToUnicodeStr (SessionConfigData->TargetName, IfrNvData->TargetName); | |
IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun); | |
// | |
// CHAP authentication parameters. | |
// | |
AuthConfigData = &ConfigFormEntry->AuthConfigData; | |
IfrNvData->CHAPType = AuthConfigData->CHAPType; | |
IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPName, IfrNvData->CHAPName); | |
IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPSecret, IfrNvData->CHAPSecret); | |
IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPName, IfrNvData->ReverseCHAPName); | |
IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPSecret, IfrNvData->ReverseCHAPSecret); | |
} | |
/** | |
This function allows the caller to request the current | |
configuration for one or more named elements. The resulting | |
string is in <ConfigAltResp> format. Any and all alternative | |
configuration strings shall also be appended to the end of the | |
current configuration string. If they are, they must appear | |
after the current configuration. They must contain the same | |
routing (GUID, NAME, PATH) as the current configuration string. | |
They must have an additional description indicating the type of | |
alternative configuration the string represents, | |
"ALTCFG=<StringToken>". That <StringToken> (when | |
converted from Hex UNICODE to binary) is a reference to a | |
string in the associated string pack. | |
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param[in] Request A null-terminated Unicode string in | |
<ConfigRequest> format. Note that this | |
includes the routing information as well as | |
the configurable name / value pairs. It is | |
invalid for this string to be in | |
<MultiConfigRequest> 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 | |
<ConfigAltResp> 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 string is filled with the | |
values corresponding to all requested | |
names. | |
@retval EFI_OUT_OF_RESOURCES Not enough memory to store the | |
parts of the results that must be | |
stored awaiting possible future | |
protocols. | |
@retval EFI_INVALID_PARAMETER For example, passing in a NULL | |
for the Request parameter | |
would result in this type of | |
error. In this case, the | |
Progress parameter would be | |
set to NULL. | |
@retval EFI_NOT_FOUND Routing data doesn't match any | |
known driver. Progress set to the | |
first character in the routing header. | |
Note: There is no requirement that the | |
driver validate the routing data. It | |
must skip the <ConfigHdr> in order to | |
process the names. | |
@retval EFI_INVALID_PARAMETER Illegal syntax. Progress set | |
to most recent & before the | |
error or the beginning of the | |
string. | |
@retval EFI_INVALID_PARAMETER Unknown name. Progress points | |
to the & before the name in | |
question.Currently not implemented. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
IScsiFormExtractConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Request, | |
OUT EFI_STRING *Progress, | |
OUT EFI_STRING *Results | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR8 InitiatorName[ISCSI_NAME_IFR_MAX_SIZE]; | |
UINTN BufferSize; | |
ISCSI_CONFIG_IFR_NVDATA *IfrNvData; | |
ISCSI_FORM_CALLBACK_INFO *Private; | |
EFI_HII_CONFIG_ROUTING_PROTOCOL *HiiConfigRouting; | |
EFI_STRING ConfigRequestHdr; | |
EFI_STRING ConfigRequest; | |
BOOLEAN AllocatedRequest; | |
UINTN Size; | |
if (Progress == NULL || Results == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
*Progress = Request; | |
if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &mVendorGuid, mVendorStorageName)) { | |
return EFI_NOT_FOUND; | |
} | |
ConfigRequestHdr = NULL; | |
ConfigRequest = NULL; | |
AllocatedRequest = FALSE; | |
Size = 0; | |
if (!mIScsiDeviceListUpdated) { | |
// | |
// Update the device list. | |
// | |
IScsiUpdateDeviceList (); | |
mIScsiDeviceListUpdated = TRUE; | |
} | |
Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This); | |
IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA)); | |
ASSERT (IfrNvData != NULL); | |
if (Private->Current != NULL) { | |
IScsiConvertDeviceConfigDataToIfrNvData (Private->Current, IfrNvData); | |
} | |
BufferSize = ISCSI_NAME_IFR_MAX_SIZE; | |
Status = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName); | |
if (EFI_ERROR (Status)) { | |
IfrNvData->InitiatorName[0] = L'\0'; | |
} else { | |
IScsiAsciiStrToUnicodeStr (InitiatorName, IfrNvData->InitiatorName); | |
} | |
// | |
// Convert buffer data to <ConfigResp> by helper function BlockToConfig() | |
// | |
HiiConfigRouting = Private->ConfigRouting; | |
BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA); | |
ConfigRequest = Request; | |
if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) { | |
// | |
// Request has no request element, construct full request string. | |
// Allocate and fill a buffer large enough to hold the <ConfigHdr> template | |
// followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator | |
// | |
ConfigRequestHdr = HiiConstructConfigHdr (&mVendorGuid, mVendorStorageName, Private->DriverHandle); | |
Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16); | |
ConfigRequest = AllocateZeroPool (Size); | |
ASSERT (ConfigRequest != NULL); | |
AllocatedRequest = TRUE; | |
UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize); | |
FreePool (ConfigRequestHdr); | |
} | |
Status = HiiConfigRouting->BlockToConfig ( | |
HiiConfigRouting, | |
ConfigRequest, | |
(UINT8 *) IfrNvData, | |
BufferSize, | |
Results, | |
Progress | |
); | |
FreePool (IfrNvData); | |
// | |
// Free the allocated config request string. | |
// | |
if (AllocatedRequest) { | |
FreePool (ConfigRequest); | |
ConfigRequest = NULL; | |
} | |
// | |
// Set Progress string to the original request string. | |
// | |
if (Request == NULL) { | |
*Progress = NULL; | |
} else if (StrStr (Request, L"OFFSET") == NULL) { | |
*Progress = Request + StrLen (Request); | |
} | |
return Status; | |
} | |
/** | |
This function applies changes in a driver's configuration. | |
Input is a Configuration, which has the routing data for this | |
driver followed by name / value configuration pairs. The driver | |
must apply those pairs to its configurable storage. If the | |
driver's configuration is stored in a linear block of data | |
and the driver's name / value pairs are in <BlockConfig> | |
format, it may use the ConfigToBlock helper function (above) to | |
simplify the job. Currently not implemented. | |
@param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL. | |
@param[in] Configuration A null-terminated Unicode string in | |
<ConfigString> format. | |
@param[out] Progress A pointer to a string filled in with the | |
offset of the most recent '&' before the | |
first failing name / value pair (or the | |
beginn ing of the string if the failure | |
is in the first name / value pair) or | |
the terminating NULL if all was | |
successful. | |
@retval EFI_SUCCESS The results have been distributed or are | |
awaiting distribution. | |
@retval EFI_OUT_OF_RESOURCES Not enough memory to store the | |
parts of the results that must be | |
stored awaiting possible future | |
protocols. | |
@retval EFI_INVALID_PARAMETERS Passing in a NULL for the | |
Results parameter would result | |
in this type of error. | |
@retval EFI_NOT_FOUND Target for the specified routing data | |
was not found. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
IScsiFormRouteConfig ( | |
IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This, | |
IN CONST EFI_STRING Configuration, | |
OUT EFI_STRING *Progress | |
) | |
{ | |
if (Configuration == NULL || Progress == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Check routing data in <ConfigHdr>. | |
// Note: if only one Storage is used, then this checking could be skipped. | |
// | |
if (!HiiIsConfigHdrMatch (Configuration, &mVendorGuid, mVendorStorageName)) { | |
*Progress = Configuration; | |
return EFI_NOT_FOUND; | |
} | |
*Progress = Configuration + StrLen (Configuration); | |
return EFI_SUCCESS; | |
} | |
/** | |
This function is called to provide results data to the driver. | |
This data consists of a unique key that is used to identify | |
which data is either being passed back or being asked for. | |
@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. The format of the data tends to | |
vary based on the opcode that enerated the callback. | |
@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.Currently not implemented. | |
@retval EFI_INVALID_PARAMETERS Passing in wrong parameter. | |
@retval Others Other errors as indicated. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
IScsiFormCallback ( | |
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 | |
) | |
{ | |
ISCSI_FORM_CALLBACK_INFO *Private; | |
UINTN BufferSize; | |
CHAR8 IScsiName[ISCSI_NAME_IFR_MAX_SIZE]; | |
CHAR16 PortString[128]; | |
CHAR8 Ip4String[IP4_STR_MAX_SIZE]; | |
CHAR8 LunString[ISCSI_LUN_STR_MAX_LEN]; | |
UINT64 Lun; | |
EFI_STRING_ID DeviceFormTitleToken; | |
ISCSI_CONFIG_IFR_NVDATA *IfrNvData; | |
ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry; | |
EFI_IP_ADDRESS HostIp; | |
EFI_IP_ADDRESS SubnetMask; | |
EFI_IP_ADDRESS Gateway; | |
EFI_STATUS Status; | |
EFI_INPUT_KEY Key; | |
if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)) { | |
// | |
// Do nothing for UEFI OPEN/CLOSE Action | |
// | |
return EFI_SUCCESS; | |
} | |
Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This); | |
// | |
// Retrive uncommitted data from Browser | |
// | |
IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA)); | |
ASSERT (IfrNvData != NULL); | |
if (!HiiGetBrowserData (&mVendorGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData)) { | |
FreePool (IfrNvData); | |
return EFI_NOT_FOUND; | |
} | |
Status = EFI_SUCCESS; | |
switch (QuestionId) { | |
case KEY_INITIATOR_NAME: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->InitiatorName, IScsiName); | |
BufferSize = AsciiStrLen (IScsiName) + 1; | |
Status = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName); | |
if (EFI_ERROR (Status)) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL); | |
} | |
break; | |
case KEY_LOCAL_IP: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->LocalIp, Ip4String); | |
Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4); | |
if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
} else { | |
CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4)); | |
} | |
break; | |
case KEY_SUBNET_MASK: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->SubnetMask, Ip4String); | |
Status = IScsiAsciiStrToIp (Ip4String, &SubnetMask.v4); | |
if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
} else { | |
CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4)); | |
} | |
break; | |
case KEY_GATE_WAY: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->Gateway, Ip4String); | |
Status = IScsiAsciiStrToIp (Ip4String, &Gateway.v4); | |
if (EFI_ERROR (Status) || ((Gateway.Addr[0] != 0) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 0))) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
} else { | |
CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4)); | |
} | |
break; | |
case KEY_TARGET_IP: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->TargetIp, Ip4String); | |
Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4); | |
if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
} else { | |
CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp.v4, sizeof (HostIp.v4)); | |
} | |
break; | |
case KEY_TARGET_NAME: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->TargetName, IScsiName); | |
Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName)); | |
if (EFI_ERROR (Status)) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL); | |
} else { | |
AsciiStrCpy (Private->Current->SessionConfigData.TargetName, IScsiName); | |
} | |
break; | |
case KEY_DHCP_ENABLE: | |
if (IfrNvData->InitiatorInfoFromDhcp == 0) { | |
IfrNvData->TargetInfoFromDhcp = 0; | |
} | |
break; | |
case KEY_BOOT_LUN: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->BootLun, LunString); | |
Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun); | |
if (EFI_ERROR (Status)) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid LUN string!", NULL); | |
} else { | |
CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun)); | |
} | |
break; | |
case KEY_CHAP_NAME: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPName, Private->Current->AuthConfigData.CHAPName); | |
break; | |
case KEY_CHAP_SECRET: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPSecret, Private->Current->AuthConfigData.CHAPSecret); | |
break; | |
case KEY_REVERSE_CHAP_NAME: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPName, Private->Current->AuthConfigData.ReverseCHAPName); | |
break; | |
case KEY_REVERSE_CHAP_SECRET: | |
IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPSecret, Private->Current->AuthConfigData.ReverseCHAPSecret); | |
break; | |
case KEY_SAVE_CHANGES: | |
// | |
// First, update those fields which don't have INTERACTIVE set. | |
// | |
Private->Current->SessionConfigData.Enabled = IfrNvData->Enabled; | |
Private->Current->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp; | |
Private->Current->SessionConfigData.TargetPort = IfrNvData->TargetPort; | |
if (Private->Current->SessionConfigData.TargetPort == 0) { | |
Private->Current->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT; | |
} | |
Private->Current->SessionConfigData.TargetInfoFromDhcp = IfrNvData->TargetInfoFromDhcp; | |
Private->Current->AuthConfigData.CHAPType = IfrNvData->CHAPType; | |
// | |
// Only do full parameter validation if iSCSI is enabled on this device. | |
// | |
if (Private->Current->SessionConfigData.Enabled) { | |
// | |
// Validate the address configuration of the Initiator if DHCP isn't | |
// deployed. | |
// | |
if (!Private->Current->SessionConfigData.InitiatorInfoFromDhcp) { | |
CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.LocalIp, sizeof (HostIp.v4)); | |
CopyMem (&SubnetMask.v4, &Private->Current->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4)); | |
CopyMem (&Gateway.v4, &Private->Current->SessionConfigData.Gateway, sizeof (Gateway.v4)); | |
if ((Gateway.Addr[0] != 0)) { | |
if (SubnetMask.Addr[0] == 0) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Gateway address is set but subnet mask is zero.", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Local IP and Gateway are not in the same subnet.", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
} | |
} | |
// | |
// Validate target configuration if DHCP isn't deployed. | |
// | |
if (!Private->Current->SessionConfigData.TargetInfoFromDhcp) { | |
CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.TargetIp, sizeof (HostIp.v4)); | |
if (!NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Target IP is invalid!", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
} | |
if (IfrNvData->CHAPType != ISCSI_CHAP_NONE) { | |
if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"CHAP Name or CHAP Secret is invalid!", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) && | |
((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0')) | |
) { | |
CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Reverse CHAP Name or Reverse CHAP Secret is invalid!", NULL); | |
Status = EFI_INVALID_PARAMETER; | |
break; | |
} | |
} | |
} | |
BufferSize = sizeof (Private->Current->SessionConfigData); | |
gRT->SetVariable ( | |
Private->Current->MacString, | |
&gEfiIScsiInitiatorNameProtocolGuid, | |
ISCSI_CONFIG_VAR_ATTR, | |
BufferSize, | |
&Private->Current->SessionConfigData | |
); | |
BufferSize = sizeof (Private->Current->AuthConfigData); | |
gRT->SetVariable ( | |
Private->Current->MacString, | |
&mIScsiCHAPAuthInfoGuid, | |
ISCSI_CONFIG_VAR_ATTR, | |
BufferSize, | |
&Private->Current->AuthConfigData | |
); | |
*ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT; | |
break; | |
default: | |
if ((QuestionId >= KEY_DEVICE_ENTRY_BASE) && (QuestionId < (mNumberOfIScsiDevices + KEY_DEVICE_ENTRY_BASE))) { | |
// | |
// In case goto the device configuration form, update the device form title. | |
// | |
ConfigFormEntry = IScsiGetConfigFormEntryByIndex ((UINT32) (QuestionId - KEY_DEVICE_ENTRY_BASE)); | |
ASSERT (ConfigFormEntry != NULL); | |
UnicodeSPrint (PortString, (UINTN) 128, L"Port %s", ConfigFormEntry->MacString); | |
DeviceFormTitleToken = (EFI_STRING_ID) STR_ISCSI_DEVICE_FORM_TITLE; | |
HiiSetString (Private->RegisteredHandle, DeviceFormTitleToken, PortString, NULL); | |
IScsiConvertDeviceConfigDataToIfrNvData (ConfigFormEntry, IfrNvData); | |
Private->Current = ConfigFormEntry; | |
} | |
break; | |
} | |
if (!EFI_ERROR (Status)) { | |
// | |
// Pass changed uncommitted data back to Form Browser | |
// | |
HiiSetBrowserData (&mVendorGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData, NULL); | |
} | |
FreePool (IfrNvData); | |
return Status; | |
} | |
/** | |
Updates the iSCSI configuration form to add/delete an entry for the iSCSI | |
device specified by the Controller. | |
@param[in] DriverBindingHandle The driverbinding handle. | |
@param[in] Controller The controller handle of the iSCSI device. | |
@param[in] AddForm Whether to add or delete a form entry. | |
@retval EFI_SUCCESS The iSCSI configuration form is updated. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory. | |
@retval Others Other errors as indicated. | |
**/ | |
EFI_STATUS | |
IScsiConfigUpdateForm ( | |
IN EFI_HANDLE DriverBindingHandle, | |
IN EFI_HANDLE Controller, | |
IN BOOLEAN AddForm | |
) | |
{ | |
LIST_ENTRY *Entry; | |
ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry; | |
BOOLEAN EntryExisted; | |
EFI_STATUS Status; | |
EFI_MAC_ADDRESS MacAddress; | |
UINTN HwAddressSize; | |
UINT16 VlanId; | |
CHAR16 PortString[128]; | |
UINT16 FormIndex; | |
UINTN BufferSize; | |
VOID *StartOpCodeHandle; | |
VOID *EndOpCodeHandle; | |
EFI_IFR_GUID_LABEL *StartLabel; | |
EFI_IFR_GUID_LABEL *EndLabel; | |
ConfigFormEntry = NULL; | |
EntryExisted = FALSE; | |
NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) { | |
ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link); | |
if (ConfigFormEntry->Controller == Controller) { | |
EntryExisted = TRUE; | |
break; | |
} | |
} | |
if (AddForm) { | |
if (EntryExisted) { | |
return EFI_SUCCESS; | |
} else { | |
// | |
// Add a new form. | |
// | |
ConfigFormEntry = (ISCSI_CONFIG_FORM_ENTRY *) AllocateZeroPool (sizeof (ISCSI_CONFIG_FORM_ENTRY)); | |
if (ConfigFormEntry == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
InitializeListHead (&ConfigFormEntry->Link); | |
ConfigFormEntry->Controller = Controller; | |
// | |
// Get the MAC address and convert it into the formatted string. | |
// | |
Status = NetLibGetMacAddress (Controller, &MacAddress, &HwAddressSize); | |
ASSERT (Status == EFI_SUCCESS); | |
VlanId = NetLibGetVlanId (Controller); | |
IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, ConfigFormEntry->MacString); | |
// | |
// Get the normal session configuration data. | |
// | |
BufferSize = sizeof (ConfigFormEntry->SessionConfigData); | |
Status = gRT->GetVariable ( | |
ConfigFormEntry->MacString, | |
&gEfiIScsiInitiatorNameProtocolGuid, | |
NULL, | |
&BufferSize, | |
&ConfigFormEntry->SessionConfigData | |
); | |
if (EFI_ERROR (Status)) { | |
ZeroMem (&ConfigFormEntry->SessionConfigData, sizeof (ConfigFormEntry->SessionConfigData)); | |
} | |
// | |
// Get the CHAP authentication configuration data. | |
// | |
BufferSize = sizeof (ConfigFormEntry->AuthConfigData); | |
Status = gRT->GetVariable ( | |
ConfigFormEntry->MacString, | |
&mIScsiCHAPAuthInfoGuid, | |
NULL, | |
&BufferSize, | |
&ConfigFormEntry->AuthConfigData | |
); | |
if (EFI_ERROR (Status)) { | |
ZeroMem (&ConfigFormEntry->AuthConfigData, sizeof (ConfigFormEntry->AuthConfigData)); | |
} | |
// | |
// Compose the Port string and create a new EFI_STRING_ID. | |
// | |
UnicodeSPrint (PortString, 128, L"Port %s", ConfigFormEntry->MacString); | |
ConfigFormEntry->PortTitleToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL); | |
// | |
// Compose the help string of this port and create a new EFI_STRING_ID. | |
// | |
UnicodeSPrint (PortString, 128, L"Set the iSCSI parameters on port %s", ConfigFormEntry->MacString); | |
ConfigFormEntry->PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL); | |
InsertTailList (&mIScsiConfigFormList, &ConfigFormEntry->Link); | |
mNumberOfIScsiDevices++; | |
} | |
} else { | |
ASSERT (EntryExisted); | |
mNumberOfIScsiDevices--; | |
RemoveEntryList (&ConfigFormEntry->Link); | |
FreePool (ConfigFormEntry); | |
} | |
// | |
// Allocate space for creation of Buffer | |
// | |
// | |
// Init OpCode Handle | |
// | |
StartOpCodeHandle = HiiAllocateOpCodeHandle (); | |
ASSERT (StartOpCodeHandle != NULL); | |
EndOpCodeHandle = HiiAllocateOpCodeHandle (); | |
ASSERT (EndOpCodeHandle != NULL); | |
// | |
// Create Hii Extend Label OpCode as the start opcode | |
// | |
StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); | |
StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; | |
StartLabel->Number = DEVICE_ENTRY_LABEL; | |
// | |
// Create Hii Extend Label OpCode as the end opcode | |
// | |
EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL)); | |
EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL; | |
EndLabel->Number = LABEL_END; | |
FormIndex = 0; | |
NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) { | |
ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link); | |
HiiCreateGotoOpCode ( | |
StartOpCodeHandle, // Container for dynamic created opcodes | |
FORMID_DEVICE_FORM, // Target Form ID | |
ConfigFormEntry->PortTitleToken, // Prompt text | |
ConfigFormEntry->PortTitleHelpToken, // Help text | |
EFI_IFR_FLAG_CALLBACK, // Question flag | |
(UINT16)(KEY_DEVICE_ENTRY_BASE + FormIndex) // Question ID | |
); | |
FormIndex++; | |
} | |
HiiUpdateForm ( | |
mCallbackInfo->RegisteredHandle, | |
&mVendorGuid, | |
FORMID_MAIN_FORM, | |
StartOpCodeHandle, // Label DEVICE_ENTRY_LABEL | |
EndOpCodeHandle // LABEL_END | |
); | |
HiiFreeOpCodeHandle (StartOpCodeHandle); | |
HiiFreeOpCodeHandle (EndOpCodeHandle); | |
return EFI_SUCCESS; | |
} | |
/** | |
Initialize the iSCSI configuration form. | |
@param[in] DriverBindingHandle The iSCSI driverbinding handle. | |
@retval EFI_SUCCESS The iSCSI configuration form is initialized. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory. | |
@retval Others Other errors as indicated. | |
**/ | |
EFI_STATUS | |
IScsiConfigFormInit ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_HII_DATABASE_PROTOCOL *HiiDatabase; | |
ISCSI_FORM_CALLBACK_INFO *CallbackInfo; | |
Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **)&HiiDatabase); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO)); | |
if (CallbackInfo == NULL) { | |
return EFI_OUT_OF_RESOURCES; | |
} | |
CallbackInfo->Signature = ISCSI_FORM_CALLBACK_INFO_SIGNATURE; | |
CallbackInfo->HiiDatabase = HiiDatabase; | |
CallbackInfo->Current = NULL; | |
CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig; | |
CallbackInfo->ConfigAccess.RouteConfig = IScsiFormRouteConfig; | |
CallbackInfo->ConfigAccess.Callback = IScsiFormCallback; | |
Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **)&CallbackInfo->ConfigRouting); | |
if (EFI_ERROR (Status)) { | |
FreePool(CallbackInfo); | |
return Status; | |
} | |
// | |
// Install Device Path Protocol and Config Access protocol to driver handle | |
// | |
Status = gBS->InstallMultipleProtocolInterfaces ( | |
&CallbackInfo->DriverHandle, | |
&gEfiDevicePathProtocolGuid, | |
&mIScsiHiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&CallbackInfo->ConfigAccess, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Publish our HII data | |
// | |
CallbackInfo->RegisteredHandle = HiiAddPackages ( | |
&mVendorGuid, | |
CallbackInfo->DriverHandle, | |
IScsiDxeStrings, | |
IScsiConfigDxeBin, | |
NULL | |
); | |
if (CallbackInfo->RegisteredHandle == NULL) { | |
FreePool(CallbackInfo); | |
return EFI_OUT_OF_RESOURCES; | |
} | |
mCallbackInfo = CallbackInfo; | |
return Status; | |
} | |
/** | |
Unload the iSCSI configuration form, this includes: delete all the iSCSI | |
device configuration entries, uninstall the form callback protocol and | |
free the resources used. | |
@param[in] DriverBindingHandle The iSCSI driverbinding handle. | |
@retval EFI_SUCCESS The iSCSI configuration form is unloaded. | |
@retval EFI_OUT_OF_RESOURCES Failed to allocate memory. | |
**/ | |
EFI_STATUS | |
IScsiConfigFormUnload ( | |
IN EFI_HANDLE DriverBindingHandle | |
) | |
{ | |
ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry; | |
while (!IsListEmpty (&mIScsiConfigFormList)) { | |
// | |
// Uninstall the device forms as the iSCSI driver instance may fail to | |
// control the controller but still install the device configuration form. | |
// In such case, upon driver unloading, the driver instance's driverbinding. | |
// stop () won't be called, so we have to take this chance here to uninstall | |
// the device form. | |
// | |
ConfigFormEntry = NET_LIST_USER_STRUCT (mIScsiConfigFormList.ForwardLink, ISCSI_CONFIG_FORM_ENTRY, Link); | |
IScsiConfigUpdateForm (DriverBindingHandle, ConfigFormEntry->Controller, FALSE); | |
} | |
// | |
// Remove HII package list | |
// | |
mCallbackInfo->HiiDatabase->RemovePackageList ( | |
mCallbackInfo->HiiDatabase, | |
mCallbackInfo->RegisteredHandle | |
); | |
// | |
// Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL | |
// | |
gBS->UninstallMultipleProtocolInterfaces ( | |
mCallbackInfo->DriverHandle, | |
&gEfiDevicePathProtocolGuid, | |
&mIScsiHiiVendorDevicePath, | |
&gEfiHiiConfigAccessProtocolGuid, | |
&mCallbackInfo->ConfigAccess, | |
NULL | |
); | |
FreePool (mCallbackInfo); | |
return EFI_SUCCESS; | |
} |