summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/IScsiDxe
diff options
context:
space:
mode:
authortye1 <tye1@6f19259b-4bc3-4df7-8a09-765794883524>2011-08-17 02:38:08 +0000
committertye1 <tye1@6f19259b-4bc3-4df7-8a09-765794883524>2011-08-17 02:38:08 +0000
commit4c5a5e0cfecf23526493bf9a05c0530805bf10ec (patch)
tree832ac48ee2b892817a1414fca804954ae6502e1a /NetworkPkg/IScsiDxe
parentab0eecec6e1233b7ca179d8a8f2b2e44e9546b7f (diff)
downloadedk2-4c5a5e0cfecf23526493bf9a05c0530805bf10ec.tar.gz
edk2-4c5a5e0cfecf23526493bf9a05c0530805bf10ec.tar.bz2
edk2-4c5a5e0cfecf23526493bf9a05c0530805bf10ec.zip
Add IScsiDxe driver to NetworkPkg in order to support iSCSI over IPv6 stack and iSCSI MPIO.
Signed-off-by: tye1 Reviewed-by: hhuan13 Reviewed-by: eric_tian git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@12149 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'NetworkPkg/IScsiDxe')
-rw-r--r--NetworkPkg/IScsiDxe/ComponentName.c181
-rw-r--r--NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c67
-rw-r--r--NetworkPkg/IScsiDxe/IScsiCHAP.c474
-rw-r--r--NetworkPkg/IScsiDxe/IScsiCHAP.h113
-rw-r--r--NetworkPkg/IScsiDxe/IScsiConfig.c2522
-rw-r--r--NetworkPkg/IScsiDxe/IScsiConfig.h158
-rw-r--r--NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h194
-rw-r--r--NetworkPkg/IScsiDxe/IScsiConfigStrings.unibin0 -> 13314 bytes
-rw-r--r--NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr429
-rw-r--r--NetworkPkg/IScsiDxe/IScsiDhcp.c498
-rw-r--r--NetworkPkg/IScsiDxe/IScsiDhcp.h62
-rw-r--r--NetworkPkg/IScsiDxe/IScsiDhcp6.c505
-rw-r--r--NetworkPkg/IScsiDxe/IScsiDhcp6.h79
-rw-r--r--NetworkPkg/IScsiDxe/IScsiDriver.c1216
-rw-r--r--NetworkPkg/IScsiDxe/IScsiDriver.h678
-rw-r--r--NetworkPkg/IScsiDxe/IScsiDxe.inf106
-rw-r--r--NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c409
-rw-r--r--NetworkPkg/IScsiDxe/IScsiIbft.c546
-rw-r--r--NetworkPkg/IScsiDxe/IScsiIbft.h39
-rw-r--r--NetworkPkg/IScsiDxe/IScsiImpl.h197
-rw-r--r--NetworkPkg/IScsiDxe/IScsiInitiatorName.c136
-rw-r--r--NetworkPkg/IScsiDxe/IScsiMisc.c1320
-rw-r--r--NetworkPkg/IScsiDxe/IScsiMisc.h343
-rw-r--r--NetworkPkg/IScsiDxe/IScsiProto.c2991
-rw-r--r--NetworkPkg/IScsiDxe/IScsiProto.h1035
25 files changed, 14298 insertions, 0 deletions
diff --git a/NetworkPkg/IScsiDxe/ComponentName.c b/NetworkPkg/IScsiDxe/ComponentName.c
new file mode 100644
index 0000000000..e4e28e8961
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/ComponentName.c
@@ -0,0 +1,181 @@
+/** @file
+ UEFI Component Name(2) protocol implementation for iSCSI.
+
+Copyright (c) 2004 - 2011, 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 Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIScsiComponentName = {
+ IScsiComponentNameGetDriverName,
+ IScsiComponentNameGetControllerName,
+ "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIScsiComponentName2 = {
+ (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IScsiComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IScsiComponentNameGetControllerName,
+ "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIScsiDriverNameTable[] = {
+ {
+ "eng;en",
+ L"iSCSI Driver"
+ },
+ {
+ NULL,
+ NULL
+ }
+};
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mIScsiDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This == &gIScsiComponentName)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified in
+ RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language, from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c b/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c
new file mode 100644
index 0000000000..f37b403ae5
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c
@@ -0,0 +1,67 @@
+/** @file
+ Implementation for EFI_AUTHENTICATION_INFO_PROTOCOL. Currently it is a
+ dummy support.
+
+Copyright (c) 2009 - 2011, 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_AUTHENTICATION_INFO_PROTOCOL gIScsiAuthenticationInfo = {
+ IScsiGetAuthenticationInfo,
+ IScsiSetAuthenticationInfo
+};
+
+/**
+ Retrieves the authentication information associated with a particular controller handle.
+
+ @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.
+ @param[in] ControllerHandle Handle to the Controller.
+ @param[out] Buffer Pointer to the authentication information. This function is
+ responsible for allocating the buffer and it is the caller's
+ responsibility to free buffer when the caller is finished with buffer.
+
+ @retval EFI_DEVICE_ERROR The authentication information could not be
+ retrieved due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiGetAuthenticationInfo (
+ IN EFI_AUTHENTICATION_INFO_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ OUT VOID **Buffer
+ )
+{
+ return EFI_DEVICE_ERROR;
+}
+
+/**
+ Set the authentication information for a given controller handle.
+
+ @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.
+ @param[in] ControllerHandle Handle to the Controller.
+ @param[in] Buffer Pointer to the authentication information.
+
+ @retval EFI_UNSUPPORTED If the platform policies do not allow setting of
+ the authentication information.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiSetAuthenticationInfo (
+ IN EFI_AUTHENTICATION_INFO_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN VOID *Buffer
+ )
+{
+ return EFI_UNSUPPORTED;
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.c b/NetworkPkg/IScsiDxe/IScsiCHAP.c
new file mode 100644
index 0000000000..4cfbba7b5c
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiCHAP.c
@@ -0,0 +1,474 @@
+/** @file
+ This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration.
+
+Copyright (c) 2004 - 2011, 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"
+
+/**
+ Initator caculates its own expected hash value.
+
+ @param[in] ChapIdentifier iSCSI CHAP identifier sent by authenticator.
+ @param[in] ChapSecret iSCSI CHAP secret of the authenticator.
+ @param[in] SecretLength The length of iSCSI CHAP secret.
+ @param[in] ChapChallenge The challenge message sent by authenticator.
+ @param[in] ChallengeLength The length of iSCSI CHAP challenge message.
+ @param[out] ChapResponse The calculation of the expected hash value.
+
+ @retval EFI_SUCCESS The expected hash value was caculatedly successfully.
+ @retval EFI_PROTOCOL_ERROR The length of the secret should be at least the
+ length of the hash value for the hashing algorithm chosen.
+ @retval EFI_PROTOCOL_ERROR MD5 hash operation fail.
+ @retval EFI_OUT_OF_RESOURCES Fail to allocate resource to complete MD5.
+
+**/
+EFI_STATUS
+IScsiCHAPCalculateResponse (
+ IN UINT32 ChapIdentifier,
+ IN CHAR8 *ChapSecret,
+ IN UINT32 SecretLength,
+ IN UINT8 *ChapChallenge,
+ IN UINT32 ChallengeLength,
+ OUT UINT8 *ChapResponse
+ )
+{
+ UINTN Md5ContextSize;
+ VOID *Md5Ctx;
+ CHAR8 IdByte[1];
+ EFI_STATUS Status;
+
+ if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Md5ContextSize = Md5GetContextSize ();
+ Md5Ctx = AllocatePool (Md5ContextSize);
+ if (Md5Ctx == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_PROTOCOL_ERROR;
+
+ if (!Md5Init (Md5Ctx)) {
+ goto Exit;
+ }
+
+ //
+ // Hash Identifier - Only calculate 1 byte data (RFC1994)
+ //
+ IdByte[0] = (CHAR8) ChapIdentifier;
+ if (!Md5Update (Md5Ctx, IdByte, 1)) {
+ goto Exit;
+ }
+
+ //
+ // Hash Secret
+ //
+ if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) {
+ goto Exit;
+ }
+
+ //
+ // Hash Challenge received from Target
+ //
+ if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) {
+ goto Exit;
+ }
+
+ if (Md5Final (Md5Ctx, ChapResponse)) {
+ Status = EFI_SUCCESS;
+ }
+
+Exit:
+ FreePool (Md5Ctx);
+ return Status;
+}
+
+/**
+ The initator checks the CHAP response replied by target against its own
+ calculation of the expected hash value.
+
+ @param[in] AuthData iSCSI CHAP authentication data.
+ @param[in] TargetResponse The response from target.
+
+ @retval EFI_SUCCESS The response from target passed authentication.
+ @retval EFI_SECURITY_VIOLATION The response from target was not expected value.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiCHAPAuthTarget (
+ IN ISCSI_CHAP_AUTH_DATA *AuthData,
+ IN UINT8 *TargetResponse
+ )
+{
+ EFI_STATUS Status;
+ UINT32 SecretSize;
+ UINT8 VerifyRsp[ISCSI_CHAP_RSP_LEN];
+
+ Status = EFI_SUCCESS;
+
+ SecretSize = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);
+ Status = IScsiCHAPCalculateResponse (
+ AuthData->OutIdentifier,
+ AuthData->AuthConfig->ReverseCHAPSecret,
+ SecretSize,
+ AuthData->OutChallenge,
+ AuthData->OutChallengeLength,
+ VerifyRsp
+ );
+
+ if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) {
+ Status = EFI_SECURITY_VIOLATION;
+ }
+
+ return Status;
+}
+
+
+/**
+ This function checks the received iSCSI Login Response during the security
+ negotiation stage.
+
+ @param[in] Conn The iSCSI connection.
+
+ @retval EFI_SUCCESS The Login Response passed the CHAP validation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiCHAPOnRspReceived (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ ISCSI_CHAP_AUTH_DATA *AuthData;
+ CHAR8 *Value;
+ UINT8 *Data;
+ UINT32 Len;
+ LIST_ENTRY *KeyValueList;
+ UINTN Algorithm;
+ CHAR8 *Identifier;
+ CHAR8 *Challenge;
+ CHAR8 *Name;
+ CHAR8 *Response;
+ UINT8 TargetRsp[ISCSI_CHAP_RSP_LEN];
+ UINT32 RspLen;
+ UINTN Result;
+
+ ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
+ ASSERT (Conn->RspQue.BufNum != 0);
+
+ Session = Conn->Session;
+ AuthData = &Session->AuthData.CHAP;
+ Len = Conn->RspQue.BufSize;
+ Data = AllocateZeroPool (Len);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Copy the data in case the data spans over multiple PDUs.
+ //
+ NetbufQueCopy (&Conn->RspQue, 0, Len, Data);
+
+ //
+ // Build the key-value list from the data segment of the Login Response.
+ //
+ KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len);
+ if (KeyValueList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Status = EFI_PROTOCOL_ERROR;
+
+ switch (Conn->AuthStep) {
+ case ISCSI_AUTH_INITIAL:
+ //
+ // The first Login Response.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
+ if (Value == NULL) {
+ goto ON_EXIT;
+ }
+
+ Result = IScsiNetNtoi (Value);
+ if (Result > 0xFFFF) {
+ goto ON_EXIT;
+ }
+
+ Session->TargetPortalGroupTag = (UINT16) Result;
+
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);
+ if (Value == NULL) {
+ goto ON_EXIT;
+ }
+ //
+ // Initiator mandates CHAP authentication but target replies without "CHAP", or
+ // initiator suggets "None" but target replies with some kind of auth method.
+ //
+ if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
+ if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {
+ goto ON_EXIT;
+ }
+ } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
+ if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {
+ goto ON_EXIT;
+ }
+ } else {
+ goto ON_EXIT;
+ }
+
+ //
+ // Transit to CHAP step one.
+ //
+ Conn->AuthStep = ISCSI_CHAP_STEP_ONE;
+ Status = EFI_SUCCESS;
+ break;
+
+ case ISCSI_CHAP_STEP_TWO:
+ //
+ // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM);
+ if (Value == NULL) {
+ goto ON_EXIT;
+ }
+
+ Algorithm = IScsiNetNtoi (Value);
+ if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {
+ //
+ // Unsupported algorithm is chosen by target.
+ //
+ goto ON_EXIT;
+ }
+
+ Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);
+ if (Identifier == NULL) {
+ goto ON_EXIT;
+ }
+
+ Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);
+ if (Challenge == NULL) {
+ goto ON_EXIT;
+ }
+ //
+ // Process the CHAP identifier and CHAP Challenge from Target.
+ // Calculate Response value.
+ //
+ Result = IScsiNetNtoi (Identifier);
+ if (Result > 0xFF) {
+ goto ON_EXIT;
+ }
+
+ AuthData->InIdentifier = (UINT32) Result;
+ AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;
+ IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);
+ Status = IScsiCHAPCalculateResponse (
+ AuthData->InIdentifier,
+ AuthData->AuthConfig->CHAPSecret,
+ (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),
+ AuthData->InChallenge,
+ AuthData->InChallengeLength,
+ AuthData->CHAPResponse
+ );
+
+ //
+ // Transit to next step.
+ //
+ Conn->AuthStep = ISCSI_CHAP_STEP_THREE;
+ break;
+
+ case ISCSI_CHAP_STEP_THREE:
+ //
+ // One way CHAP authentication and the target would like to
+ // authenticate us.
+ //
+ Status = EFI_SUCCESS;
+ break;
+
+ case ISCSI_CHAP_STEP_FOUR:
+ ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);
+ //
+ // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.
+ //
+ Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);
+ if (Name == NULL) {
+ goto ON_EXIT;
+ }
+
+ Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);
+ if (Response == NULL) {
+ goto ON_EXIT;
+ }
+
+ RspLen = ISCSI_CHAP_RSP_LEN;
+ IScsiHexToBin (TargetRsp, &RspLen, Response);
+
+ //
+ // Check the CHAP Name and Response replied by Target.
+ //
+ Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);
+ break;
+
+ default:
+ break;
+ }
+
+ON_EXIT:
+
+ if (KeyValueList != NULL) {
+ IScsiFreeKeyValueList (KeyValueList);
+ }
+
+ FreePool (Data);
+
+ return Status;
+}
+
+
+/**
+ This function fills the CHAP authentication information into the login PDU
+ during the security negotiation stage in the iSCSI connection login.
+
+ @param[in] Conn The iSCSI connection.
+ @param[in, out] Pdu The PDU to send out.
+
+ @retval EFI_SUCCESS All check passed and the phase-related CHAP
+ authentication info is filled into the iSCSI PDU.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
+
+**/
+EFI_STATUS
+IScsiCHAPToSendReq (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ ISCSI_LOGIN_REQUEST *LoginReq;
+ ISCSI_CHAP_AUTH_DATA *AuthData;
+ CHAR8 *Value;
+ CHAR8 ValueStr[256];
+ CHAR8 *Response;
+ UINT32 RspLen;
+ CHAR8 *Challenge;
+ UINT32 ChallengeLen;
+
+ ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);
+
+ Session = Conn->Session;
+ AuthData = &Session->AuthData.CHAP;
+ LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);
+ Status = EFI_SUCCESS;
+
+ RspLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
+ Response = AllocateZeroPool (RspLen);
+ if (Response == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ChallengeLen = 2 * ISCSI_CHAP_RSP_LEN + 3;
+ Challenge = AllocateZeroPool (ChallengeLen);
+ if (Challenge == NULL) {
+ FreePool (Response);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ switch (Conn->AuthStep) {
+ case ISCSI_AUTH_INITIAL:
+ //
+ // It's the initial Login Request. Fill in the key=value pairs mandatory
+ // for the initial Login Request.
+ //
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, mPrivate->InitiatorName);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");
+ IScsiAddKeyValuePair (
+ Pdu,
+ ISCSI_KEY_TARGET_NAME,
+ Session->ConfigData->SessionConfigData.TargetName
+ );
+
+ if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {
+ Value = ISCSI_KEY_VALUE_NONE;
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ } else {
+ Value = ISCSI_AUTH_METHOD_CHAP;
+ }
+
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);
+
+ break;
+
+ case ISCSI_CHAP_STEP_ONE:
+ //
+ // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.
+ //
+ AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);
+
+ Conn->AuthStep = ISCSI_CHAP_STEP_TWO;
+ break;
+
+ case ISCSI_CHAP_STEP_THREE:
+ //
+ // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or
+ // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is
+ // required too.
+ //
+ // CHAP_N=<N>
+ //
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);
+ //
+ // CHAP_R=<R>
+ //
+ IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);
+
+ if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {
+ //
+ // CHAP_I=<I>
+ //
+ IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);
+ AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);
+ //
+ // CHAP_C=<C>
+ //
+ IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);
+ AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;
+ IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);
+
+ Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;
+ }
+ //
+ // Set the stage transition flag.
+ //
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ break;
+
+ default:
+ Status = EFI_PROTOCOL_ERROR;
+ break;
+ }
+
+ FreePool (Response);
+ FreePool (Challenge);
+
+ return Status;
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.h b/NetworkPkg/IScsiDxe/IScsiCHAP.h
new file mode 100644
index 0000000000..d71db2c655
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiCHAP.h
@@ -0,0 +1,113 @@
+/** @file
+ The header file of CHAP configuration.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_CHAP_H_
+#define _ISCSI_CHAP_H_
+
+#define ISCSI_CHAP_AUTH_INFO_GUID \
+ { \
+ 0x786ec0ac, 0x65ae, 0x4d1b, { 0xb1, 0x37, 0xd, 0x11, 0xa, 0x48, 0x37, 0x97 }\
+ }
+
+#define ISCSI_AUTH_METHOD_CHAP "CHAP"
+
+#define ISCSI_KEY_CHAP_ALGORITHM "CHAP_A"
+#define ISCSI_KEY_CHAP_IDENTIFIER "CHAP_I"
+#define ISCSI_KEY_CHAP_CHALLENGE "CHAP_C"
+#define ISCSI_KEY_CHAP_NAME "CHAP_N"
+#define ISCSI_KEY_CHAP_RESPONSE "CHAP_R"
+
+#define ISCSI_CHAP_ALGORITHM_MD5 5
+
+#define ISCSI_CHAP_AUTH_MAX_LEN 1024
+///
+/// MD5_HASHSIZE
+///
+#define ISCSI_CHAP_RSP_LEN 16
+
+#define ISCSI_CHAP_STEP_ONE 1
+#define ISCSI_CHAP_STEP_TWO 2
+#define ISCSI_CHAP_STEP_THREE 3
+#define ISCSI_CHAP_STEP_FOUR 4
+
+
+#pragma pack(1)
+
+typedef struct _ISCSI_CHAP_AUTH_CONFIG_NVDATA {
+ UINT8 CHAPType;
+ CHAR8 CHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR8 CHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+ CHAR8 ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR8 ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+} ISCSI_CHAP_AUTH_CONFIG_NVDATA;
+
+#pragma pack()
+
+///
+/// ISCSI CHAP Authentication Data
+///
+typedef struct _ISCSI_CHAP_AUTH_DATA {
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;
+ UINT32 InIdentifier;
+ UINT8 InChallenge[ISCSI_CHAP_AUTH_MAX_LEN];
+ UINT32 InChallengeLength;
+ //
+ // Calculated CHAP Response (CHAP_R) value.
+ //
+ UINT8 CHAPResponse[ISCSI_CHAP_RSP_LEN];
+
+ //
+ // Auth-data to be sent out for mutual authentication.
+ //
+ UINT32 OutIdentifier;
+ UINT8 OutChallenge[ISCSI_CHAP_AUTH_MAX_LEN];
+ UINT32 OutChallengeLength;
+} ISCSI_CHAP_AUTH_DATA;
+
+/**
+ This function checks the received iSCSI Login Response during the security
+ negotiation stage.
+
+ @param[in] Conn The iSCSI connection.
+
+ @retval EFI_SUCCESS The Login Response passed the CHAP validation.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiCHAPOnRspReceived (
+ IN ISCSI_CONNECTION *Conn
+ );
+/**
+ This function fills the CHAP authentication information into the login PDU
+ during the security negotiation stage in the iSCSI connection login.
+
+ @param[in] Conn The iSCSI connection.
+ @param[in, out] Pdu The PDU to send out.
+
+ @retval EFI_SUCCESS All check passed and the phase-related CHAP
+ authentication info is filled into the iSCSI PDU.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of protocol error occurred.
+
+**/
+EFI_STATUS
+IScsiCHAPToSendReq (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ );
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiConfig.c b/NetworkPkg/IScsiDxe/IScsiConfig.c
new file mode 100644
index 0000000000..3c299be949
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiConfig.c
@@ -0,0 +1,2522 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to iSCSI.
+
+Copyright (c) 2004 - 2011, 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;
+
+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 IP address into a dotted string.
+
+ @param[in] Ip The IP address.
+ @param[in] Ipv6Flag Indicates whether the IP address is version 4 or version 6.
+ @param[out] Str The formatted IP string.
+
+**/
+VOID
+IScsiIpToStr (
+ IN EFI_IP_ADDRESS *Ip,
+ IN BOOLEAN Ipv6Flag,
+ OUT CHAR16 *Str
+ )
+{
+ EFI_IPv4_ADDRESS *Ip4;
+ EFI_IPv6_ADDRESS *Ip6;
+ UINTN Index;
+ BOOLEAN Short;
+ UINTN Number;
+ CHAR16 FormatString[8];
+
+ if (!Ipv6Flag) {
+ Ip4 = &Ip->v4;
+
+ UnicodeSPrint (
+ Str,
+ (UINTN) 2 * IP4_STR_MAX_SIZE,
+ L"%d.%d.%d.%d",
+ (UINTN) Ip4->Addr[0],
+ (UINTN) Ip4->Addr[1],
+ (UINTN) Ip4->Addr[2],
+ (UINTN) Ip4->Addr[3]
+ );
+
+ return ;
+ }
+
+ Ip6 = &Ip->v6;
+ Short = FALSE;
+
+ for (Index = 0; Index < 15; Index = Index + 2) {
+ if (!Short &&
+ Index % 2 == 0 &&
+ Ip6->Addr[Index] == 0 &&
+ Ip6->Addr[Index + 1] == 0
+ ) {
+ //
+ // Deal with the case of ::.
+ //
+ if (Index == 0) {
+ *Str = L':';
+ *(Str + 1) = L':';
+ Str = Str + 2;
+ } else {
+ *Str = L':';
+ Str = Str + 1;
+ }
+
+ while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) {
+ Index = Index + 2;
+ }
+
+ Short = TRUE;
+
+ if (Index == 16) {
+ //
+ // :: is at the end of the address.
+ //
+ *Str = L'\0';
+ break;
+ }
+ }
+
+ ASSERT (Index < 15);
+
+ if (Ip6->Addr[Index] == 0) {
+ Number = UnicodeSPrint (Str, 2 * IP_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]);
+ } else {
+ if (Ip6->Addr[Index + 1] < 0x10) {
+ CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:"));
+ } else {
+ CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:"));
+ }
+
+ Number = UnicodeSPrint (
+ Str,
+ 2 * IP_STR_MAX_SIZE,
+ (CONST CHAR16 *) FormatString,
+ (UINTN) Ip6->Addr[Index],
+ (UINTN) Ip6->Addr[Index + 1]
+ );
+ }
+
+ Str = Str + Number;
+
+ if (Index + 2 == 16) {
+ *Str = L'\0';
+ if (*(Str - 1) == L':') {
+ *(Str - 1) = L'\0';
+ }
+ }
+ }
+}
+
+/**
+ Check whether the input IP address is valid.
+
+ @param[in] Ip The IP address.
+ @param[in] IpMode Indicates iSCSI running on IP4 or IP6 stack.
+
+ @retval TRUE The input IP address is valid.
+ @retval FALSE Otherwise
+
+**/
+BOOLEAN
+IpIsUnicast (
+ IN EFI_IP_ADDRESS *Ip,
+ IN UINT8 IpMode
+ )
+{
+ if (IpMode == IP_MODE_IP4) {
+ return NetIp4IsUnicast (NTOHL (Ip->Addr[0]), 0);
+ } else if (IpMode == IP_MODE_IP6) {
+ return NetIp6IsValidUnicast (&Ip->v6);
+ } else {
+ DEBUG ((DEBUG_ERROR, "IpMode %d is invalid when configuring the iSCSI target IP!\n", IpMode));
+ return FALSE;
+ }
+}
+
+/**
+ Parse IsId in string format and convert it to binary.
+
+ @param[in] String The buffer of the string to be parsed.
+ @param[in, out] IsId The buffer to store IsId.
+
+ @retval EFI_SUCCESS The operation finished successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+IScsiParseIsIdFromString (
+ IN CONST CHAR16 *String,
+ IN OUT UINT8 *IsId
+ )
+{
+ UINT8 Index;
+ CHAR16 *IsIdStr;
+ CHAR16 TempStr[3];
+ UINTN NodeVal;
+ CHAR16 PortString[ISCSI_NAME_IFR_MAX_SIZE];
+ EFI_INPUT_KEY Key;
+
+ if ((String == NULL) || (IsId == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ IsIdStr = (CHAR16 *) String;
+
+ if (StrLen (IsIdStr) != 6) {
+ UnicodeSPrint (
+ PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Error! Input is incorrect, please input 6 hex numbers!\n"
+ );
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ PortString,
+ NULL
+ );
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 3; Index < 6; Index++) {
+ CopyMem (TempStr, IsIdStr, sizeof (TempStr));
+ TempStr[2] = L'\0';
+
+ //
+ // Convert the string to IsId. StrHexToUintn stops at the first character
+ // that is not a valid hex character, '\0' here.
+ //
+ NodeVal = StrHexToUintn (TempStr);
+
+ IsId[Index] = (UINT8) NodeVal;
+
+ IsIdStr = IsIdStr + 2;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert IsId from binary to string format.
+
+ @param[out] String The buffer to store the converted string.
+ @param[in] IsId The buffer to store IsId.
+
+ @retval EFI_SUCCESS The string converted successfully.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+
+**/
+EFI_STATUS
+IScsiConvertIsIdToString (
+ OUT CHAR16 *String,
+ IN UINT8 *IsId
+ )
+{
+ UINT8 Index;
+ UINTN Number;
+
+ if ((String == NULL) || (IsId == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < 6; Index++) {
+ if (IsId[Index] <= 0xF) {
+ Number = UnicodeSPrint (
+ String,
+ 2 * ISID_CONFIGURABLE_STORAGE,
+ L"0%X",
+ (UINTN) IsId[Index]
+ );
+ } else {
+ Number = UnicodeSPrint (
+ String,
+ 2 * ISID_CONFIGURABLE_STORAGE,
+ L"%X",
+ (UINTN) IsId[Index]
+ );
+
+ }
+
+ String = String + Number;
+ }
+
+ *String = L'\0';
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get the attempt config data from global structure by the ConfigIndex.
+
+ @param[in] AttemptConfigIndex The unique index indicates the attempt.
+
+ @return Pointer to the attempt config data.
+ @retval NULL The attempt configuration data cannot be found.
+
+**/
+ISCSI_ATTEMPT_CONFIG_NVDATA *
+IScsiConfigGetAttemptByConfigIndex (
+ IN UINT8 AttemptConfigIndex
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt;
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ if (Attempt->AttemptConfigIndex == AttemptConfigIndex) {
+ return Attempt;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Get the existing attempt config data from global structure by the NicIndex.
+
+ @param[in] NewAttempt The created new attempt
+ @param[in] IScsiMode The IScsi Mode of the new attempt, Enabled or
+ Enabled for MPIO.
+
+ @return Pointer to the existing attempt config data which
+ has the same NICIndex as the new created attempt.
+ @retval NULL The attempt with NicIndex does not exist.
+
+**/
+ISCSI_ATTEMPT_CONFIG_NVDATA *
+IScsiConfigGetAttemptByNic (
+ IN ISCSI_ATTEMPT_CONFIG_NVDATA *NewAttempt,
+ IN UINT8 IScsiMode
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt;
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ if (Attempt != NewAttempt && Attempt->NicIndex == NewAttempt->NicIndex &&
+ Attempt->SessionConfigData.Enabled == IScsiMode) {
+ return Attempt;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Convert the iSCSI configuration data into the IFR data.
+
+ @param[in] Attempt The iSCSI attempt config data.
+ @param[in, out] IfrNvData The IFR nv data.
+
+**/
+VOID
+IScsiConvertAttemptConfigDataToIfrNvData (
+ IN ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt,
+ IN OUT ISCSI_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+ ISCSI_SESSION_CONFIG_NVDATA *SessionConfigData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;
+ EFI_IP_ADDRESS Ip;
+
+ //
+ // Normal session configuration parameters.
+ //
+ SessionConfigData = &Attempt->SessionConfigData;
+ IfrNvData->Enabled = SessionConfigData->Enabled;
+ IfrNvData->IpMode = SessionConfigData->IpMode;
+
+ IfrNvData->InitiatorInfoFromDhcp = SessionConfigData->InitiatorInfoFromDhcp;
+ IfrNvData->TargetInfoFromDhcp = SessionConfigData->TargetInfoFromDhcp;
+ IfrNvData->TargetPort = SessionConfigData->TargetPort;
+
+ if (IfrNvData->IpMode == IP_MODE_IP4) {
+ CopyMem (&Ip.v4, &SessionConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->LocalIp);
+ CopyMem (&Ip.v4, &SessionConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->SubnetMask);
+ CopyMem (&Ip.v4, &SessionConfigData->Gateway, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->Gateway);
+ CopyMem (&Ip.v4, &SessionConfigData->TargetIp, sizeof (EFI_IPv4_ADDRESS));
+ IScsiIpToStr (&Ip, FALSE, IfrNvData->TargetIp);
+ } else if (IfrNvData->IpMode == IP_MODE_IP6) {
+ ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));
+ IP6_COPY_ADDRESS (&Ip.v6, &SessionConfigData->TargetIp);
+ IScsiIpToStr (&Ip, TRUE, IfrNvData->TargetIp);
+ }
+
+ AsciiStrToUnicodeStr (SessionConfigData->TargetName, IfrNvData->TargetName);
+ IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun);
+ IScsiConvertIsIdToString (IfrNvData->IsId, SessionConfigData->IsId);
+
+ IfrNvData->ConnectRetryCount = SessionConfigData->ConnectRetryCount;
+ IfrNvData->ConnectTimeout = SessionConfigData->ConnectTimeout;
+
+ //
+ // Authentication parameters.
+ //
+ IfrNvData->AuthenticationType = Attempt->AuthenticationType;
+
+ if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ AuthConfigData = &Attempt->AuthConfigData.CHAP;
+ IfrNvData->CHAPType = AuthConfigData->CHAPType;
+ AsciiStrToUnicodeStr (AuthConfigData->CHAPName, IfrNvData->CHAPName);
+ AsciiStrToUnicodeStr (AuthConfigData->CHAPSecret, IfrNvData->CHAPSecret);
+ AsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPName, IfrNvData->ReverseCHAPName);
+ AsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPSecret, IfrNvData->ReverseCHAPSecret);
+ }
+
+ //
+ // Other parameters.
+ //
+ AsciiStrToUnicodeStr (Attempt->AttemptName, IfrNvData->AttemptName);
+}
+
+
+/**
+ Convert the IFR data to iSCSI configuration data.
+
+ @param[in] IfrNvData The IFR nv data.
+ @param[in, out] Attempt The iSCSI attempt config data.
+
+ @retval EFI_INVALID_PARAMETER Any input or configured parameter is invalid.
+ @retval EFI_NOT_FOUND Cannot find the corresponding variable.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiConvertIfrNvDataToAttemptConfigData (
+ IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt
+ )
+{
+ EFI_IP_ADDRESS HostIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS Gateway;
+ CHAR16 *MacString;
+ CHAR16 *AttemptName1;
+ CHAR16 *AttemptName2;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *ExistAttempt;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *SameNicAttempt;
+ CHAR16 IScsiMode[64];
+ CHAR16 IpMode[64];
+ ISCSI_NIC_INFO *NicInfo;
+ EFI_INPUT_KEY Key;
+
+ if (IfrNvData == NULL || Attempt == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Update those fields which don't have INTERACTIVE attribute.
+ //
+ Attempt->SessionConfigData.ConnectRetryCount = IfrNvData->ConnectRetryCount;
+ Attempt->SessionConfigData.ConnectTimeout = IfrNvData->ConnectTimeout;
+ Attempt->SessionConfigData.IpMode = IfrNvData->IpMode;
+
+ if (IfrNvData->IpMode < IP_MODE_AUTOCONFIG) {
+ Attempt->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;
+ Attempt->SessionConfigData.TargetPort = IfrNvData->TargetPort;
+
+ if (Attempt->SessionConfigData.TargetPort == 0) {
+ Attempt->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+
+ Attempt->SessionConfigData.TargetInfoFromDhcp = IfrNvData->TargetInfoFromDhcp;
+ }
+
+ Attempt->AuthenticationType = IfrNvData->AuthenticationType;
+
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ Attempt->AuthConfigData.CHAP.CHAPType = IfrNvData->CHAPType;
+ }
+
+ //
+ // Only do full parameter validation if iSCSI is enabled on this device.
+ //
+ if (IfrNvData->Enabled != ISCSI_DISABLED) {
+ if (Attempt->SessionConfigData.ConnectTimeout < CONNECT_MIN_TIMEOUT) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Connection Establishing Timeout is less than minimum value 100ms.",
+ NULL
+ );
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Validate the address configuration of the Initiator if DHCP isn't
+ // deployed.
+ //
+ if (!Attempt->SessionConfigData.InitiatorInfoFromDhcp) {
+ CopyMem (&HostIp.v4, &Attempt->SessionConfigData.LocalIp, sizeof (HostIp.v4));
+ CopyMem (&SubnetMask.v4, &Attempt->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));
+ CopyMem (&Gateway.v4, &Attempt->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
+ );
+
+ return EFI_INVALID_PARAMETER;
+ } 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
+ );
+
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ }
+ //
+ // Validate target configuration if DHCP isn't deployed.
+ //
+ if (!Attempt->SessionConfigData.TargetInfoFromDhcp && Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {
+ if (!IpIsUnicast (&Attempt->SessionConfigData.TargetIp, IfrNvData->IpMode)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Target IP is invalid!",
+ NULL
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+ //
+ // Validate the authentication info.
+ //
+ if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ 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
+ );
+
+ return EFI_INVALID_PARAMETER;
+ }
+
+ 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
+ );
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ //
+ // Check whether this attempt uses NIC which is already used by existing attempt.
+ //
+ SameNicAttempt = IScsiConfigGetAttemptByNic (Attempt, IfrNvData->Enabled);
+ if (SameNicAttempt != NULL) {
+ AttemptName1 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16));
+ if (AttemptName1 == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AttemptName2 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16));
+ if (AttemptName2 == NULL) {
+ FreePool (AttemptName1);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AsciiStrToUnicodeStr (Attempt->AttemptName, AttemptName1);
+ if (StrLen (AttemptName1) > ATTEMPT_NAME_SIZE) {
+ CopyMem (&AttemptName1[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));
+ }
+
+ AsciiStrToUnicodeStr (SameNicAttempt->AttemptName, AttemptName2);
+ if (StrLen (AttemptName2) > ATTEMPT_NAME_SIZE) {
+ CopyMem (&AttemptName2[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));
+ }
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"Warning! Attempt \"%s\" uses same NIC as Attempt \"%s\".",
+ AttemptName1,
+ AttemptName2
+ );
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ mPrivate->PortString,
+ NULL
+ );
+
+ FreePool (AttemptName1);
+ FreePool (AttemptName2);
+ }
+ }
+
+ //
+ // Check whether this attempt is an existing one.
+ //
+ ExistAttempt = IScsiConfigGetAttemptByConfigIndex (Attempt->AttemptConfigIndex);
+ if (ExistAttempt != NULL) {
+ ASSERT (ExistAttempt == Attempt);
+
+ if (IfrNvData->Enabled == ISCSI_DISABLED &&
+ Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {
+
+ //
+ // User updates the Attempt from "Enabled"/"Enabled for MPIO" to "Disabled".
+ //
+ if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ if (mPrivate->MpioCount < 1) {
+ return EFI_ABORTED;
+ }
+
+ if (--mPrivate->MpioCount == 0) {
+ mPrivate->EnableMpio = FALSE;
+ }
+ } else if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) {
+ if (mPrivate->SinglePathCount < 1) {
+ return EFI_ABORTED;
+ }
+ mPrivate->SinglePathCount--;
+ }
+
+ } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO &&
+ Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) {
+ //
+ // User updates the Attempt from "Enabled" to "Enabled for MPIO".
+ //
+ if (mPrivate->SinglePathCount < 1) {
+ return EFI_ABORTED;
+ }
+
+ mPrivate->EnableMpio = TRUE;
+ mPrivate->MpioCount++;
+ mPrivate->SinglePathCount--;
+
+ } else if (IfrNvData->Enabled == ISCSI_ENABLED &&
+ Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ //
+ // User updates the Attempt from "Enabled for MPIO" to "Enabled".
+ //
+ if (mPrivate->MpioCount < 1) {
+ return EFI_ABORTED;
+ }
+
+ if (--mPrivate->MpioCount == 0) {
+ mPrivate->EnableMpio = FALSE;
+ }
+ mPrivate->SinglePathCount++;
+
+ } else if (IfrNvData->Enabled != ISCSI_DISABLED &&
+ Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) {
+ //
+ // User updates the Attempt from "Disabled" to "Enabled"/"Enabled for MPIO".
+ //
+ if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ mPrivate->EnableMpio = TRUE;
+ mPrivate->MpioCount++;
+
+ } else if (IfrNvData->Enabled == ISCSI_ENABLED) {
+ mPrivate->SinglePathCount++;
+ }
+ }
+
+ } else if (ExistAttempt == NULL && IfrNvData->Enabled != ISCSI_DISABLED) {
+ if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ //
+ // This new Attempt is enabled for MPIO; enable the multipath mode.
+ //
+ mPrivate->EnableMpio = TRUE;
+ mPrivate->MpioCount++;
+ } else if (IfrNvData->Enabled == ISCSI_ENABLED) {
+ mPrivate->SinglePathCount++;
+ }
+ }
+
+ //
+ // Update the iSCSI Mode data and record it in attempt help info.
+ //
+ Attempt->SessionConfigData.Enabled = IfrNvData->Enabled;
+ if (IfrNvData->Enabled == ISCSI_DISABLED) {
+ UnicodeSPrint (IScsiMode, 64, L"Disabled");
+ } else if (IfrNvData->Enabled == ISCSI_ENABLED) {
+ UnicodeSPrint (IScsiMode, 64, L"Enabled");
+ } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");
+ }
+
+ if (IfrNvData->IpMode == IP_MODE_IP4) {
+ UnicodeSPrint (IpMode, 64, L"IP4");
+ } else if (IfrNvData->IpMode == IP_MODE_IP6) {
+ UnicodeSPrint (IpMode, 64, L"IP6");
+ } else if (IfrNvData->IpMode == IP_MODE_AUTOCONFIG) {
+ UnicodeSPrint (IpMode, 64, L"Autoconfigure");
+ }
+
+ NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex);
+ if (NicInfo == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ MacString = (CHAR16 *) AllocateZeroPool (ISCSI_MAX_MAC_STRING_LEN * sizeof (CHAR16));
+ if (MacString == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ AsciiStrToUnicodeStr (Attempt->MacString, MacString);
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",
+ MacString,
+ NicInfo->BusNumber,
+ NicInfo->DeviceNumber,
+ NicInfo->FunctionNumber,
+ IScsiMode,
+ IpMode
+ );
+
+ Attempt->AttemptTitleHelpToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ Attempt->AttemptTitleHelpToken,
+ mPrivate->PortString,
+ NULL
+ );
+ if (Attempt->AttemptTitleHelpToken == 0) {
+ FreePool (MacString);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Record the user configuration information in NVR.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"%s%d",
+ MacString,
+ (UINTN) Attempt->AttemptConfigIndex
+ );
+
+ FreePool (MacString);
+
+ return gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ Attempt
+ );
+}
+
+/**
+ Create Hii Extend Label OpCode as the start opcode and end opcode. It is
+ a help function.
+
+ @param[in] StartLabelNumber The number of start label.
+ @param[out] StartOpCodeHandle Points to the start opcode handle.
+ @param[out] StartLabel Points to the created start opcode.
+ @param[out] EndOpCodeHandle Points to the end opcode handle.
+ @param[out] EndLabel Points to the created end opcode.
+
+ @retval EFI_OUT_OF_RESOURCES Do not have sufficient resource to finish this
+ operation.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiCreateOpCode (
+ IN UINT16 StartLabelNumber,
+ OUT VOID **StartOpCodeHandle,
+ OUT EFI_IFR_GUID_LABEL **StartLabel,
+ OUT VOID **EndOpCodeHandle,
+ OUT EFI_IFR_GUID_LABEL **EndLabel
+ )
+{
+ EFI_STATUS Status;
+ EFI_IFR_GUID_LABEL *InternalStartLabel;
+ EFI_IFR_GUID_LABEL *InternalEndLabel;
+
+ if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *StartOpCodeHandle = NULL;
+ *EndOpCodeHandle = NULL;
+ Status = EFI_OUT_OF_RESOURCES;
+
+ //
+ // Initialize the container for dynamic opcodes.
+ //
+ *StartOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (*StartOpCodeHandle == NULL) {
+ return Status;
+ }
+
+ *EndOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (*EndOpCodeHandle == NULL) {
+ goto Exit;
+ }
+
+ //
+ // Create Hii Extend Label OpCode as the start opcode.
+ //
+ InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ *StartOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ if (InternalStartLabel == NULL) {
+ goto Exit;
+ }
+
+ InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ InternalStartLabel->Number = StartLabelNumber;
+
+ //
+ // Create Hii Extend Label OpCode as the end opcode.
+ //
+ InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
+ *EndOpCodeHandle,
+ &gEfiIfrTianoGuid,
+ NULL,
+ sizeof (EFI_IFR_GUID_LABEL)
+ );
+ if (InternalEndLabel == NULL) {
+ goto Exit;
+ }
+
+ InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
+ InternalEndLabel->Number = LABEL_END;
+
+ *StartLabel = InternalStartLabel;
+ *EndLabel = InternalEndLabel;
+
+ return EFI_SUCCESS;
+
+Exit:
+
+ if (*StartOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (*StartOpCodeHandle);
+ }
+
+ if (*EndOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (*EndOpCodeHandle);
+ }
+
+ return Status;
+}
+
+/**
+ Callback function when user presses "Add an Attempt".
+
+ @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this
+ operation.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiConfigAddAttempt (
+ VOID
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_NIC_INFO *NicInfo;
+ EFI_STRING_ID PortTitleToken;
+ EFI_STRING_ID PortTitleHelpToken;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ EFI_STATUS Status;
+ VOID *StartOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *EndLabel;
+
+ Status = IScsiCreateOpCode (
+ MAC_ENTRY_LABEL,
+ &StartOpCodeHandle,
+ &StartLabel,
+ &EndOpCodeHandle,
+ &EndLabel
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Ask user to select a MAC for this attempt.
+ //
+ NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
+ NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
+ IScsiMacAddrToStr (
+ &NicInfo->PermanentAddress,
+ NicInfo->HwAddressSize,
+ NicInfo->VlanId,
+ MacString
+ );
+
+ UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"Port %s", MacString);
+ PortTitleToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ mPrivate->PortString,
+ NULL
+ );
+ if (PortTitleToken == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"PFA: Bus %d | Dev %d | Func %d",
+ NicInfo->BusNumber,
+ NicInfo->DeviceNumber,
+ NicInfo->FunctionNumber
+ );
+ PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, mPrivate->PortString, NULL);
+ if (PortTitleHelpToken == 0) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+
+ HiiCreateGotoOpCode (
+ StartOpCodeHandle, // Container for dynamic created opcodes
+ FORMID_ATTEMPT_FORM,
+ PortTitleToken,
+ PortTitleHelpToken,
+ EFI_IFR_FLAG_CALLBACK, // Question flag
+ (UINT16) (KEY_MAC_ENTRY_BASE + NicInfo->NicIndex)
+ );
+ }
+
+ Status = HiiUpdateForm (
+ mCallbackInfo->RegisteredHandle, // HII handle
+ &mVendorGuid, // Formset GUID
+ FORMID_MAC_FORM, // Form ID
+ StartOpCodeHandle, // Label for where to insert opcodes
+ EndOpCodeHandle // Replace data
+ );
+
+Exit:
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+
+ return Status;
+}
+
+
+/**
+ Update the MAIN form to display the configured attempts.
+
+**/
+VOID
+IScsiConfigUpdateAttempt (
+ VOID
+ )
+{
+ CHAR16 AttemptName[ATTEMPT_NAME_MAX_SIZE];
+ LIST_ENTRY *Entry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ VOID *StartOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *EndLabel;
+ EFI_STATUS Status;
+
+ Status = IScsiCreateOpCode (
+ ATTEMPT_ENTRY_LABEL,
+ &StartOpCodeHandle,
+ &StartLabel,
+ &EndOpCodeHandle,
+ &EndLabel
+ );
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+
+ AsciiStrToUnicodeStr (AttemptConfigData->AttemptName, AttemptName);
+ UnicodeSPrint (mPrivate->PortString, (UINTN) 128, L"Attempt %s", AttemptName);
+ AttemptConfigData->AttemptTitleToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ mPrivate->PortString,
+ NULL
+ );
+ if (AttemptConfigData->AttemptTitleToken == 0) {
+ return ;
+ }
+
+ HiiCreateGotoOpCode (
+ StartOpCodeHandle, // Container for dynamic created opcodes
+ FORMID_ATTEMPT_FORM, // Form ID
+ AttemptConfigData->AttemptTitleToken, // Prompt text
+ AttemptConfigData->AttemptTitleHelpToken, // Help text
+ EFI_IFR_FLAG_CALLBACK, // Question flag
+ (UINT16) (KEY_ATTEMPT_ENTRY_BASE + AttemptConfigData->AttemptConfigIndex) // Question ID
+ );
+ }
+
+ HiiUpdateForm (
+ mCallbackInfo->RegisteredHandle, // HII handle
+ &mVendorGuid, // Formset GUID
+ FORMID_MAIN_FORM, // Form ID
+ StartOpCodeHandle, // Label for where to insert opcodes
+ EndOpCodeHandle // Replace data
+ );
+
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+}
+
+
+/**
+ Callback function when user presses "Commit Changes and Exit" in Delete Attempts.
+
+ @param[in] IfrNvData The IFR NV data.
+
+ @retval EFI_NOT_FOUND Cannot find the corresponding variable.
+ @retval EFI_SUCCESS The operation is completed successfully.
+ @retval EFI_ABOTRED This operation is aborted cause of error
+ configuration.
+ @retval EFI_OUT_OF_RESOURCES Fail to finish the operation due to lack of
+ resources.
+
+**/
+EFI_STATUS
+IScsiConfigDeleteAttempts (
+ IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN NewIndex;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINT8 *AttemptNewOrder;
+ UINT32 Attribute;
+ UINTN Total;
+ UINTN NewTotal;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &mVendorGuid,
+ &AttemptConfigOrderSize
+ );
+ if ((AttemptConfigOrder == NULL) || (AttemptConfigOrderSize == 0)) {
+ return EFI_NOT_FOUND;
+ }
+
+ AttemptNewOrder = AllocateZeroPool (AttemptConfigOrderSize);
+ if (AttemptNewOrder == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Total = AttemptConfigOrderSize / sizeof (UINT8);
+ NewTotal = Total;
+ Index = 0;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
+ if (IfrNvData->DeleteAttemptList[Index] == 0) {
+ Index++;
+ continue;
+ }
+
+ //
+ // Delete the attempt.
+ //
+
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ if (AttemptConfigData == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto Error;
+ }
+
+ //
+ // Remove this attempt from UI configured attempt list.
+ //
+ RemoveEntryList (&AttemptConfigData->Link);
+ mPrivate->AttemptCount--;
+
+ if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ if (mPrivate->MpioCount < 1) {
+ Status = EFI_ABORTED;
+ goto Error;
+ }
+
+ //
+ // No more attempt is enabled for MPIO. Transit the iSCSI mode to single path.
+ //
+ if (--mPrivate->MpioCount == 0) {
+ mPrivate->EnableMpio = FALSE;
+ }
+ } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
+ if (mPrivate->SinglePathCount < 1) {
+ Status = EFI_ABORTED;
+ goto Error;
+ }
+
+ mPrivate->SinglePathCount--;
+ }
+
+ AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) 128,
+ L"%s%d",
+ MacString,
+ (UINTN) AttemptConfigData->AttemptConfigIndex
+ );
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ 0,
+ 0,
+ NULL
+ );
+
+ //
+ // Mark the attempt order in NVR to be deleted - 0.
+ //
+ for (NewIndex = 0; NewIndex < Total; NewIndex++) {
+ if (AttemptConfigOrder[NewIndex] == AttemptConfigData->AttemptConfigIndex) {
+ AttemptConfigOrder[NewIndex] = 0;
+ break;
+ }
+ }
+
+ NewTotal--;
+ FreePool (AttemptConfigData);
+
+ //
+ // Check next Attempt.
+ //
+ Index++;
+ }
+
+ //
+ // Construct AttemptNewOrder.
+ //
+ for (Index = 0, NewIndex = 0; Index < Total; Index++) {
+ if (AttemptConfigOrder[Index] != 0) {
+ AttemptNewOrder[NewIndex] = AttemptConfigOrder[Index];
+ NewIndex++;
+ }
+ }
+
+ Attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
+ | EFI_VARIABLE_NON_VOLATILE;
+
+ //
+ // Update AttemptOrder in NVR.
+ //
+ Status = gRT->SetVariable (
+ L"AttemptOrder",
+ &mVendorGuid,
+ Attribute,
+ NewTotal * sizeof (UINT8),
+ AttemptNewOrder
+ );
+
+Error:
+ FreePool (AttemptConfigOrder);
+ FreePool (AttemptNewOrder);
+
+ return Status;
+}
+
+
+/**
+ Callback function when user presses "Delete Attempts".
+
+ @param[in] IfrNvData The IFR nv data.
+
+ @retval EFI_INVALID_PARAMETER Any parameter is invalid.
+ @retval EFI_BUFFER_TOO_SMALL The buffer in UpdateData is too small.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiConfigDisplayDeleteAttempts (
+ IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ LIST_ENTRY *Entry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ UINT8 Index;
+ VOID *StartOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *EndLabel;
+ EFI_STATUS Status;
+
+ Status = IScsiCreateOpCode (
+ DELETE_ENTRY_LABEL,
+ &StartOpCodeHandle,
+ &StartLabel,
+ &EndOpCodeHandle,
+ &EndLabel
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &mVendorGuid,
+ &AttemptConfigOrderSize
+ );
+ if (AttemptConfigOrder != NULL) {
+ //
+ // Create the check box opcode to be deleted.
+ //
+ Index = 0;
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ IfrNvData->DeleteAttemptList[Index] = 0x00;
+
+ HiiCreateCheckBoxOpCode(
+ StartOpCodeHandle,
+ (EFI_QUESTION_ID) (ATTEMPT_DEL_QUESTION_ID + Index),
+ CONFIGURATION_VARSTORE_ID,
+ (UINT16) (ATTEMPT_DEL_VAR_OFFSET + Index),
+ AttemptConfigData->AttemptTitleToken,
+ AttemptConfigData->AttemptTitleHelpToken,
+ 0,
+ 0,
+ NULL
+ );
+
+ Index++;
+
+ if (Index == ISCSI_MAX_ATTEMPTS_NUM) {
+ break;
+ }
+ }
+
+ FreePool (AttemptConfigOrder);
+ }
+
+ Status = HiiUpdateForm (
+ mCallbackInfo->RegisteredHandle, // HII handle
+ &mVendorGuid, // Formset GUID
+ FORMID_DELETE_FORM, // Form ID
+ StartOpCodeHandle, // Label for where to insert opcodes
+ EndOpCodeHandle // Replace data
+ );
+
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+
+ return Status;
+}
+
+
+/**
+ Callback function when user presses "Change Attempt Order".
+
+ @retval EFI_INVALID_PARAMETER Any parameter is invalid.
+ @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this
+ operation.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiConfigDisplayOrderAttempts (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Index;
+ LIST_ENTRY *Entry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ VOID *StartOpCodeHandle;
+ EFI_IFR_GUID_LABEL *StartLabel;
+ VOID *EndOpCodeHandle;
+ EFI_IFR_GUID_LABEL *EndLabel;
+ VOID *OptionsOpCodeHandle;
+
+ Status = IScsiCreateOpCode (
+ ORDER_ENTRY_LABEL,
+ &StartOpCodeHandle,
+ &StartLabel,
+ &EndOpCodeHandle,
+ &EndLabel
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ OptionsOpCodeHandle = NULL;
+
+ //
+ // If no attempt to be ordered, update the original form and exit.
+ //
+ if (mPrivate->AttemptCount == 0) {
+ goto Exit;
+ }
+
+ //
+ // Create Option OpCode.
+ //
+ OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();
+ if (OptionsOpCodeHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ Index = 0;
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ HiiCreateOneOfOptionOpCode (
+ OptionsOpCodeHandle,
+ AttemptConfigData->AttemptTitleToken,
+ 0,
+ EFI_IFR_NUMERIC_SIZE_1,
+ AttemptConfigData->AttemptConfigIndex
+ );
+ Index++;
+ }
+
+ ASSERT (Index == mPrivate->AttemptCount);
+
+ HiiCreateOrderedListOpCode (
+ StartOpCodeHandle, // Container for dynamic created opcodes
+ DYNAMIC_ORDERED_LIST_QUESTION_ID, // Question ID
+ CONFIGURATION_VARSTORE_ID, // VarStore ID
+ DYNAMIC_ORDERED_LIST_VAR_OFFSET, // Offset in Buffer Storage
+ STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY), // Question prompt text
+ STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY), // Question help text
+ 0, // Question flag
+ EFI_IFR_UNIQUE_SET, // Ordered list flag, e.g. EFI_IFR_UNIQUE_SET
+ EFI_IFR_NUMERIC_SIZE_1, // Data type of Question value
+ ISCSI_MAX_ATTEMPTS_NUM, // Maximum container
+ OptionsOpCodeHandle, // Option Opcode list
+ NULL // Default Opcode is NULL
+ );
+
+Exit:
+ Status = HiiUpdateForm (
+ mCallbackInfo->RegisteredHandle, // HII handle
+ &mVendorGuid, // Formset GUID
+ FORMID_ORDER_FORM, // Form ID
+ StartOpCodeHandle, // Label for where to insert opcodes
+ EndOpCodeHandle // Replace data
+ );
+
+Error:
+ HiiFreeOpCodeHandle (StartOpCodeHandle);
+ HiiFreeOpCodeHandle (EndOpCodeHandle);
+ if (OptionsOpCodeHandle != NULL) {
+ HiiFreeOpCodeHandle (OptionsOpCodeHandle);
+ }
+
+ return Status;
+}
+
+
+/**
+ Callback function when user presses "Commit Changes and Exit" in Change Attempt Order.
+
+ @param[in] IfrNvData The IFR nv data.
+
+ @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this
+ operation.
+ @retval EFI_NOT_FOUND Cannot find the corresponding variable.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiConfigOrderAttempts (
+ IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN Indexj;
+ UINT8 AttemptConfigIndex;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ UINT8 *AttemptConfigOrder;
+ UINT8 *AttemptConfigOrderTmp;
+ UINTN AttemptConfigOrderSize;
+
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &mVendorGuid,
+ &AttemptConfigOrderSize
+ );
+ if (AttemptConfigOrder == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ AttemptConfigOrderTmp = AllocateZeroPool (AttemptConfigOrderSize);
+ if (AttemptConfigOrderTmp == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Exit;
+ }
+
+ for (Index = 0; Index < ISCSI_MAX_ATTEMPTS_NUM; Index++) {
+ //
+ // The real content ends with 0.
+ //
+ if (IfrNvData->DynamicOrderedList[Index] == 0) {
+ break;
+ }
+
+ AttemptConfigIndex = IfrNvData->DynamicOrderedList[Index];
+ AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigIndex);
+ if (AttemptConfigData == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto Exit;
+ }
+
+ //
+ // Reorder the Attempt List.
+ //
+ RemoveEntryList (&AttemptConfigData->Link);
+ InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
+
+ AttemptConfigOrderTmp[Index] = AttemptConfigIndex;
+
+ //
+ // Mark it to be deleted - 0.
+ //
+ for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) {
+ if (AttemptConfigOrder[Indexj] == AttemptConfigIndex) {
+ AttemptConfigOrder[Indexj] = 0;
+ break;
+ }
+ }
+ }
+
+ //
+ // Adjust the attempt order in NVR.
+ //
+ for (; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
+ for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) {
+ if (AttemptConfigOrder[Indexj] != 0) {
+ AttemptConfigOrderTmp[Index] = AttemptConfigOrder[Indexj];
+ AttemptConfigOrder[Indexj] = 0;
+ continue;
+ }
+ }
+ }
+
+ Status = gRT->SetVariable (
+ L"AttemptOrder",
+ &mVendorGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ AttemptConfigOrderSize,
+ AttemptConfigOrderTmp
+ );
+
+Exit:
+ if (AttemptConfigOrderTmp != NULL) {
+ FreePool (AttemptConfigOrderTmp);
+ }
+
+ FreePool (AttemptConfigOrder);
+ return Status;
+}
+
+
+/**
+ Callback function when a user presses "Attempt *" or when a user selects a NIC to
+ create the new attempt.
+
+ @param[in] KeyValue A unique value which is sent to the original
+ exporting driver so that it can identify the type
+ of data to expect.
+ @param[in] IfrNvData The IFR nv data.
+
+ @retval EFI_OUT_OF_RESOURCES Does not have sufficient resources to finish this
+ operation.
+ @retval EFI_NOT_FOUND Cannot find the corresponding variable.
+ @retval EFI_SUCCESS The operation is completed successfully.
+
+**/
+EFI_STATUS
+IScsiConfigProcessDefault (
+ IN EFI_QUESTION_ID KeyValue,
+ IN ISCSI_CONFIG_IFR_NVDATA *IfrNvData
+ )
+{
+ BOOLEAN NewAttempt;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigData;
+ UINT8 CurrentAttemptConfigIndex;
+ ISCSI_NIC_INFO *NicInfo;
+ UINT8 NicIndex;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINTN TotalNumber;
+ UINT8 *AttemptOrderTmp;
+ UINTN Index;
+ EFI_STATUS Status;
+
+ NewAttempt = FALSE;
+
+ if ((KeyValue >= KEY_MAC_ENTRY_BASE) &&
+ (KeyValue <= (UINT16) (mPrivate->MaxNic + KEY_MAC_ENTRY_BASE))) {
+ //
+ // User has pressed "Add an Attempt" and then selects a NIC.
+ //
+ NewAttempt = TRUE;
+ } else if ((KeyValue >= KEY_ATTEMPT_ENTRY_BASE) &&
+ (KeyValue < (ISCSI_MAX_ATTEMPTS_NUM + KEY_ATTEMPT_ENTRY_BASE))) {
+
+ //
+ // User has pressed "Attempt *".
+ //
+ NewAttempt = FALSE;
+ } else {
+ //
+ // Don't process anything.
+ //
+ return EFI_SUCCESS;
+ }
+
+ if (NewAttempt) {
+ //
+ // Determine which NIC user has selected for the new created attempt.
+ //
+ NicIndex = (UINT8) (KeyValue - KEY_MAC_ENTRY_BASE);
+ NicInfo = IScsiGetNicInfoByIndex (NicIndex);
+ if (NicInfo == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Create the new attempt and save to NVR.
+ //
+
+ AttemptConfigData = AllocateZeroPool (sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA));
+ if (AttemptConfigData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ConfigData = &AttemptConfigData->SessionConfigData;
+ ConfigData->TargetPort = ISCSI_WELL_KNOWN_PORT;
+ ConfigData->ConnectTimeout = CONNECT_DEFAULT_TIMEOUT;
+ ConfigData->ConnectRetryCount = CONNECT_MIN_RETRY;
+
+ AttemptConfigData->AuthenticationType = ISCSI_AUTH_TYPE_CHAP;
+ AttemptConfigData->AuthConfigData.CHAP.CHAPType = ISCSI_CHAP_UNI;
+
+ //
+ // Get current order number for this attempt.
+ //
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &mVendorGuid,
+ &AttemptConfigOrderSize
+ );
+
+ TotalNumber = AttemptConfigOrderSize / sizeof (UINT8);
+
+ if (AttemptConfigOrder == NULL) {
+ CurrentAttemptConfigIndex = 1;
+ } else {
+ //
+ // Get the max attempt config index.
+ //
+ CurrentAttemptConfigIndex = AttemptConfigOrder[0];
+ for (Index = 1; Index < TotalNumber; Index++) {
+ if (CurrentAttemptConfigIndex < AttemptConfigOrder[Index]) {
+ CurrentAttemptConfigIndex = AttemptConfigOrder[Index];
+ }
+ }
+
+ CurrentAttemptConfigIndex++;
+ }
+
+ TotalNumber++;
+
+ //
+ // Append the new created attempt order to the end.
+ //
+ AttemptOrderTmp = AllocateZeroPool (TotalNumber * sizeof (UINT8));
+ if (AttemptOrderTmp == NULL) {
+ FreePool (AttemptConfigData);
+ if (AttemptConfigOrder != NULL) {
+ FreePool (AttemptConfigOrder);
+ }
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (AttemptConfigOrder != NULL) {
+ CopyMem (AttemptOrderTmp, AttemptConfigOrder, AttemptConfigOrderSize);
+ FreePool (AttemptConfigOrder);
+ }
+
+ AttemptOrderTmp[TotalNumber - 1] = CurrentAttemptConfigIndex;
+ AttemptConfigOrder = AttemptOrderTmp;
+ AttemptConfigOrderSize = TotalNumber * sizeof (UINT8);
+
+ Status = gRT->SetVariable (
+ L"AttemptOrder",
+ &mVendorGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ AttemptConfigOrderSize,
+ AttemptConfigOrder
+ );
+ FreePool (AttemptConfigOrder);
+ if (EFI_ERROR (Status)) {
+ FreePool (AttemptConfigData);
+ return Status;
+ }
+
+ //
+ // Record the mapping between attempt order and attempt's configdata.
+ //
+ AttemptConfigData->AttemptConfigIndex = CurrentAttemptConfigIndex;
+ InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
+ mPrivate->AttemptCount++;
+
+ //
+ // Record the MAC info in Config Data.
+ //
+ IScsiMacAddrToStr (
+ &NicInfo->PermanentAddress,
+ NicInfo->HwAddressSize,
+ NicInfo->VlanId,
+ MacString
+ );
+
+ UnicodeStrToAsciiStr (MacString, AttemptConfigData->MacString);
+ AttemptConfigData->NicIndex = NicIndex;
+
+ //
+ // Generate OUI-format ISID based on MAC address.
+ //
+ CopyMem (AttemptConfigData->SessionConfigData.IsId, &NicInfo->PermanentAddress, 6);
+ AttemptConfigData->SessionConfigData.IsId[0] =
+ (UINT8) (AttemptConfigData->SessionConfigData.IsId[0] & 0x3F);
+
+ //
+ // Add the help info for the new attempt.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"MAC: %s, PFA: Bus %d | Dev %d | Func %d",
+ MacString,
+ NicInfo->BusNumber,
+ NicInfo->DeviceNumber,
+ NicInfo->FunctionNumber
+ );
+
+ AttemptConfigData->AttemptTitleHelpToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ mPrivate->PortString,
+ NULL
+ );
+ if (AttemptConfigData->AttemptTitleHelpToken == 0) {
+ FreePool (AttemptConfigData);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Set the attempt name to default.
+ //
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) 128,
+ L"%d",
+ (UINTN) AttemptConfigData->AttemptConfigIndex
+ );
+ UnicodeStrToAsciiStr (mPrivate->PortString, AttemptConfigData->AttemptName);
+
+ } else {
+ //
+ // Determine which Attempt user has selected to configure.
+ // Get the attempt configuration data.
+ //
+ CurrentAttemptConfigIndex = (UINT8) (KeyValue - KEY_ATTEMPT_ENTRY_BASE);
+
+ AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (CurrentAttemptConfigIndex);
+ if (AttemptConfigData == NULL) {
+ DEBUG ((DEBUG_ERROR, "Corresponding configuration data can not be retrieved!\n"));
+ return EFI_NOT_FOUND;
+ }
+ }
+
+ //
+ // Clear the old IFR data to avoid sharing it with other attempts.
+ //
+ if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ ZeroMem (IfrNvData->CHAPName, sizeof (IfrNvData->CHAPName));
+ ZeroMem (IfrNvData->CHAPSecret, sizeof (IfrNvData->CHAPSecret));
+ ZeroMem (IfrNvData->ReverseCHAPName, sizeof (IfrNvData->ReverseCHAPName));
+ ZeroMem (IfrNvData->ReverseCHAPSecret, sizeof (IfrNvData->ReverseCHAPSecret));
+ }
+
+ IScsiConvertAttemptConfigDataToIfrNvData (AttemptConfigData, IfrNvData);
+
+ mCallbackInfo->Current = AttemptConfigData;
+
+ IScsiConfigUpdateAttempt ();
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+
+ This function allows the caller to request the current
+ configuration for one or more named elements. The resulting
+ string is in <ConfigAltResp> format. Also, any and all alternative
+ configuration strings shall 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.
+
+**/
+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;
+ UINTN BufferSize;
+ ISCSI_CONFIG_IFR_NVDATA *IfrNvData;
+ ISCSI_FORM_CALLBACK_INFO *Private;
+ EFI_STRING ConfigRequestHdr;
+ EFI_STRING ConfigRequest;
+ BOOLEAN AllocatedRequest;
+ UINTN Size;
+
+ if (This == NULL || 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;
+
+ Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
+ IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));
+ if (IfrNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (Private->Current != NULL) {
+ IScsiConvertAttemptConfigDataToIfrNvData (Private->Current, IfrNvData);
+ }
+
+ BufferSize = ISCSI_NAME_MAX_SIZE;
+ InitiatorName = (CHAR8 *) AllocateZeroPool (BufferSize);
+ if (InitiatorName == NULL) {
+ FreePool (IfrNvData);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);
+ if (EFI_ERROR (Status)) {
+ IfrNvData->InitiatorName[0] = L'\0';
+ } else {
+ AsciiStrToUnicodeStr (InitiatorName, IfrNvData->InitiatorName);
+ }
+
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig().
+ //
+ 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 = gHiiConfigRouting->BlockToConfig (
+ gHiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) IfrNvData,
+ BufferSize,
+ Results,
+ Progress
+ );
+ FreePool (IfrNvData);
+ FreePool (InitiatorName);
+
+ //
+ // 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.
+
+ @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
+ 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 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 (This == NULL || 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 generated the callback.
+ @param[in] Type The type of value for the question.
+ @param[in, out] 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
+IScsiFormCallback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN OUT EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ ISCSI_FORM_CALLBACK_INFO *Private;
+ UINTN BufferSize;
+ CHAR8 *IScsiName;
+ CHAR8 IpString[IP_STR_MAX_SIZE];
+ CHAR8 LunString[ISCSI_LUN_STR_MAX_LEN];
+ UINT64 Lun;
+ EFI_IP_ADDRESS HostIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS Gateway;
+ ISCSI_CONFIG_IFR_NVDATA *IfrNvData;
+ ISCSI_CONFIG_IFR_NVDATA OldIfrNvData;
+ EFI_STATUS Status;
+ CHAR16 AttemptName[ATTEMPT_NAME_SIZE + 4];
+ 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;
+ }
+
+ if (Action == EFI_BROWSER_ACTION_CHANGING) {
+ if (This == NULL || Value == NULL || ActionRequest == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
+
+ //
+ // Retrieve uncommitted data from Browser
+ //
+
+ BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
+ IfrNvData = AllocateZeroPool (BufferSize);
+ if (IfrNvData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IScsiName = (CHAR8 *) AllocateZeroPool (ISCSI_NAME_MAX_SIZE);
+ if (IScsiName == NULL) {
+ FreePool (IfrNvData);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_SUCCESS;
+
+ ZeroMem (&OldIfrNvData, BufferSize);
+
+ HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);
+
+ CopyMem (&OldIfrNvData, IfrNvData, BufferSize);
+
+ switch (QuestionId) {
+ case KEY_INITIATOR_NAME:
+ UnicodeStrToAsciiStr (IfrNvData->InitiatorName, IScsiName);
+ BufferSize = AsciiStrSize (IScsiName);
+
+ Status = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName);
+ if (EFI_ERROR (Status)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid iSCSI Name!",
+ NULL
+ );
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_ADD_ATTEMPT:
+ Status = IScsiConfigAddAttempt ();
+ break;
+
+ case KEY_DELETE_ATTEMPT:
+ CopyMem (
+ OldIfrNvData.DeleteAttemptList,
+ IfrNvData->DeleteAttemptList,
+ sizeof (IfrNvData->DeleteAttemptList)
+ );
+ Status = IScsiConfigDisplayDeleteAttempts (IfrNvData);
+ break;
+
+ case KEY_SAVE_DELETE_ATTEMPT:
+ //
+ // Delete the Attempt Order from NVR
+ //
+ Status = IScsiConfigDeleteAttempts (IfrNvData);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ IScsiConfigUpdateAttempt ();
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_IGNORE_DELETE_ATTEMPT:
+ CopyMem (
+ IfrNvData->DeleteAttemptList,
+ OldIfrNvData.DeleteAttemptList,
+ sizeof (IfrNvData->DeleteAttemptList)
+ );
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_ORDER_ATTEMPT_CONFIG:
+ //
+ // Order the attempt according to user input.
+ //
+ CopyMem (
+ OldIfrNvData.DynamicOrderedList,
+ IfrNvData->DynamicOrderedList,
+ sizeof (IfrNvData->DynamicOrderedList)
+ );
+ IScsiConfigDisplayOrderAttempts ();
+ break;
+
+ case KEY_SAVE_ORDER_CHANGES:
+ //
+ // Sync the Attempt Order to NVR.
+ //
+ Status = IScsiConfigOrderAttempts (IfrNvData);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ IScsiConfigUpdateAttempt ();
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_IGNORE_ORDER_CHANGES:
+ CopyMem (
+ IfrNvData->DynamicOrderedList,
+ OldIfrNvData.DynamicOrderedList,
+ sizeof (IfrNvData->DynamicOrderedList)
+ );
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_ATTEMPT_NAME:
+ if (StrLen (IfrNvData->AttemptName) > ATTEMPT_NAME_SIZE) {
+ CopyMem (AttemptName, IfrNvData->AttemptName, ATTEMPT_NAME_SIZE * sizeof (CHAR16));
+ CopyMem (&AttemptName[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));
+ } else {
+ CopyMem (
+ AttemptName,
+ IfrNvData->AttemptName,
+ (StrLen (IfrNvData->AttemptName) + 1) * sizeof (CHAR16)
+ );
+ }
+
+ UnicodeStrToAsciiStr (IfrNvData->AttemptName, Private->Current->AttemptName);
+
+ IScsiConfigUpdateAttempt ();
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ case KEY_IP_MODE:
+ switch (Value->u8) {
+ case IP_MODE_IP6:
+ ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));
+ IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, TRUE, IfrNvData->TargetIp);
+ Private->Current->AutoConfigureMode = 0;
+ break;
+
+ case IP_MODE_IP4:
+ ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));
+ IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, FALSE, IfrNvData->TargetIp);
+ Private->Current->AutoConfigureMode = 0;
+
+ break;
+ }
+
+ break;
+
+ case KEY_LOCAL_IP:
+ Status = NetLibStrToIp4 (IfrNvData->LocalIp, &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:
+ Status = NetLibStrToIp4 (IfrNvData->SubnetMask, &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:
+ Status = NetLibStrToIp4 (IfrNvData->Gateway, &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:
+ UnicodeStrToAsciiStr (IfrNvData->TargetIp, IpString);
+ Status = IScsiAsciiStrToIp (IpString, IfrNvData->IpMode, &HostIp);
+ if (EFI_ERROR (Status) || !IpIsUnicast (&HostIp, IfrNvData->IpMode)) {
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"Invalid IP address!",
+ NULL
+ );
+ Status = EFI_INVALID_PARAMETER;
+ } else {
+ CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp, sizeof (HostIp));
+ }
+
+ break;
+
+ case KEY_TARGET_NAME:
+ UnicodeStrToAsciiStr (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:
+ UnicodeStrToAsciiStr (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_AUTH_TYPE:
+ switch (Value->u8) {
+ case ISCSI_AUTH_TYPE_CHAP:
+ IfrNvData->CHAPType = ISCSI_CHAP_UNI;
+ break;
+ default:
+ break;
+ }
+
+ break;
+
+ case KEY_CHAP_NAME:
+ UnicodeStrToAsciiStr (
+ IfrNvData->CHAPName,
+ Private->Current->AuthConfigData.CHAP.CHAPName
+ );
+ break;
+
+ case KEY_CHAP_SECRET:
+ UnicodeStrToAsciiStr (
+ IfrNvData->CHAPSecret,
+ Private->Current->AuthConfigData.CHAP.CHAPSecret
+ );
+ break;
+
+ case KEY_REVERSE_CHAP_NAME:
+ UnicodeStrToAsciiStr (
+ IfrNvData->ReverseCHAPName,
+ Private->Current->AuthConfigData.CHAP.ReverseCHAPName
+ );
+ break;
+
+ case KEY_REVERSE_CHAP_SECRET:
+ UnicodeStrToAsciiStr (
+ IfrNvData->ReverseCHAPSecret,
+ Private->Current->AuthConfigData.CHAP.ReverseCHAPSecret
+ );
+ break;
+
+ case KEY_CONFIG_ISID:
+ IScsiParseIsIdFromString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
+ IScsiConvertIsIdToString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
+
+ break;
+
+ case KEY_SAVE_ATTEMPT_CONFIG:
+ Status = IScsiConvertIfrNvDataToAttemptConfigData (IfrNvData, Private->Current);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;
+ break;
+
+ default:
+ Status = IScsiConfigProcessDefault (QuestionId, IfrNvData);
+ break;
+ }
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // Pass changed uncommitted data back to Form Browser.
+ //
+ BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
+ HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL);
+ }
+
+ FreePool (IfrNvData);
+ FreePool (IScsiName);
+
+ return Status;
+ }
+
+ //
+ // All other action return unsupported.
+ //
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ 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.
+
+**/
+EFI_STATUS
+IScsiConfigFormInit (
+ IN EFI_HANDLE DriverBindingHandle
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_FORM_CALLBACK_INFO *CallbackInfo;
+
+ 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->Current = NULL;
+
+ CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig;
+ CallbackInfo->ConfigAccess.RouteConfig = IScsiFormRouteConfig;
+ CallbackInfo->ConfigAccess.Callback = IScsiFormCallback;
+
+ //
+ // 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,
+ IScsiConfigVfrBin,
+ NULL
+ );
+ if (CallbackInfo->RegisteredHandle == NULL) {
+ gBS->UninstallMultipleProtocolInterfaces (
+ &CallbackInfo->DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ &mIScsiHiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->ConfigAccess,
+ NULL
+ );
+ FreePool(CallbackInfo);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ mCallbackInfo = CallbackInfo;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Unload the iSCSI configuration form, this includes: delete all the iSCSI
+ 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 Others Failed to unload the form.
+
+**/
+EFI_STATUS
+IScsiConfigFormUnload (
+ IN EFI_HANDLE DriverBindingHandle
+ )
+{
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_NIC_INFO *NicInfo;
+ LIST_ENTRY *Entry;
+ EFI_STATUS Status;
+
+ while (!IsListEmpty (&mPrivate->AttemptConfigs)) {
+ Entry = NetListRemoveHead (&mPrivate->AttemptConfigs);
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ FreePool (AttemptConfigData);
+ mPrivate->AttemptCount--;
+ }
+
+ ASSERT (mPrivate->AttemptCount == 0);
+
+ while (!IsListEmpty (&mPrivate->NicInfoList)) {
+ Entry = NetListRemoveHead (&mPrivate->NicInfoList);
+ NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
+ FreePool (NicInfo);
+ mPrivate->NicCount--;
+ }
+
+ ASSERT (mPrivate->NicCount == 0);
+
+ FreePool (mPrivate);
+ mPrivate = NULL;
+
+ //
+ // Remove HII package list.
+ //
+ HiiRemovePackages (mCallbackInfo->RegisteredHandle);
+
+ //
+ // Uninstall Device Path Protocol and Config Access protocol.
+ //
+ Status = gBS->UninstallMultipleProtocolInterfaces (
+ mCallbackInfo->DriverHandle,
+ &gEfiDevicePathProtocolGuid,
+ &mIScsiHiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &mCallbackInfo->ConfigAccess,
+ NULL
+ );
+
+ FreePool (mCallbackInfo);
+
+ return Status;
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiConfig.h b/NetworkPkg/IScsiDxe/IScsiConfig.h
new file mode 100644
index 0000000000..df0c7539e4
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiConfig.h
@@ -0,0 +1,158 @@
+/** @file
+ The header file of functions for configuring or getting the parameters
+ relating to iSCSI.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_CONFIG_H_
+#define _ISCSI_CONFIG_H_
+
+#include "IScsiConfigNVDataStruc.h"
+
+typedef struct _ISCSI_FORM_CALLBACK_INFO ISCSI_FORM_CALLBACK_INFO;
+
+extern UINT8 IScsiConfigVfrBin[];
+extern UINT8 IScsiDxeStrings[];
+extern ISCSI_FORM_CALLBACK_INFO *mCallbackInfo;
+extern EFI_GUID mVendorGuid;
+
+
+#define VAR_OFFSET(Field) \
+ ((UINT16) ((UINTN) &(((ISCSI_CONFIG_IFR_NVDATA *) 0)->Field)))
+
+#define QUESTION_ID(Field) \
+ ((UINT16) (VAR_OFFSET (Field) + CONFIG_OPTION_OFFSET))
+
+
+#define DYNAMIC_ONE_OF_VAR_OFFSET VAR_OFFSET (Enabled)
+#define DYNAMIC_ORDERED_LIST_QUESTION_ID QUESTION_ID (DynamicOrderedList)
+#define DYNAMIC_ORDERED_LIST_VAR_OFFSET VAR_OFFSET (DynamicOrderedList)
+#define ATTEMPT_DEL_QUESTION_ID QUESTION_ID (DeleteAttemptList)
+#define ATTEMPT_DEL_VAR_OFFSET VAR_OFFSET (DeleteAttemptList)
+
+//
+// sizeof (EFI_MAC_ADDRESS) * 3
+//
+#define ISCSI_MAX_MAC_STRING_LEN 96
+
+#define ISCSI_INITATOR_NAME_VAR_NAME L"I_NAME"
+
+#define ISCSI_CONFIG_VAR_ATTR (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE)
+
+#define ISCSI_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('I', 'f', 'c', 'i')
+
+#define ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK(Callback) \
+ CR ( \
+ Callback, \
+ ISCSI_FORM_CALLBACK_INFO, \
+ ConfigAccess, \
+ ISCSI_FORM_CALLBACK_INFO_SIGNATURE \
+ )
+
+#pragma pack(1)
+struct _ISCSI_ATTEMPT_CONFIG_NVDATA {
+ LIST_ENTRY Link;
+ UINT8 NicIndex;
+ UINT8 AttemptConfigIndex;
+ BOOLEAN DhcpSuccess;
+ BOOLEAN ValidiBFTPath;
+ BOOLEAN ValidPath;
+ UINT8 AutoConfigureMode;
+ EFI_STRING_ID AttemptTitleToken;
+ EFI_STRING_ID AttemptTitleHelpToken;
+ CHAR8 AttemptName[ATTEMPT_NAME_MAX_SIZE];
+ CHAR8 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ EFI_IP_ADDRESS PrimaryDns;
+ EFI_IP_ADDRESS SecondaryDns;
+ EFI_IP_ADDRESS DhcpServer;
+ ISCSI_SESSION_CONFIG_NVDATA SessionConfigData;
+ UINT8 AuthenticationType;
+ union {
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA CHAP;
+ } AuthConfigData;
+
+};
+
+///
+/// HII specific Vendor Device Path definition.
+///
+typedef struct {
+ VENDOR_DEVICE_PATH VendorDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL End;
+} HII_VENDOR_DEVICE_PATH;
+
+#pragma pack()
+
+struct _ISCSI_FORM_CALLBACK_INFO {
+ UINT32 Signature;
+ EFI_HANDLE DriverHandle;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
+ UINT16 *KeyList;
+ VOID *FormBuffer;
+ EFI_HII_HANDLE RegisteredHandle;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *Current;
+};
+
+/**
+ 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.
+
+**/
+EFI_STATUS
+IScsiConfigFormInit (
+ IN EFI_HANDLE DriverBindingHandle
+ );
+
+/**
+ Unload the iSCSI configuration form, this includes: delete all the iSCSI
+ 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 Others Failed to unload the form.
+
+**/
+EFI_STATUS
+IScsiConfigFormUnload (
+ IN EFI_HANDLE DriverBindingHandle
+ );
+
+/**
+ Update the MAIN form to display the configured attempts.
+
+**/
+VOID
+IScsiConfigUpdateAttempt (
+ VOID
+ );
+
+/**
+ Get the attempt config data from global structure by the ConfigIndex.
+
+ @param[in] AttemptConfigIndex The unique index indicates the attempt.
+
+ @return Pointer to the attempt config data.
+ @retval NULL The attempt configuration data can not be found.
+
+**/
+ISCSI_ATTEMPT_CONFIG_NVDATA *
+IScsiConfigGetAttemptByConfigIndex (
+ IN UINT8 AttemptConfigIndex
+ );
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h b/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h
new file mode 100644
index 0000000000..211b4ab034
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h
@@ -0,0 +1,194 @@
+/** @file
+ Define NVData structures used by the iSCSI configuration component.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_NVDATASTRUC_H_
+#define _ISCSI_NVDATASTRUC_H_
+
+#define ISCSI_CONFIG_GUID \
+ { \
+ 0x6456ed61, 0x3579, 0x41c9, { 0x8a, 0x26, 0x0a, 0x0b, 0xd6, 0x2b, 0x78, 0xfc } \
+ }
+
+#define VAR_EQ_TEST_NAME 0x100
+#define CONFIGURATION_VARSTORE_ID 0x6666
+
+#define FORMID_MAIN_FORM 1
+#define FORMID_MAC_FORM 2
+#define FORMID_ATTEMPT_FORM 3
+#define FORMID_ORDER_FORM 4
+#define FORMID_DELETE_FORM 5
+
+#define ISCSI_NAME_IFR_MIN_SIZE 4
+#define ISCSI_NAME_IFR_MAX_SIZE 223
+#define ISCSI_NAME_MAX_SIZE 224
+
+#define ATTEMPT_NAME_MAX_SIZE 96
+#define ATTEMPT_NAME_SIZE 10
+
+#define CONNECT_MIN_RETRY 0
+#define CONNECT_MAX_RETRY 16
+
+#define CONNECT_MIN_TIMEOUT 100
+#define CONNECT_MAX_TIMEOUT 20000
+#define CONNECT_DEFAULT_TIMEOUT 1000
+
+#define ISCSI_MAX_ATTEMPTS_NUM 255
+
+#define ISCSI_DISABLED 0
+#define ISCSI_ENABLED 1
+#define ISCSI_ENABLED_FOR_MPIO 2
+
+#define IP_MODE_IP4 0
+#define IP_MODE_IP6 1
+#define IP_MODE_AUTOCONFIG 2
+
+#define ISCSI_AUTH_TYPE_NONE 0
+#define ISCSI_AUTH_TYPE_CHAP 1
+#define ISCSI_AUTH_TYPE_KRB 2
+
+#define IP4_MIN_SIZE 7
+#define IP4_MAX_SIZE 15
+#define IP4_STR_MAX_SIZE 16
+
+//
+// Macros used for an IPv4 or an IPv6 address.
+//
+#define IP_MIN_SIZE 2
+#define IP_MAX_SIZE 39
+#define IP_STR_MAX_SIZE 40
+
+#define LUN_MIN_SIZE 1
+#define LUN_MAX_SIZE 20
+
+#define ISCSI_CHAP_UNI 1
+#define ISCSI_CHAP_MUTUAL 2
+
+#define TARGET_PORT_MIN_NUM 0
+#define TARGET_PORT_MAX_NUM 65535
+#define LABEL_END 0xffff
+
+#define KEY_INITIATOR_NAME 0x101
+#define KEY_DHCP_ENABLE 0x102
+#define KEY_LOCAL_IP 0x103
+#define KEY_SUBNET_MASK 0x104
+#define KEY_GATE_WAY 0x105
+#define KEY_TARGET_IP 0x106
+#define KEY_CHAP_NAME 0x107
+#define KEY_CHAP_SECRET 0x108
+#define KEY_REVERSE_CHAP_NAME 0x109
+#define KEY_REVERSE_CHAP_SECRET 0x10a
+#define KEY_SAVE_CHANGES 0x10b
+#define KEY_TARGET_NAME 0x10c
+#define KEY_BOOT_LUN 0x10d
+
+#define KEY_ADD_ATTEMPT 0x10e
+#define KEY_SAVE_ATTEMPT_CONFIG 0x10f
+#define KEY_ORDER_ATTEMPT_CONFIG 0x110
+#define KEY_SAVE_ORDER_CHANGES 0x111
+#define KEY_IGNORE_ORDER_CHANGES 0x112
+#define KEY_ATTEMPT_NAME 0x113
+#define KEY_SAVE_DELETE_ATTEMPT 0x114
+#define KEY_IGNORE_DELETE_ATTEMPT 0x115
+#define KEY_DELETE_ATTEMPT 0x116
+
+#define KEY_KERBEROS_USER_NAME 0x117
+#define KEY_KERBEROS_USER_SECRET 0x118
+#define KEY_KERBEROS_KDC_NAME 0x119
+#define KEY_KERBEROS_KDC_REALM 0x11a
+#define KEY_KERBEROS_KDC_IP_ADDR 0x11b
+
+#define KEY_IP_MODE 0x11c
+#define KEY_AUTH_TYPE 0x11d
+#define KEY_CONFIG_ISID 0x11e
+
+#define ATTEMPT_ENTRY_LABEL 0x9000
+#define KEY_ATTEMPT_ENTRY_BASE 0xa000
+#define KEY_DE_ATTEMPT_ENTRY_BASE 0xb000
+
+#define KEY_DEVICE_ENTRY_BASE 0x1000
+#define KEY_MAC_ENTRY_BASE 0x2000
+#define MAC_ENTRY_LABEL 0x3000
+#define ORDER_ENTRY_LABEL 0x4000
+#define DELETE_ENTRY_LABEL 0x5000
+#define CONFIG_OPTION_OFFSET 0x9000
+
+#define ISCSI_LUN_STR_MAX_LEN 21
+#define ISCSI_CHAP_SECRET_MIN_LEN 12
+#define ISCSI_CHAP_SECRET_MAX_LEN 16
+//
+// ISCSI_CHAP_SECRET_STORAGE = ISCSI_CHAP_SECRET_MAX_LEN + sizeof (NULL-Terminator)
+//
+#define ISCSI_CHAP_SECRET_STORAGE 17
+#define ISCSI_CHAP_NAME_MAX_LEN 126
+#define ISCSI_CHAP_NAME_STORAGE 127
+
+#define KERBEROS_SECRET_MIN_LEN 12
+#define KERBEROS_SECRET_MAX_LEN 16
+#define KERBEROS_SECRET_STORAGE 17
+#define KERBEROS_NAME_MAX_LEN 96
+#define KERBEROS_KDC_PORT_MIN_NUM 0
+#define KERBEROS_KDC_PORT_MAX_NUM 65535
+
+#define ISID_CONFIGURABLE_MIN_LEN 6
+#define ISID_CONFIGURABLE_MAX_LEN 12
+#define ISID_CONFIGURABLE_STORAGE 13
+
+#pragma pack(1)
+typedef struct _ISCSI_CONFIG_IFR_NVDATA {
+ CHAR16 InitiatorName[ISCSI_NAME_MAX_SIZE];
+ CHAR16 AttemptName[ATTEMPT_NAME_MAX_SIZE];
+
+ UINT8 Enabled;
+ UINT8 IpMode;
+
+ UINT8 ConnectRetryCount;
+ UINT8 Padding1;
+ UINT16 ConnectTimeout; // Timeout value in milliseconds.
+
+ UINT8 InitiatorInfoFromDhcp;
+ UINT8 TargetInfoFromDhcp;
+ CHAR16 LocalIp[IP4_STR_MAX_SIZE];
+ CHAR16 SubnetMask[IP4_STR_MAX_SIZE];
+ CHAR16 Gateway[IP4_STR_MAX_SIZE];
+
+ CHAR16 TargetName[ISCSI_NAME_MAX_SIZE];
+ CHAR16 TargetIp[IP_STR_MAX_SIZE];
+ UINT16 TargetPort;
+ CHAR16 BootLun[ISCSI_LUN_STR_MAX_LEN];
+
+ UINT8 AuthenticationType;
+
+ UINT8 CHAPType;
+ CHAR16 CHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR16 CHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+ CHAR16 ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE];
+ CHAR16 ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE];
+
+ BOOLEAN MutualRequired;
+ UINT8 Padding2;
+ CHAR16 KerberosUserName[KERBEROS_NAME_MAX_LEN];
+ CHAR16 KerberosUserSecret[KERBEROS_SECRET_STORAGE];
+ CHAR16 KerberosKDCName[KERBEROS_NAME_MAX_LEN];
+ CHAR16 KerberosKDCRealm[KERBEROS_NAME_MAX_LEN];
+ CHAR16 KerberosKDCIp[IP_STR_MAX_SIZE];
+ UINT16 KerberosKDCPort;
+
+ UINT8 DynamicOrderedList[ISCSI_MAX_ATTEMPTS_NUM];
+ UINT8 DeleteAttemptList[ISCSI_MAX_ATTEMPTS_NUM];
+
+ CHAR16 IsId[ISID_CONFIGURABLE_STORAGE];
+} ISCSI_CONFIG_IFR_NVDATA;
+#pragma pack()
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni b/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni
new file mode 100644
index 0000000000..8df9cef13e
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni
Binary files differ
diff --git a/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr b/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr
new file mode 100644
index 0000000000..94b00ee60d
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr
@@ -0,0 +1,429 @@
+/** @file
+ VFR file used by the iSCSI configuration component.
+
+Copyright (c) 2004 - 2011, 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 "IScsiConfigNVDataStruc.h"
+
+#define EFI_NETWORK_DEVICE_CLASS 0x04
+
+formset
+ guid = ISCSI_CONFIG_GUID,
+ title = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_TITLE),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_HELP),
+ class = EFI_NETWORK_DEVICE_CLASS,
+ subclass = 0x03,
+
+ varstore ISCSI_CONFIG_IFR_NVDATA,
+ varid = CONFIGURATION_VARSTORE_ID,
+ name = ISCSI_CONFIG_IFR_NVDATA,
+ guid = ISCSI_CONFIG_GUID;
+
+ form formid = FORMID_MAIN_FORM,
+ title = STRING_TOKEN(STR_ISCSI_MAIN_FORM_TITLE);
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorName,
+ prompt = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME_HELP),
+ flags = INTERACTIVE,
+ key = KEY_INITIATOR_NAME,
+ minsize = ISCSI_NAME_IFR_MIN_SIZE,
+ maxsize = ISCSI_NAME_IFR_MAX_SIZE,
+ endstring;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto FORMID_MAC_FORM,
+ prompt = STRING_TOKEN(STR_ADD_ATTEMPT_ENTRY),
+ help = STRING_TOKEN(STR_ADD_ATTEMPT_ENTRY),
+ flags = INTERACTIVE,
+ key = KEY_ADD_ATTEMPT;
+
+ label ATTEMPT_ENTRY_LABEL;
+ label LABEL_END;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto FORMID_DELETE_FORM,
+ prompt = STRING_TOKEN (STR_DEL_ATTEMPT_ENTRY),
+ help = STRING_TOKEN (STR_DEL_ATTEMPT_ENTRY_HELP),
+ flags = INTERACTIVE,
+ key = KEY_DELETE_ATTEMPT;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto FORMID_ORDER_FORM,
+ prompt = STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),
+ help = STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),
+ flags = INTERACTIVE,
+ key = KEY_ORDER_ATTEMPT_CONFIG;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ endform;
+
+ form formid = FORMID_MAC_FORM,
+ title = STRING_TOKEN(STR_ISCSI_MAC_FORM_TITLE);
+
+ label MAC_ENTRY_LABEL;
+ label LABEL_END;
+
+ endform;
+
+ form formid = FORMID_ORDER_FORM,
+ title = STRING_TOKEN(STR_ORDER_ATTEMPT_ENTRY);
+
+ label ORDER_ENTRY_LABEL;
+ label LABEL_END;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_ORDER_CHANGES;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_IGNORE_ORDER_CHANGES;
+
+ endform;
+
+ form formid = FORMID_DELETE_FORM,
+ title = STRING_TOKEN(STR_DEL_ATTEMPT_ENTRY);
+
+ label DELETE_ENTRY_LABEL;
+ label LABEL_END;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_DELETE_ATTEMPT;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ help = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),
+ flags = INTERACTIVE,
+ key = KEY_IGNORE_DELETE_ATTEMPT;
+
+ endform;
+
+ form formid = FORMID_ATTEMPT_FORM,
+ title = STRING_TOKEN(STR_ISCSI_ATTEMPT_FORM_TITLE);
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.AttemptName,
+ prompt = STRING_TOKEN(STR_ISCSI_ATTEMPT_NAME),
+ help = STRING_TOKEN(STR_ISCSI_ATTEMPT_NAME_HELP),
+ flags = INTERACTIVE,
+ key = KEY_ATTEMPT_NAME,
+ minsize = 0,
+ maxsize = ATTEMPT_NAME_MAX_SIZE,
+ endstring;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ oneof varid = ISCSI_CONFIG_IFR_NVDATA.Enabled,
+ prompt = STRING_TOKEN(STR_ISCSI_MODE_PROMPT),
+ help = STRING_TOKEN(STR_ISCSI_MODE_HELP),
+ option text = STRING_TOKEN(STR_ISCSI_MODE_DISABLED), value = ISCSI_DISABLED, flags = DEFAULT;
+ option text = STRING_TOKEN(STR_ISCSI_MODE_ENABLED), value = ISCSI_ENABLED, flags = 0;
+ option text = STRING_TOKEN(STR_ISCSI_MODE_ENABLED_FOR_MPIO), value = ISCSI_ENABLED_FOR_MPIO, flags = 0;
+ endoneof;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ oneof varid = ISCSI_CONFIG_IFR_NVDATA.IpMode,
+ questionid = KEY_IP_MODE,
+ prompt = STRING_TOKEN(STR_IP_MODE_PROMPT),
+ help = STRING_TOKEN(STR_IP_MODE_HELP),
+ option text = STRING_TOKEN(STR_IP_MODE_IP4), value = IP_MODE_IP4, flags = INTERACTIVE;
+ option text = STRING_TOKEN(STR_IP_MODE_IP6), value = IP_MODE_IP6, flags = INTERACTIVE;
+ option text = STRING_TOKEN(STR_IP_MODE_AUTOCONFIG), value = IP_MODE_AUTOCONFIG, flags = INTERACTIVE;
+ endoneof;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ numeric varid = ISCSI_CONFIG_IFR_NVDATA.ConnectRetryCount,
+ prompt = STRING_TOKEN(STR_ISCSI_CONFIG_RETRY),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_RETRY_HELP),
+ flags = 0,
+ minimum = CONNECT_MIN_RETRY,
+ maximum = CONNECT_MAX_RETRY,
+ step = 0,
+ endnumeric;
+
+ numeric varid = ISCSI_CONFIG_IFR_NVDATA.ConnectTimeout,
+ prompt = STRING_TOKEN(STR_ISCSI_CONFIG_TIMEOUT),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_TIMEOUT_HELP),
+ flags = 0,
+ minimum = CONNECT_MIN_TIMEOUT,
+ maximum = CONNECT_MAX_TIMEOUT,
+ step = 0,
+ default = CONNECT_DEFAULT_TIMEOUT,
+ endnumeric;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.IsId,
+ prompt = STRING_TOKEN(STR_ISCSI_CONFIG_ISID),
+ help = STRING_TOKEN(STR_ISCSI_CONFIG_ISID_HELP),
+ flags = INTERACTIVE,
+ key = KEY_CONFIG_ISID,
+ minsize = ISID_CONFIGURABLE_MIN_LEN,
+ maxsize = ISID_CONFIGURABLE_MAX_LEN,
+ endstring;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;
+ checkbox varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp,
+ prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),
+ help = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),
+ flags = INTERACTIVE,
+ key = KEY_DHCP_ENABLE,
+ endcheckbox;
+ endif;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x01 OR
+ ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_IP6 OR
+ ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;
+ string varid = ISCSI_CONFIG_IFR_NVDATA.LocalIp,
+ prompt = STRING_TOKEN(STR_ISCSI_LOCAL_IP_ADDRESS),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_LOCAL_IP,
+ minsize = IP4_MIN_SIZE,
+ maxsize = IP4_MAX_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.SubnetMask,
+ prompt = STRING_TOKEN(STR_ISCSI_LOCAL_MASK),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_SUBNET_MASK,
+ minsize = IP4_MIN_SIZE,
+ maxsize = IP4_MAX_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.Gateway,
+ prompt = STRING_TOKEN(STR_ISCSI_LOCAL_GATEWAY),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_GATE_WAY,
+ minsize = IP4_MIN_SIZE,
+ maxsize = IP4_MAX_SIZE,
+ endstring;
+
+ endif;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;
+ subtitle text = STRING_TOKEN(STR_NULL);
+ endif;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG OR
+ ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x00;
+ checkbox varid = ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp,
+ prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),
+ help = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),
+ flags = 0,
+ endcheckbox;
+ endif;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG OR
+ ideqval ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp == 0x01;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.TargetName,
+ prompt = STRING_TOKEN(STR_ISCSI_TARGET_NAME),
+ help = STRING_TOKEN(STR_ISCSI_TARGET_NAME_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TARGET_NAME,
+ minsize = ISCSI_NAME_IFR_MIN_SIZE,
+ maxsize = ISCSI_NAME_IFR_MAX_SIZE,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.TargetIp,
+ prompt = STRING_TOKEN(STR_ISCSI_TARGET_IP_ADDRESS),
+ help = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),
+ flags = INTERACTIVE,
+ key = KEY_TARGET_IP,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ numeric varid = ISCSI_CONFIG_IFR_NVDATA.TargetPort,
+ prompt = STRING_TOKEN(STR_ISCSI_TARGET_PORT),
+ help = STRING_TOKEN(STR_ISCSI_TARGET_PORT),
+ flags = 0,
+ minimum = TARGET_PORT_MIN_NUM,
+ maximum = TARGET_PORT_MAX_NUM,
+ step = 0,
+ endnumeric;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.BootLun,
+ prompt = STRING_TOKEN(STR_ISCSI_BOOT_LUN),
+ help = STRING_TOKEN(STR_ISCSI_BOOT_LUN_HELP),
+ flags = INTERACTIVE,
+ key = KEY_BOOT_LUN,
+ minsize = LUN_MIN_SIZE,
+ maxsize = LUN_MAX_SIZE,
+ endstring;
+
+ endif;
+
+ suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;
+ subtitle text = STRING_TOKEN(STR_NULL);
+ endif;
+
+ oneof varid = ISCSI_CONFIG_IFR_NVDATA.AuthenticationType,
+ questionid = KEY_AUTH_TYPE,
+ prompt = STRING_TOKEN(STR_AUTHEN_TYPE_PROMPT),
+ help = STRING_TOKEN(STR_AUTHEN_TYPE_HELP),
+ option text = STRING_TOKEN(STR_AUTHEN_TYPE_CHAP), value = ISCSI_AUTH_TYPE_CHAP, flags = 0;
+ option text = STRING_TOKEN(STR_AUTHEN_TYPE_NONE), value = ISCSI_AUTH_TYPE_NONE, flags = DEFAULT;
+ endoneof;
+
+ suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP;
+ oneof varid = ISCSI_CONFIG_IFR_NVDATA.CHAPType,
+ prompt = STRING_TOKEN(STR_CHAP_TYPE_PROMPT),
+ help = STRING_TOKEN(STR_CHAP_TYPE_HELP),
+ option text = STRING_TOKEN(STR_CHAP_TYPE_UNI), value = ISCSI_CHAP_UNI, flags = 0;
+ option text = STRING_TOKEN(STR_CHAP_TYPE_MUTUAL), value = ISCSI_CHAP_MUTUAL, flags = DEFAULT;
+ endoneof;
+ endif;
+
+ suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP;
+ string varid = ISCSI_CONFIG_IFR_NVDATA.CHAPName,
+ prompt = STRING_TOKEN(STR_ISCSI_CHAP_NAME),
+ help = STRING_TOKEN(STR_ISCSI_CHAP_NAME),
+ flags = INTERACTIVE,
+ key = KEY_CHAP_NAME,
+ minsize = 0,
+ maxsize = ISCSI_CHAP_NAME_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.CHAPSecret,
+ prompt = STRING_TOKEN(STR_ISCSI_CHAP_SECRET),
+ help = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),
+ flags = INTERACTIVE,
+ key = KEY_CHAP_SECRET,
+ minsize = ISCSI_CHAP_SECRET_MIN_LEN,
+ maxsize = ISCSI_CHAP_SECRET_MAX_LEN,
+ endstring;
+
+ endif;
+
+ suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP OR
+ NOT ideqval ISCSI_CONFIG_IFR_NVDATA.CHAPType == ISCSI_CHAP_MUTUAL;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPName,
+ prompt = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),
+ help = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),
+ flags = INTERACTIVE,
+ key = KEY_REVERSE_CHAP_NAME,
+ minsize = 0,
+ maxsize = ISCSI_CHAP_NAME_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPSecret,
+ prompt = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_SECRET),
+ help = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),
+ flags = INTERACTIVE,
+ key = KEY_REVERSE_CHAP_SECRET,
+ minsize = ISCSI_CHAP_SECRET_MIN_LEN,
+ maxsize = ISCSI_CHAP_SECRET_MAX_LEN,
+ endstring;
+
+ endif;
+
+ suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_KRB;
+
+ checkbox varid = ISCSI_CONFIG_IFR_NVDATA.MutualRequired,
+ prompt = STRING_TOKEN(STR_ISCSI_MUTUAL_REQUIRED),
+ help = STRING_TOKEN(STR_ISCSI_MUTUAL_REQUIRED_HELP),
+ flags = 0,
+ endcheckbox;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.KerberosUserName,
+ prompt = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_NAME),
+ help = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_NAME),
+ flags = INTERACTIVE,
+ key = KEY_KERBEROS_USER_NAME,
+ minsize = 0,
+ maxsize = KERBEROS_NAME_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.KerberosUserSecret,
+ prompt = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_SECRET),
+ help = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_SECRET),
+ flags = INTERACTIVE,
+ key = KEY_KERBEROS_USER_SECRET,
+ minsize = KERBEROS_SECRET_MIN_LEN,
+ maxsize = KERBEROS_SECRET_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCName,
+ prompt = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_NAME),
+ help = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_NAME),
+ flags = INTERACTIVE,
+ key = KEY_KERBEROS_KDC_NAME,
+ minsize = 0,
+ maxsize = KERBEROS_NAME_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCRealm,
+ prompt = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_REALM),
+ help = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_REALM),
+ flags = INTERACTIVE,
+ key = KEY_KERBEROS_KDC_REALM,
+ minsize = 0,
+ maxsize = KERBEROS_NAME_MAX_LEN,
+ endstring;
+
+ string varid = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCIp,
+ prompt = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_IP),
+ help = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_IP),
+ flags = INTERACTIVE,
+ key = KEY_KERBEROS_KDC_IP_ADDR,
+ minsize = IP_MIN_SIZE,
+ maxsize = IP_MAX_SIZE,
+ endstring;
+
+ numeric varid = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCPort,
+ prompt = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_PORT),
+ help = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_PORT),
+ flags = 0,
+ minimum = KERBEROS_KDC_PORT_MIN_NUM,
+ maximum = KERBEROS_KDC_PORT_MAX_NUM,
+ step = 0,
+ endnumeric;
+
+ endif;
+
+ subtitle text = STRING_TOKEN(STR_NULL);
+
+ goto FORMID_ATTEMPT_FORM,
+ prompt = STRING_TOKEN (STR_SAVE_CHANGES),
+ help = STRING_TOKEN (STR_SAVE_CHANGES),
+ flags = INTERACTIVE,
+ key = KEY_SAVE_ATTEMPT_CONFIG;
+
+ goto FORMID_MAIN_FORM,
+ prompt = STRING_TOKEN (STR_RETURN_MAIN_FORM),
+ help = STRING_TOKEN (STR_RETURN_MAIN_FORM),
+ flags = 0;
+
+ endform;
+
+endformset;
+
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp.c b/NetworkPkg/IScsiDxe/IScsiDhcp.c
new file mode 100644
index 0000000000..3706256db5
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiDhcp.c
@@ -0,0 +1,498 @@
+/** @file
+ iSCSI DHCP4 related configuration routines.
+
+Copyright (c) 2004 - 2011, 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"
+
+
+/**
+ Extract the Root Path option and get the required target information.
+
+ @param[in] RootPath The RootPath.
+ @param[in] Length Length of the RootPath option payload.
+ @param[in, out] ConfigData The iSCSI attempt configuration data read
+ from a nonvolatile device.
+
+ @retval EFI_SUCCESS All required information is extracted from the RootPath option.
+ @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
+
+**/
+EFI_STATUS
+IScsiDhcpExtractRootPath (
+ IN CHAR8 *RootPath,
+ IN UINT8 Length,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ )
+{
+ EFI_STATUS Status;
+ UINT8 IScsiRootPathIdLen;
+ CHAR8 *TmpStr;
+ ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
+ ISCSI_ROOT_PATH_FIELD *Field;
+ UINT32 FieldIndex;
+ UINT8 Index;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+ EFI_IP_ADDRESS Ip;
+ UINT8 IpMode;
+
+ ConfigNvData = &ConfigData->SessionConfigData;
+
+ //
+ // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
+ //
+ IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID);
+
+ if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
+ return EFI_NOT_FOUND;
+ }
+ //
+ // Skip the iSCSI RootPath ID "iscsi:".
+ //
+ RootPath += IScsiRootPathIdLen;
+ Length = (UINT8) (Length - IScsiRootPathIdLen);
+
+ TmpStr = (CHAR8 *) AllocatePool (Length + 1);
+ if (TmpStr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (TmpStr, RootPath, Length);
+ TmpStr[Length] = '\0';
+
+ Index = 0;
+ FieldIndex = RP_FIELD_IDX_SERVERNAME;
+ ZeroMem (&Fields[0], sizeof (Fields));
+
+ //
+ // Extract the fields in the Root Path option string.
+ //
+ for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
+ if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
+ Fields[FieldIndex].Str = &TmpStr[Index];
+ }
+
+ while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
+ Index++;
+ }
+
+ if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
+ if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
+ TmpStr[Index] = '\0';
+ Index++;
+ }
+
+ if (Fields[FieldIndex].Str != NULL) {
+ Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);
+ }
+ }
+ }
+
+ if (FieldIndex != RP_FIELD_IDX_MAX) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
+ (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
+ (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
+ ) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Get the IP address of the target.
+ //
+ Field = &Fields[RP_FIELD_IDX_SERVERNAME];
+
+ if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
+ IpMode = ConfigNvData->IpMode;
+ } else {
+ IpMode = ConfigData->AutoConfigureMode;
+ }
+
+ Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
+ CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Check the protocol type.
+ //
+ Field = &Fields[RP_FIELD_IDX_PROTOCOL];
+ if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Get the port of the iSCSI target.
+ //
+ Field = &Fields[RP_FIELD_IDX_PORT];
+ if (Field->Str != NULL) {
+ ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);
+ } else {
+ ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+ //
+ // Get the LUN.
+ //
+ Field = &Fields[RP_FIELD_IDX_LUN];
+ if (Field->Str != NULL) {
+ Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else {
+ ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
+ }
+ //
+ // Get the target iSCSI Name.
+ //
+ Field = &Fields[RP_FIELD_IDX_TARGETNAME];
+
+ if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Validate the iSCSI name.
+ //
+ Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ AsciiStrCpy (ConfigNvData->TargetName, Field->Str);
+
+ON_EXIT:
+
+ FreePool (TmpStr);
+
+ return Status;
+}
+
+/**
+ The callback function registerd to the DHCP4 instance that is used to select
+ the qualified DHCP OFFER.
+
+ @param[in] This The DHCP4 protocol.
+ @param[in] Context The context set when configuring the DHCP4 protocol.
+ @param[in] CurrentState The current state of the DHCP4 protocol.
+ @param[in] Dhcp4Event The event occurs in the current state.
+ @param[in] Packet The DHCP packet that is to be sent or was already received.
+ @param[out] NewPacket The packet used to replace the above Packet.
+
+ @retval EFI_SUCCESS Either the DHCP OFFER is qualified or we're not intereseted
+ in the Dhcp4Event.
+ @retval EFI_NOT_READY The DHCP OFFER packet doesn't match our requirements.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDhcpSelectOffer (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP4_STATE CurrentState,
+ IN EFI_DHCP4_EVENT Dhcp4Event,
+ IN EFI_DHCP4_PACKET *Packet, OPTIONAL
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ UINT32 OptionCount;
+ EFI_DHCP4_PACKET_OPTION **OptionList;
+ UINT32 Index;
+
+ if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
+ return EFI_SUCCESS;
+ }
+
+ OptionCount = 0;
+
+ Status = This->Parse (This, Packet, &OptionCount, NULL);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_NOT_READY;
+ }
+
+ OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+ if (OptionList == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ Status = This->Parse (This, Packet, &OptionCount, OptionList);
+ if (EFI_ERROR (Status)) {
+ FreePool (OptionList);
+ return EFI_NOT_READY;
+ }
+
+ for (Index = 0; Index < OptionCount; Index++) {
+ if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) {
+ continue;
+ }
+
+ Status = IScsiDhcpExtractRootPath (
+ (CHAR8 *) &OptionList[Index]->Data[0],
+ OptionList[Index]->Length,
+ (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context
+ );
+
+ break;
+ }
+
+ if ((Index == OptionCount)) {
+ Status = EFI_NOT_READY;
+ }
+
+ FreePool (OptionList);
+
+ return Status;
+}
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Dhcp4 The DHCP4 protocol.
+ @param[in, out] ConfigData The session configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_NO_MAPPING DHCP failed to acquire address and other information.
+ @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
+ @retval EFI_DEVICE_ERROR Other errors as indicated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+IScsiParseDhcpAck (
+ IN EFI_DHCP4_PROTOCOL *Dhcp4,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ )
+{
+ EFI_STATUS Status;
+ EFI_DHCP4_MODE_DATA Dhcp4ModeData;
+ UINT32 OptionCount;
+ EFI_DHCP4_PACKET_OPTION **OptionList;
+ UINT32 Index;
+ ISCSI_SESSION_CONFIG_NVDATA *NvData;
+
+ Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Dhcp4ModeData.State != Dhcp4Bound) {
+ return EFI_NO_MAPPING;
+ }
+
+ NvData = &ConfigData->SessionConfigData;
+
+ CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ OptionCount = 0;
+ OptionList = NULL;
+
+ Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
+ if (OptionList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);
+ if (EFI_ERROR (Status)) {
+ FreePool (OptionList);
+ return EFI_DEVICE_ERROR;
+ }
+
+ for (Index = 0; Index < OptionCount; Index++) {
+ //
+ // Get DNS server addresses and DHCP server address from this offer.
+ //
+ if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) {
+
+ if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ //
+ // Primary DNS server address.
+ //
+ CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
+
+ if (OptionList[Index]->Length > 4) {
+ //
+ // Secondary DNS server address.
+ //
+ CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));
+ }
+ } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {
+ if (OptionList[Index]->Length != 4) {
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+
+ CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));
+ }
+ }
+
+ FreePool (OptionList);
+
+ return Status;
+}
+
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in, out] ConfigData The attempt configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiDoDhcp (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ )
+{
+ EFI_HANDLE Dhcp4Handle;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_STATUS Status;
+ EFI_DHCP4_PACKET_OPTION *ParaList;
+ EFI_DHCP4_CONFIG_DATA Dhcp4ConfigData;
+ ISCSI_SESSION_CONFIG_NVDATA *NvData;
+ BOOLEAN MediaPresent;
+
+ Dhcp4Handle = NULL;
+ Dhcp4 = NULL;
+ ParaList = NULL;
+
+ //
+ // Check media status before doing DHCP.
+ //
+ MediaPresent = TRUE;
+ NetLibDetectMedia (Controller, &MediaPresent);
+ if (!MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // Create a DHCP4 child instance and get the protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &Dhcp4Handle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dhcp4Handle,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Dhcp4,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ NvData = &ConfigData->SessionConfigData;
+
+ ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);
+ if (ParaList == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Ask the server to reply with Netmask, Router, DNS, and RootPath options.
+ //
+ ParaList->OpCode = DHCP4_TAG_PARA_LIST;
+ ParaList->Length = (UINT8) (NvData->TargetInfoFromDhcp ? 4 : 3);
+ ParaList->Data[0] = DHCP4_TAG_NETMASK;
+ ParaList->Data[1] = DHCP4_TAG_ROUTER;
+ ParaList->Data[2] = DHCP4_TAG_DNS;
+ ParaList->Data[3] = DHCP4_TAG_ROOT_PATH;
+
+ ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));
+ Dhcp4ConfigData.OptionCount = 1;
+ Dhcp4ConfigData.OptionList = &ParaList;
+
+ if (NvData->TargetInfoFromDhcp) {
+ //
+ // Use callback to select an offer that contains target information.
+ //
+ Dhcp4ConfigData.Dhcp4Callback = IScsiDhcpSelectOffer;
+ Dhcp4ConfigData.CallbackContext = ConfigData;
+ }
+
+ Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Dhcp4->Start (Dhcp4, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Parse the ACK to get required information.
+ //
+ Status = IScsiParseDhcpAck (Dhcp4, ConfigData);
+
+ON_EXIT:
+
+ if (ParaList != NULL) {
+ FreePool (ParaList);
+ }
+
+ if (Dhcp4 != NULL) {
+ Dhcp4->Stop (Dhcp4);
+ Dhcp4->Configure (Dhcp4, NULL);
+
+ gBS->CloseProtocol (
+ Dhcp4Handle,
+ &gEfiDhcp4ProtocolGuid,
+ Image,
+ Controller
+ );
+ }
+
+ NetLibDestroyServiceChild (
+ Controller,
+ Image,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Dhcp4Handle
+ );
+
+ return Status;
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp.h b/NetworkPkg/IScsiDxe/IScsiDhcp.h
new file mode 100644
index 0000000000..3165100099
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiDhcp.h
@@ -0,0 +1,62 @@
+/** @file
+ The head file of iSCSI DHCP4 related configuration routines.
+
+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.
+
+**/
+
+#ifndef _ISCSI_DHCP_H_
+#define _ISCSI_DHCP_H_
+
+
+#define DHCP4_TAG_PARA_LIST 55
+#define DHCP4_TAG_NETMASK 1
+#define DHCP4_TAG_ROUTER 3
+#define DHCP4_TAG_DNS 6
+#define DHCP4_TAG_SERVER_ID 54
+#define DHCP4_TAG_ROOT_PATH 17
+#define ISCSI_ROOT_PATH_ID "iscsi:"
+#define ISCSI_ROOT_PATH_FIELD_DELIMITER ':'
+
+#define RP_FIELD_IDX_SERVERNAME 0
+#define RP_FIELD_IDX_PROTOCOL 1
+#define RP_FIELD_IDX_PORT 2
+#define RP_FIELD_IDX_LUN 3
+#define RP_FIELD_IDX_TARGETNAME 4
+#define RP_FIELD_IDX_MAX 5
+
+typedef struct _ISCSI_ATTEMPT_CONFIG_NVDATA ISCSI_ATTEMPT_CONFIG_NVDATA;
+
+typedef struct _ISCSI_ROOT_PATH_FIELD {
+ CHAR8 *Str;
+ UINT8 Len;
+} ISCSI_ROOT_PATH_FIELD;
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+ @param[in, out] ConfigData The attempt configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiDoDhcp (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ );
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp6.c b/NetworkPkg/IScsiDxe/IScsiDhcp6.c
new file mode 100644
index 0000000000..2bf102ba8b
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiDhcp6.c
@@ -0,0 +1,505 @@
+/** @file
+ iSCSI DHCP6 related configuration routines.
+
+Copyright (c) 2009 - 2011, 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"
+
+
+/**
+ Extract the Root Path option and get the required target information from
+ Boot File Uniform Resource Locator (URL) Option.
+
+ @param[in] RootPath The RootPath string.
+ @param[in] Length Length of the RootPath option payload.
+ @param[in, out] ConfigData The iSCSI session configuration data read from
+ nonvolatile device.
+
+ @retval EFI_SUCCESS All required information is extracted from the
+ RootPath option.
+ @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
+
+**/
+EFI_STATUS
+IScsiDhcp6ExtractRootPath (
+ IN CHAR8 *RootPath,
+ IN UINT16 Length,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ )
+{
+ EFI_STATUS Status;
+ UINT16 IScsiRootPathIdLen;
+ CHAR8 *TmpStr;
+ ISCSI_ROOT_PATH_FIELD Fields[RP_FIELD_IDX_MAX];
+ ISCSI_ROOT_PATH_FIELD *Field;
+ UINT32 FieldIndex;
+ UINT8 Index;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+ EFI_IP_ADDRESS Ip;
+ UINT8 IpMode;
+
+ ConfigNvData = &ConfigData->SessionConfigData;
+
+ //
+ // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>
+ //
+ IScsiRootPathIdLen = (UINT16) AsciiStrLen (ISCSI_ROOT_PATH_ID);
+
+ if ((Length <= IScsiRootPathIdLen) ||
+ (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {
+ return EFI_NOT_FOUND;
+ }
+ //
+ // Skip the iSCSI RootPath ID "iscsi:".
+ //
+ RootPath = RootPath + IScsiRootPathIdLen;
+ Length = (UINT16) (Length - IScsiRootPathIdLen);
+
+ TmpStr = (CHAR8 *) AllocatePool (Length + 1);
+ if (TmpStr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (TmpStr, RootPath, Length);
+ TmpStr[Length] = '\0';
+
+ Index = 0;
+ FieldIndex = 0;
+ ZeroMem (&Fields[0], sizeof (Fields));
+
+ //
+ // Extract SERVERNAME field in the Root Path option.
+ //
+ if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ } else {
+ Index++;
+ }
+
+ Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index];
+
+ while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER) && (Index < Length)) {
+ Index++;
+ }
+
+ //
+ // Skip ']' and ':'.
+ //
+ TmpStr[Index] = '\0';
+ Index += 2;
+
+ Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8) AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str);
+
+ //
+ // Extract others fields in the Root Path option string.
+ //
+ for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {
+
+ if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {
+ Fields[FieldIndex].Str = &TmpStr[Index];
+ }
+
+ while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {
+ Index++;
+ }
+
+ if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {
+ if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {
+ TmpStr[Index] = '\0';
+ Index++;
+ }
+
+ if (Fields[FieldIndex].Str != NULL) {
+ Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);
+ }
+ }
+ }
+
+ if (FieldIndex != RP_FIELD_IDX_MAX) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+
+ if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||
+ (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||
+ (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)
+ ) {
+
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Get the IP address of the target.
+ //
+ Field = &Fields[RP_FIELD_IDX_SERVERNAME];
+ if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {
+ IpMode = ConfigNvData->IpMode;
+ } else {
+ IpMode = ConfigData->AutoConfigureMode;
+ }
+
+ Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);
+ CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));
+
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Check the protocol type.
+ //
+ Field = &Fields[RP_FIELD_IDX_PROTOCOL];
+ if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Get the port of the iSCSI target.
+ //
+ Field = &Fields[RP_FIELD_IDX_PORT];
+ if (Field->Str != NULL) {
+ ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);
+ } else {
+ ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+ //
+ // Get the LUN.
+ //
+ Field = &Fields[RP_FIELD_IDX_LUN];
+ if (Field->Str != NULL) {
+ Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ } else {
+ ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));
+ }
+ //
+ // Get the target iSCSI Name.
+ //
+ Field = &Fields[RP_FIELD_IDX_TARGETNAME];
+
+ if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {
+ Status = EFI_INVALID_PARAMETER;
+ goto ON_EXIT;
+ }
+ //
+ // Validate the iSCSI name.
+ //
+ Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ AsciiStrCpy (ConfigNvData->TargetName, Field->Str);
+
+ON_EXIT:
+
+ FreePool (TmpStr);
+
+ return Status;
+}
+
+/**
+ EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
+ instance to intercept events that occurs in the DHCPv6 Information Request
+ exchange process.
+
+ @param[in] This Pointer to the EFI_DHCP6_PROTOCOL instance that
+ is used to configure this callback function.
+ @param[in] Context Pointer to the context that is initialized in
+ the EFI_DHCP6_PROTOCOL.InfoRequest().
+ @param[in] Packet Pointer to Reply packet that has been received.
+ The EFI DHCPv6 Protocol instance is responsible
+ for freeing the buffer.
+
+ @retval EFI_SUCCESS Tell the EFI DHCPv6 Protocol instance to finish
+ Information Request exchange process.
+ @retval EFI_NOT_READY Tell the EFI DHCPv6 Protocol instance to continue
+ Information Request exchange process.
+ @retval EFI_ABORTED Tell the EFI DHCPv6 Protocol instance to abort
+ the Information Request exchange process.
+ @retval EFI_UNSUPPORTED Tell the EFI DHCPv6 Protocol instance to finish
+ the Information Request exchange process because some
+ request information are not received.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDhcp6ParseReply (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP6_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Index;
+ UINT32 OptionCount;
+ EFI_DHCP6_PACKET_OPTION *BootFileOpt;
+ EFI_DHCP6_PACKET_OPTION **OptionList;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;
+
+ OptionCount = 0;
+ BootFileOpt = NULL;
+
+ Status = This->Parse (This, Packet, &OptionCount, NULL);
+ if (Status != EFI_BUFFER_TOO_SMALL) {
+ return EFI_NOT_READY;
+ }
+
+ OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));
+ if (OptionList == NULL) {
+ return EFI_NOT_READY;
+ }
+
+ Status = This->Parse (This, Packet, &OptionCount, OptionList);
+ if (EFI_ERROR (Status)) {
+ Status = EFI_NOT_READY;
+ goto Exit;
+ }
+
+ ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context;
+
+ for (Index = 0; Index < OptionCount; Index++) {
+ OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);
+ OptionList[Index]->OpLen = NTOHS (OptionList[Index]->OpLen);
+
+ //
+ // Get DNS server addresses from this reply packet.
+ //
+ if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {
+
+ if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {
+ Status = EFI_INVALID_PARAMETER;
+ goto Exit;
+ }
+ //
+ // Primary DNS server address.
+ //
+ CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));
+
+ if (OptionList[Index]->OpLen > 16) {
+ //
+ // Secondary DNS server address
+ //
+ CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) {
+ //
+ // The server sends this option to inform the client about an URL to a boot file.
+ //
+ BootFileOpt = OptionList[Index];
+ }
+ }
+
+ if (BootFileOpt == NULL) {
+ Status = EFI_UNSUPPORTED;
+ goto Exit;
+ }
+
+ //
+ // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option
+ //
+ Status = IScsiDhcp6ExtractRootPath (
+ (CHAR8 *) BootFileOpt->Data,
+ BootFileOpt->OpLen,
+ ConfigData
+ );
+
+Exit:
+
+ FreePool (OptionList);
+ return Status;
+}
+
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller;
+ @param[in, out] ConfigData The attempt configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_NO_MAPPING DHCP failed to acquire address and other
+ information.
+ @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
+ @retval EFI_DEVICE_ERROR Some unexpected error occurred.
+ @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
+ operation.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+IScsiDoDhcp6 (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ )
+{
+ EFI_HANDLE Dhcp6Handle;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ EFI_STATUS Status;
+ EFI_STATUS TimerStatus;
+ EFI_DHCP6_PACKET_OPTION *Oro;
+ EFI_DHCP6_RETRANSMISSION InfoReqReXmit;
+ EFI_EVENT Timer;
+ BOOLEAN MediaPresent;
+
+ //
+ // Check media status before doing DHCP.
+ //
+ MediaPresent = TRUE;
+ NetLibDetectMedia (Controller, &MediaPresent);
+ if (!MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // iSCSI will only request target info from DHCPv6 server.
+ //
+ if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) {
+ return EFI_SUCCESS;
+ }
+
+ Dhcp6Handle = NULL;
+ Dhcp6 = NULL;
+ Oro = NULL;
+ Timer = NULL;
+
+ //
+ // Create a DHCP6 child instance and get the protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Controller,
+ Image,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ &Dhcp6Handle
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dhcp6Handle,
+ &gEfiDhcp6ProtocolGuid,
+ (VOID **) &Dhcp6,
+ Image,
+ Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 3);
+ if (Oro == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Ask the server to reply with DNS and Boot File URL options by info request.
+ // All members in EFI_DHCP6_PACKET_OPTION are in network order.
+ //
+ Oro->OpCode = HTONS (DHCP6_OPT_REQUEST_OPTION);
+ Oro->OpLen = HTONS (2 * 2);
+ Oro->Data[1] = DHCP6_OPT_DNS_SERVERS;
+ Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL;
+
+ InfoReqReXmit.Irt = 4;
+ InfoReqReXmit.Mrc = 1;
+ InfoReqReXmit.Mrt = 10;
+ InfoReqReXmit.Mrd = 30;
+
+ Status = Dhcp6->InfoRequest (
+ Dhcp6,
+ TRUE,
+ Oro,
+ 0,
+ NULL,
+ &InfoReqReXmit,
+ NULL,
+ IScsiDhcp6ParseReply,
+ ConfigData
+ );
+ if (Status == EFI_NO_MAPPING) {
+ Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = gBS->SetTimer (
+ Timer,
+ TimerRelative,
+ ISCSI_GET_MAPPING_TIMEOUT
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ do {
+
+ TimerStatus = gBS->CheckEvent (Timer);
+
+ if (!EFI_ERROR (TimerStatus)) {
+ Status = Dhcp6->InfoRequest (
+ Dhcp6,
+ TRUE,
+ Oro,
+ 0,
+ NULL,
+ &InfoReqReXmit,
+ NULL,
+ IScsiDhcp6ParseReply,
+ ConfigData
+ );
+ }
+
+ } while (TimerStatus == EFI_NOT_READY);
+
+ }
+
+ON_EXIT:
+
+ if (Oro != NULL) {
+ FreePool (Oro);
+ }
+
+ if (Timer != NULL) {
+ gBS->CloseEvent (Timer);
+ }
+
+ if (Dhcp6 != NULL) {
+ gBS->CloseProtocol (
+ Dhcp6Handle,
+ &gEfiDhcp6ProtocolGuid,
+ Image,
+ Controller
+ );
+ }
+
+ NetLibDestroyServiceChild (
+ Controller,
+ Image,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ Dhcp6Handle
+ );
+
+ return Status;
+}
+
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp6.h b/NetworkPkg/IScsiDxe/IScsiDhcp6.h
new file mode 100644
index 0000000000..fe3dfb7e4d
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiDhcp6.h
@@ -0,0 +1,79 @@
+/** @file
+ The header file of iSCSI DHCP6 related configuration routines.
+
+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.
+
+**/
+
+#ifndef _ISCSI_DHCP6_H_
+#define _ISCSI_DHCP6_H_
+
+#define DHCP6_OPT_REQUEST_OPTION 6
+#define DHCP6_OPT_VENDOR_INFO 17
+#define DHCP6_OPT_DNS_SERVERS 23
+///
+/// Assigned by IANA, RFC 5970
+///
+#define DHCP6_OPT_BOOT_FILE_URL 59
+
+#define ISCSI_ROOT_PATH_ID "iscsi:"
+#define ISCSI_ROOT_PATH_FIELD_DELIMITER ':'
+#define ISCSI_ROOT_PATH_ADDR_START_DELIMITER '['
+#define ISCSI_ROOT_PATH_ADDR_END_DELIMITER ']'
+
+
+/**
+ Extract the Root Path option and get the required target information from
+ Boot File Uniform Resource Locator (URL) Option.
+
+ @param[in] RootPath The RootPath string.
+ @param[in] Length Length of the RootPath option payload.
+ @param[in, out] ConfigData The iSCSI session configuration data read from
+ nonvolatile device.
+
+ @retval EFI_SUCCESS All required information is extracted from the
+ RootPath option.
+ @retval EFI_NOT_FOUND The RootPath is not an iSCSI RootPath.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_INVALID_PARAMETER The RootPath is malformatted.
+
+**/
+EFI_STATUS
+IScsiDhcp6ExtractRootPath (
+ IN CHAR8 *RootPath,
+ IN UINT16 Length,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ );
+
+/**
+ Parse the DHCP ACK to get the address configuration and DNS information.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller;
+ @param[in, out] ConfigData The attempt configuration data.
+
+ @retval EFI_SUCCESS The DNS information is got from the DHCP ACK.
+ @retval EFI_NO_MAPPING DHCP failed to acquire address and other
+ information.
+ @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.
+ @retval EFI_DEVICE_ERROR Some unexpected error happened.
+ @retval EFI_OUT_OF_RESOURCES There is no sufficient resource to finish the
+ operation.
+ @retval EFI_NO_MEDIA There was a media error.
+
+**/
+EFI_STATUS
+IScsiDoDhcp6 (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller,
+ IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData
+ );
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiDriver.c b/NetworkPkg/IScsiDxe/IScsiDriver.c
new file mode 100644
index 0000000000..2d922eecca
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiDriver.c
@@ -0,0 +1,1216 @@
+/** @file
+ The entry point of IScsi driver.
+
+Copyright (c) 2004 - 2011, 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_DRIVER_BINDING_PROTOCOL gIScsiDriverBinding = {
+ IScsiDriverBindingSupported,
+ IScsiDriverBindingStart,
+ IScsiDriverBindingStop,
+ 0xa,
+ NULL,
+ NULL
+};
+
+EFI_GUID mIScsiV4PrivateGuid = ISCSI_V4_PRIVATE_GUID;
+EFI_GUID mIScsiV6PrivateGuid = ISCSI_V6_PRIVATE_GUID;
+ISCSI_PRIVATE_DATA *mPrivate = NULL;
+
+/**
+ Tests to see if this driver supports the RemainingDevicePath.
+
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The RemainingDevicePath is supported or NULL.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+IScsiIsDevicePathSupported (
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *CurrentDevicePath;
+
+ CurrentDevicePath = RemainingDevicePath;
+ if (CurrentDevicePath != NULL) {
+ while (!IsDevicePathEnd (CurrentDevicePath)) {
+ if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) {
+ return EFI_SUCCESS;
+ }
+
+ CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);
+ }
+
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN IsIscsi4Started;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &mIScsiV4PrivateGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ IsIscsi4Started = TRUE;
+ } else {
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = IScsiIsDevicePathSupported (RemainingDevicePath);
+ if (!EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+ }
+
+ IsIscsi4Started = FALSE;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &mIScsiV6PrivateGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ if (IsIscsi4Started) {
+ return EFI_ALREADY_STARTED;
+ }
+ } else {
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiTcp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = IScsiIsDevicePathSupported (RemainingDevicePath);
+ if (!EFI_ERROR (Status)) {
+ return EFI_SUCCESS;
+ }
+ }
+ }
+
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Start to manage the controller. This is the worker function for
+ IScsiDriverBindingStart.
+
+ @param[in] Image Handle of the image.
+ @param[in] ControllerHandle Handle of the controller.
+ @param[in] IpVersion Ip4 or Ip6
+
+ @retval EFI_SUCCES This driver supports this device.
+ @retval EFI_ALREADY_STARTED This driver is already running on this device.
+ @retval EFI_INVALID_PARAMETER Any input parameter is invalid.
+ @retval EFI_NOT_FOUND There is no sufficient information to establish
+ the iScsi session.
+ @retval EFI_DEVICE_ERROR Failed to get TCP connection device path.
+
+**/
+EFI_STATUS
+IScsiStart (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINT8 IpVersion
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_DRIVER_DATA *Private;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_SESSION *Session;
+ UINT8 Index;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExistIScsiExtScsiPassThru;
+ ISCSI_DRIVER_DATA *ExistPrivate;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ UINT8 BootSelected;
+ EFI_HANDLE *HandleBuffer;
+ UINTN NumberOfHandles;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_GUID *IScsiPrivateGuid;
+ EFI_GUID *TcpServiceBindingGuid;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ BOOLEAN NeedUpdate;
+ VOID *Interface;
+ EFI_GUID *ProtocolGuid;
+
+ //
+ // Test to see if iSCSI driver supports the given controller.
+ //
+
+ if (IpVersion == IP_VERSION_4) {
+ IScsiPrivateGuid = &mIScsiV4PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else if (IpVersion == IP_VERSION_6) {
+ IScsiPrivateGuid = &mIScsiV6PrivateGuid;
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ IScsiPrivateGuid,
+ NULL,
+ Image,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ TcpServiceBindingGuid,
+ NULL,
+ Image,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Record the incoming NIC info.
+ //
+ Status = IScsiAddNic (ControllerHandle);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create the instance private data.
+ //
+ Private = IScsiCreateDriverData (Image, ControllerHandle);
+ if (Private == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Create a underlayer child instance, but not need to configure it. Just open ChildHandle
+ // via BY_DRIVER. That is, establishing the relationship between ControllerHandle and ChildHandle.
+ // Therefore, when DisconnectController(), especially VLAN virtual controller handle,
+ // IScsiDriverBindingStop() will be called.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ Image,
+ TcpServiceBindingGuid,
+ &Private->ChildHandle
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->ChildHandle,
+ ProtocolGuid,
+ &Interface,
+ Image,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Always install private protocol no matter what happens later. We need to
+ // keep the relationship between ControllerHandle and ChildHandle.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ IScsiPrivateGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->IScsiIdentifier
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ if (IpVersion == IP_VERSION_4) {
+ mPrivate->Ipv6Flag = FALSE;
+ } else {
+ mPrivate->Ipv6Flag = TRUE;
+ }
+
+ //
+ // Get the current iSCSI configuration data.
+ //
+ Status = IScsiGetConfigData (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // If there is already a successul attempt, check whether this attempt is the
+ // first "enabled for MPIO" attempt. If not, still try the first attempt.
+ // In single path mode, try all attempts.
+ //
+ ExistPrivate = NULL;
+ Status = EFI_NOT_FOUND;
+
+ if (mPrivate->OneSessionEstablished && mPrivate->EnableMpio) {
+ AttemptConfigData = NULL;
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ break;
+ }
+ }
+
+ if (AttemptConfigData == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (AttemptConfigData->AttemptConfigIndex == mPrivate->BootSelectedIndex) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Uninstall the original ExtScsiPassThru first.
+ //
+
+ //
+ // Locate all ExtScsiPassThru protocol instances.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiExtScsiPassThruProtocolGuid,
+ NULL,
+ &NumberOfHandles,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Find ExtScsiPassThru protocol instance produced by this driver.
+ //
+ ExistIScsiExtScsiPassThru = NULL;
+ for (Index = 0; Index < NumberOfHandles && ExistIScsiExtScsiPassThru == NULL; Index++) {
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ while (!IsDevicePathEnd (DevicePath)) {
+ if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_MAC_ADDR_DP)) {
+ //
+ // Get the ExtScsiPassThru protocol instance.
+ //
+ Status = gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID **) &ExistIScsiExtScsiPassThru
+ );
+ ASSERT_EFI_ERROR (Status);
+ break;
+ }
+
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+ }
+
+ FreePool (HandleBuffer);
+
+ if (ExistIScsiExtScsiPassThru == NULL) {
+ Status = EFI_NOT_FOUND;
+ goto ON_ERROR;
+ }
+
+ ExistPrivate = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (ExistIScsiExtScsiPassThru);
+
+ Status = gBS->UninstallProtocolInterface (
+ ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &ExistPrivate->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+ }
+
+ //
+ // Install the Ext SCSI PASS THRU protocol.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ BootSelected = 0;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ //
+ // Don't process the attempt that does not associate with the current NIC or
+ // this attempt is disabled or established.
+ //
+ if (AttemptConfigData->NicIndex != mPrivate->CurrentNic ||
+ AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED ||
+ AttemptConfigData->ValidPath) {
+ continue;
+ }
+
+ //
+ // In multipath mode, don't process attempts configured for single path.
+ // In default single path mode, don't process attempts configured for multipath.
+ //
+ if ((mPrivate->EnableMpio &&
+ AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) ||
+ (!mPrivate->EnableMpio &&
+ AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED)) {
+ continue;
+ }
+
+ //
+ // Don't process the attempt that fails to get the init/target information from DHCP.
+ //
+ if (AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp &&
+ !AttemptConfigData->DhcpSuccess) {
+ if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {
+ mPrivate->ValidSinglePathCount--;
+ }
+ continue;
+ }
+
+ //
+ // Don't process the autoconfigure path if it is already established.
+ //
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
+ AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_SUCCESS) {
+ continue;
+ }
+
+ //
+ // Don't process the attempt if its IP mode is not in the current IP version.
+ //
+ if (!mPrivate->Ipv6Flag) {
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {
+ continue;
+ }
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
+ AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {
+ continue;
+ }
+ } else {
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {
+ continue;
+ }
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
+ AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {
+ continue;
+ }
+ }
+
+ //
+ // Fill in the Session and init it.
+ //
+ Session = (ISCSI_SESSION *) AllocateZeroPool (sizeof (ISCSI_SESSION));
+ if (Session == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ Session->Private = Private;
+ Session->ConfigData = AttemptConfigData;
+ Session->AuthType = AttemptConfigData->AuthenticationType;
+
+ AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"%s%d",
+ MacString,
+ (UINTN) AttemptConfigData->AttemptConfigIndex
+ );
+
+ if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {
+ Session->AuthData.CHAP.AuthConfig = &AttemptConfigData->AuthConfigData.CHAP;
+ }
+
+ IScsiSessionInit (Session, FALSE);
+
+ //
+ // Try to login and create an iSCSI session according to the configuration.
+ //
+ Status = IScsiSessionLogin (Session);
+ if (Status == EFI_MEDIA_CHANGED) {
+ //
+ // The specified target is not available, and the redirection information is
+ // received. Login the session again with the updated target address.
+ //
+ Status = IScsiSessionLogin (Session);
+ } else if (Status == EFI_NOT_READY) {
+ Status = IScsiSessionReLogin (Session);
+ }
+
+ if (EFI_ERROR (Status)) {
+ //
+ // In Single path mode, only the successful attempt will be recorded in iBFT;
+ // in multi-path mode, all the attempt entries in MPIO will be recorded in iBFT.
+ //
+ if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {
+ mPrivate->ValidSinglePathCount--;
+ }
+
+ FreePool (Session);
+
+ } else {
+ AttemptConfigData->ValidPath = TRUE;
+
+ //
+ // Do not record the attempt in iBFT if it login with KRB5.
+ // TODO: record KRB5 attempt information in the iSCSI device path.
+ //
+ if (Session->AuthType == ISCSI_AUTH_TYPE_KRB) {
+ if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {
+ mPrivate->ValidSinglePathCount--;
+ }
+
+ AttemptConfigData->ValidiBFTPath = FALSE;
+ } else {
+ AttemptConfigData->ValidiBFTPath = TRUE;
+ }
+
+ //
+ // IScsi session success. Update the attempt state to NVR.
+ //
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
+ AttemptConfigData->AutoConfigureMode = IP_MODE_AUTOCONFIG_SUCCESS;
+ }
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+
+ //
+ // Select the first login session. Abort others.
+ //
+ if (Private->Session == NULL) {
+ Private->Session = Session;
+ BootSelected = AttemptConfigData->AttemptConfigIndex;
+ //
+ // Don't validate other attempt in multipath mode if one is success.
+ //
+ if (mPrivate->EnableMpio) {
+ break;
+ }
+ } else {
+ IScsiSessionAbort (Session);
+ FreePool (Session);
+ }
+ }
+ }
+
+ //
+ // All attempts configured for this driver instance are not valid.
+ //
+ if (Private->Session == NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ ASSERT_EFI_ERROR (Status);
+ Private->ExtScsiPassThruHandle = NULL;
+
+ //
+ // Reinstall the original ExtScsiPassThru back.
+ //
+ if (mPrivate->OneSessionEstablished && ExistPrivate != NULL) {
+ Status = gBS->InstallProtocolInterface (
+ &ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &ExistPrivate->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ goto ON_EXIT;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ goto ON_ERROR;
+ }
+
+ NeedUpdate = TRUE;
+ //
+ // More than one attempt successes.
+ //
+ if (Private->Session != NULL && mPrivate->OneSessionEstablished) {
+
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &mVendorGuid,
+ &AttemptConfigOrderSize
+ );
+ ASSERT (AttemptConfigOrder != NULL);
+ for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
+ if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex ||
+ AttemptConfigOrder[Index] == BootSelected) {
+ break;
+ }
+ }
+
+ if (mPrivate->EnableMpio) {
+ //
+ // Use the attempt in earlier order. Abort the later one in MPIO.
+ //
+ if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) {
+ IScsiSessionAbort (Private->Session);
+ FreePool (Private->Session);
+ Private->Session = NULL;
+ gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ Private->ExtScsiPassThruHandle = NULL;
+
+ //
+ // Reinstall the original ExtScsiPassThru back.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ExistPrivate->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &ExistPrivate->IScsiExtScsiPassThru
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ goto ON_EXIT;
+ } else {
+ ASSERT (AttemptConfigOrder[Index] == BootSelected);
+ mPrivate->BootSelectedIndex = BootSelected;
+ //
+ // Clear the resource in ExistPrivate.
+ //
+ gBS->UninstallProtocolInterface (
+ ExistPrivate->Controller,
+ IScsiPrivateGuid,
+ &ExistPrivate->IScsiIdentifier
+ );
+
+ IScsiRemoveNic (ExistPrivate->Controller);
+ if (ExistPrivate->Session != NULL) {
+ IScsiSessionAbort (ExistPrivate->Session);
+ }
+
+ IScsiCleanDriverData (ExistPrivate);
+ }
+ } else {
+ //
+ // Use the attempt in earlier order as boot selected in single path mode.
+ //
+ if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) {
+ NeedUpdate = FALSE;
+ }
+ }
+
+ }
+
+ if (NeedUpdate) {
+ mPrivate->OneSessionEstablished = TRUE;
+ mPrivate->BootSelectedIndex = BootSelected;
+ }
+
+ //
+ // Duplicate the Session's tcp connection device path. The source port field
+ // will be set to zero as one iSCSI session is comprised of several iSCSI
+ // connections.
+ //
+ Private->DevicePath = IScsiGetTcpConnDevicePath (Private->Session);
+ if (Private->DevicePath == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_ERROR;
+ }
+ //
+ // Install the updated device path onto the ExtScsiPassThruHandle.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &Private->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ Private->DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ON_EXIT:
+
+ //
+ // Update/Publish the iSCSI Boot Firmware Table.
+ //
+ if (mPrivate->BootSelectedIndex != 0) {
+ IScsiPublishIbft ();
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+
+ if (Private->Session != NULL) {
+ IScsiSessionAbort (Private->Session);
+ }
+
+ return Status;
+}
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS V4Status;
+ EFI_STATUS V6Status;
+
+ V4Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_4);
+ if (V4Status == EFI_ALREADY_STARTED) {
+ V4Status = EFI_SUCCESS;
+ }
+
+ V6Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_6);
+ if (V6Status == EFI_ALREADY_STARTED) {
+ V6Status = EFI_SUCCESS;
+ }
+
+ if (!EFI_ERROR (V4Status) || !EFI_ERROR (V6Status)) {
+ return EFI_SUCCESS;
+ } else if (EFI_ERROR (V4Status)) {
+ return V4Status;
+ } else {
+ return V6Status;
+ }
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_HANDLE IScsiController;
+ EFI_STATUS Status;
+ ISCSI_PRIVATE_PROTOCOL *IScsiIdentifier;
+ ISCSI_DRIVER_DATA *Private;
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;
+ ISCSI_CONNECTION *Conn;
+ EFI_GUID *ProtocolGuid;
+ EFI_GUID *TcpServiceBindingGuid;
+ EFI_GUID *TcpProtocolGuid;
+
+
+ if (NumberOfChildren != 0) {
+ //
+ // We should have only one child.
+ //
+ Status = gBS->OpenProtocol (
+ ChildHandleBuffer[0],
+ &gEfiExtScsiPassThruProtocolGuid,
+ (VOID **) &PassThru,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
+ Conn = NET_LIST_HEAD (&Private->Session->Conns, ISCSI_CONNECTION, Link);
+
+ //
+ // Previously the TCP protocol is opened BY_CHILD_CONTROLLER. Just close
+ // the protocol here, but do not uninstall the device path protocol and
+ // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle.
+ //
+ if (!Conn->Ipv6Flag) {
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ gBS->CloseProtocol (
+ Conn->TcpIo.Handle,
+ ProtocolGuid,
+ Private->Image,
+ Private->ExtScsiPassThruHandle
+ );
+
+ return EFI_SUCCESS;
+ }
+ //
+ // Get the handle of the controller we are controling.
+ //
+ IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid);
+ if (IScsiController != NULL) {
+ ProtocolGuid = &mIScsiV4PrivateGuid;
+ TcpProtocolGuid = &gEfiTcp4ProtocolGuid;
+ TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;
+ } else {
+ IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp6ProtocolGuid);
+ ASSERT (IScsiController != NULL);
+ ProtocolGuid = &mIScsiV6PrivateGuid;
+ TcpProtocolGuid = &gEfiTcp6ProtocolGuid;
+ TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;
+ }
+
+ Status = gBS->OpenProtocol (
+ IScsiController,
+ ProtocolGuid,
+ (VOID **) &IScsiIdentifier,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);
+ ASSERT (Private != NULL);
+
+ if (Private->ChildHandle != NULL) {
+ Status = gBS->CloseProtocol (
+ Private->ChildHandle,
+ TcpProtocolGuid,
+ This->DriverBindingHandle,
+ IScsiController
+ );
+
+ ASSERT (!EFI_ERROR (Status));
+
+ Status = NetLibDestroyServiceChild (
+ IScsiController,
+ This->DriverBindingHandle,
+ TcpServiceBindingGuid,
+ Private->ChildHandle
+ );
+
+ ASSERT (!EFI_ERROR (Status));
+ }
+
+ gBS->UninstallProtocolInterface (
+ IScsiController,
+ ProtocolGuid,
+ &Private->IScsiIdentifier
+ );
+
+ //
+ // Remove this NIC.
+ //
+ IScsiRemoveNic (IScsiController);
+
+ //
+ // Update the iSCSI Boot Firware Table.
+ //
+ IScsiPublishIbft ();
+
+ if (Private->Session != NULL) {
+ IScsiSessionAbort (Private->Session);
+ }
+
+ IScsiCleanDriverData (Private);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Unload the iSCSI driver.
+
+ @param[in] ImageHandle The handle of the driver image.
+
+ @retval EFI_SUCCESS The driver is unloaded.
+ @retval EFI_DEVICE_ERROR An unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiUnload (
+ IN EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ UINTN DeviceHandleCount;
+ EFI_HANDLE *DeviceHandleBuffer;
+ UINTN Index;
+
+ //
+ // Try to disonnect the driver from the devices it's controlling.
+ //
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &DeviceHandleCount,
+ &DeviceHandleBuffer
+ );
+ if (!EFI_ERROR (Status)) {
+ for (Index = 0; Index < DeviceHandleCount; Index++) {
+ Status = gBS->DisconnectController (
+ DeviceHandleBuffer[Index],
+ ImageHandle,
+ NULL
+ );
+ }
+
+ if (DeviceHandleBuffer != NULL) {
+ FreePool (DeviceHandleBuffer);
+ }
+ }
+ //
+ // Unload the iSCSI configuration form.
+ //
+ IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);
+
+ //
+ // Uninstall the protocols installed by iSCSI driver.
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiAuthenticationInfoProtocolGuid,
+ &gIScsiAuthenticationInfo,
+ NULL
+ );
+
+ return gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gIScsiDriverBinding,
+ &gEfiComponentName2ProtocolGuid,
+ &gIScsiComponentName2,
+ &gEfiComponentNameProtocolGuid,
+ &gIScsiComponentName,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ &gIScsiInitiatorName,
+ NULL
+ );
+}
+
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ The entry point for iSCSI driver which initializes the global variables and
+ installs the driver binding, component name protocol, iSCSI initiator name
+ protocol and Authentication Info protocol on its image.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ EFI_ISCSI_INITIATOR_NAME_PROTOCOL *IScsiInitiatorName;
+ EFI_AUTHENTICATION_INFO_PROTOCOL *AuthenticationInfo;
+
+ //
+ // There should be only one EFI_ISCSI_INITIATOR_NAME_PROTOCOL.
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ NULL,
+ (VOID **) &IScsiInitiatorName
+ );
+ if (!EFI_ERROR (Status)) {
+ return EFI_ACCESS_DENIED;
+ }
+
+ //
+ // Initialize the EFI Driver Library.
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gIScsiDriverBinding,
+ ImageHandle,
+ &gIScsiComponentName,
+ &gIScsiComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Install the iSCSI Initiator Name Protocol.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &gIScsiInitiatorName
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error1;
+ }
+
+ //
+ // Create the private data structures.
+ //
+ mPrivate = AllocateZeroPool (sizeof (ISCSI_PRIVATE_DATA));
+ if (mPrivate == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error2;
+ }
+
+ InitializeListHead (&mPrivate->NicInfoList);
+ InitializeListHead (&mPrivate->AttemptConfigs);
+
+ //
+ // Initialize the configuration form of iSCSI.
+ //
+ Status = IScsiConfigFormInit (gIScsiDriverBinding.DriverBindingHandle);
+ if (EFI_ERROR (Status)) {
+ goto Error3;
+ }
+
+ //
+ // There should be only one EFI_AUTHENTICATION_INFO_PROTOCOL. If already exists,
+ // do not produce the protocol instance.
+ //
+ Status = gBS->LocateProtocol (
+ &gEfiAuthenticationInfoProtocolGuid,
+ NULL,
+ (VOID **) &AuthenticationInfo
+ );
+ if (Status == EFI_NOT_FOUND) {
+ Status = gBS->InstallProtocolInterface (
+ &ImageHandle,
+ &gEfiAuthenticationInfoProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &gIScsiAuthenticationInfo
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error4;
+ }
+ }
+
+ return EFI_SUCCESS;
+
+Error4:
+ IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);
+
+Error3:
+ FreePool (mPrivate);
+
+Error2:
+ gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ &gIScsiInitiatorName,
+ NULL
+ );
+
+Error1:
+ gBS->UninstallMultipleProtocolInterfaces (
+ ImageHandle,
+ &gEfiDriverBindingProtocolGuid,
+ &gIScsiDriverBinding,
+ &gEfiComponentName2ProtocolGuid,
+ &gIScsiComponentName2,
+ &gEfiComponentNameProtocolGuid,
+ &gIScsiComponentName,
+ NULL
+ );
+
+ return Status;
+}
+
diff --git a/NetworkPkg/IScsiDxe/IScsiDriver.h b/NetworkPkg/IScsiDxe/IScsiDriver.h
new file mode 100644
index 0000000000..ad6b83a3c3
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiDriver.h
@@ -0,0 +1,678 @@
+/** @file
+ The header file of IScsiDriver.c.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_DRIVER_H_
+#define _ISCSI_DRIVER_H_
+
+#define ISCSI_V4_PRIVATE_GUID \
+ { \
+ 0xfa3cde4c, 0x87c2, 0x427d, { 0xae, 0xde, 0x7d, 0xd0, 0x96, 0xc8, 0x8c, 0x58 } \
+ }
+
+#define ISCSI_V6_PRIVATE_GUID \
+ { \
+ 0x28be27e5, 0x66cc, 0x4a31, { 0xa3, 0x15, 0xdb, 0x14, 0xc3, 0x74, 0x4d, 0x85 } \
+ }
+
+#define ISCSI_INITIATOR_NAME_VAR_NAME L"I_NAME"
+
+#define IP_MODE_AUTOCONFIG_IP4 3
+#define IP_MODE_AUTOCONFIG_IP6 4
+#define IP_MODE_AUTOCONFIG_SUCCESS 5
+
+extern EFI_COMPONENT_NAME2_PROTOCOL gIScsiComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gIScsiComponentName;
+extern EFI_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName;
+extern EFI_AUTHENTICATION_INFO_PROTOCOL gIScsiAuthenticationInfo;
+extern EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate;
+
+typedef struct {
+ CHAR16 PortString[ISCSI_NAME_IFR_MAX_SIZE];
+ LIST_ENTRY NicInfoList;
+ UINT8 NicCount;
+ UINT8 CurrentNic;
+ UINT8 MaxNic;
+ BOOLEAN Ipv6Flag;
+ BOOLEAN OneSessionEstablished;
+ BOOLEAN EnableMpio;
+ UINT8 MpioCount; // The number of attempts in MPIO.
+ UINT8 Krb5MpioCount; // The number of attempts login with KRB5 in MPIO.
+ UINT8 SinglePathCount; // The number of single path attempts.
+ UINT8 ValidSinglePathCount; // The number of valid single path attempts.
+ UINT8 BootSelectedIndex;
+ UINT8 AttemptCount;
+ LIST_ENTRY AttemptConfigs; // User configured Attempt list.
+ CHAR8 InitiatorName[ISCSI_NAME_MAX_SIZE];
+ UINTN InitiatorNameLength;
+} ISCSI_PRIVATE_DATA;
+
+extern ISCSI_PRIVATE_DATA *mPrivate;
+
+typedef struct {
+ LIST_ENTRY Link;
+ UINT32 HwAddressSize;
+ EFI_MAC_ADDRESS PermanentAddress;
+ UINT8 NicIndex;
+ UINT16 VlanId;
+ UINTN BusNumber;
+ UINTN DeviceNumber;
+ UINTN FunctionNumber;
+} ISCSI_NIC_INFO;
+
+typedef struct _ISCSI_PRIVATE_PROTOCOL {
+ UINT32 Reserved;
+} ISCSI_PRIVATE_PROTOCOL;
+
+//
+// EFI Driver Binding Protocol for iSCSI driver.
+//
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Since ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error. Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+//
+// EFI Component Name(2) Protocol for iSCSI driver.
+//
+
+/**
+ Retrieves a Unicode string that is the user readable name of the driver.
+
+ This function retrieves the user readable name of a driver in the form of a
+ Unicode string. If the driver specified by This has a user readable name in
+ the language specified by Language, then a pointer to the driver name is
+ returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+ by This does not support the language specified by Language,
+ then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up
+ to the driver writer. Language is specified
+ in RFC 4646 or ISO 639-2 language code format.
+
+ @param[out] DriverName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by
+ This and the language specified by Language was
+ returned in DriverName.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by a driver.
+
+ This function retrieves the user readable name of the controller specified by
+ ControllerHandle and ChildHandle in the form of a Unicode string. If the
+ driver specified by This has a user readable name in the language specified by
+ Language, then a pointer to the controller name is returned in ControllerName,
+ and EFI_SUCCESS is returned. If the driver specified by This is not currently
+ managing the controller specified by ControllerHandle and ChildHandle,
+ then EFI_UNSUPPORTED is returned. If the driver specified by This does not
+ support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+ @param[in] This A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+ EFI_COMPONENT_NAME_PROTOCOL instance.
+
+ @param[in] ControllerHandle The handle of a controller that the driver
+ specified by This is managing. This handle
+ specifies the controller whose name is to be
+ returned.
+
+ @param[in] ChildHandle The handle of the child controller to retrieve
+ the name of. This is an optional parameter that
+ may be NULL. It will be NULL for device
+ drivers. It will also be NULL for a bus drivers
+ that wish to retrieve the name of the bus
+ controller. It will not be NULL for a bus
+ driver that wishes to retrieve the name of a
+ child controller.
+
+ @param[in] Language A pointer to a Null-terminated ASCII string
+ array indicating the language. This is the
+ language of the driver name that the caller is
+ requesting, and it must match one of the
+ languages specified in SupportedLanguages. The
+ number of languages supported by a driver is
+ determined by the driver writer. Language is
+ specified inRFC 4646 or ISO 639-2 language code
+ format.
+
+ @param[out] ControllerName A pointer to the Unicode string to return.
+ This Unicode string is the name of the
+ controller specified by ControllerHandle and
+ ChildHandle in the language specified by
+ Language from the point of view of the driver
+ specified by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user readable name in
+ the language specified by Language for the
+ driver specified by This was returned in
+ DriverName.
+
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid
+ EFI_HANDLE.
+
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently
+ managing the controller specified by
+ ControllerHandle and ChildHandle.
+
+ @retval EFI_UNSUPPORTED The driver specified by This does not support
+ the language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+//
+// EFI iSCSI Initiator Name Protocol for iSCSI driver.
+//
+
+/**
+ Retrieves the current set value of iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL
+ instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer /
+ Actual size of the variable data buffer.
+ @param[out] Buffer Pointer to the buffer for data to be read.
+
+ @retval EFI_SUCCESS Data was successfully retrieved into the provided
+ buffer and the BufferSize was sufficient to handle
+ the iSCSI initiator name.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the result. BufferSize
+ will be updated with the size required to complete
+ the request. Buffer will not be affected.
+ @retval EFI_INVALID_PARAMETER BufferSize is NULL. BufferSize and Buffer will not
+ be affected.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be
+ affected.
+ @retval EFI_DEVICE_ERROR The iSCSI initiator name could not be retrieved
+ due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiGetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ );
+
+/**
+ Sets the iSSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL
+ instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer.
+ @param[in] Buffer Pointer to the buffer for data to be written.
+
+ @retval EFI_SUCCESS Data was successfully stored by the protocol.
+ @retval EFI_UNSUPPORTED Platform policies do not allow for data to be
+ written.
+ @retval EFI_INVALID_PARAMETER BufferSize exceeds the maximum allowed limit.
+ BufferSize will be updated with the maximum size
+ required to complete the request.
+ @retval EFI_INVALID_PARAMETER Buffersize is NULL. BufferSize and Buffer will not
+ be affected.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be
+ affected.
+ @retval EFI_DEVICE_ERROR The data could not be stored due to a hardware
+ error.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the data
+ @retval EFI_PROTOCOL_ERROR Input iSCSI initiator name does not adhere to RFC
+ 3720
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiSetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ );
+
+//
+// EFI_AUTHENTICATION_INFO_PROTOCOL for iSCSI driver.
+//
+
+/**
+ Retrieves the authentication information associated with a particular controller handle.
+
+ @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.
+ @param[in] ControllerHandle Handle to the Controller.
+ @param[out] Buffer Pointer to the authentication information. This function is
+ responsible for allocating the buffer and it is the caller's
+ responsibility to free buffer when the caller is finished with buffer.
+
+ @retval EFI_DEVICE_ERROR The authentication information could not be
+ retrieved due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiGetAuthenticationInfo (
+ IN EFI_AUTHENTICATION_INFO_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ OUT VOID **Buffer
+ );
+
+/**
+ Set the authentication information for a given controller handle.
+
+ @param[in] This Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.
+ @param[in] ControllerHandle Handle to the Controller.
+ @param[in] Buffer Pointer to the authentication information.
+
+ @retval EFI_UNSUPPORTED If the platform policies do not allow setting of
+ the authentication information.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiSetAuthenticationInfo (
+ IN EFI_AUTHENTICATION_INFO_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN VOID *Buffer
+ );
+
+//
+// EFI_EXT_SCSI_PASS_THRU_PROTOCOL for iSCSI driver.
+//
+
+/**
+ Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel.
+ This function supports both blocking I/O and nonblocking I/O. The blocking I/O
+ functionality is required, and the nonblocking I/O functionality is optional.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in] Target The Target is an array of size TARGET_MAX_BYTES and it
+ represents the id of the SCSI device to send the SCSI
+ Request Packet. Each transport driver may choose to
+ utilize a subset of this size to suit the needs
+ of transport target representation. For example, a
+ Fibre Channel driver may use only 8 bytes (WWN)
+ to represent an FC target.
+ @param[in] Lun The LUN of the SCSI device to send the SCSI Request Packet.
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to the
+ SCSI device specified by Target and Lun.
+ @param[in] Event If nonblocking I/O is not supported then Event is ignored,
+ and blocking I/O is performed. If Event is NULL, then
+ blocking I/O is performed. If Event is not NULL and non
+ blocking I/O is supported, then nonblocking I/O is performed,
+ and Event will be signaled when the SCSI Request Packet
+ completes.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For
+ bi-directional commands, InTransferLength bytes
+ were transferred from InDataBuffer.
+ For write and bi-directional commands, OutTransferLength
+ bytes were transferred by OutDataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed.
+ The number of bytes that could be transferred is
+ returned in InTransferLength. For write and
+ bi-directional commands, OutTransferLength bytes
+ were transferred by OutDataBuffer.
+ @retval EFI_NOT_READY The SCSI Request Packet could not be sent because
+ there are too many SCSI Request Packets already
+ queued. The caller may retry later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send
+ the SCSI Request Packet.
+ @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket
+ are invalid.
+ @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet
+ is not supported by the host adapter.
+ This includes the case of Bi-directional SCSI
+ commands not supported by the implementation.
+ The SCSI Request Packet was not sent,
+ so no additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI
+ Request Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruFunction (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ );
+
+/**
+ Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on
+ a SCSI channel. These can either be the list SCSI devices that are actually
+ present on the SCSI channel, or the list of legal Target Ids and LUNs for the
+ SCSI channel. Regardless, the caller of this function must probe the Target ID
+ and LUN returned to see if a SCSI device is actually present at that location
+ on the SCSI channel.
+
+ @param[in] This The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in, out] Target On input, a pointer to the Target ID of a SCSI
+ device present on the SCSI channel. On output, a
+ pointer to the Target ID of the next SCSI device
+ present on a SCSI channel. An input value of
+ 0xFFFFFFFF retrieves the Target ID of the first
+ SCSI device present on a SCSI channel.
+ @param[in, out] Lun On input, a pointer to the LUN of a SCSI device
+ present on the SCSI channel. On output, a pointer
+ to the LUN of the next SCSI device present on a
+ SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID and Lun of the next SCSI device on
+ the SCSI channel was returned in Target and Lun.
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI
+ channel.
+ @retval EFI_INVALID_PARAMETER Target is not 0xFFFFFFFF,and Target and Lun were
+ not returned on a previous call to
+ GetNextDevice().
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetNextTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target,
+ IN OUT UINT64 *Lun
+ );
+
+/**
+ Allocate and build a device path node for a SCSI device on a SCSI channel.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Target The Target ID of the SCSI device for which a
+ device path node is to be allocated and built.
+ @param[in] Lun The LUN of the SCSI device for which a device
+ path node is to be allocated and built.
+ @param[in, out] DevicePath A pointer to a single device path node that
+ describes the SCSI device specified by Target and
+ Lun. This function is responsible for allocating
+ the buffer DevicePath with the boot service
+ AllocatePool(). It is the caller's
+ responsibility to free DevicePath when the caller
+ is finished with DevicePath.
+
+ @retval EFI_SUCCESS The device path node that describes the SCSI
+ device specified by Target and Lun was allocated
+ and returned in DevicePath.
+ @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does
+ not exist on the SCSI channel.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate
+ DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruBuildDevicePath (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ );
+
+/**
+ Translate a device path node to a Target ID and LUN.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] DevicePath A pointer to the device path node that describes
+ a SCSI device on the SCSI channel.
+ @param[out] Target A pointer to the Target ID of a SCSI device on
+ the SCSI channel.
+ @param[out] Lun A pointer to the LUN of a SCSI device on the SCSI
+ channel.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to a
+ Target ID and LUN, and they were returned in
+ Target and Lun.
+ @retval EFI_INVALID_PARAMETER DevicePath/Target/Lun is NULL.
+ @retval EFI_UNSUPPORTED This driver does not support the device path node
+ type in DevicePath.
+ @retval EFI_NOT_FOUND A valid translation from DevicePath to a Target
+ ID and LUN does not exist.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 **Target,
+ OUT UINT64 *Lun
+ );
+
+/**
+ Resets a SCSI channel.This operation resets all the SCSI devices connected to
+ the SCSI channel.
+
+ @param[in] This Protocol instance pointer.
+
+ @retval EFI_UNSUPPORTED It is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruResetChannel (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+ );
+
+/**
+ Resets a SCSI device that is connected to a SCSI channel.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Target The Target ID of the SCSI device to reset.
+ @param[in] Lun The LUN of the SCSI device to reset.
+
+ @retval EFI_UNSUPPORTED It is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruResetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun
+ );
+
+/**
+ Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL
+ instance.
+ @param[in, out] Target (TARGET_MAX_BYTES) of a SCSI device present on
+ the SCSI channel. On output, a pointer to the
+ Target ID (an array of TARGET_MAX_BYTES) of the
+ next SCSI device present on a SCSI channel.
+ An input value of 0xF(all bytes in the array are 0xF)
+ in the Target array retrieves the Target ID of the
+ first SCSI device present on a SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI
+ channel was returned in Target.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not
+ returned on a previous call to GetNextTarget().
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetNextTarget (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target
+ );
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiDxe.inf b/NetworkPkg/IScsiDxe/IScsiDxe.inf
new file mode 100644
index 0000000000..24799fc57c
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiDxe.inf
@@ -0,0 +1,106 @@
+## @file
+# Component description file for IScsi module.
+#
+# Copyright (c) 2004 - 2011, 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.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = IScsiDxe
+ FILE_GUID = 86CDDF93-4872-4597-8AF9-A35AE4D3725F
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = IScsiDriverEntryPoint
+ UNLOAD_IMAGE = IScsiUnload
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF
+#
+# DRIVER_BINDING = gIScsiDriverBinding
+# COMPONENT_NAME = gIScsiComponentName
+# COMPONENT_NAME2 = gIScsiComponentName2
+#
+
+
+[Sources]
+ ComponentName.c
+ IScsiAuthenticationInfo.c
+ IScsiCHAP.h
+ IScsiCHAP.c
+ IScsiConfig.c
+ IScsiConfig.h
+ IScsiConfigNVDataStruc.h
+ IScsiConfigStrings.uni
+ IScsiConfigVfr.vfr
+ IScsiDhcp.c
+ IScsiDhcp.h
+ IScsiDhcp6.c
+ IScsiDhcp6.h
+ IScsiDriver.c
+ IScsiDriver.h
+ IScsiExtScsiPassThru.c
+ IScsiIbft.c
+ IScsiIbft.h
+ IScsiInitiatorName.c
+ IScsiImpl.h
+ IScsiMisc.c
+ IScsiMisc.h
+ IScsiProto.c
+ IScsiProto.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ CryptoPkg/CryptoPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ BaseMemoryLib
+ DebugLib
+ DevicePathLib
+ HiiLib
+ MemoryAllocationLib
+ NetLib
+ TcpIoLib
+ PrintLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiLib
+ UefiRuntimeServicesTableLib
+ UefiHiiServicesLib
+ BaseCryptLib
+
+[Protocols]
+ gEfiAcpiTableProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDriverBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiPciIoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDhcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDhcp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDhcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDhcp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp4ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp6ProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp4ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiTcp6ServiceBindingProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiExtScsiPassThruProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiHiiConfigAccessProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiDevicePathProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiIScsiInitiatorNameProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+ gEfiAuthenticationInfoProtocolGuid # PROTOCOL ALWAYS_CONSUMED
+
+[Guids]
+ gEfiEventExitBootServicesGuid
+ gEfiIfrTianoGuid ## CONSUMES ## GUID
+ gEfiAcpiTableGuid ## CONSUMES ## GUID
+ gEfiAcpi10TableGuid ## CONSUMES ## GUID
+ gEfiAcpi20TableGuid ## CONSUMES ## GUID
diff --git a/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c b/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c
new file mode 100644
index 0000000000..c4aed4c4ab
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c
@@ -0,0 +1,409 @@
+/** @file
+ The implementation of EFI_EXT_SCSI_PASS_THRU_PROTOCOL.
+
+Copyright (c) 2004 - 2011, 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_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate = {
+ NULL,
+ IScsiExtScsiPassThruFunction,
+ IScsiExtScsiPassThruGetNextTargetLun,
+ IScsiExtScsiPassThruBuildDevicePath,
+ IScsiExtScsiPassThruGetTargetLun,
+ IScsiExtScsiPassThruResetChannel,
+ IScsiExtScsiPassThruResetTargetLun,
+ IScsiExtScsiPassThruGetNextTarget
+};
+
+
+/**
+ Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel.
+ This function supports both blocking I/O and nonblocking I/O. The blocking I/O
+ functionality is required, and the nonblocking I/O functionality is optional.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in] Target The Target is an array of size TARGET_MAX_BYTES and it
+ represents the id of the SCSI device to send the SCSI
+ Request Packet. Each transport driver may choose to
+ utilize a subset of this size to suit the needs
+ of transport target representation. For example, a
+ Fibre Channel driver may use only 8 bytes (WWN)
+ to represent an FC target.
+ @param[in] Lun The LUN of the SCSI device to send the SCSI Request Packet.
+ @param[in, out] Packet A pointer to the SCSI Request Packet to send to the
+ SCSI device specified by Target and Lun.
+ @param[in] Event If nonblocking I/O is not supported then Event is ignored,
+ and blocking I/O is performed. If Event is NULL, then
+ blocking I/O is performed. If Event is not NULL and non
+ blocking I/O is supported, then nonblocking I/O is performed,
+ and Event will be signaled when the SCSI Request Packet
+ completes.
+
+ @retval EFI_SUCCESS The SCSI Request Packet was sent by the host. For
+ bi-directional commands, InTransferLength bytes
+ were transferred from InDataBuffer.
+ For write and bi-directional commands, OutTransferLength
+ bytes were transferred by OutDataBuffer.
+ @retval EFI_BAD_BUFFER_SIZE The SCSI Request Packet was not executed.
+ The number of bytes that could be transferred is
+ returned in InTransferLength. For write and
+ bi-directional commands, OutTransferLength bytes
+ were transferred by OutDataBuffer.
+ @retval EFI_NOT_READY The SCSI Request Packet could not be sent because
+ there are too many SCSI Request Packets already
+ queued. The caller may retry later.
+ @retval EFI_DEVICE_ERROR A device error occurred while attempting to send
+ the SCSI Request Packet.
+ @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket,
+ are invalid.
+ @retval EFI_UNSUPPORTED The command described by the SCSI Request Packet
+ is not supported by the host adapter.
+ This includes the case of Bi-directional SCSI
+ commands not supported by the implementation.
+ The SCSI Request Packet was not sent,
+ so no additional status information is available.
+ @retval EFI_TIMEOUT A timeout occurred while waiting for the SCSI
+ Request Packet to execute.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruFunction (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN EFI_EVENT Event OPTIONAL
+ )
+{
+ if (Target[0] != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Packet == NULL) || (Packet->Cdb == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return IScsiExecuteScsiCommand (This, Target, Lun, Packet);
+}
+
+
+/**
+ Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on
+ a SCSI channel. These can either be the list SCSI devices that are actually
+ present on the SCSI channel, or the list of legal Target Ids and LUNs for the
+ SCSI channel. Regardless, the caller of this function must probe the Target ID
+ and LUN returned to see if a SCSI device is actually present at that location
+ on the SCSI channel.
+
+ @param[in] This The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.
+ @param[in, out] Target On input, a pointer to the Target ID of a SCSI
+ device present on the SCSI channel. On output, a
+ pointer to the Target ID of the next SCSI device
+ present on a SCSI channel. An input value of
+ 0xFFFFFFFF retrieves the Target ID of the first
+ SCSI device present on a SCSI channel.
+ @param[in, out] Lun On input, a pointer to the LUN of a SCSI device
+ present on the SCSI channel. On output, a pointer
+ to the LUN of the next SCSI device present on a
+ SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID and Lun of the next SCSI device on
+ the SCSI channel was returned in Target and Lun.
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI
+ channel.
+ @retval EFI_INVALID_PARAMETER Target is not 0xFFFFFFFF,and Target and Lun were
+ not returned on a previous call to
+ GetNextDevice().
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetNextTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target,
+ IN OUT UINT64 *Lun
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+ UINT8 TargetId[TARGET_MAX_BYTES];
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
+ ConfigNvData = &Private->Session->ConfigData->SessionConfigData;
+
+ if ((*Target)[0] == 0 && (CompareMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)) == 0)) {
+ //
+ // Only one <Target, Lun> pair per iSCSI Driver instance.
+ //
+ return EFI_NOT_FOUND;
+ }
+
+ SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);
+ if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {
+ (*Target)[0] = 0;
+ CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));
+
+ return EFI_SUCCESS;
+ }
+
+ return EFI_INVALID_PARAMETER;
+}
+
+
+/**
+ Allocate and build a device path node for a SCSI device on a SCSI channel.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Target The Target ID of the SCSI device for which a
+ device path node is to be allocated and built.
+ @param[in] Lun The LUN of the SCSI device for which a device
+ path node is to be allocated and built.
+ @param[in, out] DevicePath A pointer to a single device path node that
+ describes the SCSI device specified by Target and
+ Lun. This function is responsible for allocating
+ the buffer DevicePath with the boot service
+ AllocatePool(). It is the caller's
+ responsibility to free DevicePath when the caller
+ is finished with DevicePath.
+
+ @retval EFI_SUCCESS The device path node that describes the SCSI
+ device specified by Target and Lun was allocated
+ and returned in DevicePath.
+ @retval EFI_NOT_FOUND The SCSI devices specified by Target and Lun does
+ not exist on the SCSI channel.
+ @retval EFI_INVALID_PARAMETER DevicePath is NULL.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources to allocate
+ DevicePath.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruBuildDevicePath (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION *Session;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;
+ EFI_DEV_PATH *Node;
+ UINTN DevPathNodeLen;
+
+ if ((DevicePath == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Target[0] != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
+ Session = Private->Session;
+ ConfigNvData = &Session->ConfigData->SessionConfigData;
+ AuthConfig = Session->AuthData.CHAP.AuthConfig;
+
+ if (CompareMem (&Lun, ConfigNvData->BootLun, sizeof (UINT64)) != 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ DevPathNodeLen = sizeof (ISCSI_DEVICE_PATH) + AsciiStrLen (ConfigNvData->TargetName) + 1;
+ Node = AllocateZeroPool (DevPathNodeLen);
+ if (Node == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_ISCSI_DP;
+ SetDevicePathNodeLength (&Node->DevPath, DevPathNodeLen);
+
+ //
+ // 0 for TCP, others are reserved.
+ //
+ Node->Iscsi.NetworkProtocol = 0;
+
+ Node->Iscsi.LoginOption = 0;
+
+ switch (Session->AuthType) {
+ case ISCSI_AUTH_TYPE_NONE:
+ Node->Iscsi.LoginOption |= 0x0800;
+ break;
+
+ case ISCSI_AUTH_TYPE_CHAP:
+ //
+ // Bit12: 0=CHAP_BI, 1=CHAP_UNI
+ //
+ if (AuthConfig->CHAPType == ISCSI_CHAP_UNI) {
+ Node->Iscsi.LoginOption |= 0x1000;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ CopyMem (&Node->Iscsi.Lun, ConfigNvData->BootLun, sizeof (UINT64));
+ Node->Iscsi.TargetPortalGroupTag = Session->TargetPortalGroupTag;
+ AsciiStrCpy ((CHAR8 *) Node + sizeof (ISCSI_DEVICE_PATH), ConfigNvData->TargetName);
+
+ *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Translate a device path node to a Target ID and LUN.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] DevicePath A pointer to the device path node that describes
+ a SCSI device on the SCSI channel.
+ @param[out] Target A pointer to the Target ID of a SCSI device on
+ the SCSI channel.
+ @param[out] Lun A pointer to the LUN of a SCSI device on the SCSI
+ channel.
+
+ @retval EFI_SUCCESS DevicePath was successfully translated to a
+ Target ID and LUN, and they were returned in
+ Target and Lun.
+ @retval EFI_INVALID_PARAMETER DevicePath/Target/Lun is NULL.
+ @retval EFI_UNSUPPORTED This driver does not support the device path node
+ type in DevicePath.
+ @retval EFI_NOT_FOUND A valid translation does not exist from DevicePath
+ to a TargetID and LUN.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ OUT UINT8 **Target,
+ OUT UINT64 *Lun
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;
+
+ if ((DevicePath == NULL) || (Target == NULL) || (Lun == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||
+ (DevicePath->SubType != MSG_ISCSI_DP) ||
+ (DevicePathNodeLength (DevicePath) <= sizeof (ISCSI_DEVICE_PATH))
+ ) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);
+ ConfigNvData = &Private->Session->ConfigData->SessionConfigData;
+
+ SetMem (*Target, TARGET_MAX_BYTES, 0xFF);
+ (*Target)[0] = 0;
+
+ if (AsciiStrCmp (ConfigNvData->TargetName, (CHAR8 *) DevicePath + sizeof (ISCSI_DEVICE_PATH)) != 0) {
+ return EFI_UNSUPPORTED;
+ }
+
+ CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Resets a SCSI channel. This operation resets all the SCSI devices connected to
+ the SCSI channel.
+
+ @param[in] This Protocol instance pointer.
+
+ @retval EFI_UNSUPPORTED It is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruResetChannel (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+
+/**
+ Resets a SCSI device that is connected to a SCSI channel.
+
+ @param[in] This Protocol instance pointer.
+ @param[in] Target The Target ID of the SCSI device to reset.
+ @param[in] Lun The LUN of the SCSI device to reset.
+
+ @retval EFI_UNSUPPORTED It is not supported.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruResetTargetLun (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN UINT8 *Target,
+ IN UINT64 Lun
+ )
+{
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel.
+
+ @param[in] This A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL
+ instance.
+ @param[in, out] Target (TARGET_MAX_BYTES) of a SCSI device present on
+ the SCSI channel. On output, a pointer to the
+ Target ID (an array of TARGET_MAX_BYTES) of the
+ next SCSI device present on a SCSI channel.
+ An input value of 0xF(all bytes in the array are 0xF)
+ in the Target array retrieves the Target ID of the
+ first SCSI device present on a SCSI channel.
+
+ @retval EFI_SUCCESS The Target ID of the next SCSI device on the SCSI
+ channel was returned in Target.
+ @retval EFI_INVALID_PARAMETER Target or Lun is NULL.
+ @retval EFI_TIMEOUT Target array is not all 0xF, and Target was not
+ returned on a previous call to GetNextTarget().
+ @retval EFI_NOT_FOUND There are no more SCSI devices on this SCSI channel.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiExtScsiPassThruGetNextTarget (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *This,
+ IN OUT UINT8 **Target
+ )
+{
+ UINT8 TargetId[TARGET_MAX_BYTES];
+
+ SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);
+
+ if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {
+ (*Target)[0] = 0;
+ return EFI_SUCCESS;
+ } else if ((*Target)[0] == 0) {
+ return EFI_NOT_FOUND;
+ } else {
+ return EFI_INVALID_PARAMETER;
+ }
+}
+
diff --git a/NetworkPkg/IScsiDxe/IScsiIbft.c b/NetworkPkg/IScsiDxe/IScsiIbft.c
new file mode 100644
index 0000000000..e90c982bc2
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiIbft.c
@@ -0,0 +1,546 @@
+/** @file
+ Implementation for iSCSI Boot Firmware Table publication.
+
+Copyright (c) 2004 - 2011, 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"
+
+BOOLEAN mIbftInstalled = FALSE;
+UINTN mTableKey;
+
+/**
+ Initialize the header of the iSCSI Boot Firmware Table.
+
+ @param[out] Header The header of the iSCSI Boot Firmware Table.
+ @param[in] OemId The OEM ID.
+ @param[in] OemTableId The OEM table ID for the iBFT.
+
+**/
+VOID
+IScsiInitIbfTableHeader (
+ OUT EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Header,
+ IN UINT8 *OemId,
+ IN UINT64 *OemTableId
+ )
+{
+ Header->Signature = EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE;
+ Header->Length = IBFT_HEAP_OFFSET;
+ Header->Revision = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_REVISION;
+ Header->Checksum = 0;
+
+ CopyMem (Header->OemId, OemId, sizeof (Header->OemId));
+ CopyMem (&Header->OemTableId, OemTableId, sizeof (UINT64));
+}
+
+
+/**
+ Initialize the control section of the iSCSI Boot Firmware Table.
+
+ @param[in] Table The ACPI table.
+
+**/
+VOID
+IScsiInitControlSection (
+ IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table
+ )
+{
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
+ UINTN NumOffset;
+
+ Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
+
+ Control->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_ID;
+ Control->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_VERSION;
+ Control->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE);
+
+ //
+ // If in multipathing mode, enable the Boot Failover Flag.
+ // If in single path mode, disable it. Mix-model is not allowed.
+ //
+ // BUGBUG: if Boot Failover Flag is set to 1, the OS installer cannot
+ // find the iSCSI mapped disk. So still keep not set for single path mode.
+ //
+ if (mPrivate->EnableMpio) {
+ Control->Header.Flags = 0;
+ NumOffset = 2 * (mPrivate->MpioCount - mPrivate->Krb5MpioCount);
+ } else {
+ NumOffset = 2 * mPrivate->ValidSinglePathCount;
+ }
+
+ //
+ // Each attempt occupies two offsets: one for the NIC section;
+ // the other for the Target section.
+ //
+ if (NumOffset > 4) {
+ //
+ // Need expand the control section if more than 2 NIC/Target attempts
+ // exist.
+ //
+ Control->Header.Length = (UINT16) (Control->Header.Length + (NumOffset - 4) * sizeof (UINT16));
+ }
+}
+
+
+/**
+ Add one item into the heap.
+
+ @param[in, out] Heap On input, the current address of the heap. On output, the address of
+ the heap after the item is added.
+ @param[in] Data The data to add into the heap.
+ @param[in] Len Length of the Data in byte.
+
+**/
+VOID
+IScsiAddHeapItem (
+ IN OUT UINT8 **Heap,
+ IN VOID *Data,
+ IN UINTN Len
+ )
+{
+ //
+ // Add one byte for the NULL delimiter.
+ //
+ *Heap -= Len + 1;
+
+ CopyMem (*Heap, Data, Len);
+ *(*Heap + Len) = 0;
+}
+
+
+/**
+ Fill the Initiator section of the iSCSI Boot Firmware Table.
+
+ @param[in] Table The ACPI table.
+ @param[in, out] Heap The heap.
+
+**/
+VOID
+IScsiFillInitiatorSection (
+ IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
+ IN OUT UINT8 **Heap
+ )
+{
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *Initiator;
+
+ Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
+
+ //
+ // Initiator section immediately follows the control section.
+ //
+ Initiator = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *)
+ ((UINT8 *) Control + IBFT_ROUNDUP (Control->Header.Length));
+
+ Control->InitiatorOffset = (UINT16) ((UINTN) Initiator - (UINTN) Table);
+
+ Initiator->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_ID;
+ Initiator->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_VERSION;
+ Initiator->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE);
+ Initiator->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BLOCK_VALID |
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BOOT_SELECTED;
+
+ //
+ // Fill the iSCSI Initiator Name into the heap.
+ //
+ IScsiAddHeapItem (Heap, mPrivate->InitiatorName, mPrivate->InitiatorNameLength - 1);
+
+ Initiator->IScsiNameLength = (UINT16) (mPrivate->InitiatorNameLength - 1);
+ Initiator->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+}
+
+
+/**
+ Map the v4 IP address into v6 IP address.
+
+ @param[in] V4 The v4 IP address.
+ @param[out] V6 The v6 IP address.
+
+**/
+VOID
+IScsiMapV4ToV6Addr (
+ IN EFI_IPv4_ADDRESS *V4,
+ OUT EFI_IPv6_ADDRESS *V6
+ )
+{
+ UINTN Index;
+
+ ZeroMem (V6, sizeof (EFI_IPv6_ADDRESS));
+
+ V6->Addr[10] = 0xff;
+ V6->Addr[11] = 0xff;
+
+ for (Index = 0; Index < 4; Index++) {
+ V6->Addr[12 + Index] = V4->Addr[Index];
+ }
+}
+
+
+/**
+ Fill the NIC and target sections in iSCSI Boot Firmware Table.
+
+ @param[in] Table The buffer of the ACPI table.
+ @param[in, out] Heap The heap buffer used to store the variable length
+ parameters such as iSCSI name.
+
+**/
+VOID
+IScsiFillNICAndTargetSections (
+ IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table,
+ IN OUT UINT8 **Heap
+ )
+{
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *Control;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *Nic;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *Target;
+ ISCSI_SESSION_CONFIG_NVDATA *NvData;
+ ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;
+ UINT16 *SectionOffset;
+ UINTN Index;
+ UINT16 Length;
+ LIST_ENTRY *Entry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *Attempt;
+ ISCSI_NIC_INFO *NicInfo;
+ BOOLEAN Flag;
+
+ //
+ // Get the offset of the first Nic and Target section.
+ //
+ Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);
+ Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Table +
+ Control->InitiatorOffset + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE)));
+ Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +
+ IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));
+
+ SectionOffset = &Control->NIC0Offset;
+
+ Index = 0;
+ Flag = TRUE;
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
+ if (Index == 0) {
+ //
+ // First entry should be boot selected entry.
+ //
+ Attempt = IScsiConfigGetAttemptByConfigIndex (mPrivate->BootSelectedIndex);
+ if (Attempt == NULL) {
+ //
+ // First boot selected entry can not be found.
+ //
+ break;
+ }
+
+ ASSERT (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED);
+
+ } else {
+ if (Index == 1 && Flag) {
+ Entry = mPrivate->AttemptConfigs.ForwardLink;
+ Flag = FALSE;
+ }
+
+ Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ if (Attempt->AttemptConfigIndex == mPrivate->BootSelectedIndex) {
+ continue;
+ }
+ }
+
+ if (Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) {
+ continue;
+ }
+
+ //
+ // Krb5 attempt will not be recorded in iBFT.
+ //
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_KRB) {
+ continue;
+ }
+
+ //
+ // If multipath mode is enabled, only the attempts in MPIO will be recorded in iBFT.
+ //
+ if (mPrivate->EnableMpio && Attempt->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) {
+ continue;
+ }
+
+ //
+ // Only the valid attempts will be recorded.
+ //
+ if (!Attempt->ValidiBFTPath) {
+ continue;
+ }
+
+ NvData = &Attempt->SessionConfigData;
+ AuthConfig = &Attempt->AuthConfigData.CHAP;
+
+ //
+ // Fill the Nic section.
+ //
+
+ Nic->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_ID;
+ Nic->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_VERSION;
+ Nic->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE);
+ Nic->Header.Index = (UINT8) Index;
+ Nic->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BLOCK_VALID |
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_GLOBAL;
+
+ if (Index == 0) {
+ Nic->Header.Flags |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BOOT_SELECTED;
+ }
+
+ if (NvData->InitiatorInfoFromDhcp) {
+ Nic->Origin = IpPrefixOriginDhcp;
+ } else {
+ Nic->Origin = IpPrefixOriginManual;
+ }
+
+ if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) {
+ //
+ // Get the subnet mask prefix length.
+ //
+ Nic->SubnetMaskPrefixLength = IScsiGetSubnetMaskPrefixLength (&NvData->SubnetMask);
+
+ //
+ // Map the various v4 addresses into v6 addresses.
+ //
+ IScsiMapV4ToV6Addr (&NvData->LocalIp, &Nic->Ip);
+ IScsiMapV4ToV6Addr (&NvData->Gateway, &Nic->Gateway);
+ IScsiMapV4ToV6Addr (&Attempt->PrimaryDns.v4, &Nic->PrimaryDns);
+ IScsiMapV4ToV6Addr (&Attempt->SecondaryDns.v4, &Nic->SecondaryDns);
+ IScsiMapV4ToV6Addr (&Attempt->DhcpServer.v4, &Nic->DhcpServer);
+
+ } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) {
+ //
+ // TODO: The subnet mask/local ip/gateway/dhcpserver for iBFT-IPv6 needs to be
+ // confirmed with spec owner.
+ //
+
+ CopyMem (&Nic->PrimaryDns, &Attempt->PrimaryDns, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Nic->SecondaryDns, &Attempt->SecondaryDns, sizeof (EFI_IPv6_ADDRESS));
+ //
+ // TODO: DHCP server address cannot be retrieved by DHCPv6 process since
+ // DHCP server option is removed.
+ //CopyMem (&Nic->DhcpServer, &Attempt->DhcpServer, sizeof (EFI_IPv6_ADDRESS));
+ //
+ } else {
+ ASSERT (FALSE);
+ }
+
+ //
+ // Get Nic Info: VLAN tag, Mac address, PCI location.
+ //
+ NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex);
+ ASSERT (NicInfo != NULL);
+
+ Nic->VLanTag = NicInfo->VlanId;
+ CopyMem (Nic->Mac, &NicInfo->PermanentAddress, sizeof (Nic->Mac));
+ Nic->PciLocation = (UINT16) ((NicInfo->BusNumber << 8) |
+ (NicInfo->DeviceNumber << 3) | NicInfo->FunctionNumber);
+ *SectionOffset = (UINT16) ((UINTN) Nic - (UINTN) Table);
+ SectionOffset++;
+
+ //
+ // Fill the Target section.
+ //
+
+ Target->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_ID;
+ Target->Header.Version = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_VERSION;
+ Target->Header.Length = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE);
+ Target->Header.Index = (UINT8) Index;
+ Target->Header.Flags = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BLOCK_VALID;
+
+ if (Index == 0) {
+ Target->Header.Flags |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BOOT_SELECTED;
+ }
+
+ Target->Port = NvData->TargetPort;
+
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ Target->CHAPType = AuthConfig->CHAPType;
+ } else if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_NONE) {
+ Target->CHAPType = ISCSI_AUTH_TYPE_NONE;
+ }
+
+ Target->NicIndex = (UINT8) Index;
+
+ if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) {
+ IScsiMapV4ToV6Addr (&NvData->TargetIp.v4, &Target->Ip);
+ } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) {
+ CopyMem (&Target->Ip, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS));
+ } else {
+ ASSERT (FALSE);
+ }
+
+ CopyMem (Target->BootLun, NvData->BootLun, sizeof (Target->BootLun));
+
+ //
+ // Target iSCSI Name, CHAP name/secret, reverse CHAP name/secret.
+ //
+ Length = (UINT16) AsciiStrLen (NvData->TargetName);
+ IScsiAddHeapItem (Heap, NvData->TargetName, Length);
+
+ Target->IScsiNameLength = Length;
+ Target->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
+ //
+ // CHAP Name
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->CHAPName);
+ IScsiAddHeapItem (Heap, AuthConfig->CHAPName, Length);
+ Target->CHAPNameLength = Length;
+ Target->CHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ //
+ // CHAP Secret
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->CHAPSecret);
+ IScsiAddHeapItem (Heap, AuthConfig->CHAPSecret, Length);
+ Target->CHAPSecretLength = Length;
+ Target->CHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ if (Target->CHAPType == ISCSI_CHAP_MUTUAL) {
+ //
+ // Reverse CHAP Name.
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPName);
+ IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPName, Length);
+ Target->ReverseCHAPNameLength = Length;
+ Target->ReverseCHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+
+ //
+ // Reverse CHAP Secret.
+ //
+ Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPSecret);
+ IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPSecret, Length);
+ Target->ReverseCHAPSecretLength = Length;
+ Target->ReverseCHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);
+ }
+ }
+
+ *SectionOffset = (UINT16) ((UINTN) Target - (UINTN) Table);
+ SectionOffset++;
+
+ //
+ // Advance to the next NIC/Target pair.
+ //
+ Nic = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Target +
+ IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE)));
+ Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +
+ IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));
+
+ Index++;
+ }
+}
+
+
+/**
+ Publish and remove the iSCSI Boot Firmware Table according to the iSCSI
+ session status.
+
+**/
+VOID
+IScsiPublishIbft (
+ IN VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol;
+ EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table;
+ EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *Rsdp;
+ EFI_ACPI_DESCRIPTION_HEADER *Rsdt;
+ UINT8 *Heap;
+ UINT8 Checksum;
+ UINTN Index;
+
+
+ Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol);
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+
+ //
+ // Find ACPI table RSD_PTR from the system table.
+ //
+ for (Index = 0, Rsdp = NULL; Index < gST->NumberOfTableEntries; Index++) {
+ if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi20TableGuid) ||
+ CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi10TableGuid) ||
+ CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpiTableGuid)
+ ) {
+ //
+ // A match was found.
+ //
+ Rsdp = (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *) gST->ConfigurationTable[Index].VendorTable;
+ break;
+ }
+ }
+
+ if (Rsdp == NULL) {
+ return ;
+ } else {
+ Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress;
+ }
+
+ if (mIbftInstalled) {
+ Status = AcpiTableProtocol->UninstallAcpiTable (
+ AcpiTableProtocol,
+ mTableKey
+ );
+ if (EFI_ERROR (Status)) {
+ return ;
+ }
+ mIbftInstalled = FALSE;
+ }
+
+ //
+ // If there is no valid attempt configuration, just return.
+ //
+ if ((!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount == 0) ||
+ (mPrivate->EnableMpio && mPrivate->MpioCount <= mPrivate->Krb5MpioCount)) {
+ return ;
+ }
+
+ //
+ // Allocate 4k bytes to hold the ACPI table.
+ //
+ Table = AllocateZeroPool (IBFT_MAX_SIZE);
+ if (Table == NULL) {
+ return ;
+ }
+
+ Heap = (UINT8 *) Table + IBFT_HEAP_OFFSET;
+
+ //
+ // Fill in the various section of the iSCSI Boot Firmware Table.
+ //
+ IScsiInitIbfTableHeader (Table, Rsdt->OemId, &Rsdt->OemTableId);
+ IScsiInitControlSection (Table);
+ IScsiFillInitiatorSection (Table, &Heap);
+ IScsiFillNICAndTargetSections (Table, &Heap);
+
+ Checksum = CalculateCheckSum8((UINT8 *)Table, Table->Length);
+ Table->Checksum = Checksum;
+
+ //
+ // Install or update the iBFT table.
+ //
+ Status = AcpiTableProtocol->InstallAcpiTable (
+ AcpiTableProtocol,
+ Table,
+ Table->Length,
+ &mTableKey
+ );
+ if (EFI_ERROR(Status)) {
+ return;
+ }
+
+ mIbftInstalled = TRUE;
+ FreePool (Table);
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiIbft.h b/NetworkPkg/IScsiDxe/IScsiIbft.h
new file mode 100644
index 0000000000..35d5216419
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiIbft.h
@@ -0,0 +1,39 @@
+/** @file
+ Some extra definitions for iBFT.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_IBFT_H_
+#define _ISCSI_IBFT_H_
+
+#include <IndustryStandard/Acpi.h>
+#include <IndustryStandard/IScsiBootFirmwareTable.h>
+#include <Protocol/AcpiTable.h>
+#include <Protocol/PciIo.h>
+
+#define IBFT_TABLE_VAR_NAME L"iBFT"
+#define IBFT_MAX_SIZE 4096
+#define IBFT_HEAP_OFFSET 2048
+
+#define IBFT_ROUNDUP(size) NET_ROUNDUP ((size), EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_STRUCTURE_ALIGNMENT)
+
+/**
+ Publish and remove the iSCSI Boot Firmware Table according to the iSCSI
+ session status.
+
+**/
+VOID
+IScsiPublishIbft (
+ IN VOID
+ );
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiImpl.h b/NetworkPkg/IScsiDxe/IScsiImpl.h
new file mode 100644
index 0000000000..ccb83edb84
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiImpl.h
@@ -0,0 +1,197 @@
+/** @file
+ The shared head file for iSCSI driver.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_IMPL_H_
+#define _ISCSI_IMPL_H_
+
+#include <Uefi.h>
+
+#include <Protocol/ComponentName.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/HiiConfigAccess.h>
+
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Tcp4.h>
+#include <Protocol/Tcp6.h>
+
+#include <Protocol/AuthenticationInfo.h>
+#include <Protocol/IScsiInitiatorName.h>
+#include <Protocol/ScsiPassThruExt.h>
+
+#include <Library/HiiLib.h>
+#include <Library/UefiHiiServicesLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DpcLib.h>
+#include <Library/NetLib.h>
+#include <Library/TcpIoLib.h>
+#include <Library/BaseCryptLib.h>
+
+#include <Guid/MdeModuleHii.h>
+#include <Guid/EventGroup.h>
+#include <Guid/Acpi.h>
+
+#include "IScsiConfigNVDataStruc.h"
+#include "IScsiDriver.h"
+#include "IScsiProto.h"
+#include "IScsiCHAP.h"
+#include "IScsiDhcp.h"
+#include "IScsiDhcp6.h"
+#include "IScsiIbft.h"
+#include "IScsiMisc.h"
+#include "IScsiConfig.h"
+
+#define ISCSI_AUTH_INITIAL 0
+
+#define ISCSI_SESSION_SIGNATURE SIGNATURE_32 ('I', 'S', 'S', 'N')
+///
+/// 10 seconds
+///
+#define ISCSI_GET_MAPPING_TIMEOUT 100000000U
+///
+/// 3 seconds
+///
+#define ISCSI_WAIT_IPSEC_TIMEOUT 30000000U
+
+struct _ISCSI_SESSION {
+ UINT32 Signature;
+
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;
+
+ UINT8 AuthType;
+ union {
+ ISCSI_CHAP_AUTH_DATA CHAP;
+ } AuthData;
+
+ UINT8 State;
+
+ UINT8 Isid[6];
+ UINT16 Tsih;
+
+ UINT32 CmdSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+
+ UINT32 InitiatorTaskTag;
+ UINT16 NextCid;
+
+ LIST_ENTRY Conns;
+ UINT32 NumConns;
+
+ LIST_ENTRY TcbList;
+
+ //
+ // Session-wide parameters
+ //
+ UINT16 TargetPortalGroupTag;
+ UINT32 MaxConnections;
+ BOOLEAN InitialR2T;
+ BOOLEAN ImmediateData;
+ UINT32 MaxBurstLength;
+ UINT32 FirstBurstLength;
+ UINT32 DefaultTime2Wait;
+ UINT32 DefaultTime2Retain;
+ UINT16 MaxOutstandingR2T;
+ BOOLEAN DataPDUInOrder;
+ BOOLEAN DataSequenceInOrder;
+ UINT8 ErrorRecoveryLevel;
+};
+
+#define ISCSI_CONNECTION_SIGNATURE SIGNATURE_32 ('I', 'S', 'C', 'N')
+
+struct _ISCSI_CONNECTION {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ EFI_EVENT TimeoutEvent;
+
+ ISCSI_SESSION *Session;
+
+ UINT8 State;
+ UINT8 CurrentStage;
+ UINT8 NextStage;
+
+ UINT8 AuthStep;
+
+ BOOLEAN PartialReqSent;
+ BOOLEAN PartialRspRcvd;
+
+ BOOLEAN TransitInitiated;
+ BOOLEAN ParamNegotiated;
+
+ UINT16 Cid;
+ UINT32 ExpStatSN;
+
+ //
+ // Queues...
+ //
+ NET_BUF_QUEUE RspQue;
+
+ BOOLEAN Ipv6Flag;
+ TCP_IO TcpIo;
+
+ //
+ // Connection-only parameters.
+ //
+ UINT32 MaxRecvDataSegmentLength;
+ ISCSI_DIGEST_TYPE HeaderDigest;
+ ISCSI_DIGEST_TYPE DataDigest;
+};
+
+#define ISCSI_DRIVER_DATA_SIGNATURE SIGNATURE_32 ('I', 'S', 'D', 'A')
+
+#define ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU(PassThru) \
+ CR ( \
+ PassThru, \
+ ISCSI_DRIVER_DATA, \
+ IScsiExtScsiPassThru, \
+ ISCSI_DRIVER_DATA_SIGNATURE \
+ )
+
+#define ISCSI_DRIVER_DATA_FROM_IDENTIFIER(Identifier) \
+ CR ( \
+ Identifier, \
+ ISCSI_DRIVER_DATA, \
+ IScsiIdentifier, \
+ ISCSI_DRIVER_DATA_SIGNATURE \
+ )
+
+struct _ISCSI_DRIVER_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Image;
+ EFI_HANDLE Controller;
+ ISCSI_PRIVATE_PROTOCOL IScsiIdentifier;
+
+ EFI_EVENT ExitBootServiceEvent;
+
+ EFI_EXT_SCSI_PASS_THRU_PROTOCOL IScsiExtScsiPassThru;
+ EFI_EXT_SCSI_PASS_THRU_MODE ExtScsiPassThruMode;
+ EFI_HANDLE ExtScsiPassThruHandle;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE ChildHandle;
+ ISCSI_SESSION *Session;
+};
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiInitiatorName.c b/NetworkPkg/IScsiDxe/IScsiInitiatorName.c
new file mode 100644
index 0000000000..7a3934c5c9
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiInitiatorName.c
@@ -0,0 +1,136 @@
+/** @file
+ Implementation for EFI iSCSI Initiator Name Protocol.
+
+Copyright (c) 2004 - 2011, 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_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName = {
+ IScsiGetInitiatorName,
+ IScsiSetInitiatorName
+};
+
+
+/**
+ Retrieves the current set value of iSCSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL
+ instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer /
+ Actual size of the variable data buffer.
+ @param[out] Buffer Pointer to the buffer for data to be read.
+ The data is a null-terminated UTF-8 encoded string.
+ The maximum length is 223 characters, including the null-terminator.
+
+ @retval EFI_SUCCESS Data was successfully retrieved into the provided
+ buffer and the BufferSize was sufficient to handle
+ the iSCSI initiator name.
+ @retval EFI_BUFFER_TOO_SMALL BufferSize is too small for the result. BufferSize
+ will be updated with the size required to complete
+ the request. Buffer will not be affected.
+ @retval EFI_INVALID_PARAMETER BufferSize is NULL. BufferSize and Buffer will not
+ be affected.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be
+ affected.
+ @retval EFI_DEVICE_ERROR The iSCSI initiator name could not be retrieved
+ due to a hardware error.
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiGetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+
+ if ((BufferSize == NULL) || (Buffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = gRT->GetVariable (
+ ISCSI_INITIATOR_NAME_VAR_NAME,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ NULL,
+ BufferSize,
+ Buffer
+ );
+
+ return Status;
+}
+
+
+/**
+ Sets the iSSI Initiator Name.
+
+ @param[in] This Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL
+ instance.
+ @param[in, out] BufferSize Size of the buffer in bytes pointed to by Buffer.
+ @param[in] Buffer Pointer to the buffer for data to be written.
+ The data is a null-terminated UTF-8 encoded string.
+ The maximum length is 223 characters, including the null-terminator.
+
+ @retval EFI_SUCCESS Data was successfully stored by the protocol.
+ @retval EFI_UNSUPPORTED Platform policies do not allow for data to be
+ written.
+ @retval EFI_INVALID_PARAMETER BufferSize exceeds the maximum allowed limit.
+ BufferSize will be updated with the maximum size
+ required to complete the request.
+ @retval EFI_INVALID_PARAMETER Buffersize is NULL. BufferSize and Buffer will not
+ be affected.
+ @retval EFI_INVALID_PARAMETER Buffer is NULL. BufferSize and Buffer will not be
+ affected.
+ @retval EFI_DEVICE_ERROR The data could not be stored due to a hardware
+ error.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the data
+ @retval EFI_PROTOCOL_ERROR Input iSCSI initiator name does not adhere to RFC
+ 3720
+
+**/
+EFI_STATUS
+EFIAPI
+IScsiSetInitiatorName (
+ IN EFI_ISCSI_INITIATOR_NAME_PROTOCOL *This,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer
+ )
+{
+ EFI_STATUS Status;
+
+ if ((BufferSize == NULL) || (Buffer == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*BufferSize > ISCSI_NAME_MAX_SIZE) {
+ *BufferSize = ISCSI_NAME_MAX_SIZE;
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Only support iqn iSCSI names.
+ //
+ Status = IScsiNormalizeName ((CHAR8 *) Buffer, *BufferSize - 1);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gRT->SetVariable (
+ ISCSI_INITIATOR_NAME_VAR_NAME,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ *BufferSize,
+ Buffer
+ );
+
+ return Status;
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiMisc.c b/NetworkPkg/IScsiDxe/IScsiMisc.c
new file mode 100644
index 0000000000..a697659f49
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiMisc.c
@@ -0,0 +1,1320 @@
+/** @file
+ Miscellaneous routines for iSCSI driver.
+
+Copyright (c) 2004 - 2011, 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"
+
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8 IScsiHexString[] = "0123456789ABCDEFabcdef";
+
+/**
+ Removes (trims) specified leading and trailing characters from a string.
+
+ @param[in, out] Str Pointer to the null-terminated string to be trimmed.
+ On return, Str will hold the trimmed string.
+
+ @param[in] CharC Character will be trimmed from str.
+
+**/
+VOID
+IScsiStrTrim (
+ IN OUT CHAR16 *Str,
+ IN CHAR16 CharC
+ )
+{
+ CHAR16 *Pointer1;
+ CHAR16 *Pointer2;
+
+ if (*Str == 0) {
+ return ;
+ }
+
+ //
+ // Trim off the leading and trailing characters c
+ //
+ for (Pointer1 = Str; (*Pointer1 != 0) && (*Pointer1 == CharC); Pointer1++) {
+ ;
+ }
+
+ Pointer2 = Str;
+ if (Pointer2 == Pointer1) {
+ while (*Pointer1 != 0) {
+ Pointer2++;
+ Pointer1++;
+ }
+ } else {
+ while (*Pointer1 != 0) {
+ *Pointer2 = *Pointer1;
+ Pointer1++;
+ Pointer2++;
+ }
+ *Pointer2 = 0;
+ }
+
+
+ for (Pointer1 = Str + StrLen(Str) - 1; Pointer1 >= Str && *Pointer1 == CharC; Pointer1--) {
+ ;
+ }
+ if (Pointer1 != Str + StrLen(Str) - 1) {
+ *(Pointer1 + 1) = 0;
+ }
+}
+
+/**
+ Calculate the prefix length of the IPv4 subnet mask.
+
+ @param[in] SubnetMask The IPv4 subnet mask.
+
+ @return The prefix length of the subnet mask.
+ @retval 0 Other errors as indicated.
+
+**/
+UINT8
+IScsiGetSubnetMaskPrefixLength (
+ IN EFI_IPv4_ADDRESS *SubnetMask
+ )
+{
+ UINT8 Len;
+ UINT32 ReverseMask;
+
+ //
+ // The SubnetMask is in network byte order.
+ //
+ ReverseMask = (SubnetMask->Addr[0] << 24) | (SubnetMask->Addr[1] << 16) | (SubnetMask->Addr[2] << 8) | (SubnetMask->Addr[3]);
+
+ //
+ // Reverse it.
+ //
+ ReverseMask = ~ReverseMask;
+
+ if ((ReverseMask & (ReverseMask + 1)) != 0) {
+ return 0;
+ }
+
+ Len = 0;
+
+ while (ReverseMask != 0) {
+ ReverseMask = ReverseMask >> 1;
+ Len++;
+ }
+
+ return (UINT8) (32 - Len);
+}
+
+
+/**
+ Convert the hexadecimal encoded LUN string into the 64-bit LUN.
+
+ @param[in] Str The hexadecimal encoded LUN string.
+ @param[out] Lun Storage to return the 64-bit LUN.
+
+ @retval EFI_SUCCESS The 64-bit LUN is stored in Lun.
+ @retval EFI_INVALID_PARAMETER The string is malformatted.
+
+**/
+EFI_STATUS
+IScsiAsciiStrToLun (
+ IN CHAR8 *Str,
+ OUT UINT8 *Lun
+ )
+{
+ UINTN Index, IndexValue, IndexNum, SizeStr;
+ CHAR8 TemStr[2];
+ UINT8 TemValue;
+ UINT16 Value[4];
+
+ ZeroMem (Lun, 8);
+ ZeroMem (TemStr, 2);
+ ZeroMem ((UINT8 *) Value, sizeof (Value));
+ SizeStr = AsciiStrLen (Str);
+ IndexValue = 0;
+ IndexNum = 0;
+
+ for (Index = 0; Index < SizeStr; Index ++) {
+ TemStr[0] = Str[Index];
+ TemValue = (UINT8) AsciiStrHexToUint64 (TemStr);
+ if (TemValue == 0 && TemStr[0] != '0') {
+ if ((TemStr[0] != '-') || (IndexNum == 0)) {
+ //
+ // Invalid Lun Char.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ if ((TemValue == 0) && (TemStr[0] == '-')) {
+ //
+ // Next Lun value.
+ //
+ if (++IndexValue >= 4) {
+ //
+ // Max 4 Lun value.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+ //
+ // Restart str index for the next lun value.
+ //
+ IndexNum = 0;
+ continue;
+ }
+
+ if (++IndexNum > 4) {
+ //
+ // Each Lun Str can't exceed size 4, because it will be as UINT16 value.
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Combine UINT16 value.
+ //
+ Value[IndexValue] = (UINT16) ((Value[IndexValue] << 4) + TemValue);
+ }
+
+ for (Index = 0; Index <= IndexValue; Index ++) {
+ *((UINT16 *) &Lun[Index * 2]) = HTONS (Value[Index]);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Convert the 64-bit LUN into the hexadecimal encoded LUN string.
+
+ @param[in] Lun The 64-bit LUN.
+ @param[out] Str The storage to return the hexadecimal encoded LUN string.
+
+**/
+VOID
+IScsiLunToUnicodeStr (
+ IN UINT8 *Lun,
+ OUT CHAR16 *Str
+ )
+{
+ UINTN Index;
+ CHAR16 *TempStr;
+
+ TempStr = Str;
+
+ for (Index = 0; Index < 4; Index++) {
+
+ if ((Lun[2 * Index] | Lun[2 * Index + 1]) == 0) {
+ StrCpy (TempStr, L"0-");
+ } else {
+ TempStr[0] = (CHAR16) IScsiHexString[Lun[2 * Index] >> 4];
+ TempStr[1] = (CHAR16) IScsiHexString[Lun[2 * Index] & 0x0F];
+ TempStr[2] = (CHAR16) IScsiHexString[Lun[2 * Index + 1] >> 4];
+ TempStr[3] = (CHAR16) IScsiHexString[Lun[2 * Index + 1] & 0x0F];
+ TempStr[4] = L'-';
+ TempStr[5] = 0;
+
+ IScsiStrTrim (TempStr, L'0');
+ }
+
+ TempStr += StrLen (TempStr);
+ }
+
+ Str[StrLen (Str) - 1] = 0;
+
+ for (Index = StrLen (Str) - 1; Index > 1; Index = Index - 2) {
+ if ((Str[Index] == L'0') && (Str[Index - 1] == L'-')) {
+ Str[Index - 1] = 0;
+ } else {
+ break;
+ }
+ }
+}
+
+/**
+ Convert the formatted IP address into the binary IP address.
+
+ @param[in] Str The UNICODE string.
+ @param[in] IpMode Indicates whether the IP address is v4 or v6.
+ @param[out] Ip The storage to return the ASCII string.
+
+ @retval EFI_SUCCESS The binary IP address is returned in Ip.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted or IpMode is
+ invalid.
+
+**/
+EFI_STATUS
+IScsiAsciiStrToIp (
+ IN CHAR8 *Str,
+ IN UINT8 IpMode,
+ OUT EFI_IP_ADDRESS *Ip
+ )
+{
+ EFI_STATUS Status;
+
+ if (IpMode == IP_MODE_IP4 || IpMode == IP_MODE_AUTOCONFIG_IP4) {
+ return NetLibAsciiStrToIp4 (Str, &Ip->v4);
+
+ } else if (IpMode == IP_MODE_IP6 || IpMode == IP_MODE_AUTOCONFIG_IP6) {
+ return NetLibAsciiStrToIp6 (Str, &Ip->v6);
+
+ } else if (IpMode == IP_MODE_AUTOCONFIG) {
+ Status = NetLibAsciiStrToIp4 (Str, &Ip->v4);
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+ return NetLibAsciiStrToIp6 (Str, &Ip->v6);
+
+ }
+
+ return EFI_INVALID_PARAMETER;
+}
+
+/**
+ Convert the mac address into a hexadecimal encoded "-" seperated string.
+
+ @param[in] Mac The mac address.
+ @param[in] Len Length in bytes of the mac address.
+ @param[in] VlanId VLAN ID of the network device.
+ @param[out] Str The storage to return the mac string.
+
+**/
+VOID
+IScsiMacAddrToStr (
+ IN EFI_MAC_ADDRESS *Mac,
+ IN UINT32 Len,
+ IN UINT16 VlanId,
+ OUT CHAR16 *Str
+ )
+{
+ UINT32 Index;
+ CHAR16 *String;
+
+ for (Index = 0; Index < Len; Index++) {
+ Str[3 * Index] = (CHAR16) IScsiHexString[(Mac->Addr[Index] >> 4) & 0x0F];
+ Str[3 * Index + 1] = (CHAR16) IScsiHexString[Mac->Addr[Index] & 0x0F];
+ Str[3 * Index + 2] = L'-';
+ }
+
+ String = &Str[3 * Index - 1] ;
+ if (VlanId != 0) {
+ String += UnicodeSPrint (String, 6 * sizeof (CHAR16), L"\\%04x", (UINTN) VlanId);
+ }
+
+ *String = L'\0';
+}
+
+/**
+ Convert the binary encoded buffer into a hexadecimal encoded string.
+
+ @param[in] BinBuffer The buffer containing the binary data.
+ @param[in] BinLength Length of the binary buffer.
+ @param[in, out] HexStr Pointer to the string.
+ @param[in, out] HexLength The length of the string.
+
+ @retval EFI_SUCCESS The binary data is converted to the hexadecimal string
+ and the length of the string is updated.
+ @retval EFI_BUFFER_TOO_SMALL The string is too small.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+
+**/
+EFI_STATUS
+IScsiBinToHex (
+ IN UINT8 *BinBuffer,
+ IN UINT32 BinLength,
+ IN OUT CHAR8 *HexStr,
+ IN OUT UINT32 *HexLength
+ )
+{
+ UINTN Index;
+
+ if ((HexStr == NULL) || (BinBuffer == NULL) || (BinLength == 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((*HexLength) - 3) < BinLength * 2) {
+ *HexLength = BinLength * 2 + 3;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ *HexLength = BinLength * 2 + 3;
+ //
+ // Prefix for Hex String.
+ //
+ HexStr[0] = '0';
+ HexStr[1] = 'x';
+
+ for (Index = 0; Index < BinLength; Index++) {
+ HexStr[Index * 2 + 2] = IScsiHexString[BinBuffer[Index] >> 4];
+ HexStr[Index * 2 + 3] = IScsiHexString[BinBuffer[Index] & 0xf];
+ }
+
+ HexStr[Index * 2 + 2] = '\0';
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Convert the hexadecimal string into a binary encoded buffer.
+
+ @param[in, out] BinBuffer The binary buffer.
+ @param[in, out] BinLength Length of the binary buffer.
+ @param[in] HexStr The hexadecimal string.
+
+ @retval EFI_SUCCESS The hexadecimal string is converted into a binary
+ encoded buffer.
+ @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.
+
+**/
+EFI_STATUS
+IScsiHexToBin (
+ IN OUT UINT8 *BinBuffer,
+ IN OUT UINT32 *BinLength,
+ IN CHAR8 *HexStr
+ )
+{
+ UINTN Index;
+ UINTN Length;
+ UINT8 Digit;
+ CHAR8 TemStr[2];
+
+ ZeroMem (TemStr, sizeof (TemStr));
+
+ //
+ // Find out how many hex characters the string has.
+ //
+ if ((HexStr[0] == '0') && ((HexStr[1] == 'x') || (HexStr[1] == 'X'))) {
+ HexStr += 2;
+ }
+
+ Length = AsciiStrLen (HexStr);
+
+ for (Index = 0; Index < Length; Index ++) {
+ TemStr[0] = HexStr[Index];
+ Digit = (UINT8) AsciiStrHexToUint64 (TemStr);
+ if (Digit == 0 && TemStr[0] != '0') {
+ //
+ // Invalid Lun Char.
+ //
+ break;
+ }
+ if ((Index & 1) == 0) {
+ BinBuffer [Index/2] = Digit;
+ } else {
+ BinBuffer [Index/2] = (UINT8) ((BinBuffer [Index/2] << 4) + Digit);
+ }
+ }
+
+ *BinLength = (UINT32) ((Index + 1)/2);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Convert the decimal-constant string or hex-constant string into a numerical value.
+
+ @param[in] Str String in decimal or hex.
+
+ @return The numerical value.
+
+**/
+UINTN
+IScsiNetNtoi (
+ IN CHAR8 *Str
+ )
+{
+ if ((Str[0] == '0') && ((Str[1] == 'x') || (Str[1] == 'X'))) {
+ Str += 2;
+
+ return AsciiStrHexToUintn (Str);
+ }
+
+ return AsciiStrDecimalToUintn (Str);
+}
+
+
+/**
+ Generate random numbers.
+
+ @param[in, out] Rand The buffer to contain random numbers.
+ @param[in] RandLength The length of the Rand buffer.
+
+**/
+VOID
+IScsiGenRandom (
+ IN OUT UINT8 *Rand,
+ IN UINTN RandLength
+ )
+{
+ UINT32 Random;
+
+ while (RandLength > 0) {
+ Random = NET_RANDOM (NetRandomInitSeed ());
+ *Rand++ = (UINT8) (Random);
+ RandLength--;
+ }
+}
+
+
+/**
+ Record the NIC info in global structure.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval EFI_SUCCESS The operation is completed.
+ @retval EFI_OUT_OF_RESOURCES Do not have sufficient resources to finish this
+ operation.
+
+**/
+EFI_STATUS
+IScsiAddNic (
+ IN EFI_HANDLE Controller
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_NIC_INFO *NicInfo;
+ LIST_ENTRY *Entry;
+ EFI_MAC_ADDRESS MacAddr;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+
+ //
+ // Get MAC address of this network device.
+ //
+ Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get VLAN ID of this network device.
+ //
+ VlanId = NetLibGetVlanId (Controller);
+
+ //
+ // Check whether the NIC info already exists. Return directly if so.
+ //
+ NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
+ NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
+ if (NicInfo->HwAddressSize == HwAddressSize &&
+ CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&
+ NicInfo->VlanId == VlanId) {
+ mPrivate->CurrentNic = NicInfo->NicIndex;
+ return EFI_SUCCESS;
+ }
+
+ if (mPrivate->MaxNic < NicInfo->NicIndex) {
+ mPrivate->MaxNic = NicInfo->NicIndex;
+ }
+ }
+
+ //
+ // Record the NIC info in private structure.
+ //
+ NicInfo = AllocateZeroPool (sizeof (ISCSI_NIC_INFO));
+ if (NicInfo == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ CopyMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize);
+ NicInfo->HwAddressSize = (UINT32) HwAddressSize;
+ NicInfo->VlanId = VlanId;
+ NicInfo->NicIndex = (UINT8) (mPrivate->MaxNic + 1);
+ mPrivate->MaxNic = NicInfo->NicIndex;
+
+ //
+ // Get the PCI location.
+ //
+ IScsiGetNICPciLocation (
+ Controller,
+ &NicInfo->BusNumber,
+ &NicInfo->DeviceNumber,
+ &NicInfo->FunctionNumber
+ );
+
+ InsertTailList (&mPrivate->NicInfoList, &NicInfo->Link);
+ mPrivate->NicCount++;
+
+ mPrivate->CurrentNic = NicInfo->NicIndex;
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Delete the recorded NIC info from global structure. Also delete corresponding
+ attempts.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval EFI_SUCCESS The operation is completed.
+ @retval EFI_NOT_FOUND The NIC info to be deleted is not recorded.
+
+**/
+EFI_STATUS
+IScsiRemoveNic (
+ IN EFI_HANDLE Controller
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_NIC_INFO *NicInfo;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_NIC_INFO *ThisNic;
+ EFI_MAC_ADDRESS MacAddr;
+ UINTN HwAddressSize;
+ UINT16 VlanId;
+
+ //
+ // Get MAC address of this network device.
+ //
+ Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get VLAN ID of this network device.
+ //
+ VlanId = NetLibGetVlanId (Controller);
+
+ //
+ // Check whether the NIC information exists.
+ //
+ ThisNic = NULL;
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
+ NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
+ if (NicInfo->HwAddressSize == HwAddressSize &&
+ CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&
+ NicInfo->VlanId == VlanId) {
+
+ ThisNic = NicInfo;
+ break;
+ }
+ }
+
+ if (ThisNic == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ mPrivate->CurrentNic = ThisNic->NicIndex;
+
+ RemoveEntryList (&ThisNic->Link);
+ FreePool (ThisNic);
+ mPrivate->NicCount--;
+
+ //
+ // Remove all attempts related to this NIC.
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
+ AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
+ if (AttemptConfigData->NicIndex == mPrivate->CurrentNic) {
+ RemoveEntryList (&AttemptConfigData->Link);
+ mPrivate->AttemptCount--;
+
+ if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO && mPrivate->MpioCount > 0) {
+ if (--mPrivate->MpioCount == 0) {
+ mPrivate->EnableMpio = FALSE;
+ }
+
+ if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB && mPrivate->Krb5MpioCount > 0) {
+ mPrivate->Krb5MpioCount--;
+ }
+
+ } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED && mPrivate->SinglePathCount > 0) {
+ mPrivate->SinglePathCount--;
+
+ if (mPrivate->ValidSinglePathCount > 0) {
+ mPrivate->ValidSinglePathCount--;
+ }
+ }
+
+ FreePool (AttemptConfigData);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the recorded NIC info from global structure by the Index.
+
+ @param[in] NicIndex The index indicates the position of NIC info.
+
+ @return Pointer to the NIC info, or NULL if not found.
+
+**/
+ISCSI_NIC_INFO *
+IScsiGetNicInfoByIndex (
+ IN UINT8 NicIndex
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_NIC_INFO *NicInfo;
+
+ NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
+ NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
+ if (NicInfo->NicIndex == NicIndex) {
+ return NicInfo;
+ }
+ }
+
+ return NULL;
+}
+
+
+/**
+ Get the NIC's PCI location and return it accroding to the composited
+ format defined in iSCSI Boot Firmware Table.
+
+ @param[in] Controller The handle of the controller.
+ @param[out] Bus The bus number.
+ @param[out] Device The device number.
+ @param[out] Function The function number.
+
+ @return The composited representation of the NIC PCI location.
+
+**/
+UINT16
+IScsiGetNICPciLocation (
+ IN EFI_HANDLE Controller,
+ OUT UINTN *Bus,
+ OUT UINTN *Device,
+ OUT UINTN *Function
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_HANDLE PciIoHandle;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINTN Segment;
+
+ Status = gBS->HandleProtocol (
+ Controller,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ Status = gBS->LocateDevicePath (
+ &gEfiPciIoProtocolGuid,
+ &DevicePath,
+ &PciIoHandle
+ );
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ Status = PciIo->GetLocation (PciIo, &Segment, Bus, Device, Function);
+ if (EFI_ERROR (Status)) {
+ return 0;
+ }
+
+ return (UINT16) ((*Bus << 8) | (*Device << 3) | *Function);
+}
+
+
+/**
+ Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
+ buffer, and the size of the buffer. If failure, return NULL.
+
+ @param[in] Name String part of EFI variable name.
+ @param[in] VendorGuid GUID part of EFI variable name.
+ @param[out] VariableSize Returns the size of the EFI variable that was read.
+
+ @return Dynamically allocated memory that contains a copy of the EFI variable.
+ @return Caller is responsible freeing the buffer.
+ @retval NULL Variable was not read.
+
+**/
+VOID *
+IScsiGetVariableAndSize (
+ IN CHAR16 *Name,
+ IN EFI_GUID *VendorGuid,
+ OUT UINTN *VariableSize
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ VOID *Buffer;
+
+ Buffer = NULL;
+
+ //
+ // Pass in a zero size buffer to find the required buffer size.
+ //
+ BufferSize = 0;
+ Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // Allocate the buffer to return
+ //
+ Buffer = AllocateZeroPool (BufferSize);
+ if (Buffer == NULL) {
+ return NULL;
+ }
+ //
+ // Read variable into the allocated buffer.
+ //
+ Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
+ if (EFI_ERROR (Status)) {
+ BufferSize = 0;
+ }
+ }
+
+ *VariableSize = BufferSize;
+ return Buffer;
+}
+
+
+/**
+ Create the iSCSI driver data.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+
+ @return The iSCSI driver data created.
+ @retval NULL Other errors as indicated.
+
+**/
+ISCSI_DRIVER_DATA *
+IScsiCreateDriverData (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ EFI_STATUS Status;
+
+ Private = AllocateZeroPool (sizeof (ISCSI_DRIVER_DATA));
+ if (Private == NULL) {
+ return NULL;
+ }
+
+ Private->Signature = ISCSI_DRIVER_DATA_SIGNATURE;
+ Private->Image = Image;
+ Private->Controller = Controller;
+ Private->Session = NULL;
+
+ //
+ // Create an event to be signaled when the BS to RT transition is triggerd so
+ // as to abort the iSCSI session.
+ //
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ IScsiOnExitBootService,
+ Private,
+ &gEfiEventExitBootServicesGuid,
+ &Private->ExitBootServiceEvent
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Private);
+ return NULL;
+ }
+
+ Private->ExtScsiPassThruHandle = NULL;
+ CopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL));
+
+ //
+ // 0 is designated to the TargetId, so use another value for the AdapterId.
+ //
+ Private->ExtScsiPassThruMode.AdapterId = 2;
+ Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
+ Private->ExtScsiPassThruMode.IoAlign = 4;
+ Private->IScsiExtScsiPassThru.Mode = &Private->ExtScsiPassThruMode;
+
+ return Private;
+}
+
+
+/**
+ Clean the iSCSI driver data.
+
+ @param[in] Private The iSCSI driver data.
+
+**/
+VOID
+IScsiCleanDriverData (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ if (Private->DevicePath != NULL) {
+ gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiDevicePathProtocolGuid,
+ Private->DevicePath
+ );
+
+ FreePool (Private->DevicePath);
+ }
+
+ if (Private->ExtScsiPassThruHandle != NULL) {
+ Status = gBS->UninstallProtocolInterface (
+ Private->ExtScsiPassThruHandle,
+ &gEfiExtScsiPassThruProtocolGuid,
+ &Private->IScsiExtScsiPassThru
+ );
+ if (!EFI_ERROR (Status)) {
+ mPrivate->OneSessionEstablished = FALSE;
+ }
+ }
+
+ gBS->CloseEvent (Private->ExitBootServiceEvent);
+
+ FreePool (Private);
+}
+
+
+/**
+ Get the various configuration data.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCESS The configuration data is retrieved.
+ @retval EFI_NOT_FOUND This iSCSI driver is not configured yet.
+
+**/
+EFI_STATUS
+IScsiGetConfigData (
+ IN ISCSI_DRIVER_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 MacString[ISCSI_MAX_MAC_STRING_LEN];
+ UINTN Index;
+ ISCSI_NIC_INFO *NicInfo;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
+ ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp;
+ UINT8 *AttemptConfigOrder;
+ UINTN AttemptConfigOrderSize;
+ CHAR16 IScsiMode[64];
+ CHAR16 IpMode[64];
+
+ //
+ // There should be at least one attempt configured.
+ //
+ AttemptConfigOrder = IScsiGetVariableAndSize (
+ L"AttemptOrder",
+ &mVendorGuid,
+ &AttemptConfigOrderSize
+ );
+ if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Get the iSCSI Initiator Name.
+ //
+ mPrivate->InitiatorNameLength = ISCSI_NAME_MAX_SIZE;
+ Status = gIScsiInitiatorName.Get (
+ &gIScsiInitiatorName,
+ &mPrivate->InitiatorNameLength,
+ mPrivate->InitiatorName
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Get the normal configuration.
+ //
+ for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
+
+ //
+ // Check whether the attempt exists in AttemptConfig.
+ //
+ AttemptTmp = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
+ if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) {
+ continue;
+ } else if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled != ISCSI_DISABLED) {
+ //
+ // Check the autoconfig path to see whether it should be retried.
+ //
+ if (AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
+ AttemptTmp->AutoConfigureMode != IP_MODE_AUTOCONFIG_SUCCESS) {
+ if (mPrivate->Ipv6Flag &&
+ AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {
+ //
+ // Autoconfigure for IP6 already attempted but failed. Do not try again.
+ //
+ continue;
+ } else if (!mPrivate->Ipv6Flag &&
+ AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {
+ //
+ // Autoconfigure for IP4 already attempted but failed. Do not try again.
+ //
+ continue;
+ } else {
+ //
+ // Try another approach for this autoconfigure path.
+ //
+ AttemptTmp->AutoConfigureMode =
+ (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
+ AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
+ AttemptTmp->SessionConfigData.TargetInfoFromDhcp = TRUE;
+ AttemptTmp->DhcpSuccess = FALSE;
+
+ //
+ // Get some information from the dhcp server.
+ //
+ if (!mPrivate->Ipv6Flag) {
+ Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
+ if (!EFI_ERROR (Status)) {
+ AttemptTmp->DhcpSuccess = TRUE;
+ }
+ } else {
+ Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
+ if (!EFI_ERROR (Status)) {
+ AttemptTmp->DhcpSuccess = TRUE;
+ }
+ }
+
+ //
+ // Refresh the state of this attempt to NVR.
+ //
+ AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"%s%d",
+ MacString,
+ (UINTN) AttemptTmp->AttemptConfigIndex
+ );
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptTmp
+ );
+
+ continue;
+ }
+ } else if (AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp && !AttemptTmp->ValidPath) {
+ //
+ // Get DHCP information for already added, but failed, attempt.
+ //
+ AttemptTmp->DhcpSuccess = FALSE;
+ if (!mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP4)) {
+ Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
+ if (!EFI_ERROR (Status)) {
+ AttemptTmp->DhcpSuccess = TRUE;
+ }
+ } else if (mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP6)) {
+ Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
+ if (!EFI_ERROR (Status)) {
+ AttemptTmp->DhcpSuccess = TRUE;
+ }
+ }
+
+ //
+ // Refresh the state of this attempt to NVR.
+ //
+ AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"%s%d",
+ MacString,
+ (UINTN) AttemptTmp->AttemptConfigIndex
+ );
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptTmp
+ );
+
+ continue;
+
+ } else {
+ continue;
+ }
+ }
+
+ //
+ // This attempt does not exist in AttemptConfig. Try to add a new one.
+ //
+
+ NicInfo = IScsiGetNicInfoByIndex (mPrivate->CurrentNic);
+ ASSERT (NicInfo != NULL);
+ IScsiMacAddrToStr (&NicInfo->PermanentAddress, NicInfo->HwAddressSize, NicInfo->VlanId, MacString);
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) 128,
+ L"%s%d",
+ MacString,
+ (UINTN) AttemptConfigOrder[Index]
+ );
+
+ AttemptConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) GetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid
+ );
+
+ if (AttemptConfigData == NULL) {
+ continue;
+ }
+
+ ASSERT (AttemptConfigOrder[Index] == AttemptConfigData->AttemptConfigIndex);
+
+ AttemptConfigData->NicIndex = NicInfo->NicIndex;
+ AttemptConfigData->DhcpSuccess = FALSE;
+ AttemptConfigData->ValidiBFTPath = (BOOLEAN) (mPrivate->EnableMpio ? TRUE : FALSE);
+ AttemptConfigData->ValidPath = FALSE;
+
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
+ AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
+ AttemptConfigData->SessionConfigData.TargetInfoFromDhcp = TRUE;
+
+ AttemptConfigData->AutoConfigureMode =
+ (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
+ }
+
+ //
+ // Get some information from dhcp server.
+ //
+ if (AttemptConfigData->SessionConfigData.Enabled != ISCSI_DISABLED &&
+ AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp) {
+
+ if (!mPrivate->Ipv6Flag &&
+ (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4 ||
+ AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4)) {
+ Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptConfigData);
+ if (!EFI_ERROR (Status)) {
+ AttemptConfigData->DhcpSuccess = TRUE;
+ }
+ } else if (mPrivate->Ipv6Flag &&
+ (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6 ||
+ AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6)) {
+ Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptConfigData);
+ if (!EFI_ERROR (Status)) {
+ AttemptConfigData->DhcpSuccess = TRUE;
+ }
+ }
+
+ //
+ // Refresh the state of this attempt to NVR.
+ //
+ AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"%s%d",
+ MacString,
+ (UINTN) AttemptConfigData->AttemptConfigIndex
+ );
+
+ gRT->SetVariable (
+ mPrivate->PortString,
+ &gEfiIScsiInitiatorNameProtocolGuid,
+ ISCSI_CONFIG_VAR_ATTR,
+ sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
+ AttemptConfigData
+ );
+ }
+
+ //
+ // Update Attempt Help Info.
+ //
+
+ if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED) {
+ UnicodeSPrint (IScsiMode, 64, L"Disabled");
+ } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
+ UnicodeSPrint (IScsiMode, 64, L"Enabled");
+ } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");
+ }
+
+ if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {
+ UnicodeSPrint (IpMode, 64, L"IP4");
+ } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {
+ UnicodeSPrint (IpMode, 64, L"IP6");
+ } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
+ UnicodeSPrint (IpMode, 64, L"Autoconfigure");
+ }
+
+ UnicodeSPrint (
+ mPrivate->PortString,
+ (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
+ L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",
+ MacString,
+ NicInfo->BusNumber,
+ NicInfo->DeviceNumber,
+ NicInfo->FunctionNumber,
+ IScsiMode,
+ IpMode
+ );
+
+ AttemptConfigData->AttemptTitleHelpToken = HiiSetString (
+ mCallbackInfo->RegisteredHandle,
+ 0,
+ mPrivate->PortString,
+ NULL
+ );
+ ASSERT (AttemptConfigData->AttemptTitleHelpToken != 0);
+
+ //
+ // Record the attempt in global link list.
+ //
+ InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
+ mPrivate->AttemptCount++;
+
+ if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
+ mPrivate->MpioCount++;
+ mPrivate->EnableMpio = TRUE;
+
+ if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB) {
+ mPrivate->Krb5MpioCount++;
+ }
+ } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
+ mPrivate->SinglePathCount++;
+ }
+ }
+
+ //
+ // Reorder the AttemptConfig by the configured order.
+ //
+ for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
+ AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
+ if (AttemptConfigData == NULL) {
+ continue;
+ }
+
+ RemoveEntryList (&AttemptConfigData->Link);
+ InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
+ }
+
+ //
+ // Update the Main Form.
+ //
+ IScsiConfigUpdateAttempt ();
+
+ FreePool (AttemptConfigOrder);
+
+ //
+ // There should be at least one attempt configuration.
+ //
+ if (!mPrivate->EnableMpio) {
+ if (mPrivate->SinglePathCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+ mPrivate->ValidSinglePathCount = mPrivate->SinglePathCount;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Get the device path of the iSCSI tcp connection and update it.
+
+ @param Session The iSCSI session.
+
+ @return The updated device path.
+ @retval NULL Other errors as indicated.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+IScsiGetTcpConnDevicePath (
+ IN ISCSI_SESSION *Session
+ )
+{
+ ISCSI_CONNECTION *Conn;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_STATUS Status;
+ EFI_DEV_PATH *DPathNode;
+
+ if (Session->State != SESSION_STATE_LOGGED_IN) {
+ return NULL;
+ }
+
+ Conn = NET_LIST_USER_STRUCT_S (
+ Session->Conns.ForwardLink,
+ ISCSI_CONNECTION,
+ Link,
+ ISCSI_CONNECTION_SIGNATURE
+ );
+
+ Status = gBS->HandleProtocol (
+ Conn->TcpIo.Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ //
+ // Duplicate it.
+ //
+ DevicePath = DuplicateDevicePath (DevicePath);
+ if (DevicePath == NULL) {
+ return NULL;
+ }
+
+ DPathNode = (EFI_DEV_PATH *) DevicePath;
+
+ while (!IsDevicePathEnd (&DPathNode->DevPath)) {
+ if (DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) {
+ if (!Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP) {
+ DPathNode->Ipv4.LocalPort = 0;
+ DPathNode->Ipv4.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;
+ break;
+ } else if (Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv6_DP) {
+ DPathNode->Ipv6.LocalPort = 0;
+ DPathNode->Ipv6.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;
+ break;
+ }
+ }
+
+ DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath);
+ }
+
+ return DevicePath;
+}
+
+
+/**
+ Abort the session when the transition from BS to RT is initiated.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The iSCSI driver data.
+
+**/
+VOID
+EFIAPI
+IScsiOnExitBootService (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+
+ Private = (ISCSI_DRIVER_DATA *) Context;
+ gBS->CloseEvent (Private->ExitBootServiceEvent);
+
+ if (Private->Session != NULL) {
+ IScsiSessionAbort (Private->Session);
+ }
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiMisc.h b/NetworkPkg/IScsiDxe/IScsiMisc.h
new file mode 100644
index 0000000000..8646f666a4
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiMisc.h
@@ -0,0 +1,343 @@
+/** @file
+ Miscellaneous definitions for iSCSI driver.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_MISC_H_
+#define _ISCSI_MISC_H_
+
+typedef struct _ISCSI_DRIVER_DATA ISCSI_DRIVER_DATA;
+
+#pragma pack(1)
+typedef struct _ISCSI_SESSION_CONFIG_NVDATA {
+ UINT16 TargetPort;
+ UINT8 Enabled;
+ UINT8 IpMode;
+
+ EFI_IPv4_ADDRESS LocalIp;
+ EFI_IPv4_ADDRESS SubnetMask;
+ EFI_IPv4_ADDRESS Gateway;
+
+ BOOLEAN InitiatorInfoFromDhcp;
+ BOOLEAN TargetInfoFromDhcp;
+ CHAR8 TargetName[ISCSI_NAME_MAX_SIZE];
+ EFI_IP_ADDRESS TargetIp;
+ UINT8 BootLun[8];
+
+ UINT16 ConnectTimeout; ///< timout value in milliseconds
+ UINT8 ConnectRetryCount;
+ UINT8 IsId[6];
+} ISCSI_SESSION_CONFIG_NVDATA;
+#pragma pack()
+
+/**
+ Calculate the prefix length of the IPv4 subnet mask.
+
+ @param[in] SubnetMask The IPv4 subnet mask.
+
+ @return The prefix length of the subnet mask.
+ @retval 0 Other errors as indicated.
+
+**/
+UINT8
+IScsiGetSubnetMaskPrefixLength (
+ IN EFI_IPv4_ADDRESS *SubnetMask
+ );
+
+/**
+ Convert the hexadecimal encoded LUN string into the 64-bit LUN.
+
+ @param[in] Str The hexadecimal encoded LUN string.
+ @param[out] Lun Storage to return the 64-bit LUN.
+
+ @retval EFI_SUCCESS The 64-bit LUN is stored in Lun.
+ @retval EFI_INVALID_PARAMETER The string is malformatted.
+
+**/
+EFI_STATUS
+IScsiAsciiStrToLun (
+ IN CHAR8 *Str,
+ OUT UINT8 *Lun
+ );
+
+/**
+ Convert the 64-bit LUN into the hexadecimal encoded LUN string.
+
+ @param[in] Lun The 64-bit LUN.
+ @param[out] String The storage to return the hexadecimal encoded LUN string.
+
+**/
+VOID
+IScsiLunToUnicodeStr (
+ IN UINT8 *Lun,
+ OUT CHAR16 *String
+ );
+
+/**
+ Convert the mac address into a hexadecimal encoded "-" seperated string.
+
+ @param[in] Mac The mac address.
+ @param[in] Len Length in bytes of the mac address.
+ @param[in] VlanId VLAN ID of the network device.
+ @param[out] Str The storage to return the mac string.
+
+**/
+VOID
+IScsiMacAddrToStr (
+ IN EFI_MAC_ADDRESS *Mac,
+ IN UINT32 Len,
+ IN UINT16 VlanId,
+ OUT CHAR16 *Str
+ );
+
+/**
+ Convert the formatted IP address into the binary IP address.
+
+ @param[in] Str The UNICODE string.
+ @param[in] IpMode Indicates whether the IP address is v4 or v6.
+ @param[out] Ip The storage to return the ASCII string.
+
+ @retval EFI_SUCCESS The binary IP address is returned in Ip.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted or IpMode is
+ invalid.
+
+**/
+EFI_STATUS
+IScsiAsciiStrToIp (
+ IN CHAR8 *Str,
+ IN UINT8 IpMode,
+ OUT EFI_IP_ADDRESS *Ip
+ );
+
+/**
+ Convert the binary encoded buffer into a hexadecimal encoded string.
+
+ @param[in] BinBuffer The buffer containing the binary data.
+ @param[in] BinLength Length of the binary buffer.
+ @param[in, out] HexStr Pointer to the string.
+ @param[in, out] HexLength The length of the string.
+
+ @retval EFI_SUCCESS The binary data is converted to the hexadecimal string
+ and the length of the string is updated.
+ @retval EFI_BUFFER_TOO_SMALL The string is too small.
+ @retval EFI_INVALID_PARAMETER The IP string is malformatted.
+
+**/
+EFI_STATUS
+IScsiBinToHex (
+ IN UINT8 *BinBuffer,
+ IN UINT32 BinLength,
+ IN OUT CHAR8 *HexStr,
+ IN OUT UINT32 *HexLength
+ );
+
+/**
+ Convert the hexadecimal string into a binary encoded buffer.
+
+ @param[in, out] BinBuffer The binary buffer.
+ @param[in, out] BinLength Length of the binary buffer.
+ @param[in] HexStr The hexadecimal string.
+
+ @retval EFI_SUCCESS The hexadecimal string is converted into a binary
+ encoded buffer.
+ @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.
+
+**/
+EFI_STATUS
+IScsiHexToBin (
+ IN OUT UINT8 *BinBuffer,
+ IN OUT UINT32 *BinLength,
+ IN CHAR8 *HexStr
+ );
+
+
+/**
+ Convert the decimal-constant string or hex-constant string into a numerical value.
+
+ @param[in] Str String in decimal or hex.
+
+ @return The numerical value.
+
+**/
+UINTN
+IScsiNetNtoi (
+ IN CHAR8 *Str
+ );
+
+/**
+ Generate random numbers.
+
+ @param[in, out] Rand The buffer to contain random numbers.
+ @param[in] RandLength The length of the Rand buffer.
+
+**/
+VOID
+IScsiGenRandom (
+ IN OUT UINT8 *Rand,
+ IN UINTN RandLength
+ );
+
+/**
+ Record the NIC information in a global structure.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval EFI_SUCCESS The operation is completed.
+ @retval EFI_OUT_OF_RESOURCES Do not have sufficient resource to finish this
+ operation.
+
+**/
+EFI_STATUS
+IScsiAddNic (
+ IN EFI_HANDLE Controller
+ );
+
+/**
+ Delete the recorded NIC information from a global structure. Also delete corresponding
+ attempts.
+
+ @param[in] Controller The handle of the controller.
+
+ @retval EFI_SUCCESS The operation completed.
+ @retval EFI_NOT_FOUND The NIC information to be deleted is not recorded.
+
+**/
+EFI_STATUS
+IScsiRemoveNic (
+ IN EFI_HANDLE Controller
+ );
+
+/**
+ Get the recorded NIC information from a global structure by the Index.
+
+ @param[in] NicIndex The index indicates the position of NIC info.
+
+ @return Pointer to the NIC info or NULL if not found.
+
+**/
+ISCSI_NIC_INFO *
+IScsiGetNicInfoByIndex (
+ IN UINT8 NicIndex
+ );
+
+
+/**
+ Get the NIC's PCI location and return it accroding to the composited
+ format defined in iSCSI Boot Firmware Table.
+
+ @param[in] Controller The handle of the controller.
+ @param[out] Bus The bus number.
+ @param[out] Device The device number.
+ @param[out] Function The function number.
+
+ @return The composited representation of the NIC PCI location.
+
+**/
+UINT16
+IScsiGetNICPciLocation (
+ IN EFI_HANDLE Controller,
+ OUT UINTN *Bus,
+ OUT UINTN *Device,
+ OUT UINTN *Function
+ );
+
+/**
+ Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
+ buffer, and the size of the buffer. If failure, return NULL.
+
+ @param[in] Name String part of EFI variable name.
+ @param[in] VendorGuid GUID part of EFI variable name.
+ @param[out] VariableSize Returns the size of the EFI variable that was read.
+
+ @return Dynamically allocated memory that contains a copy of the EFI variable.
+ @return Caller is responsible freeing the buffer.
+ @retval NULL Variable was not read.
+
+**/
+VOID *
+IScsiGetVariableAndSize (
+ IN CHAR16 *Name,
+ IN EFI_GUID *VendorGuid,
+ OUT UINTN *VariableSize
+ );
+
+/**
+ Create the iSCSI driver data.
+
+ @param[in] Image The handle of the driver image.
+ @param[in] Controller The handle of the controller.
+
+ @return The iSCSI driver data created.
+ @retval NULL Other errors as indicated.
+
+**/
+ISCSI_DRIVER_DATA *
+IScsiCreateDriverData (
+ IN EFI_HANDLE Image,
+ IN EFI_HANDLE Controller
+ );
+
+/**
+ Clean the iSCSI driver data.
+
+ @param[in] Private The iSCSI driver data.
+
+**/
+VOID
+IScsiCleanDriverData (
+ IN ISCSI_DRIVER_DATA *Private
+ );
+
+/**
+ Get the various configuration data of this iSCSI instance.
+
+ @param[in] Private The iSCSI driver data.
+
+ @retval EFI_SUCCESS Obtained the configuration of this instance.
+ @retval EFI_ABORTED The operation was aborted.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiGetConfigData (
+ IN ISCSI_DRIVER_DATA *Private
+ );
+
+/**
+ Get the device path of the iSCSI tcp connection and update it.
+
+ @param[in] Session The iSCSI session data.
+
+ @return The updated device path.
+ @retval NULL Other errors as indicated.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+IScsiGetTcpConnDevicePath (
+ IN ISCSI_SESSION *Session
+ );
+
+/**
+ Abort the session when the transition from BS to RT is initiated.
+
+ @param[in] Event The event signaled.
+ @param[in] Context The iSCSI driver data.
+
+**/
+VOID
+EFIAPI
+IScsiOnExitBootService (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+#endif
diff --git a/NetworkPkg/IScsiDxe/IScsiProto.c b/NetworkPkg/IScsiDxe/IScsiProto.c
new file mode 100644
index 0000000000..2ab4da8cda
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiProto.c
@@ -0,0 +1,2991 @@
+/** @file
+ The implementation of iSCSI protocol based on RFC3720.
+
+Copyright (c) 2004 - 2011, 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"
+
+UINT32 mDataSegPad = 0;
+
+/**
+ Attach the iSCSI connection to the iSCSI session.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in, out] Conn The iSCSI connection.
+
+**/
+VOID
+IScsiAttatchConnection (
+ IN OUT ISCSI_SESSION *Session,
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ InsertTailList (&Session->Conns, &Conn->Link);
+ Conn->Session = Session;
+ Session->NumConns++;
+}
+
+/**
+ Detach the iSCSI connection from the session it belongs to.
+
+ @param[in, out] Conn The iSCSI connection.
+
+**/
+VOID
+IScsiDetatchConnection (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ RemoveEntryList (&Conn->Link);
+ Conn->Session->NumConns--;
+ Conn->Session = NULL;
+}
+
+
+/**
+ Check the sequence number according to RFC3720.
+
+ @param[in, out] ExpSN The currently expected sequence number.
+ @param[in] NewSN The sequence number to check.
+
+ @retval EFI_SUCCESS The check passed and the ExpSN is increased.
+ @retval EFI_NOT_READY Response was sent due to a retransmission request.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
+
+**/
+EFI_STATUS
+IScsiCheckSN (
+ IN OUT UINT32 *ExpSN,
+ IN UINT32 NewSN
+ )
+{
+ if (!ISCSI_SEQ_EQ (NewSN, *ExpSN)) {
+ if (ISCSI_SEQ_LT (NewSN, *ExpSN)) {
+ //
+ // Duplicate
+ //
+ return EFI_NOT_READY;
+ } else {
+ return EFI_PROTOCOL_ERROR;
+ }
+ } else {
+ //
+ // Advance the ExpSN
+ //
+ (*ExpSN)++;
+ return EFI_SUCCESS;
+ }
+}
+
+
+/**
+ Update the sequence numbers for the iSCSI command.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] MaxCmdSN Maximum CmdSN from the target.
+ @param[in] ExpCmdSN Next expected CmdSN from the target.
+
+**/
+VOID
+IScsiUpdateCmdSN (
+ IN OUT ISCSI_SESSION *Session,
+ IN UINT32 MaxCmdSN,
+ IN UINT32 ExpCmdSN
+ )
+{
+ if (ISCSI_SEQ_LT (MaxCmdSN, ExpCmdSN - 1)) {
+ return ;
+ }
+
+ if (ISCSI_SEQ_GT (MaxCmdSN, Session->MaxCmdSN)) {
+ Session->MaxCmdSN = MaxCmdSN;
+ }
+
+ if (ISCSI_SEQ_GT (ExpCmdSN, Session->ExpCmdSN)) {
+ Session->ExpCmdSN = ExpCmdSN;
+ }
+}
+
+
+/**
+ This function does the iSCSI connection login.
+
+ @param[in, out] Conn The iSCSI connection to login.
+ @param Timeout The timeout value in millisecond.
+
+ @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target.
+ @retval EFI_TIMEOUT Timeout occurred during the login procedure.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiConnLogin (
+ IN OUT ISCSI_CONNECTION *Conn,
+ IN UINT16 Timeout
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Start the timer, and wait Timeout seconds to establish the TCP connection.
+ //
+ Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout * TICKS_PER_MS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Try to establish the tcp connection.
+ //
+ Status = TcpIoConnect (&Conn->TcpIo, Conn->TimeoutEvent);
+ gBS->SetTimer (Conn->TimeoutEvent, TimerCancel, 0);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Conn->State = CONN_STATE_IN_LOGIN;
+
+ //
+ // Connection is established, start the iSCSI Login.
+ //
+ do {
+ Status = IScsiSendLoginReq (Conn);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+
+ Status = IScsiReceiveLoginRsp (Conn);
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ } while (Conn->CurrentStage != ISCSI_FULL_FEATURE_PHASE);
+
+ return Status;
+}
+
+
+/**
+ Reset the iSCSI connection.
+
+ @param[in, out] Conn The iSCSI connection to reset.
+
+**/
+VOID
+IScsiConnReset (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ TcpIoReset (&Conn->TcpIo);
+}
+
+
+/**
+ Create a TCP connection for the iSCSI session.
+
+ @param[in] Session Points to the iSCSI session.
+
+ @return The newly created iSCSI connection.
+
+**/
+ISCSI_CONNECTION *
+IScsiCreateConnection (
+ IN ISCSI_SESSION *Session
+ )
+{
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION_CONFIG_NVDATA *NvData;
+ ISCSI_CONNECTION *Conn;
+ TCP_IO_CONFIG_DATA TcpIoConfig;
+ TCP4_IO_CONFIG_DATA *Tcp4IoConfig;
+ TCP6_IO_CONFIG_DATA *Tcp6IoConfig;
+ EFI_STATUS Status;
+
+ Private = Session->Private;
+ NvData = &Session->ConfigData->SessionConfigData;
+
+ Conn = AllocateZeroPool (sizeof (ISCSI_CONNECTION));
+ if (Conn == NULL) {
+ return NULL;
+ }
+
+ Conn->Signature = ISCSI_CONNECTION_SIGNATURE;
+ Conn->State = CONN_STATE_FREE;
+ Conn->CurrentStage = ISCSI_SECURITY_NEGOTIATION;
+ Conn->NextStage = ISCSI_LOGIN_OPERATIONAL_NEGOTIATION;
+ Conn->AuthStep = ISCSI_AUTH_INITIAL;
+ Conn->ExpStatSN = 0;
+ Conn->PartialReqSent = FALSE;
+ Conn->PartialRspRcvd = FALSE;
+ Conn->ParamNegotiated = FALSE;
+ Conn->Cid = Session->NextCid++;
+ Conn->Ipv6Flag = mPrivate->Ipv6Flag;
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &Conn->TimeoutEvent
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Conn);
+ return NULL;
+ }
+
+ NetbufQueInit (&Conn->RspQue);
+
+ //
+ // Set the default connection-only parameters.
+ //
+ Conn->MaxRecvDataSegmentLength = DEFAULT_MAX_RECV_DATA_SEG_LEN;
+ Conn->HeaderDigest = IScsiDigestNone;
+ Conn->DataDigest = IScsiDigestNone;
+
+ if (!Conn->Ipv6Flag) {
+ Tcp4IoConfig = &TcpIoConfig.Tcp4IoConfigData;
+
+ CopyMem (&Tcp4IoConfig->LocalIp, &NvData->LocalIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Tcp4IoConfig->SubnetMask, &NvData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Tcp4IoConfig->Gateway, &NvData->Gateway, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Tcp4IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv4_ADDRESS));
+
+ Tcp4IoConfig->RemotePort = NvData->TargetPort;
+ Tcp4IoConfig->ActiveFlag = TRUE;
+
+ } else {
+ Tcp6IoConfig = &TcpIoConfig.Tcp6IoConfigData;
+
+ CopyMem (&Tcp6IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS));
+ Tcp6IoConfig->RemotePort = NvData->TargetPort;
+ Tcp6IoConfig->ActiveFlag = TRUE;
+ }
+
+ //
+ // Create the TCP IO for this connection.
+ //
+ Status = TcpIoCreateSocket (
+ Private->Image,
+ Private->Controller,
+ (UINT8) (!Conn->Ipv6Flag ? TCP_VERSION_4: TCP_VERSION_6),
+ &TcpIoConfig,
+ &Conn->TcpIo
+ );
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (Conn->TimeoutEvent);
+ FreePool (Conn);
+ Conn = NULL;
+ }
+
+ return Conn;
+}
+
+
+/**
+ Destroy an iSCSI connection.
+
+ @param[in] Conn The connection to destroy.
+
+**/
+VOID
+IScsiDestroyConnection (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ TcpIoDestroySocket (&Conn->TcpIo);
+
+ NetbufQueFlush (&Conn->RspQue);
+ gBS->CloseEvent (Conn->TimeoutEvent);
+ FreePool (Conn);
+}
+
+
+/**
+ Login the iSCSI session.
+
+ @param[in] Session The iSCSI session.
+
+ @retval EFI_SUCCESS The iSCSI session login procedure finished.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiSessionLogin (
+ IN ISCSI_SESSION *Session
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_CONNECTION *Conn;
+ VOID *Tcp;
+ EFI_GUID *ProtocolGuid;
+ UINT8 RetryCount;
+ BOOLEAN MediaPresent;
+
+ //
+ // Check media status before session login.
+ //
+ MediaPresent = TRUE;
+ NetLibDetectMedia (Session->Private->Controller, &MediaPresent);
+ if (!MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // Set session identifier
+ //
+ CopyMem (Session->Isid, Session->ConfigData->SessionConfigData.IsId, 6);
+
+ RetryCount = 0;
+
+ do {
+ //
+ // Create a connection for the session.
+ //
+ Conn = IScsiCreateConnection (Session);
+ if (Conn == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ IScsiAttatchConnection (Session, Conn);
+
+ //
+ // Login througth the newly created connection.
+ //
+ Status = IScsiConnLogin (Conn, Session->ConfigData->SessionConfigData.ConnectTimeout);
+ if (EFI_ERROR (Status)) {
+ IScsiConnReset (Conn);
+ IScsiDetatchConnection (Conn);
+ IScsiDestroyConnection (Conn);
+ }
+
+ if (Status != EFI_TIMEOUT) {
+ break;
+ }
+
+ RetryCount++;
+ } while (RetryCount <= Session->ConfigData->SessionConfigData.ConnectRetryCount);
+
+ if (!EFI_ERROR (Status)) {
+ Session->State = SESSION_STATE_LOGGED_IN;
+
+ if (!mPrivate->Ipv6Flag) {
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ Status = gBS->OpenProtocol (
+ Conn->TcpIo.Handle,
+ ProtocolGuid,
+ (VOID **) &Tcp,
+ Session->Private->Image,
+ Session->Private->ExtScsiPassThruHandle,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ return Status;
+}
+
+
+/**
+ Wait for IPsec negotiation, then try to login the iSCSI session again.
+
+ @param[in] Session The iSCSI session.
+
+ @retval EFI_SUCCESS The iSCSI session login procedure finished.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
+
+**/
+EFI_STATUS
+IScsiSessionReLogin (
+ IN ISCSI_SESSION *Session
+ )
+{
+
+ EFI_STATUS Status;
+ EFI_STATUS TimerStatus;
+ EFI_EVENT Timer;
+
+ Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->SetTimer (
+ Timer,
+ TimerRelative,
+ ISCSI_WAIT_IPSEC_TIMEOUT
+ );
+
+ if (EFI_ERROR (Status)) {
+ gBS->CloseEvent (Timer);
+ return Status;
+ }
+
+ do {
+
+ TimerStatus = gBS->CheckEvent (Timer);
+
+ if (!EFI_ERROR (TimerStatus)) {
+ Status = IScsiSessionLogin (Session);
+ }
+
+ } while (TimerStatus == EFI_NOT_READY);
+
+ gBS->CloseEvent (Timer);
+ return Status;
+}
+
+
+/**
+ Build and send the iSCSI login request to the iSCSI target according to
+ the current login stage.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
+ connection.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR Some kind of device error occurred.
+
+**/
+EFI_STATUS
+IScsiSendLoginReq (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ NET_BUF *Pdu;
+ EFI_STATUS Status;
+
+ //
+ // Build the Login Request PDU.
+ //
+ Pdu = IScsiPrepareLoginReq (Conn);
+ if (Pdu == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Send it to the iSCSI target.
+ //
+ Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
+
+ NetbufFree (Pdu);
+
+ return Status;
+}
+
+
+/**
+ Receive and process the iSCSI login response.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiReceiveLoginRsp (
+ IN ISCSI_CONNECTION *Conn
+ )
+{
+ EFI_STATUS Status;
+ NET_BUF *Pdu;
+
+ //
+ // Receive the iSCSI login response.
+ //
+ Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // A Login Response is received; process it.
+ //
+ Status = IScsiProcessLoginRsp (Conn, Pdu);
+
+ NetbufFree (Pdu);
+
+ return Status;
+}
+
+
+/**
+ Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
+ The DataSegmentLength and the actual size of the net buffer containing this PDU will be
+ updated.
+
+ @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
+ be added to.
+ @param[in] Key The key name string.
+ @param[in] Value The value string.
+
+ @retval EFI_SUCCESS The key-value pair is added to the PDU's data segment and
+ the correspondence length fields are updated.
+ @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
+ pair.
+**/
+EFI_STATUS
+IScsiAddKeyValuePair (
+ IN OUT NET_BUF *Pdu,
+ IN CHAR8 *Key,
+ IN CHAR8 *Value
+ )
+{
+ UINT32 DataSegLen;
+ UINT32 KeyLen;
+ UINT32 ValueLen;
+ UINT32 TotalLen;
+ ISCSI_LOGIN_REQUEST *LoginReq;
+ CHAR8 *Data;
+
+ LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL);
+ DataSegLen = NTOH24 (LoginReq->DataSegmentLength);
+
+ KeyLen = (UINT32) AsciiStrLen (Key);
+ ValueLen = (UINT32) AsciiStrLen (Value);
+
+ //
+ // 1 byte for the key value separator '=' and 1 byte for the null
+ // delimiter after the value.
+ //
+ TotalLen = KeyLen + 1 + ValueLen + 1;
+
+ //
+ // Allocate the space for the key-value pair.
+ //
+ Data = (CHAR8 *) NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Add the key.
+ //
+ CopyMem (Data, Key, KeyLen);
+ Data += KeyLen;
+
+ *Data = '=';
+ Data++;
+
+ //
+ // Add the value.
+ //
+ CopyMem (Data, Value, ValueLen);
+ Data += ValueLen;
+
+ *Data = '\0';
+
+ //
+ // Update the DataSegmentLength
+ //
+ ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Prepare the iSCSI login request to be sent according to the current login status.
+
+ @param[in, out] Conn The connection in the iSCSI login phase.
+
+ @return The pointer to the net buffer containing the iSCSI login request built.
+ @retval NULL Other errors as indicated.
+
+**/
+NET_BUF *
+IScsiPrepareLoginReq (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ ISCSI_SESSION *Session;
+ NET_BUF *Nbuf;
+ ISCSI_LOGIN_REQUEST *LoginReq;
+ EFI_STATUS Status;
+
+ Session = Conn->Session;
+
+ Nbuf = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN);
+ if (Nbuf == NULL) {
+ return NULL;
+ }
+
+ LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufAllocSpace (Nbuf, sizeof (ISCSI_LOGIN_REQUEST), NET_BUF_TAIL);
+ ASSERT (LoginReq != NULL);
+ ZeroMem (LoginReq, sizeof (ISCSI_LOGIN_REQUEST));
+
+ //
+ // Init the login request pdu
+ //
+ ISCSI_SET_OPCODE (LoginReq, ISCSI_OPCODE_LOGIN_REQ, ISCSI_REQ_IMMEDIATE);
+ ISCSI_SET_STAGES (LoginReq, Conn->CurrentStage, Conn->NextStage);
+ LoginReq->VersionMax = ISCSI_VERSION_MAX;
+ LoginReq->VersionMin = ISCSI_VERSION_MIN;
+ LoginReq->Tsih = HTONS (Session->Tsih);
+ LoginReq->InitiatorTaskTag = HTONL (Session->InitiatorTaskTag);
+ LoginReq->Cid = HTONS (Conn->Cid);
+ LoginReq->CmdSN = HTONL (Session->CmdSN);
+
+ //
+ // For the first Login Request on a coonection this is ExpStatSN for the
+ // old connection, and this field is only valid if the Login Request restarts
+ // a connection.
+ // For subsequent Login Requests it is used to acknowledge the Login Responses
+ // with their increasing StatSN values.
+ //
+ LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN);
+ CopyMem (LoginReq->Isid, Session->Isid, sizeof (LoginReq->Isid));
+
+ if (Conn->PartialRspRcvd) {
+ //
+ // A partial response. The initiator must send an empty Login Request.
+ //
+ return Nbuf;
+ }
+
+ Status = EFI_SUCCESS;
+
+ switch (Conn->CurrentStage) {
+ case ISCSI_SECURITY_NEGOTIATION:
+ //
+ // Both none authentication and CHAP authentication share the CHAP path.
+ //
+ //
+ if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
+ Status = IScsiCHAPToSendReq (Conn, Nbuf);
+ }
+
+ break;
+
+ case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
+ //
+ // Only negotiate the paramter once.
+ //
+ if (!Conn->ParamNegotiated) {
+ IScsiFillOpParams (Conn, Nbuf);
+ }
+
+ ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ break;
+
+ default:
+ //
+ // An error occurs...
+ //
+ Status = EFI_DEVICE_ERROR;
+ break;
+ }
+
+ if (EFI_ERROR (Status)) {
+ NetbufFree (Nbuf);
+ Nbuf = NULL;
+ } else {
+ //
+ // Pad the data segment if needed.
+ //
+ IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq));
+ //
+ // Check whether we will issue the stage transition signal?
+ //
+ Conn->TransitInitiated = ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
+ }
+
+ return Nbuf;
+}
+
+
+/**
+ Process the iSCSI Login Response.
+
+ @param[in, out] Conn The connection on which the iSCSI login response is received.
+ @param[in, out] Pdu The iSCSI login response PDU.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is processed, and all checks are passed.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
+ @retval EFI_MEDIA_CHANGED Target is redirected.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiProcessLoginRsp (
+ IN OUT ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_SESSION *Session;
+ ISCSI_LOGIN_RESPONSE *LoginRsp;
+ BOOLEAN Transit;
+ BOOLEAN Continue;
+ UINT8 CurrentStage;
+ UINT8 NextStage;
+ UINT8 *DataSeg;
+ UINT32 DataSegLen;
+
+ Status = EFI_SUCCESS;
+ Session = Conn->Session;
+
+ LoginRsp = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
+ if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) {
+ //
+ // It is not a Login Response.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Get the data segment, if any.
+ //
+ DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp);
+ if (DataSegLen != 0) {
+ DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL);
+ } else {
+ DataSeg = NULL;
+ }
+ //
+ // Check the status class in the login response PDU.
+ //
+ switch (LoginRsp->StatusClass) {
+ case ISCSI_LOGIN_STATUS_SUCCESS:
+ //
+ // Just break here; the response and the data segment will be processed later.
+ //
+ break;
+
+ case ISCSI_LOGIN_STATUS_REDIRECTION:
+ //
+ // The target may be moved to a different address.
+ //
+ if (DataSeg == NULL) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Process the TargetAddress key-value strings in the data segment to update the
+ // target address info.
+ //
+ Status = IScsiUpdateTargetAddress (Session, (CHAR8 *) DataSeg, DataSegLen);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ //
+ // Session will be restarted on this error status because the Target is
+ // redirected by this Login Response.
+ //
+ return EFI_MEDIA_CHANGED;
+
+ default:
+ //
+ // Initiator Error, Target Error, or any other undefined error code.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // The status is success; extract the wanted fields from the header segment.
+ //
+ Transit = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT);
+ Continue = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE);
+
+ CurrentStage = ISCSI_GET_CURRENT_STAGE (LoginRsp);
+ NextStage = ISCSI_GET_NEXT_STAGE (LoginRsp);
+
+ LoginRsp->InitiatorTaskTag = NTOHL (LoginRsp->InitiatorTaskTag);
+
+ if ((Transit && Continue) ||
+ (CurrentStage != Conn->CurrentStage) ||
+ (!Conn->TransitInitiated && Transit) ||
+ (Transit && (NextStage != Conn->NextStage)) ||
+ (CompareMem (Session->Isid, LoginRsp->Isid, sizeof (LoginRsp->Isid)) != 0) ||
+ (LoginRsp->InitiatorTaskTag != Session->InitiatorTaskTag)
+ ) {
+ //
+ // A Login Response with the C bit set to 1 MUST have the T bit set to 0.
+ // The CSG in the Login Response MUST be the same with the I-end of this connection.
+ // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
+ // initiate the transistion.
+ // The NSG MUST be the same with the I-end of this connection if Transit is required.
+ // The ISID in the Login Response MUST be the same with this session.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ LoginRsp->StatSN = NTOHL (LoginRsp->StatSN);
+ LoginRsp->ExpCmdSN = NTOHL (LoginRsp->ExpCmdSN);
+ LoginRsp->MaxCmdSN = NTOHL (LoginRsp->MaxCmdSN);
+
+ if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->AuthStep == ISCSI_AUTH_INITIAL)) {
+ //
+ // If the Login Request is a leading Login Request, the target MUST use
+ // the value presented in CmdSN as the target value for ExpCmdSN.
+ //
+ if ((Session->State == SESSION_STATE_FREE) && (Session->CmdSN != LoginRsp->ExpCmdSN)) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ //
+ // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
+ // and ExpCmdSN.
+ //
+ Conn->ExpStatSN = LoginRsp->StatSN + 1;
+ Session->MaxCmdSN = LoginRsp->MaxCmdSN;
+ Session->ExpCmdSN = LoginRsp->ExpCmdSN;
+ } else {
+ //
+ // Check the StatSN of this PDU.
+ //
+ Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Update the MaxCmdSN and ExpCmdSN.
+ //
+ IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN);
+ } else {
+ return Status;
+ }
+ }
+ //
+ // Trim off the header segment.
+ //
+ NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD);
+
+ //
+ // Queue this login response first in case it's a partial response so that
+ // later when the full response list is received we can combine these scattered
+ // responses' data segment and then process it.
+ //
+ NET_GET_REF (Pdu);
+ NetbufQueAppend (&Conn->RspQue, Pdu);
+
+ Conn->PartialRspRcvd = Continue;
+ if (Continue) {
+ //
+ // It is a partial response; must wait for another or more Request/Response
+ // conversations to get the full response.
+ //
+ return EFI_SUCCESS;
+ }
+
+ switch (CurrentStage) {
+ case ISCSI_SECURITY_NEGOTIATION:
+ //
+ // In security negotiation stage, let CHAP module handle it.
+ //
+ if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
+ Status = IScsiCHAPOnRspReceived (Conn);
+ }
+ break;
+
+ case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
+ //
+ // Response received with negotiation response on iSCSI parameters: check them.
+ //
+ Status = IScsiCheckOpParams (Conn);
+ if (!EFI_ERROR (Status)) {
+ Conn->ParamNegotiated = TRUE;
+ }
+
+ break;
+
+ default:
+ //
+ // Should never get here.
+ //
+ Status = EFI_PROTOCOL_ERROR;
+ break;
+ }
+
+ if (Transit && (Status == EFI_SUCCESS)) {
+ //
+ // Do the state transition.
+ //
+ Conn->CurrentStage = Conn->NextStage;
+
+ if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) {
+ Conn->NextStage = ISCSI_FULL_FEATURE_PHASE;
+ } else {
+ //
+ // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
+ // get the TSIH from the Login Response.
+ //
+ Session->Tsih = NTOHS (LoginRsp->Tsih);
+ }
+ }
+ //
+ // Flush the response(s) received.
+ //
+ NetbufQueFlush (&Conn->RspQue);
+
+ return Status;
+}
+
+
+/**
+ Updated the target information according the data received in the iSCSI
+ login response with an target redirection status.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Data The data segment that should contain the
+ TargetAddress key-value list.
+ @param[in] Len Length of the data.
+
+ @retval EFI_SUCCESS The target address is updated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_FOUND The TargetAddress key is not found.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiUpdateTargetAddress (
+ IN OUT ISCSI_SESSION *Session,
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *KeyValueList;
+ CHAR8 *TargetAddress;
+ CHAR8 *IpStr;
+ EFI_STATUS Status;
+ UINTN Number;
+ UINT8 IpMode;
+
+ KeyValueList = IScsiBuildKeyValueList (Data, Len);
+ if (KeyValueList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_NOT_FOUND;
+
+ while (TRUE) {
+ TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);
+ if (TargetAddress == NULL) {
+ break;
+ }
+
+ if (!NET_IS_DIGIT (TargetAddress[0])) {
+ //
+ // The domainname of the target may be presented in three formats: a DNS host name,
+ // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted
+ // IPv4 address.
+ //
+ continue;
+ }
+
+ IpStr = TargetAddress;
+
+ while ((*TargetAddress != 0) && (*TargetAddress != ':') && (*TargetAddress != ',')) {
+ //
+ // NULL, ':', or ',' ends the IPv4 string.
+ //
+ TargetAddress++;
+ }
+
+ if (*TargetAddress == ',') {
+ //
+ // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
+ // as the result of a redirection.
+ //
+ continue;
+ } else if (*TargetAddress == ':') {
+ *TargetAddress = '\0';
+
+ TargetAddress++;
+
+ Number = AsciiStrDecimalToUintn (TargetAddress);
+ if (Number > 0xFFFF) {
+ continue;
+ } else {
+ Session->ConfigData->SessionConfigData.TargetPort = (UINT16) Number;
+ }
+ } else {
+ //
+ // The string only contains the IPv4 address. Use the well-known port.
+ //
+ Session->ConfigData->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
+ }
+ //
+ // Update the target IP address.
+ //
+ if (Session->ConfigData->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {
+ IpMode = Session->ConfigData->SessionConfigData.IpMode;
+ } else {
+ IpMode = Session->ConfigData->AutoConfigureMode;
+ }
+
+ Status = IScsiAsciiStrToIp (
+ IpStr,
+ IpMode,
+ &Session->ConfigData->SessionConfigData.TargetIp
+ );
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ IScsiFreeKeyValueList (KeyValueList);
+
+ return Status;
+}
+
+
+/**
+ The callback function to free the net buffer list.
+
+ @param[in] Arg The opaque parameter.
+
+**/
+VOID
+EFIAPI
+IScsiFreeNbufList (
+ VOID *Arg
+ )
+{
+ ASSERT (Arg != NULL);
+
+ NetbufFreeList ((LIST_ENTRY *) Arg);
+ FreePool (Arg);
+}
+
+
+/**
+ The callback function called in NetBufFree; it does nothing.
+
+ @param[in] Arg The opaque parameter.
+
+**/
+VOID
+EFIAPI
+IScsiNbufExtFree (
+ VOID *Arg
+ )
+{
+}
+
+
+/**
+ Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
+ an optional data segment. The two parts will be put into two blocks of buffers in the
+ net buffer. The digest check will be conducted in this function if needed and the digests
+ will be trimmed from the PDU buffer.
+
+ @param[in] Conn The iSCSI connection to receive data from.
+ @param[out] Pdu The received iSCSI pdu.
+ @param[in] Context The context used to describe information on the caller provided
+ buffer to receive data segment of the iSCSI pdu. It is optional.
+ @param[in] HeaderDigest Whether there will be header digest received.
+ @param[in] DataDigest Whether there will be data digest.
+ @param[in] TimeoutEvent The timeout event. It is optional.
+
+ @retval EFI_SUCCESS An iSCSI pdu is received.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiReceivePdu (
+ IN ISCSI_CONNECTION *Conn,
+ OUT NET_BUF **Pdu,
+ IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL
+ IN BOOLEAN HeaderDigest,
+ IN BOOLEAN DataDigest,
+ IN EFI_EVENT TimeoutEvent OPTIONAL
+ )
+{
+ LIST_ENTRY *NbufList;
+ UINT32 Len;
+ NET_BUF *PduHdr;
+ UINT8 *Header;
+ EFI_STATUS Status;
+ UINT32 PadLen;
+ UINT32 InDataOffset;
+ NET_FRAGMENT Fragment[2];
+ UINT32 FragmentCount;
+ NET_BUF *DataSeg;
+ UINT32 PadAndCRC32[2];
+
+ NbufList = AllocatePool (sizeof (LIST_ENTRY));
+ if (NbufList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (NbufList);
+
+ //
+ // The header digest will be received together with the PDU header, if exists.
+ //
+ Len = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0);
+ PduHdr = NetbufAlloc (Len);
+ if (PduHdr == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);
+ ASSERT (Header != NULL);
+ InsertTailList (NbufList, &PduHdr->List);
+
+ //
+ // First step, receive the BHS of the PDU.
+ //
+ Status = TcpIoReceive (&Conn->TcpIo, PduHdr, FALSE, TimeoutEvent);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (HeaderDigest) {
+ //
+ // TODO: check the header-digest.
+ //
+ //
+ // Trim off the digest.
+ //
+ NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL);
+ }
+
+ Len = ISCSI_GET_DATASEG_LEN (Header);
+ if (Len == 0) {
+ //
+ // No data segment.
+ //
+ goto FORM_PDU;
+ }
+ //
+ // Get the length of the padding bytes of the data segment.
+ //
+ PadLen = ISCSI_GET_PAD_LEN (Len);
+
+ switch (ISCSI_GET_OPCODE (Header)) {
+ case ISCSI_OPCODE_SCSI_DATA_IN:
+ //
+ // To reduce memory copy overhead, try to use the buffer described by Context
+ // if the PDU is an iSCSI SCSI data.
+ //
+ InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header);
+ if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) {
+ Status = EFI_PROTOCOL_ERROR;
+ goto ON_EXIT;
+ }
+
+ Fragment[0].Len = Len;
+ Fragment[0].Bulk = Context->InData + InDataOffset;
+
+ if (DataDigest || (PadLen != 0)) {
+ //
+ // The data segment is padded. Use two fragments to receive it:
+ // the first to receive the useful data; the second to receive the padding.
+ //
+ Fragment[1].Len = PadLen + (DataDigest ? sizeof (UINT32) : 0);
+ Fragment[1].Bulk = (UINT8 *)PadAndCRC32 + (4 - PadLen);
+
+ FragmentCount = 2;
+ } else {
+ FragmentCount = 1;
+ }
+
+ DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
+ if (DataSeg == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ break;
+
+ case ISCSI_OPCODE_SCSI_RSP:
+ case ISCSI_OPCODE_NOP_IN:
+ case ISCSI_OPCODE_LOGIN_RSP:
+ case ISCSI_OPCODE_TEXT_RSP:
+ case ISCSI_OPCODE_ASYNC_MSG:
+ case ISCSI_OPCODE_REJECT:
+ case ISCSI_OPCODE_VENDOR_T0:
+ case ISCSI_OPCODE_VENDOR_T1:
+ case ISCSI_OPCODE_VENDOR_T2:
+ //
+ // Allocate buffer to receive the data segment.
+ //
+ Len += PadLen + (DataDigest ? sizeof (UINT32) : 0);
+ DataSeg = NetbufAlloc (Len);
+ if (DataSeg == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);
+ break;
+
+ default:
+ Status = EFI_PROTOCOL_ERROR;
+ goto ON_EXIT;
+ }
+
+ InsertTailList (NbufList, &DataSeg->List);
+
+ //
+ // Receive the data segment with the data digest, if any.
+ //
+ Status = TcpIoReceive (&Conn->TcpIo, DataSeg, FALSE, TimeoutEvent);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (DataDigest) {
+ //
+ // TODO: Check the data digest.
+ //
+ NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL);
+ }
+
+ if (PadLen != 0) {
+ //
+ // Trim off the padding bytes in the data segment.
+ //
+ NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL);
+ }
+
+FORM_PDU:
+ //
+ // Form the pdu from a list of pdu segments.
+ //
+ *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
+ if (*Pdu == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ }
+
+ON_EXIT:
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Free the Nbufs in this NbufList and the NbufList itself.
+ //
+ IScsiFreeNbufList (NbufList);
+ }
+
+ return Status;
+}
+
+
+/**
+ Check and get the result of the prameter negotiation.
+
+ @param[in, out] Conn The connection in iSCSI login.
+
+ @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+IScsiCheckOpParams (
+ IN OUT ISCSI_CONNECTION *Conn
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *KeyValueList;
+ CHAR8 *Data;
+ UINT32 Len;
+ ISCSI_SESSION *Session;
+ CHAR8 *Value;
+ UINTN NumericValue;
+
+ ASSERT (Conn->RspQue.BufNum != 0);
+
+ Session = Conn->Session;
+
+ Len = Conn->RspQue.BufSize;
+ Data = AllocatePool (Len);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ NetbufQueCopy (&Conn->RspQue, 0, Len, (UINT8 *) Data);
+
+ Status = EFI_PROTOCOL_ERROR;
+
+ //
+ // Extract the Key-Value pairs into a list.
+ //
+ KeyValueList = IScsiBuildKeyValueList (Data, Len);
+ if (KeyValueList == NULL) {
+ FreePool (Data);
+ return Status;
+ }
+ //
+ // HeaderDigest
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (AsciiStrCmp (Value, "CRC32") == 0) {
+ if (Conn->HeaderDigest != IScsiDigestCRC32) {
+ goto ON_ERROR;
+ }
+ } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
+ Conn->HeaderDigest = IScsiDigestNone;
+ } else {
+ goto ON_ERROR;
+ }
+ //
+ // DataDigest
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ if (AsciiStrCmp (Value, "CRC32") == 0) {
+ if (Conn->DataDigest != IScsiDigestCRC32) {
+ goto ON_ERROR;
+ }
+ } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
+ Conn->DataDigest = IScsiDigestNone;
+ } else {
+ goto ON_ERROR;
+ }
+ //
+ // ErrorRecoveryLevel: result fuction is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = IScsiNetNtoi (Value);
+ if (NumericValue > 2) {
+ goto ON_ERROR;
+ }
+
+ Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue);
+
+ //
+ // InitialR2T: result function is OR.
+ //
+ if (!Session->InitialR2T) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->InitialR2T = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
+ }
+
+ //
+ // ImmediateData: result function is AND.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->ImmediateData = (BOOLEAN) (Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0));
+
+ //
+ // MaxRecvDataSegmentLength is declarative.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH);
+ if (Value != NULL) {
+ Conn->MaxRecvDataSegmentLength = (UINT32) IScsiNetNtoi (Value);
+ }
+ //
+ // MaxBurstLength: result funtion is Mininum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = IScsiNetNtoi (Value);
+ Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue);
+
+ //
+ // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
+ // ImmediateData=No.
+ //
+ if (!(Session->InitialR2T && !Session->ImmediateData)) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = IScsiNetNtoi (Value);
+ Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue);
+ }
+
+ //
+ // MaxConnections: result function is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = IScsiNetNtoi (Value);
+ if ((NumericValue == 0) || (NumericValue > 65535)) {
+ goto ON_ERROR;
+ }
+
+ Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue);
+
+ //
+ // DataPDUInOrder: result function is OR.
+ //
+ if (!Session->DataPDUInOrder) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->DataPDUInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
+ }
+
+ //
+ // DataSequenceInorder: result function is OR.
+ //
+ if (!Session->DataSequenceInOrder) {
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ Session->DataSequenceInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
+ }
+
+ //
+ // DefaultTime2Wait: result function is Maximum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = IScsiNetNtoi (Value);
+ if (NumericValue == 0) {
+ Session->DefaultTime2Wait = 0;
+ } else if (NumericValue > 3600) {
+ goto ON_ERROR;
+ } else {
+ Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue);
+ }
+ //
+ // DefaultTime2Retain: result function is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = IScsiNetNtoi (Value);
+ if (NumericValue == 0) {
+ Session->DefaultTime2Retain = 0;
+ } else if (NumericValue > 3600) {
+ goto ON_ERROR;
+ } else {
+ Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue);
+ }
+ //
+ // MaxOutstandingR2T: result function is Minimum.
+ //
+ Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T);
+ if (Value == NULL) {
+ goto ON_ERROR;
+ }
+
+ NumericValue = IScsiNetNtoi (Value);
+ if ((NumericValue == 0) || (NumericValue > 65535)) {
+ goto ON_ERROR;
+ }
+
+ Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue);
+
+ //
+ // Remove declarative key-value pairs, if any.
+ //
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
+
+
+ //
+ // Remove the key-value that may not needed for result function is OR.
+ //
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
+ IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
+
+
+ if (IsListEmpty (KeyValueList)) {
+ //
+ // Succeed if no more keys in the list.
+ //
+ Status = EFI_SUCCESS;
+ }
+
+ON_ERROR:
+
+ IScsiFreeKeyValueList (KeyValueList);
+
+ FreePool (Data);
+
+ return Status;
+}
+
+
+/**
+ Fill the oprational parameters.
+
+ @param[in] Conn The connection in iSCSI login.
+ @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
+
+**/
+VOID
+IScsiFillOpParams (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ )
+{
+ ISCSI_SESSION *Session;
+ CHAR8 Value[256];
+
+ Session = Conn->Session;
+
+ AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);
+
+ AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);
+ IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);
+}
+
+
+/**
+ Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
+
+ @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
+ @param[in] Len The length of the last segment in the PDU.
+
+ @retval EFI_SUCCESS The segment is padded or there is no need to pad it.
+ @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
+ padding bytes.
+**/
+EFI_STATUS
+IScsiPadSegment (
+ IN OUT NET_BUF *Pdu,
+ IN UINT32 Len
+ )
+{
+ UINT32 PadLen;
+ UINT8 *Data;
+
+ PadLen = ISCSI_GET_PAD_LEN (Len);
+
+ if (PadLen != 0) {
+ Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);
+ if (Data == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (Data, PadLen);
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Build a key-value list from the data segment.
+
+ @param[in] Data The data segment containing the key-value pairs.
+ @param[in] Len Length of the data segment.
+
+ @return The key-value list.
+ @retval NULL Other errors as indicated.
+
+**/
+LIST_ENTRY *
+IScsiBuildKeyValueList (
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ )
+{
+ LIST_ENTRY *ListHead;
+ ISCSI_KEY_VALUE_PAIR *KeyValuePair;
+
+ ListHead = AllocatePool (sizeof (LIST_ENTRY));
+ if (ListHead == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (ListHead);
+
+ while (Len > 0) {
+ KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));
+ if (KeyValuePair == NULL) {
+ goto ON_ERROR;
+ }
+
+ InitializeListHead (&KeyValuePair->List);
+
+ KeyValuePair->Key = Data;
+
+ while ((Len > 0) && (*Data != '=')) {
+ Len--;
+ Data++;
+ }
+
+ if (*Data == '=') {
+ *Data = '\0';
+
+ Data++;
+ Len--;
+ } else {
+ FreePool (KeyValuePair);
+ goto ON_ERROR;
+ }
+
+ KeyValuePair->Value = Data;
+
+ InsertTailList (ListHead, &KeyValuePair->List);;
+
+ Data += AsciiStrLen (KeyValuePair->Value) + 1;
+ Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;
+ }
+
+ return ListHead;
+
+ON_ERROR:
+
+ IScsiFreeKeyValueList (ListHead);
+
+ return NULL;
+}
+
+
+/**
+ Get the value string by the key name from the key-value list. If found,
+ the key-value entry will be removed from the list.
+
+ @param[in, out] KeyValueList The key-value list.
+ @param[in] Key The key name to find.
+
+ @return The value string.
+ @retval NULL The key value pair cannot be found.
+
+**/
+CHAR8 *
+IScsiGetValueByKeyFromList (
+ IN OUT LIST_ENTRY *KeyValueList,
+ IN CHAR8 *Key
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_KEY_VALUE_PAIR *KeyValuePair;
+ CHAR8 *Value;
+
+ Value = NULL;
+
+ NET_LIST_FOR_EACH (Entry, KeyValueList) {
+ KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
+
+ if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {
+ Value = KeyValuePair->Value;
+
+ RemoveEntryList (&KeyValuePair->List);
+ FreePool (KeyValuePair);
+ break;
+ }
+ }
+
+ return Value;
+}
+
+
+/**
+ Free the key-value list.
+
+ @param[in] KeyValueList The key-value list.
+
+**/
+VOID
+IScsiFreeKeyValueList (
+ IN LIST_ENTRY *KeyValueList
+ )
+{
+ LIST_ENTRY *Entry;
+ ISCSI_KEY_VALUE_PAIR *KeyValuePair;
+
+ while (!IsListEmpty (KeyValueList)) {
+ Entry = NetListRemoveHead (KeyValueList);
+ KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
+
+ FreePool (KeyValuePair);
+ }
+
+ FreePool (KeyValueList);
+}
+
+
+/**
+ Normalize the iSCSI name according to RFC.
+
+ @param[in, out] Name The iSCSI name.
+ @param[in] Len Length of the iSCSI name.
+
+ @retval EFI_SUCCESS The iSCSI name is valid and normalized.
+ @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.
+
+**/
+EFI_STATUS
+IScsiNormalizeName (
+ IN OUT CHAR8 *Name,
+ IN UINTN Len
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Len; Index++) {
+ if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {
+ //
+ // Convert the upper-case characters to lower-case ones.
+ //
+ Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');
+ }
+
+ if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&
+ !NET_IS_DIGIT (Name[Index]) &&
+ (Name[Index] != '-') &&
+ (Name[Index] != '.') &&
+ (Name[Index] != ':')
+ ) {
+ //
+ // ASCII dash, dot, colon lower-case characters and digit characters
+ // are allowed.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ }
+
+ if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {
+ //
+ // Only IQN format is accepted now.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Create an iSCSI task control block.
+
+ @param[in] Conn The connection on which the task control block will be created.
+ @param[out] Tcb The newly created task control block.
+
+ @retval EFI_SUCCESS The task control block is created.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_READY The target cannot accept new commands.
+
+**/
+EFI_STATUS
+IScsiNewTcb (
+ IN ISCSI_CONNECTION *Conn,
+ OUT ISCSI_TCB **Tcb
+ )
+{
+ ISCSI_SESSION *Session;
+ ISCSI_TCB *NewTcb;
+
+ ASSERT (Tcb != NULL);
+
+ Session = Conn->Session;
+
+ if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {
+ return EFI_NOT_READY;
+ }
+
+ NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));
+ if (NewTcb == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InitializeListHead (&NewTcb->Link);
+
+ NewTcb->SoFarInOrder = TRUE;
+ NewTcb->InitiatorTaskTag = Session->InitiatorTaskTag;
+ NewTcb->CmdSN = Session->CmdSN;
+ NewTcb->Conn = Conn;
+
+ InsertTailList (&Session->TcbList, &NewTcb->Link);
+
+ //
+ // Advance the initiator task tag.
+ //
+ Session->InitiatorTaskTag++;
+ Session->CmdSN++;
+
+ *Tcb = NewTcb;
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Delete the tcb from the connection and destroy it.
+
+ @param[in] Tcb The tcb to delete.
+
+**/
+VOID
+IScsiDelTcb (
+ IN ISCSI_TCB *Tcb
+ )
+{
+ RemoveEntryList (&Tcb->Link);
+
+ FreePool (Tcb);
+}
+
+
+/**
+ Find the task control block by the initator task tag.
+
+ @param[in] TcbList The tcb list.
+ @param[in] InitiatorTaskTag The initiator task tag.
+
+ @return The task control block found.
+ @retval NULL The task control block cannot be found.
+
+**/
+ISCSI_TCB *
+IScsiFindTcbByITT (
+ IN LIST_ENTRY *TcbList,
+ IN UINT32 InitiatorTaskTag
+ )
+{
+ ISCSI_TCB *Tcb;
+ LIST_ENTRY *Entry;
+
+ Tcb = NULL;
+
+ NET_LIST_FOR_EACH (Entry, TcbList) {
+ Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);
+
+ if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {
+ break;
+ }
+ }
+
+ return Tcb;
+}
+
+
+/**
+ Create a data segment, pad it, and calculate the CRC if needed.
+
+ @param[in] Data The data to fill into the data segment.
+ @param[in] Len Length of the data.
+ @param[in] DataDigest Whether to calculate CRC for this data segment.
+
+ @return The net buffer wrapping the data segment.
+
+**/
+NET_BUF *
+IScsiNewDataSegment (
+ IN UINT8 *Data,
+ IN UINT32 Len,
+ IN BOOLEAN DataDigest
+ )
+{
+ NET_FRAGMENT Fragment[2];
+ UINT32 FragmentCount;
+ UINT32 PadLen;
+ NET_BUF *DataSeg;
+
+ Fragment[0].Len = Len;
+ Fragment[0].Bulk = Data;
+
+ PadLen = ISCSI_GET_PAD_LEN (Len);
+ if (PadLen != 0) {
+ Fragment[1].Len = PadLen;
+ Fragment[1].Bulk = (UINT8 *) &mDataSegPad;
+
+ FragmentCount = 2;
+ } else {
+ FragmentCount = 1;
+ }
+
+ DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
+
+ return DataSeg;
+}
+
+
+/**
+ Create a iSCSI SCSI command PDU to encapsulate the command issued
+ by SCSI through the EXT SCSI PASS THRU Protocol.
+
+ @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
+ @param[in] Lun The LUN.
+ @param[in] Tcb The tcb assocated with this SCSI command.
+
+ @return The created iSCSI SCSI command PDU.
+ @retval NULL Other errors as indicated.
+
+**/
+NET_BUF *
+IScsiNewScsiCmdPdu (
+ IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
+ IN UINT64 Lun,
+ IN ISCSI_TCB *Tcb
+ )
+{
+ LIST_ENTRY *NbufList;
+ NET_BUF *Pdu;
+ NET_BUF *PduHeader;
+ NET_BUF *DataSeg;
+ SCSI_COMMAND *ScsiCmd;
+ UINT8 AHSLength;
+ UINT32 Length;
+ ISCSI_ADDITIONAL_HEADER *Header;
+ ISCSI_BI_EXP_READ_DATA_LEN_AHS *BiExpReadDataLenAHS;
+ ISCSI_SESSION *Session;
+ UINT32 ImmediateDataLen;
+
+ AHSLength = 0;
+
+ if (Packet->DataDirection == DataBi) {
+ //
+ // Bidirectional Read/Write command, the bidirectional expected
+ // read data length AHS is required.
+ //
+ AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);
+ }
+
+ if (Packet->CdbLength > 16) {
+ //
+ // The CDB exceeds 16 bytes. An extended CDB AHS is required.
+ //
+ AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER));
+ }
+
+ Length = sizeof (SCSI_COMMAND) + AHSLength;
+ PduHeader = NetbufAlloc (Length);
+ if (PduHeader == NULL) {
+ return NULL;
+ }
+
+ ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);
+ if (ScsiCmd == NULL) {
+ NetbufFree (PduHeader);
+ return NULL;
+ }
+ Header = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);
+
+ ZeroMem (ScsiCmd, Length);
+
+ ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);
+ ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);
+
+ //
+ // Set the READ/WRITE flags according to the IO type of this request.
+ //
+ switch (Packet->DataDirection) {
+ case DataIn:
+ ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);
+ ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);
+ break;
+
+ case DataOut:
+ ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);
+ ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
+ break;
+
+ case DataBi:
+ ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);
+ ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
+
+ //
+ // Fill the bidirectional expected read data length AHS.
+ //
+ BiExpReadDataLenAHS = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;
+ Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);
+
+ BiExpReadDataLenAHS->Length = NTOHS (5);
+ BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;
+ BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);
+
+ break;
+ }
+
+ ScsiCmd->TotalAHSLength = AHSLength;
+ CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));
+ ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);
+ ScsiCmd->CmdSN = NTOHL (Tcb->CmdSN);
+ ScsiCmd->ExpStatSN = NTOHL (Tcb->Conn->ExpStatSN);
+
+ CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));
+
+ if (Packet->CdbLength > 16) {
+ Header->Length = NTOHS ((UINT16) (Packet->CdbLength - 15));
+ Header->Type = ISCSI_AHS_TYPE_EXT_CDB;
+
+ CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);
+ }
+
+ Pdu = PduHeader;
+ Session = Tcb->Conn->Session;
+ ImmediateDataLen = 0;
+
+ if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {
+ //
+ // Send immediate data in this SCSI Command PDU. The length of the immeidate
+ // data is the minimum of FirstBurstLength, the data length to be xfered, and
+ // the MaxRecvdataSegmentLength on this connection.
+ //
+ ImmediateDataLen = MIN (Session->FirstBurstLength, Packet->OutTransferLength);
+ ImmediateDataLen = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);
+
+ //
+ // Update the data segment length in the PDU header.
+ //
+ ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);
+
+ //
+ // Create the data segment.
+ //
+ DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);
+ if (DataSeg == NULL) {
+ NetbufFree (PduHeader);
+ Pdu = NULL;
+ goto ON_EXIT;
+ }
+
+ NbufList = AllocatePool (sizeof (LIST_ENTRY));
+ if (NbufList == NULL) {
+ NetbufFree (PduHeader);
+ NetbufFree (DataSeg);
+
+ Pdu = NULL;
+ goto ON_EXIT;
+ }
+
+ InitializeListHead (NbufList);
+ InsertTailList (NbufList, &PduHeader->List);
+ InsertTailList (NbufList, &DataSeg->List);
+
+ Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
+ if (Pdu == NULL) {
+ IScsiFreeNbufList (NbufList);
+ }
+ }
+
+ if (Session->InitialR2T ||
+ (ImmediateDataLen == Session->FirstBurstLength) ||
+ (ImmediateDataLen == Packet->OutTransferLength)
+ ) {
+ //
+ // Unsolicited data out sequence is not allowed,
+ // or FirstBustLength data is already sent out by immediate data,
+ // or all the OUT data accompany this SCSI packet are sent as
+ // immediate data. The final flag should be set on this SCSI Command
+ // PDU.
+ //
+ ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);
+ }
+
+ON_EXIT:
+
+ return Pdu;
+}
+
+
+/**
+ Create a new iSCSI SCSI Data Out PDU.
+
+ @param[in] Data The data to put into the Data Out PDU.
+ @param[in] Len Length of the data.
+ @param[in] DataSN The DataSN of the Data Out PDU.
+ @param[in] Tcb The task control block of this Data Out PDU.
+ @param[in] Lun The LUN.
+
+ @return The net buffer wrapping the Data Out PDU.
+ @retval NULL Other errors as indicated.
+
+**/
+NET_BUF *
+IScsiNewDataOutPdu (
+ IN UINT8 *Data,
+ IN UINT32 Len,
+ IN UINT32 DataSN,
+ IN ISCSI_TCB *Tcb,
+ IN UINT64 Lun
+ )
+{
+ LIST_ENTRY *NbufList;
+ NET_BUF *PduHdr;
+ NET_BUF *DataSeg;
+ NET_BUF *Pdu;
+ ISCSI_SCSI_DATA_OUT *DataOutHdr;
+ ISCSI_XFER_CONTEXT *XferContext;
+
+ NbufList = AllocatePool (sizeof (LIST_ENTRY));
+ if (NbufList == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (NbufList);
+
+ //
+ // Allocate memory for the BHS.
+ //
+ PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));
+ if (PduHdr == NULL) {
+ FreePool (NbufList);
+ return NULL;
+ }
+ //
+ // Insert the BHS into the buffer list.
+ //
+ InsertTailList (NbufList, &PduHdr->List);
+
+ DataOutHdr = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);
+ ASSERT (DataOutHdr != NULL);
+ XferContext = &Tcb->XferContext;
+
+ ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));
+
+ //
+ // Set the flags and fields of the Data Out PDU BHS.
+ //
+ ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);
+ ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);
+
+ DataOutHdr->InitiatorTaskTag = HTONL (Tcb->InitiatorTaskTag);
+ DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);
+ DataOutHdr->ExpStatSN = HTONL (Tcb->Conn->ExpStatSN);
+ DataOutHdr->DataSN = HTONL (DataSN);
+ DataOutHdr->BufferOffset = HTONL (XferContext->Offset);
+
+ if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {
+ CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));
+ }
+ //
+ // Build the data segment for this Data Out PDU.
+ //
+ DataSeg = IScsiNewDataSegment (Data, Len, FALSE);
+ if (DataSeg == NULL) {
+ IScsiFreeNbufList (NbufList);
+ return NULL;
+ }
+ //
+ // Put the data segment into the buffer list and combine it with the BHS
+ // into a full Data Out PDU.
+ //
+ InsertTailList (NbufList, &DataSeg->List);
+ Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
+ if (Pdu == NULL) {
+ IScsiFreeNbufList (NbufList);
+ }
+
+ return Pdu;
+}
+
+
+/**
+ Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
+
+ @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
+ @param[in] Tcb The task control block of the data to send out.
+ @param[in] Lun The LUN the data will be sent to.
+
+ @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.
+ @retval NULL Other errors as indicated.
+
+**/
+LIST_ENTRY *
+IScsiGenerateDataOutPduSequence (
+ IN UINT8 *Data,
+ IN ISCSI_TCB *Tcb,
+ IN UINT64 Lun
+ )
+{
+ LIST_ENTRY *PduList;
+ UINT32 DataSN;
+ UINT32 DataLen;
+ NET_BUF *DataOutPdu;
+ ISCSI_CONNECTION *Conn;
+ ISCSI_XFER_CONTEXT *XferContext;
+
+ PduList = AllocatePool (sizeof (LIST_ENTRY));
+ if (PduList == NULL) {
+ return NULL;
+ }
+
+ InitializeListHead (PduList);
+
+ DataSN = 0;
+ Conn = Tcb->Conn;
+ DataOutPdu = NULL;
+ XferContext = &Tcb->XferContext;
+
+ while (XferContext->DesiredLength > 0) {
+ //
+ // Determine the length of data this Data Out PDU can carry.
+ //
+ DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);
+
+ //
+ // Create a Data Out PDU.
+ //
+ DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);
+ if (DataOutPdu == NULL) {
+ IScsiFreeNbufList (PduList);
+ PduList = NULL;
+
+ goto ON_EXIT;
+ }
+
+ InsertTailList (PduList, &DataOutPdu->List);
+
+ //
+ // Update the context and DataSN.
+ //
+ Data += DataLen;
+ XferContext->Offset += DataLen;
+ XferContext->DesiredLength -= DataLen;
+ DataSN++;
+ }
+ //
+ // Set the F bit for the last data out PDU in this sequence.
+ //
+ ISCSI_SET_FLAG (NetbufGetByte (DataOutPdu, 0, NULL), ISCSI_BHS_FLAG_FINAL);
+
+ON_EXIT:
+
+ return PduList;
+}
+
+/**
+ Send the Data in a sequence of Data Out PDUs one by one.
+
+ @param[in] Data The data to carry by Data Out PDUs.
+ @param[in] Lun The LUN the data will be sent to.
+ @param[in] Tcb The task control block.
+
+ @retval EFI_SUCCES The data is sent out to the LUN.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiSendDataOutPduSequence (
+ IN UINT8 *Data,
+ IN UINT64 Lun,
+ IN ISCSI_TCB *Tcb
+ )
+{
+ LIST_ENTRY *DataOutPduList;
+ LIST_ENTRY *Entry;
+ NET_BUF *Pdu;
+ EFI_STATUS Status;
+
+ //
+ // Generate the Data Out PDU sequence.
+ //
+ DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);
+ if (DataOutPduList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Send the Data Out PDU's one by one.
+ //
+ NET_LIST_FOR_EACH (Entry, DataOutPduList) {
+ Pdu = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
+
+ Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ IScsiFreeNbufList (DataOutPduList);
+
+ return Status;
+}
+
+
+/**
+ Process the received iSCSI SCSI Data In PDU.
+
+ @param[in] Pdu The Data In PDU received.
+ @param[in] Tcb The task control block.
+ @param[in, out] Packet The EXT SCSI PASS THRU request packet.
+
+ @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
+ actions are taken.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
+ @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiOnDataInRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ ISCSI_SCSI_DATA_IN *DataInHdr;
+ EFI_STATUS Status;
+
+ DataInHdr = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);
+
+ DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);
+ DataInHdr->ExpCmdSN = NTOHL (DataInHdr->ExpCmdSN);
+ DataInHdr->MaxCmdSN = NTOHL (DataInHdr->MaxCmdSN);
+ DataInHdr->DataSN = NTOHL (DataInHdr->DataSN);
+
+ //
+ // Check the DataSN.
+ //
+ Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Update the command related sequence numbers.
+ //
+ IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {
+ if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {
+ //
+ // The S bit is on but the F bit is off.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ Tcb->StatusXferd = TRUE;
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {
+ //
+ // Underflow and Overflow are mutual flags.
+ //
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // S bit is on, the StatSN is valid.
+ //
+ Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Packet->HostAdapterStatus = 0;
+ Packet->TargetStatus = DataInHdr->Status;
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
+ Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
+ Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Process the received iSCSI R2T PDU.
+
+ @param[in] Pdu The R2T PDU received.
+ @param[in] Tcb The task control block.
+ @param[in] Lun The Lun.
+ @param[in, out] Packet The EXT SCSI PASS THRU request packet.
+
+ @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiOnR2TRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ ISCSI_READY_TO_TRANSFER *R2THdr;
+ EFI_STATUS Status;
+ ISCSI_XFER_CONTEXT *XferContext;
+ UINT8 *Data;
+
+ R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);
+
+ R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);
+ R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);
+ R2THdr->StatSN = NTOHL (R2THdr->StatSN);
+ R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum);
+ R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);
+ R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);
+
+ if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {
+ return EFI_PROTOCOL_ERROR;;
+ }
+ //
+ // Check the sequence number.
+ //
+ Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ XferContext = &Tcb->XferContext;
+ XferContext->TargetTransferTag = R2THdr->TargetTransferTag;
+ XferContext->Offset = R2THdr->BufferOffset;
+ XferContext->DesiredLength = R2THdr->DesiredDataTransferLength;
+
+ if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||
+ (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)
+ ) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ //
+ // Send the data solicited by this R2T.
+ //
+ Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
+ Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
+
+ return Status;
+}
+
+
+/**
+ Process the received iSCSI SCSI Response PDU.
+
+ @param[in] Pdu The Response PDU received.
+ @param[in] Tcb The task control block.
+ @param[in, out] Packet The EXT SCSI PASS THRU request packet.
+
+ @retval EFI_SUCCES The Response PDU is processed.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
+ @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiOnScsiRspRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ SCSI_RESPONSE *ScsiRspHdr;
+ ISCSI_SENSE_DATA *SenseData;
+ EFI_STATUS Status;
+ UINT32 DataSegLen;
+
+ ScsiRspHdr = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
+
+ ScsiRspHdr->InitiatorTaskTag = NTOHL (ScsiRspHdr->InitiatorTaskTag);
+ if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ ScsiRspHdr->StatSN = NTOHL (ScsiRspHdr->StatSN);
+
+ Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ScsiRspHdr->MaxCmdSN = NTOHL (ScsiRspHdr->MaxCmdSN);
+ ScsiRspHdr->ExpCmdSN = NTOHL (ScsiRspHdr->ExpCmdSN);
+ IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);
+
+ Tcb->StatusXferd = TRUE;
+
+ Packet->HostAdapterStatus = ScsiRspHdr->Response;
+ if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {
+ return EFI_SUCCESS;
+ }
+
+ Packet->TargetStatus = ScsiRspHdr->Status;
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||
+ ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)
+ ) {
+ return EFI_PROTOCOL_ERROR;
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {
+ Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {
+ Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
+ if (Packet->DataDirection == DataIn) {
+ Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
+ } else {
+ Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
+ }
+
+ Status = EFI_BAD_BUFFER_SIZE;
+ }
+
+ if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
+ if (Packet->DataDirection == DataIn) {
+ Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
+ } else {
+ Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
+ }
+ }
+
+ DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);
+ if (DataSegLen != 0) {
+ SenseData = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);
+
+ SenseData->Length = NTOHS (SenseData->Length);
+
+ Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);
+ if (Packet->SenseDataLength != 0) {
+ CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);
+ }
+ } else {
+ Packet->SenseDataLength = 0;
+ }
+
+ return Status;
+}
+
+
+/**
+ Process the received NOP In PDU.
+
+ @param[in] Pdu The NOP In PDU received.
+ @param[in] Tcb The task control block.
+
+ @retval EFI_SUCCES The NOP In PDU is processed and the related sequence
+ numbers are updated.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
+
+**/
+EFI_STATUS
+IScsiOnNopInRcvd (
+ IN NET_BUF *Pdu,
+ IN ISCSI_TCB *Tcb
+ )
+{
+ ISCSI_NOP_IN *NopInHdr;
+ EFI_STATUS Status;
+
+ NopInHdr = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);
+
+ NopInHdr->StatSN = NTOHL (NopInHdr->StatSN);
+ NopInHdr->ExpCmdSN = NTOHL (NopInHdr->ExpCmdSN);
+ NopInHdr->MaxCmdSN = NTOHL (NopInHdr->MaxCmdSN);
+
+ if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {
+ if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {
+ return EFI_PROTOCOL_ERROR;
+ }
+ } else {
+ Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
+
+ @param[in] PassThru The EXT SCSI PASS THRU protocol.
+ @param[in] Target The target ID.
+ @param[in] Lun The LUN.
+ @param[in, out] Packet The request packet containing IO request, SCSI command
+ buffer and buffers to read/write.
+
+ @retval EFI_SUCCES The SCSI command is executed and the result is updated to
+ the Packet.
+ @retval EFI_DEVICE_ERROR Session state was not as required.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiExecuteScsiCommand (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ )
+{
+ EFI_STATUS Status;
+ ISCSI_DRIVER_DATA *Private;
+ ISCSI_SESSION *Session;
+ EFI_EVENT TimeoutEvent;
+ ISCSI_CONNECTION *Conn;
+ ISCSI_TCB *Tcb;
+ NET_BUF *Pdu;
+ ISCSI_XFER_CONTEXT *XferContext;
+ UINT8 *Data;
+ ISCSI_IN_BUFFER_CONTEXT InBufferContext;
+ UINT64 Timeout;
+ UINT8 *PduHdr;
+
+ Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
+ Session = Private->Session;
+ Status = EFI_SUCCESS;
+ Tcb = NULL;
+ TimeoutEvent = NULL;
+ Timeout = 0;
+
+ if (Session->State != SESSION_STATE_LOGGED_IN) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Conn = NET_LIST_USER_STRUCT_S (
+ Session->Conns.ForwardLink,
+ ISCSI_CONNECTION,
+ Link,
+ ISCSI_CONNECTION_SIGNATURE
+ );
+
+ if (Packet->Timeout != 0) {
+ Timeout = MultU64x32 (Packet->Timeout, 4);
+ }
+
+ Status = IScsiNewTcb (Conn, &Tcb);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
+ //
+ Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);
+ if (Pdu == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ XferContext = &Tcb->XferContext;
+ PduHdr = NetbufGetByte (Pdu, 0, NULL);
+ XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);
+
+ //
+ // Transmit the SCSI Command PDU.
+ //
+ Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
+
+ NetbufFree (Pdu);
+
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ if (!Session->InitialR2T &&
+ (XferContext->Offset < Session->FirstBurstLength) &&
+ (XferContext->Offset < Packet->OutTransferLength)
+ ) {
+ //
+ // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
+ // OUT data, and the limit of FirstBurstLength is not reached.
+ //
+ XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;
+ XferContext->DesiredLength = MIN (
+ Session->FirstBurstLength,
+ Packet->OutTransferLength - XferContext->Offset
+ );
+
+ Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
+ Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ InBufferContext.InData = (UINT8 *) Packet->InDataBuffer;
+ InBufferContext.InDataLen = Packet->InTransferLength;
+
+ while (!Tcb->StatusXferd) {
+ //
+ // Start the timeout timer.
+ //
+ if (Timeout != 0) {
+ Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ TimeoutEvent = Conn->TimeoutEvent;
+ }
+
+ //
+ // Try to receive PDU from target.
+ //
+ Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ switch (ISCSI_GET_OPCODE (NetbufGetByte (Pdu, 0, NULL))) {
+ case ISCSI_OPCODE_SCSI_DATA_IN:
+ Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);
+ break;
+
+ case ISCSI_OPCODE_R2T:
+ Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);
+ break;
+
+ case ISCSI_OPCODE_SCSI_RSP:
+ Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);
+ break;
+
+ case ISCSI_OPCODE_NOP_IN:
+ Status = IScsiOnNopInRcvd (Pdu, Tcb);
+ break;
+
+ case ISCSI_OPCODE_VENDOR_T0:
+ case ISCSI_OPCODE_VENDOR_T1:
+ case ISCSI_OPCODE_VENDOR_T2:
+ //
+ // These messages are vendor specific. Skip them.
+ //
+ break;
+
+ default:
+ Status = EFI_PROTOCOL_ERROR;
+ break;
+ }
+
+ NetbufFree (Pdu);
+
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ }
+
+ON_EXIT:
+
+ if (TimeoutEvent != NULL) {
+ gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
+ }
+
+ if (Tcb != NULL) {
+ IScsiDelTcb (Tcb);
+ }
+
+ if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
+ //
+ // Reinstate the session.
+ //
+ if (EFI_ERROR (IScsiSessionReinstatement (Session))) {
+ Status = EFI_DEVICE_ERROR;
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Reinstate the session on some error.
+
+ @param[in] Session The iSCSI session
+
+ @retval EFI_SUCCESS The session is reinstated from some error.
+ @retval Other Reinstatement failed.
+
+**/
+EFI_STATUS
+IScsiSessionReinstatement (
+ IN ISCSI_SESSION *Session
+ )
+{
+ EFI_STATUS Status;
+
+ ASSERT (Session->State == SESSION_STATE_LOGGED_IN);
+
+ //
+ // Abort the session and re-init it.
+ //
+ IScsiSessionAbort (Session);
+ IScsiSessionInit (Session, TRUE);
+
+ //
+ // Login again.
+ //
+ Status = IScsiSessionLogin (Session);
+
+ return Status;
+}
+
+
+/**
+ Initialize some session parameters before login.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Recovery Whether the request is from a fresh new start or recovery.
+
+**/
+VOID
+IScsiSessionInit (
+ IN OUT ISCSI_SESSION *Session,
+ IN BOOLEAN Recovery
+ )
+{
+ if (!Recovery) {
+ Session->Signature = ISCSI_SESSION_SIGNATURE;
+ Session->State = SESSION_STATE_FREE;
+
+ InitializeListHead (&Session->Conns);
+ InitializeListHead (&Session->TcbList);
+ }
+
+ Session->Tsih = 0;
+
+ Session->CmdSN = 1;
+ Session->InitiatorTaskTag = 1;
+ Session->NextCid = 1;
+
+ Session->TargetPortalGroupTag = 0;
+ Session->MaxConnections = ISCSI_MAX_CONNS_PER_SESSION;
+ Session->InitialR2T = FALSE;
+ Session->ImmediateData = TRUE;
+ Session->MaxBurstLength = 262144;
+ Session->FirstBurstLength = MAX_RECV_DATA_SEG_LEN_IN_FFP;
+ Session->DefaultTime2Wait = 2;
+ Session->DefaultTime2Retain = 20;
+ Session->MaxOutstandingR2T = DEFAULT_MAX_OUTSTANDING_R2T;
+ Session->DataPDUInOrder = TRUE;
+ Session->DataSequenceInOrder = TRUE;
+ Session->ErrorRecoveryLevel = 0;
+}
+
+
+/**
+ Abort the iSCSI session. That is, reset all the connection(s), and free the
+ resources.
+
+ @param[in, out] Session The iSCSI session.
+
+**/
+VOID
+IScsiSessionAbort (
+ IN OUT ISCSI_SESSION *Session
+ )
+{
+ ISCSI_CONNECTION *Conn;
+ EFI_GUID *ProtocolGuid;
+
+ if (Session->State != SESSION_STATE_LOGGED_IN) {
+ return ;
+ }
+
+ ASSERT (!IsListEmpty (&Session->Conns));
+
+ while (!IsListEmpty (&Session->Conns)) {
+ Conn = NET_LIST_USER_STRUCT_S (
+ Session->Conns.ForwardLink,
+ ISCSI_CONNECTION,
+ Link,
+ ISCSI_CONNECTION_SIGNATURE
+ );
+ if (!Conn->Ipv6Flag) {
+ ProtocolGuid = &gEfiTcp4ProtocolGuid;
+ } else {
+ ProtocolGuid = &gEfiTcp6ProtocolGuid;
+ }
+
+ gBS->CloseProtocol (
+ Conn->TcpIo.Handle,
+ ProtocolGuid,
+ Session->Private->Image,
+ Session->Private->ExtScsiPassThruHandle
+ );
+
+ IScsiConnReset (Conn);
+
+ IScsiDetatchConnection (Conn);
+ IScsiDestroyConnection (Conn);
+ }
+
+ Session->State = SESSION_STATE_FAILED;
+
+ return ;
+}
diff --git a/NetworkPkg/IScsiDxe/IScsiProto.h b/NetworkPkg/IScsiDxe/IScsiProto.h
new file mode 100644
index 0000000000..80c62b765c
--- /dev/null
+++ b/NetworkPkg/IScsiDxe/IScsiProto.h
@@ -0,0 +1,1035 @@
+/** @file
+ The header file of iSCSI Protocol that defines many specific data structures.
+
+Copyright (c) 2004 - 2011, 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.
+
+**/
+
+#ifndef _ISCSI_PROTO_H_
+#define _ISCSI_PROTO_H_
+
+//
+// RFC 1982 Serial Number Arithmetic, SERIAL_BITS = 32
+//
+#define ISCSI_SEQ_EQ(s1, s2) ((s1) == (s2))
+#define ISCSI_SEQ_LT(s1, s2) \
+ ( \
+ (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) < ((UINT32) 1 << 31)) || \
+ (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) > ((UINT32) 1 << 31)) \
+ )
+#define ISCSI_SEQ_GT(s1, s2) \
+ ( \
+ (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) > ((UINT32) 1 << 31)) || \
+ (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) < ((UINT32) 1 << 31)) \
+ )
+
+#define ISCSI_WELL_KNOWN_PORT 3260
+#define ISCSI_MAX_CONNS_PER_SESSION 1
+
+#define DEFAULT_MAX_RECV_DATA_SEG_LEN 8192
+#define MAX_RECV_DATA_SEG_LEN_IN_FFP 65536
+#define DEFAULT_MAX_OUTSTANDING_R2T 1
+
+#define ISCSI_VERSION_MAX 0x00
+#define ISCSI_VERSION_MIN 0x00
+
+#define ISCSI_KEY_AUTH_METHOD "AuthMethod"
+#define ISCSI_KEY_HEADER_DIGEST "HeaderDigest"
+#define ISCSI_KEY_DATA_DIGEST "DataDigest"
+#define ISCSI_KEY_MAX_CONNECTIONS "MaxConnections"
+#define ISCSI_KEY_TARGET_NAME "TargetName"
+#define ISCSI_KEY_INITIATOR_NAME "InitiatorName"
+#define ISCSI_KEY_TARGET_ALIAS "TargetAlias"
+#define ISCSI_KEY_INITIATOR_ALIAS "InitiatorAlias"
+#define ISCSI_KEY_TARGET_ADDRESS "TargetAddress"
+#define ISCSI_KEY_INITIAL_R2T "InitialR2T"
+#define ISCSI_KEY_IMMEDIATE_DATA "ImmediateData"
+#define ISCSI_KEY_TARGET_PORTAL_GROUP_TAG "TargetPortalGroupTag"
+#define ISCSI_KEY_MAX_BURST_LENGTH "MaxBurstLength"
+#define ISCSI_KEY_FIRST_BURST_LENGTH "FirstBurstLength"
+#define ISCSI_KEY_DEFAULT_TIME2WAIT "DefaultTime2Wait"
+#define ISCSI_KEY_DEFAULT_TIME2RETAIN "DefaultTime2Retain"
+#define ISCSI_KEY_MAX_OUTSTANDING_R2T "MaxOutstandingR2T"
+#define ISCSI_KEY_DATA_PDU_IN_ORDER "DataPDUInOrder"
+#define ISCSI_KEY_DATA_SEQUENCE_IN_ORDER "DataSequenceInOrder"
+#define ISCSI_KEY_ERROR_RECOVERY_LEVEL "ErrorRecoveryLevel"
+#define ISCSI_KEY_SESSION_TYPE "SessionType"
+#define ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH "MaxRecvDataSegmentLength"
+
+#define ISCSI_KEY_VALUE_NONE "None"
+
+///
+/// connection state for initiator
+///
+
+#define CONN_STATE_FREE 0
+#define CONN_STATE_XPT_WAIT 1
+#define CONN_STATE_IN_LOGIN 2
+#define CONN_STATE_LOGGED_IN 3
+#define CONN_STATE_IN_LOGOUT 4
+#define CONN_STATE_LOGOUT_REQUESTED 5
+#define CONN_STATE_CLEANUP_WAIT 6
+#define CONN_STATE_IN_CLEANUP 7
+
+///
+/// session state for initiator
+///
+#define SESSION_STATE_FREE 0
+#define SESSION_STATE_LOGGED_IN 1
+#define SESSION_STATE_FAILED 2
+
+#define ISCSI_RESERVED_TAG 0xffffffff
+
+#define ISCSI_REQ_IMMEDIATE 0x40
+#define ISCSI_OPCODE_MASK 0x3F
+
+#define ISCSI_SET_OPCODE(PduHdr, Op, Flgs) ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) = ((Op) | (Flgs)))
+#define ISCSI_GET_OPCODE(PduHdr) ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) & ISCSI_OPCODE_MASK)
+#define ISCSI_CHECK_OPCODE(PduHdr, Op) ((((PduHdr)->OpCode) & ISCSI_OPCODE_MASK) == (Op))
+#define ISCSI_IMMEDIATE_ON(PduHdr) ((PduHdr)->OpCode & ISCSI_REQ_IMMEDIATE)
+#define ISCSI_SET_FLAG(PduHdr, Flag) (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags |= (BOOLEAN)(Flag))
+#define ISCSI_CLEAR_FLAG(PduHdr, Flag) (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags &= ~(Flag))
+#define ISCSI_FLAG_ON(PduHdr, Flag) ((BOOLEAN) ((((ISCSI_BASIC_HEADER *) (PduHdr))->Flags & (Flag)) == (Flag)))
+#define ISCSI_SET_STAGES(PduHdr, Cur, Nxt) ((PduHdr)->Flags = (UINT8) ((PduHdr)->Flags | ((Cur) << 2 | (Nxt))))
+#define ISCSI_GET_CURRENT_STAGE(PduHdr) ((UINT8) (((PduHdr)->Flags >> 2) & 0x3))
+#define ISCSI_GET_NEXT_STAGE(PduHdr) ((UINT8) (((PduHdr)->Flags) & 0x3))
+
+#define ISCSI_GET_PAD_LEN(DataLen) ((~(DataLen) + 1) & 0x3)
+#define ISCSI_ROUNDUP(DataLen) (((DataLen) + 3) &~(0x3))
+
+#define HTON24(Dst, Src) \
+ do { \
+ (Dst)[0] = (UINT8) ((UINT8) ((Src) >> 16) & 0xFF); \
+ (Dst)[1] = (UINT8) ((UINT8) ((Src) >> 8) & 0xFF); \
+ (Dst)[2] = (UINT8) ((UINT8) (Src) & 0xFF); \
+ } while (0);
+
+#define NTOH24(src) (((src)[0] << 16) | ((src)[1] << 8) | ((src)[2]))
+
+#define ISCSI_GET_DATASEG_LEN(PduHdr) NTOH24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength)
+#define ISCSI_SET_DATASEG_LEN(PduHdr, Len) HTON24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength, (Len))
+#define ISCSI_GET_BUFFER_OFFSET(PduHdr) NTOHL (((ISCSI_SCSI_DATA_IN *) (PduHdr))->BufferOffset)
+
+//
+// Initiator opcodes.
+//
+#define ISCSI_OPCODE_NOP_OUT 0x00
+#define ISCSI_OPCODE_SCSI_CMD 0x01
+#define ISCSI_OPCODE_SCSI_TMF_REQ 0x02
+#define ISCSI_OPCODE_LOGIN_REQ 0x03
+#define ISCSI_OPCODE_TEXT_REQ 0x04
+#define ISCSI_OPCODE_SCSI_DATA_OUT 0x05
+#define ISCSI_OPCODE_LOGOUT_REQ 0x06
+#define ISCSI_OPCODE_SNACK_REQ 0x10
+#define ISCSI_OPCODE_VENDOR_I0 0x1c
+#define ISCSI_OPCODE_VENDOR_I1 0x1d
+#define ISCSI_OPCODE_VENDOR_I2 0x1e
+
+//
+// Target opcodes.
+//
+#define ISCSI_OPCODE_NOP_IN 0x20
+#define ISCSI_OPCODE_SCSI_RSP 0x21
+#define ISCSI_OPCODE_SCSI_TMF_RSP 0x22
+#define ISCSI_OPCODE_LOGIN_RSP 0x23
+#define ISCSI_OPCODE_TEXT_RSP 0x24
+#define ISCSI_OPCODE_SCSI_DATA_IN 0x25
+#define ISCSI_OPCODE_LOGOUT_RSP 0x26
+#define ISCSI_OPCODE_R2T 0x31
+#define ISCSI_OPCODE_ASYNC_MSG 0x32
+#define ISCSI_OPCODE_VENDOR_T0 0x3c
+#define ISCSI_OPCODE_VENDOR_T1 0x3d
+#define ISCSI_OPCODE_VENDOR_T2 0x3e
+#define ISCSI_OPCODE_REJECT 0x3f
+
+#define ISCSI_BHS_FLAG_FINAL 0x80
+
+//
+// Defined AHS types, others are reserved.
+//
+#define ISCSI_AHS_TYPE_EXT_CDB 0x1
+#define ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN 0x2
+
+#define SCSI_CMD_PDU_FLAG_READ 0x40
+#define SCSI_CMD_PDU_FLAG_WRITE 0x20
+
+#define ISCSI_CMD_PDU_TASK_ATTR_MASK 0x07
+
+//
+// task attributes
+//
+#define ISCSI_TASK_ATTR_UNTAGGED 0x00
+#define ISCSI_TASK_ATTR_SIMPLE 0x01
+#define ISCSI_TASK_ATTR_ORDERD 0x02
+#define ISCSI_TASK_ATTR_HOQ 0x03
+#define ISCSI_TASK_ATTR_ACA 0x04
+
+//
+// Flag bit definitions in SCSI response.
+//
+#define SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW 0x10
+#define SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW 0x08
+#define SCSI_RSP_PDU_FLAG_OVERFLOW 0x04
+#define SCSI_RSP_PDU_FLAG_UNDERFLOW 0x02
+
+//
+// iSCSI service response codes.
+//
+#define ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET 0x00
+#define ISCSI_SERVICE_RSP_TARGET_FAILURE 0x01
+
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_COMPLETE 0
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_NOT_EXIST 1
+#define ISCSI_TMF_RSP_PDU_RSP_LUN_NOT_EXIST 2
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_STILL_ALLEGIANT 3
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_REASSGIN_NOT_SUPPORTED 4
+#define ISCSI_TMF_RSP_PDU_RSP_NOT_SUPPORTED 5
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_AHTH_FAILED 6
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_REJECTED 255
+
+#define SCSI_DATA_IN_PDU_FLAG_ACKKNOWLEDGE 0x40
+#define SCSI_DATA_IN_PDU_FLAG_OVERFLOW SCSI_RSP_PDU_FLAG_OVERFLOW
+#define SCSI_DATA_IN_PDU_FLAG_UNDERFLOW SCSI_RSP_PDU_FLAG_UNDERFLOW
+#define SCSI_DATA_IN_PDU_FLAG_STATUS_VALID 0x01
+
+#define ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT 0x80
+#define ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE 0x40
+
+#define ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
+#define ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE
+
+#define ISCSI_LOGIN_STATUS_SUCCESS 0
+#define ISCSI_LOGIN_STATUS_REDIRECTION 1
+#define ISCSI_LOGIN_STATUS_INITIATOR_ERROR 2
+#define ISCSI_LOGIN_STATUS_TARGET_ERROR 3
+
+#define ISCSI_LOGOUT_REASON_CLOSE_SESSION 0
+#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION 1
+#define ISCSI_LOGOUT_REASON_REMOVE_CONNECTION_FOR_RECOVERY 2
+
+#define ISCSI_LOGOUT_RESPONSE_SESSION_CLOSED_SUCCESS 0
+#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND 1
+#define ISCSI_LOGOUT_RESPONSE_RECOVERY_NOT_SUPPORTED 2
+#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED 3
+
+#define ISCSI_SNACK_REQUEST_TYPE_DATA_OR_R2T 0
+#define ISCSI_SNACK_REQUEST_TYPE_STATUS 1
+#define ISCSI_SNACK_REQUEST_TYPE_DATA_ACK 2
+#define ISCSI_SNACK_REQUEST_TYPE_RDATA 3
+
+#define ISCSI_SECURITY_NEGOTIATION 0
+#define ISCSI_LOGIN_OPERATIONAL_NEGOTIATION 1
+#define ISCSI_FULL_FEATURE_PHASE 3
+
+typedef struct _ISCSI_SESSION ISCSI_SESSION;
+typedef struct _ISCSI_CONNECTION ISCSI_CONNECTION;
+
+typedef enum {
+ DataIn = 0,
+ DataOut = 1,
+ DataBi = 2
+} DATA_DIRECTION;
+
+///
+/// iSCSI Basic Header Segment
+///
+typedef struct _ISCSI_BASIC_HEADER {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT16 OpCodeSpecific1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 OpCodeSpecific2[7];
+} ISCSI_BASIC_HEADER;
+
+typedef struct _ISCSI_ADDTIONAL_HEADER {
+ UINT16 Length;
+ UINT8 Type;
+ UINT8 TypeSpecific[1];
+} ISCSI_ADDITIONAL_HEADER;
+
+typedef struct _ISCSI_BI_EXP_READ_DATA_LEN_AHS {
+ UINT16 Length;
+ UINT8 Type;
+ UINT8 Reserved;
+ UINT32 ExpReadDataLength;
+} ISCSI_BI_EXP_READ_DATA_LEN_AHS;
+
+///
+/// SCSI Command
+///
+typedef struct _SCSI_COMMAND {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT16 Reserved;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 ExpDataXferLength;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT8 Cdb[16];
+} SCSI_COMMAND;
+
+///
+/// SCSI Response
+///
+typedef struct _SCSI_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 Response;
+ UINT8 Status;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Reserved[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 SNACKTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 ExpDataSN;
+ UINT32 BiReadResidualCount;
+ UINT32 ResidualCount;
+} SCSI_RESPONSE;
+
+typedef struct _ISCSI_SENSE_DATA {
+ UINT16 Length;
+ UINT8 Data[2];
+} ISCSI_SENSE_DATA;
+
+///
+/// iSCSI Task Managment Function Request.
+///
+typedef struct _ISCSI_TMF_REQUEST {
+ UINT8 OpCode;
+ UINT8 Fuction;
+ UINT16 Reserved1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 ReferencedTaskTag;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 RefCmdSN;
+ UINT32 ExpDataSN;
+ UINT32 Reserved2[2];
+} ISCSI_TMF_REQUEST;
+
+///
+/// iSCSI Task Management Function Response.
+///
+typedef struct _ISCSI_TMF_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Reserved1;
+ UINT8 Response;
+ UINT8 Reserved2;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserver3[2];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved4;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 Reserved[3];
+} ISCSI_TMF_RESPONSE;
+
+///
+/// SCSI Data-Out
+///
+typedef struct _ISCSI_SCSI_DATA_OUT {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 Reserved2;
+ UINT32 ExpStatSN;
+ UINT32 Reserved3;
+ UINT32 DataSN;
+ UINT32 BufferOffset;
+ UINT32 Reserved4;
+} ISCSI_SCSI_DATA_OUT;
+
+///
+/// SCSI Data-In
+///
+typedef struct _ISCSI_SCSI_DATA_IN {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 Reserved1;
+ UINT8 Status;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 DataSN;
+ UINT32 BufferOffset;
+ UINT32 ResidualCount;
+} ISCSI_SCSI_DATA_IN;
+
+///
+/// Ready To Transfer.
+///
+typedef struct _ISCSI_READY_TO_TRANSFER {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 R2TSeqNum;
+ UINT32 BufferOffset;
+ UINT32 DesiredDataTransferLength;
+} ISCSI_READY_TO_TRANSFER;
+
+typedef struct _ISCSI_ASYNC_MESSAGE {
+ UINT8 OpCode;
+ UINT8 Reserved1[8];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved2;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT8 AsyncEvent;
+ UINT8 AsyncVCode;
+ UINT16 Parameter1;
+ UINT16 Parameter2;
+ UINT16 Parameter3;
+ UINT32 Reserved3;
+} ISCSI_ASYNC_MESSAGE;
+
+///
+/// Login Request.
+///
+typedef struct _ISCSI_LOGIN_REQUEST {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 VersionMax;
+ UINT8 VersionMin;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Isid[6];
+ UINT16 Tsih;
+ UINT32 InitiatorTaskTag;
+ UINT16 Cid;
+ UINT16 Reserved1;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 Reserved2[4];
+} ISCSI_LOGIN_REQUEST;
+
+///
+/// Login Response.
+///
+typedef struct _ISCSI_LOGIN_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Flags;
+ UINT8 VersionMax;
+ UINT8 VersionActive;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Isid[6];
+ UINT16 Tsih;
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved1;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT8 StatusClass;
+ UINT8 StatusDetail;
+ UINT8 Reserved2[10];
+} ISCSI_LOGIN_RESPONSE;
+
+///
+/// Logout Request.
+///
+typedef struct _ISCSI_LOGOUT_REQUEST {
+ UINT8 OpCode;
+ UINT8 ReasonCode;
+ UINT16 Reserved1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserved2[2];
+ UINT32 InitiatorTaskTag;
+ UINT16 Cid;
+ UINT16 Reserved3;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 Reserved4[4];
+} ISCSI_LOGOUT_REQUEST;
+
+///
+/// Logout Response.
+///
+typedef struct _ISCSI_LOGOUT_RESPONSE {
+ UINT8 OpCode;
+ UINT8 Reserved1;
+ UINT8 Response;
+ UINT8 Reserved2;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserved3[2];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved4;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 Reserved5;
+ UINT16 Time2Wait;
+ UINT16 Time2Retain;
+ UINT32 Reserved6;
+} ISCSI_LOGOUT_RESPONSE;
+
+///
+/// SNACK Request.
+///
+typedef struct _ISCSI_SNACK_REQUEST {
+ UINT8 OpCode;
+ UINT8 Type;
+ UINT16 Reserved1;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 Reserved2;
+ UINT32 ExpStatSN;
+ UINT32 Reserved[2];
+ UINT32 BegRun;
+ UINT32 RunLength;
+} ISCSI_SNACK_REQUEST;
+
+///
+/// Reject.
+///
+typedef struct _ISCSI_REJECT {
+ UINT8 OpCode;
+ UINT8 Reserved1;
+ UINT8 Reason;
+ UINT8 Reserved2;
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT32 Reserved3[2];
+ UINT32 InitiatorTaskTag;
+ UINT32 Reserved4;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 DataSN;
+ UINT32 Reserved5[2];
+} ISCSI_REJECT;
+
+///
+/// NOP-Out.
+///
+typedef struct _ISCSI_NOP_OUT {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 CmdSN;
+ UINT32 ExpStatSN;
+ UINT32 Reserved2[4];
+} ISCSI_NOP_OUT;
+
+///
+/// NOP-In.
+///
+typedef struct _ISCSI_NOP_IN {
+ UINT8 OpCode;
+ UINT8 Reserved1[3];
+ UINT8 TotalAHSLength;
+ UINT8 DataSegmentLength[3];
+ UINT8 Lun[8];
+ UINT32 InitiatorTaskTag;
+ UINT32 TargetTransferTag;
+ UINT32 StatSN;
+ UINT32 ExpCmdSN;
+ UINT32 MaxCmdSN;
+ UINT32 Reserved2[3];
+} ISCSI_NOP_IN;
+
+typedef enum {
+ IScsiDigestNone,
+ IScsiDigestCRC32
+} ISCSI_DIGEST_TYPE;
+
+typedef struct _ISCSI_XFER_CONTEXT {
+ UINT32 TargetTransferTag;
+ UINT32 Offset;
+ UINT32 DesiredLength;
+ UINT32 ExpDataSN;
+} ISCSI_XFER_CONTEXT;
+
+typedef struct _ISCSI_IN_BUFFER_CONTEXT {
+ UINT8 *InData;
+ UINT32 InDataLen;
+} ISCSI_IN_BUFFER_CONTEXT;
+
+typedef struct _ISCSI_TCB {
+ LIST_ENTRY Link;
+
+ BOOLEAN SoFarInOrder;
+ UINT32 ExpDataSN;
+ BOOLEAN FbitReceived;
+ BOOLEAN StatusXferd;
+ UINT32 ActiveR2Ts;
+ UINT32 Response;
+ CHAR8 *Reason;
+ UINT32 InitiatorTaskTag;
+ UINT32 CmdSN;
+ UINT32 SNACKTag;
+
+ ISCSI_XFER_CONTEXT XferContext;
+
+ ISCSI_CONNECTION *Conn;
+} ISCSI_TCB;
+
+typedef struct _ISCSI_KEY_VALUE_PAIR {
+ LIST_ENTRY List;
+
+ CHAR8 *Key;
+ CHAR8 *Value;
+} ISCSI_KEY_VALUE_PAIR;
+
+/**
+ Attach the iSCSI connection to the iSCSI session.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in, out] Conn The iSCSI connection.
+
+**/
+VOID
+IScsiAttatchConnection (
+ IN OUT ISCSI_SESSION *Session,
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Detach the iSCSI connection from the session it belongs to.
+
+ @param[in, out] Conn The iSCSI connection.
+
+**/
+VOID
+IScsiDetatchConnection (
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ This function performs the iSCSI connection login.
+
+ @param[in, out] Conn The iSCSI connection to login.
+ @param Timeout The timeout value in milliseconds.
+
+ @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target.
+ @retval EFI_TIMEOUT Timeout occurred during the login procedure.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiConnLogin (
+ IN OUT ISCSI_CONNECTION *Conn,
+ IN UINT16 Timeout
+ );
+
+/**
+ Create a TCP connection for the iSCSI session.
+
+ @param[in] Session Points to the iSCSI session.
+
+ @return The newly created iSCSI connection.
+
+**/
+ISCSI_CONNECTION *
+IScsiCreateConnection (
+ IN ISCSI_SESSION *Session
+ );
+
+/**
+ Destroy an iSCSI connection.
+
+ @param[in] Conn The connection to destroy.
+
+**/
+VOID
+IScsiDestroyConnection (
+ IN ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Login the iSCSI session.
+
+ @param[in] Session The iSCSI session
+
+ @retval EFI_SUCCESS The iSCSI session login procedure finished.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NO_MEDIA There was a media error.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiSessionLogin (
+ IN ISCSI_SESSION *Session
+ );
+
+/**
+ Wait for IPsec negotiation, then try to login the iSCSI session again.
+
+ @param[in] Session The iSCSI session
+
+ @retval EFI_SUCCESS The iSCSI session login procedure finished.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+
+**/
+EFI_STATUS
+IScsiSessionReLogin (
+ IN ISCSI_SESSION *Session
+ );
+
+/**
+ Build and send the iSCSI login request to the iSCSI target according to
+ the current login stage.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
+ connection.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_DEVICE_ERROR Some kind of device error happened.
+
+**/
+EFI_STATUS
+IScsiSendLoginReq (
+ IN ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Receive and process the iSCSI login response.
+
+ @param[in] Conn The connection in the iSCSI login phase.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiReceiveLoginRsp (
+ IN ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
+ The DataSegmentLength and the actual size of the net buffer containing this PDU will be
+ updated.
+
+ @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
+ be added to.
+ @param[in] Key The key name string.
+ @param[in] Value The value string.
+
+ @retval EFI_SUCCESS The key-valu pair is added to the PDU's datasegment and
+ the correspondence length fields are updated.
+ @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
+ pair.
+**/
+EFI_STATUS
+IScsiAddKeyValuePair (
+ IN OUT NET_BUF *Pdu,
+ IN CHAR8 *Key,
+ IN CHAR8 *Value
+ );
+
+/**
+ Prepare the iSCSI login request to be sent according to the current login status.
+
+ @param[in, out] Conn The connection in the iSCSI login phase.
+
+ @return The pointer to the net buffer containing the iSCSI login request built.
+ @retval NULL Other errors as indicated.
+
+**/
+NET_BUF *
+IScsiPrepareLoginReq (
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Process the iSCSI Login Response.
+
+ @param[in, out] Conn The connection on which the iSCSI login response is received.
+ @param[in, out] Pdu The iSCSI login response PDU.
+
+ @retval EFI_SUCCESS The iSCSI login response PDU is processed and all check are passed.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
+ @retval EFI_MEDIA_CHANGED Target is redirected.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiProcessLoginRsp (
+ IN OUT ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ );
+
+/**
+ Updated the target information according the data received in the iSCSI
+ login response with an target redirection status.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Data The data segment which should contain the
+ TargetAddress key-value list.
+ @param[in] Len Length of the data.
+
+ @retval EFI_SUCCESS The target address is updated.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_NOT_FOUND The TargetAddress key is not found.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiUpdateTargetAddress (
+ IN OUT ISCSI_SESSION *Session,
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ );
+
+/**
+ The callback function to free the net buffer list.
+
+ @param[in] Arg The opaque parameter.
+
+**/
+VOID
+EFIAPI
+IScsiFreeNbufList (
+ VOID *Arg
+ );
+
+/**
+ Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
+ an optional data segment. The two parts will be put into two blocks of buffers in the
+ net buffer. The digest check will be conducted in this function if needed and the digests
+ will be trimmed from the PDU buffer.
+
+ @param[in] Conn The iSCSI connection to receive data from.
+ @param[out] Pdu The received iSCSI pdu.
+ @param[in] Context The context used to describe information on the caller provided
+ buffer to receive data segment of the iSCSI pdu, it's optional.
+ @param[in] HeaderDigest Whether there will be header digest received.
+ @param[in] DataDigest Whether there will be data digest.
+ @param[in] TimeoutEvent The timeout event, it's optional.
+
+ @retval EFI_SUCCESS An iSCSI pdu is received.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiReceivePdu (
+ IN ISCSI_CONNECTION *Conn,
+ OUT NET_BUF **Pdu,
+ IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL
+ IN BOOLEAN HeaderDigest,
+ IN BOOLEAN DataDigest,
+ IN EFI_EVENT TimeoutEvent OPTIONAL
+ );
+
+/**
+ Check and get the result of the prameter negotiation.
+
+ @param[in, out] Conn The connection in iSCSI login.
+
+ @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
+ @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+IScsiCheckOpParams (
+ IN OUT ISCSI_CONNECTION *Conn
+ );
+
+/**
+ Fill the oprational prameters.
+
+ @param[in] Conn The connection in iSCSI login.
+ @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
+
+**/
+VOID
+IScsiFillOpParams (
+ IN ISCSI_CONNECTION *Conn,
+ IN OUT NET_BUF *Pdu
+ );
+
+/**
+ Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
+
+ @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
+ @param[in] Len The length of the last semgnet in the PDU.
+
+ @retval EFI_SUCCESS The segment is padded or no need to pad it.
+ @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
+ padding bytes.
+**/
+EFI_STATUS
+IScsiPadSegment (
+ IN OUT NET_BUF *Pdu,
+ IN UINT32 Len
+ );
+
+/**
+ Build a key-value list from the data segment.
+
+ @param[in] Data The data segment containing the key-value pairs.
+ @param[in] Len Length of the data segment.
+
+ @return The key-value list.
+ @retval NULL Other errors as indicated.
+
+**/
+LIST_ENTRY *
+IScsiBuildKeyValueList (
+ IN CHAR8 *Data,
+ IN UINT32 Len
+ );
+
+/**
+ Get the value string by the key name from the key-value list. If found,
+ the key-value entry will be removed from the list.
+
+ @param[in, out] KeyValueList The key-value list.
+ @param[in] Key The key name to find.
+
+ @return The value string.
+ @retval NULL The key value pair can not be found.
+
+**/
+CHAR8 *
+IScsiGetValueByKeyFromList (
+ IN OUT LIST_ENTRY *KeyValueList,
+ IN CHAR8 *Key
+ );
+
+/**
+ Free the key-value list.
+
+ @param[in] KeyValueList The key-value list.
+
+**/
+VOID
+IScsiFreeKeyValueList (
+ IN LIST_ENTRY *KeyValueList
+ );
+
+/**
+ Normalize the iSCSI name according to RFC.
+
+ @param[in, out] Name The iSCSI name.
+ @param[in] Len length of the iSCSI name.
+
+ @retval EFI_SUCCESS The iSCSI name is valid and normalized.
+ @retval EFI_PROTOCOL_ERROR The iSCSI name is mal-formatted or not in the IQN format.
+
+**/
+EFI_STATUS
+IScsiNormalizeName (
+ IN OUT CHAR8 *Name,
+ IN UINTN Len
+ );
+
+/**
+ Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
+
+ @param[in] PassThru The EXT SCSI PASS THRU protocol.
+ @param[in] Target The target ID.
+ @param[in] Lun The LUN.
+ @param[in, out] Packet The request packet containing IO request, SCSI command
+ buffer and buffers to read/write.
+
+ @retval EFI_SUCCES The SCSI command is executed and the result is updated to
+ the Packet.
+ @retval EFI_DEVICE_ERROR Session state was not as required.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+IScsiExecuteScsiCommand (
+ IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,
+ IN UINT8 *Target,
+ IN UINT64 Lun,
+ IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
+ );
+
+/**
+ Reinstate the session on some error.
+
+ @param[in] Session The iSCSI session
+
+ @retval EFI_SUCCES The session is reinstated from some error.
+ @retval Other Reinstatement failed.
+
+**/
+EFI_STATUS
+IScsiSessionReinstatement (
+ IN ISCSI_SESSION *Session
+ );
+
+/**
+ Initialize some session parameters before login.
+
+ @param[in, out] Session The iSCSI session.
+ @param[in] Recovery Whether the request is from a fresh new start or recovery.
+
+**/
+VOID
+IScsiSessionInit (
+ IN OUT ISCSI_SESSION *Session,
+ IN BOOLEAN Recovery
+ );
+
+/**
+ Abort the iSCSI session, that is, reset all the connection and free the
+ resources.
+
+ @param[in, out] Session The iSCSI session.
+
+**/
+VOID
+IScsiSessionAbort (
+ IN OUT ISCSI_SESSION *Session
+ );
+
+#endif