]> git.proxmox.com Git - mirror_edk2.git/commitdiff
Add IScsiDxe driver to NetworkPkg in order to support iSCSI over IPv6 stack and iSCSI...
authortye1 <tye1@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 17 Aug 2011 02:38:08 +0000 (02:38 +0000)
committertye1 <tye1@6f19259b-4bc3-4df7-8a09-765794883524>
Wed, 17 Aug 2011 02:38:08 +0000 (02:38 +0000)
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

26 files changed:
NetworkPkg/IScsiDxe/ComponentName.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiCHAP.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiCHAP.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiConfig.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiConfig.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiConfigStrings.uni [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiDhcp.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiDhcp.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiDhcp6.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiDhcp6.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiDriver.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiDriver.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiDxe.inf [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiIbft.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiIbft.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiImpl.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiInitiatorName.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiMisc.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiMisc.h [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiProto.c [new file with mode: 0644]
NetworkPkg/IScsiDxe/IScsiProto.h [new file with mode: 0644]
NetworkPkg/NetworkPkg.dsc

diff --git a/NetworkPkg/IScsiDxe/ComponentName.c b/NetworkPkg/IScsiDxe/ComponentName.c
new file mode 100644 (file)
index 0000000..e4e28e8
--- /dev/null
@@ -0,0 +1,181 @@
+/** @file\r
+  UEFI Component Name(2) protocol implementation for iSCSI.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+//\r
+// EFI Component Name Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL gIScsiComponentName = {\r
+  IScsiComponentNameGetDriverName,\r
+  IScsiComponentNameGetControllerName,\r
+  "eng"\r
+};\r
+\r
+//\r
+// EFI Component Name 2 Protocol\r
+//\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL gIScsiComponentName2 = {\r
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IScsiComponentNameGetDriverName,\r
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IScsiComponentNameGetControllerName,\r
+  "en"\r
+};\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE     mIScsiDriverNameTable[] = {\r
+  {\r
+    "eng;en",\r
+    L"iSCSI Driver"\r
+  },\r
+  {\r
+    NULL,\r
+    NULL\r
+  }\r
+};\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user readable name of the driver.\r
+\r
+  This function retrieves the user readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,\r
+  IN  CHAR8                         *Language,\r
+  OUT CHAR16                        **DriverName\r
+  )\r
+{\r
+  return LookupUnicodeString2 (\r
+           Language,\r
+           This->SupportedLanguages,\r
+           mIScsiDriverNameTable,\r
+           DriverName,\r
+           (BOOLEAN) (This == &gIScsiComponentName)\r
+           );\r
+}\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified in\r
+                                RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language, from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL, and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,\r
+  IN  EFI_HANDLE                    ControllerHandle,\r
+  IN  EFI_HANDLE                    ChildHandle        OPTIONAL,\r
+  IN  CHAR8                         *Language,\r
+  OUT CHAR16                        **ControllerName\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c b/NetworkPkg/IScsiDxe/IScsiAuthenticationInfo.c
new file mode 100644 (file)
index 0000000..f37b403
--- /dev/null
@@ -0,0 +1,67 @@
+/** @file\r
+  Implementation for EFI_AUTHENTICATION_INFO_PROTOCOL. Currently it is a\r
+  dummy support.\r
+\r
+Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+EFI_AUTHENTICATION_INFO_PROTOCOL gIScsiAuthenticationInfo = {\r
+  IScsiGetAuthenticationInfo,\r
+  IScsiSetAuthenticationInfo\r
+};\r
+\r
+/**\r
+  Retrieves the authentication information associated with a particular controller handle.\r
+\r
+  @param[in]  This              Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.\r
+  @param[in]  ControllerHandle  Handle to the Controller.\r
+  @param[out] Buffer            Pointer to the authentication information. This function is\r
+                                responsible for allocating the buffer and it is the caller's\r
+                                responsibility to free buffer when the caller is finished with buffer.\r
+\r
+  @retval EFI_DEVICE_ERROR      The authentication information could not be\r
+                                retrieved due to a hardware error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiGetAuthenticationInfo (\r
+  IN  EFI_AUTHENTICATION_INFO_PROTOCOL *This,\r
+  IN  EFI_HANDLE                       ControllerHandle,\r
+  OUT VOID                             **Buffer\r
+  )\r
+{\r
+  return EFI_DEVICE_ERROR;\r
+}\r
+\r
+/**\r
+  Set the authentication information for a given controller handle.\r
+\r
+  @param[in]  This             Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.\r
+  @param[in]  ControllerHandle Handle to the Controller.\r
+  @param[in]  Buffer           Pointer to the authentication information.\r
+\r
+  @retval EFI_UNSUPPORTED      If the platform policies do not allow setting of\r
+                               the authentication information.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiSetAuthenticationInfo (\r
+  IN EFI_AUTHENTICATION_INFO_PROTOCOL  *This,\r
+  IN EFI_HANDLE                        ControllerHandle,\r
+  IN VOID                              *Buffer\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.c b/NetworkPkg/IScsiDxe/IScsiCHAP.c
new file mode 100644 (file)
index 0000000..4cfbba7
--- /dev/null
@@ -0,0 +1,474 @@
+/** @file\r
+  This file is for Challenge-Handshake Authentication Protocol (CHAP) Configuration.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+/**\r
+  Initator caculates its own expected hash value.\r
+  \r
+  @param[in]   ChapIdentifier     iSCSI CHAP identifier sent by authenticator.\r
+  @param[in]   ChapSecret         iSCSI CHAP secret of the authenticator.\r
+  @param[in]   SecretLength       The length of iSCSI CHAP secret.\r
+  @param[in]   ChapChallenge      The challenge message sent by authenticator.\r
+  @param[in]   ChallengeLength    The length of iSCSI CHAP challenge message.\r
+  @param[out]  ChapResponse       The calculation of the expected hash value.\r
+  \r
+  @retval EFI_SUCCESS             The expected hash value was caculatedly successfully.\r
+  @retval EFI_PROTOCOL_ERROR      The length of the secret should be at least the\r
+                                  length of the hash value for the hashing algorithm chosen.\r
+  @retval EFI_PROTOCOL_ERROR      MD5 hash operation fail.\r
+  @retval EFI_OUT_OF_RESOURCES    Fail to allocate resource to complete MD5.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPCalculateResponse (\r
+  IN  UINT32  ChapIdentifier,\r
+  IN  CHAR8   *ChapSecret,\r
+  IN  UINT32  SecretLength,\r
+  IN  UINT8   *ChapChallenge,\r
+  IN  UINT32  ChallengeLength,\r
+  OUT UINT8   *ChapResponse\r
+  )\r
+{\r
+  UINTN       Md5ContextSize;\r
+  VOID        *Md5Ctx;\r
+  CHAR8       IdByte[1];\r
+  EFI_STATUS  Status;\r
+\r
+  if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  Md5ContextSize = Md5GetContextSize ();\r
+  Md5Ctx = AllocatePool (Md5ContextSize);\r
+  if (Md5Ctx == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = EFI_PROTOCOL_ERROR;\r
+\r
+  if (!Md5Init (Md5Ctx)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Hash Identifier - Only calculate 1 byte data (RFC1994)\r
+  //\r
+  IdByte[0] = (CHAR8) ChapIdentifier;\r
+  if (!Md5Update (Md5Ctx, IdByte, 1)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Hash Secret\r
+  //\r
+  if (!Md5Update (Md5Ctx, ChapSecret, SecretLength)) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Hash Challenge received from Target\r
+  //\r
+  if (!Md5Update (Md5Ctx, ChapChallenge, ChallengeLength)) {\r
+    goto Exit;\r
+  }\r
+\r
+  if (Md5Final (Md5Ctx, ChapResponse)) {\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+Exit:\r
+  FreePool (Md5Ctx);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The initator checks the CHAP response replied by target against its own\r
+  calculation of the expected hash value. \r
+  \r
+  @param[in]   AuthData             iSCSI CHAP authentication data. \r
+  @param[in]   TargetResponse       The response from target.    \r
+\r
+  @retval EFI_SUCCESS               The response from target passed authentication.\r
+  @retval EFI_SECURITY_VIOLATION    The response from target was not expected value.\r
+  @retval Others                    Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPAuthTarget (\r
+  IN  ISCSI_CHAP_AUTH_DATA  *AuthData,\r
+  IN  UINT8                 *TargetResponse\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT32      SecretSize;\r
+  UINT8       VerifyRsp[ISCSI_CHAP_RSP_LEN];\r
+\r
+  Status      = EFI_SUCCESS;\r
+\r
+  SecretSize  = (UINT32) AsciiStrLen (AuthData->AuthConfig->ReverseCHAPSecret);\r
+  Status = IScsiCHAPCalculateResponse (\r
+             AuthData->OutIdentifier,\r
+             AuthData->AuthConfig->ReverseCHAPSecret,\r
+             SecretSize,\r
+             AuthData->OutChallenge,\r
+             AuthData->OutChallengeLength,\r
+             VerifyRsp\r
+             );\r
+\r
+  if (CompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN) != 0) {\r
+    Status = EFI_SECURITY_VIOLATION;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function checks the received iSCSI Login Response during the security\r
+  negotiation stage.\r
+\r
+  @param[in] Conn             The iSCSI connection.\r
+\r
+  @retval EFI_SUCCESS          The Login Response passed the CHAP validation.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of protocol error occurred.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPOnRspReceived (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  ISCSI_SESSION               *Session;\r
+  ISCSI_CHAP_AUTH_DATA        *AuthData;\r
+  CHAR8                       *Value;\r
+  UINT8                       *Data;\r
+  UINT32                      Len;\r
+  LIST_ENTRY                  *KeyValueList;\r
+  UINTN                       Algorithm;\r
+  CHAR8                       *Identifier;\r
+  CHAR8                       *Challenge;\r
+  CHAR8                       *Name;\r
+  CHAR8                       *Response;\r
+  UINT8                       TargetRsp[ISCSI_CHAP_RSP_LEN];\r
+  UINT32                      RspLen;\r
+  UINTN                       Result;\r
+\r
+  ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);\r
+  ASSERT (Conn->RspQue.BufNum != 0);\r
+\r
+  Session     = Conn->Session;\r
+  AuthData    = &Session->AuthData.CHAP;\r
+  Len         = Conn->RspQue.BufSize;\r
+  Data        = AllocateZeroPool (Len);\r
+  if (Data == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Copy the data in case the data spans over multiple PDUs.\r
+  //\r
+  NetbufQueCopy (&Conn->RspQue, 0, Len, Data);\r
+\r
+  //\r
+  // Build the key-value list from the data segment of the Login Response.\r
+  //\r
+  KeyValueList = IScsiBuildKeyValueList ((CHAR8 *) Data, Len);\r
+  if (KeyValueList == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = EFI_PROTOCOL_ERROR;\r
+\r
+  switch (Conn->AuthStep) {\r
+  case ISCSI_AUTH_INITIAL:\r
+    //\r
+    // The first Login Response.\r
+    //\r
+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);\r
+    if (Value == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Result = IScsiNetNtoi (Value);\r
+    if (Result > 0xFFFF) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Session->TargetPortalGroupTag = (UINT16) Result;\r
+\r
+    Value                         = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);\r
+    if (Value == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // Initiator mandates CHAP authentication but target replies without "CHAP", or\r
+    // initiator suggets "None" but target replies with some kind of auth method.\r
+    //\r
+    if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {\r
+      if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) != 0) {\r
+        goto ON_EXIT;\r
+      }\r
+    } else if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {\r
+      if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) != 0) {\r
+        goto ON_EXIT;\r
+      }\r
+    } else {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    //\r
+    // Transit to CHAP step one.\r
+    //\r
+    Conn->AuthStep  = ISCSI_CHAP_STEP_ONE;\r
+    Status          = EFI_SUCCESS;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_TWO:\r
+    //\r
+    // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>\r
+    //\r
+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM);\r
+    if (Value == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Algorithm = IScsiNetNtoi (Value);\r
+    if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {\r
+      //\r
+      // Unsupported algorithm is chosen by target.\r
+      //\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);\r
+    if (Identifier == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);\r
+    if (Challenge == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+    //\r
+    // Process the CHAP identifier and CHAP Challenge from Target.\r
+    // Calculate Response value.\r
+    //    \r
+    Result = IScsiNetNtoi (Identifier);\r
+    if (Result > 0xFF) {\r
+      goto ON_EXIT;\r
+    }    \r
+    \r
+    AuthData->InIdentifier      = (UINT32) Result;\r
+    AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;\r
+    IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);\r
+    Status = IScsiCHAPCalculateResponse (\r
+               AuthData->InIdentifier,\r
+               AuthData->AuthConfig->CHAPSecret,\r
+               (UINT32) AsciiStrLen (AuthData->AuthConfig->CHAPSecret),\r
+               AuthData->InChallenge,\r
+               AuthData->InChallengeLength,\r
+               AuthData->CHAPResponse\r
+               );\r
+\r
+    //\r
+    // Transit to next step.\r
+    //\r
+    Conn->AuthStep = ISCSI_CHAP_STEP_THREE;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_THREE:\r
+    //\r
+    // One way CHAP authentication and the target would like to\r
+    // authenticate us.\r
+    //\r
+    Status = EFI_SUCCESS;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_FOUR:\r
+    ASSERT (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL);\r
+    //\r
+    // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.\r
+    //\r
+    Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);\r
+    if (Name == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);\r
+    if (Response == NULL) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    RspLen = ISCSI_CHAP_RSP_LEN;\r
+    IScsiHexToBin (TargetRsp, &RspLen, Response);\r
+\r
+    //\r
+    // Check the CHAP Name and Response replied by Target.\r
+    //\r
+    Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (KeyValueList != NULL) {\r
+    IScsiFreeKeyValueList (KeyValueList);\r
+  }  \r
+\r
+  FreePool (Data);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  This function fills the CHAP authentication information into the login PDU\r
+  during the security negotiation stage in the iSCSI connection login.\r
+\r
+  @param[in]       Conn        The iSCSI connection.\r
+  @param[in, out]  Pdu         The PDU to send out.\r
+\r
+  @retval EFI_SUCCESS           All check passed and the phase-related CHAP\r
+                                authentication info is filled into the iSCSI PDU.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR    Some kind of protocol error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPToSendReq (\r
+  IN      ISCSI_CONNECTION  *Conn,\r
+  IN OUT  NET_BUF           *Pdu\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  ISCSI_SESSION               *Session;\r
+  ISCSI_LOGIN_REQUEST         *LoginReq;\r
+  ISCSI_CHAP_AUTH_DATA        *AuthData;\r
+  CHAR8                       *Value;\r
+  CHAR8                       ValueStr[256];\r
+  CHAR8                       *Response;\r
+  UINT32                      RspLen;\r
+  CHAR8                       *Challenge;\r
+  UINT32                      ChallengeLen;\r
+\r
+  ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);\r
+\r
+  Session     = Conn->Session;\r
+  AuthData    = &Session->AuthData.CHAP;\r
+  LoginReq    = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);\r
+  Status      = EFI_SUCCESS;\r
+\r
+  RspLen      = 2 * ISCSI_CHAP_RSP_LEN + 3;\r
+  Response    = AllocateZeroPool (RspLen);\r
+  if (Response == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  ChallengeLen  = 2 * ISCSI_CHAP_RSP_LEN + 3;\r
+  Challenge     = AllocateZeroPool (ChallengeLen);\r
+  if (Challenge == NULL) {\r
+    FreePool (Response);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  switch (Conn->AuthStep) {\r
+  case ISCSI_AUTH_INITIAL:\r
+    //\r
+    // It's the initial Login Request. Fill in the key=value pairs mandatory\r
+    // for the initial Login Request.\r
+    //\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, mPrivate->InitiatorName);\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");\r
+    IScsiAddKeyValuePair (\r
+      Pdu,\r
+      ISCSI_KEY_TARGET_NAME,\r
+      Session->ConfigData->SessionConfigData.TargetName\r
+      );\r
+\r
+    if (Session->AuthType == ISCSI_AUTH_TYPE_NONE) {\r
+      Value = ISCSI_KEY_VALUE_NONE;\r
+      ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+    } else {\r
+      Value = ISCSI_AUTH_METHOD_CHAP;\r
+    }\r
+\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);\r
+\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_ONE:\r
+    //\r
+    // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.\r
+    //\r
+    AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);\r
+\r
+    Conn->AuthStep = ISCSI_CHAP_STEP_TWO;\r
+    break;\r
+\r
+  case ISCSI_CHAP_STEP_THREE:\r
+    //\r
+    // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or\r
+    // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target authentication is\r
+    // required too.\r
+    //\r
+    // CHAP_N=<N>\r
+    //\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (CHAR8 *) &AuthData->AuthConfig->CHAPName);\r
+    //\r
+    // CHAP_R=<R>\r
+    //\r
+    IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);\r
+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);\r
+\r
+    if (AuthData->AuthConfig->CHAPType == ISCSI_CHAP_MUTUAL) {\r
+      //\r
+      // CHAP_I=<I>\r
+      //\r
+      IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);\r
+      AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);\r
+      IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);\r
+      //\r
+      // CHAP_C=<C>\r
+      //\r
+      IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);\r
+      AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;\r
+      IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);\r
+      IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);\r
+\r
+      Conn->AuthStep = ISCSI_CHAP_STEP_FOUR;\r
+    }\r
+    //\r
+    // Set the stage transition flag.\r
+    //\r
+    ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_PROTOCOL_ERROR;\r
+    break;\r
+  }\r
+\r
+  FreePool (Response);\r
+  FreePool (Challenge);\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiCHAP.h b/NetworkPkg/IScsiDxe/IScsiCHAP.h
new file mode 100644 (file)
index 0000000..d71db2c
--- /dev/null
@@ -0,0 +1,113 @@
+/** @file\r
+  The header file of CHAP configuration.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_CHAP_H_\r
+#define _ISCSI_CHAP_H_\r
+\r
+#define ISCSI_CHAP_AUTH_INFO_GUID \\r
+  { \\r
+    0x786ec0ac, 0x65ae, 0x4d1b, { 0xb1, 0x37, 0xd, 0x11, 0xa, 0x48, 0x37, 0x97 }\\r
+  }\r
+\r
+#define ISCSI_AUTH_METHOD_CHAP    "CHAP"\r
+\r
+#define ISCSI_KEY_CHAP_ALGORITHM  "CHAP_A"\r
+#define ISCSI_KEY_CHAP_IDENTIFIER "CHAP_I"\r
+#define ISCSI_KEY_CHAP_CHALLENGE  "CHAP_C"\r
+#define ISCSI_KEY_CHAP_NAME       "CHAP_N"\r
+#define ISCSI_KEY_CHAP_RESPONSE   "CHAP_R"\r
+\r
+#define ISCSI_CHAP_ALGORITHM_MD5  5\r
+\r
+#define ISCSI_CHAP_AUTH_MAX_LEN   1024\r
+///\r
+/// MD5_HASHSIZE\r
+///\r
+#define ISCSI_CHAP_RSP_LEN        16  \r
+\r
+#define ISCSI_CHAP_STEP_ONE       1\r
+#define ISCSI_CHAP_STEP_TWO       2\r
+#define ISCSI_CHAP_STEP_THREE     3\r
+#define ISCSI_CHAP_STEP_FOUR      4\r
+\r
+\r
+#pragma pack(1)\r
+\r
+typedef struct _ISCSI_CHAP_AUTH_CONFIG_NVDATA {\r
+  UINT8 CHAPType;\r
+  CHAR8 CHAPName[ISCSI_CHAP_NAME_STORAGE];\r
+  CHAR8 CHAPSecret[ISCSI_CHAP_SECRET_STORAGE];\r
+  CHAR8 ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE];\r
+  CHAR8 ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE];\r
+} ISCSI_CHAP_AUTH_CONFIG_NVDATA;\r
+\r
+#pragma pack()\r
+\r
+///\r
+/// ISCSI CHAP Authentication Data\r
+///\r
+typedef struct _ISCSI_CHAP_AUTH_DATA {\r
+  ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;\r
+  UINT32                        InIdentifier;\r
+  UINT8                         InChallenge[ISCSI_CHAP_AUTH_MAX_LEN];\r
+  UINT32                        InChallengeLength;\r
+  //\r
+  // Calculated CHAP Response (CHAP_R) value.\r
+  //\r
+  UINT8                         CHAPResponse[ISCSI_CHAP_RSP_LEN];\r
+\r
+  //\r
+  // Auth-data to be sent out for mutual authentication.\r
+  //\r
+  UINT32                        OutIdentifier;\r
+  UINT8                         OutChallenge[ISCSI_CHAP_AUTH_MAX_LEN];\r
+  UINT32                        OutChallengeLength;\r
+} ISCSI_CHAP_AUTH_DATA;\r
+\r
+/**\r
+  This function checks the received iSCSI Login Response during the security\r
+  negotiation stage.\r
+\r
+  @param[in] Conn             The iSCSI connection.\r
+\r
+  @retval EFI_SUCCESS          The Login Response passed the CHAP validation.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of protocol error occurred.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPOnRspReceived (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  );\r
+/**\r
+  This function fills the CHAP authentication information into the login PDU\r
+  during the security negotiation stage in the iSCSI connection login.\r
+\r
+  @param[in]       Conn        The iSCSI connection.\r
+  @param[in, out]  Pdu         The PDU to send out.\r
+\r
+  @retval EFI_SUCCESS           All check passed and the phase-related CHAP\r
+                                authentication info is filled into the iSCSI PDU.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR    Some kind of protocol error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCHAPToSendReq (\r
+  IN      ISCSI_CONNECTION  *Conn,\r
+  IN OUT  NET_BUF           *Pdu\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiConfig.c b/NetworkPkg/IScsiDxe/IScsiConfig.c
new file mode 100644 (file)
index 0000000..3c299be
--- /dev/null
@@ -0,0 +1,2522 @@
+/** @file\r
+  Helper functions for configuring or getting the parameters relating to iSCSI.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+EFI_GUID        mVendorGuid              = ISCSI_CONFIG_GUID;\r
+CHAR16          mVendorStorageName[]     = L"ISCSI_CONFIG_IFR_NVDATA";\r
+BOOLEAN         mIScsiDeviceListUpdated  = FALSE;\r
+UINTN           mNumberOfIScsiDevices    = 0;\r
+ISCSI_FORM_CALLBACK_INFO  *mCallbackInfo = NULL;\r
+\r
+HII_VENDOR_DEVICE_PATH  mIScsiHiiVendorDevicePath = {\r
+  {\r
+    {\r
+      HARDWARE_DEVICE_PATH,\r
+      HW_VENDOR_DP,\r
+      {\r
+        (UINT8) (sizeof (VENDOR_DEVICE_PATH)),\r
+        (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)\r
+      }\r
+    },\r
+    //\r
+    // {49D7B73E-143D-4716-977B-C45F1CB038CC}\r
+    //\r
+    { 0x49d7b73e, 0x143d, 0x4716, { 0x97, 0x7b, 0xc4, 0x5f, 0x1c, 0xb0, 0x38, 0xcc } }\r
+  },\r
+  {\r
+    END_DEVICE_PATH_TYPE,\r
+    END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    { \r
+      (UINT8) (END_DEVICE_PATH_LENGTH),\r
+      (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)\r
+    }\r
+  }\r
+};\r
+\r
+\r
+/**\r
+  Convert the IP address into a dotted string.\r
+\r
+  @param[in]  Ip        The IP address.\r
+  @param[in]  Ipv6Flag  Indicates whether the IP address is version 4 or version 6.\r
+  @param[out] Str       The formatted IP string.\r
+\r
+**/\r
+VOID\r
+IScsiIpToStr (\r
+  IN  EFI_IP_ADDRESS    *Ip,\r
+  IN  BOOLEAN           Ipv6Flag,\r
+  OUT CHAR16            *Str\r
+  )\r
+{\r
+  EFI_IPv4_ADDRESS      *Ip4;\r
+  EFI_IPv6_ADDRESS      *Ip6;\r
+  UINTN                 Index;\r
+  BOOLEAN               Short;\r
+  UINTN                 Number;\r
+  CHAR16                FormatString[8];\r
+\r
+  if (!Ipv6Flag) {\r
+    Ip4 = &Ip->v4;\r
+\r
+    UnicodeSPrint (\r
+      Str,\r
+      (UINTN) 2 * IP4_STR_MAX_SIZE,\r
+      L"%d.%d.%d.%d",\r
+      (UINTN) Ip4->Addr[0],\r
+      (UINTN) Ip4->Addr[1],\r
+      (UINTN) Ip4->Addr[2],\r
+      (UINTN) Ip4->Addr[3]\r
+      );\r
+\r
+    return ;\r
+  }\r
+\r
+  Ip6   = &Ip->v6;\r
+  Short = FALSE;\r
+\r
+  for (Index = 0; Index < 15; Index = Index + 2) {\r
+    if (!Short &&\r
+        Index % 2 == 0 &&\r
+        Ip6->Addr[Index] == 0 &&\r
+        Ip6->Addr[Index + 1] == 0\r
+        ) {\r
+      //\r
+      // Deal with the case of ::.\r
+      //\r
+      if (Index == 0) {\r
+        *Str       = L':';\r
+        *(Str + 1) = L':';\r
+        Str        = Str + 2;\r
+      } else {\r
+        *Str       = L':';\r
+        Str        = Str + 1;\r
+      }\r
+\r
+      while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) {\r
+        Index = Index + 2;\r
+      }\r
+\r
+      Short = TRUE;\r
+\r
+      if (Index == 16) {\r
+        //\r
+        // :: is at the end of the address.\r
+        //\r
+        *Str = L'\0';\r
+        break;\r
+      }\r
+    }\r
+\r
+    ASSERT (Index < 15);\r
+\r
+    if (Ip6->Addr[Index] == 0) {\r
+      Number = UnicodeSPrint (Str, 2 * IP_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]);\r
+    } else {\r
+      if (Ip6->Addr[Index + 1] < 0x10) {\r
+        CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:"));\r
+      } else {\r
+        CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:"));\r
+      }\r
+\r
+      Number = UnicodeSPrint (\r
+                 Str,\r
+                 2 * IP_STR_MAX_SIZE,\r
+                 (CONST CHAR16 *) FormatString,\r
+                 (UINTN) Ip6->Addr[Index],\r
+                 (UINTN) Ip6->Addr[Index + 1]\r
+                 );\r
+    }\r
+\r
+    Str = Str + Number;\r
+\r
+    if (Index + 2 == 16) {\r
+      *Str = L'\0';\r
+      if (*(Str - 1) == L':') {\r
+        *(Str - 1) = L'\0';\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Check whether the input IP address is valid.\r
+\r
+  @param[in]  Ip        The IP address.\r
+  @param[in]  IpMode    Indicates iSCSI running on IP4 or IP6 stack.\r
+\r
+  @retval     TRUE      The input IP address is valid.\r
+  @retval     FALSE     Otherwise\r
+\r
+**/\r
+BOOLEAN\r
+IpIsUnicast (\r
+  IN EFI_IP_ADDRESS *Ip,\r
+  IN  UINT8          IpMode\r
+  )\r
+{\r
+  if (IpMode == IP_MODE_IP4) {\r
+    return NetIp4IsUnicast (NTOHL (Ip->Addr[0]), 0);\r
+  } else if (IpMode == IP_MODE_IP6) {\r
+    return NetIp6IsValidUnicast (&Ip->v6);\r
+  } else {\r
+    DEBUG ((DEBUG_ERROR, "IpMode %d is invalid when configuring the iSCSI target IP!\n", IpMode));\r
+    return FALSE;\r
+  }\r
+}\r
+\r
+/**\r
+  Parse IsId in string format and convert it to binary.\r
+\r
+  @param[in]        String  The buffer of the string to be parsed.\r
+  @param[in, out]   IsId    The buffer to store IsId.\r
+\r
+  @retval EFI_SUCCESS              The operation finished successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiParseIsIdFromString (\r
+  IN CONST CHAR16                    *String,\r
+  IN OUT   UINT8                     *IsId\r
+  )\r
+{\r
+  UINT8                          Index;\r
+  CHAR16                         *IsIdStr;\r
+  CHAR16                         TempStr[3];\r
+  UINTN                          NodeVal;\r
+  CHAR16                         PortString[ISCSI_NAME_IFR_MAX_SIZE];\r
+  EFI_INPUT_KEY                  Key;\r
+\r
+  if ((String == NULL) || (IsId == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  IsIdStr = (CHAR16 *) String;\r
+\r
+  if (StrLen (IsIdStr) != 6) {\r
+    UnicodeSPrint (\r
+      PortString,\r
+      (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+      L"Error! Input is incorrect, please input 6 hex numbers!\n"\r
+      );\r
+\r
+    CreatePopUp (\r
+      EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+      &Key,\r
+      PortString,\r
+      NULL\r
+      );\r
+\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  for (Index = 3; Index < 6; Index++) {\r
+    CopyMem (TempStr, IsIdStr, sizeof (TempStr));\r
+    TempStr[2] = L'\0';\r
+\r
+    //\r
+    // Convert the string to IsId. StrHexToUintn stops at the first character\r
+    // that is not a valid hex character, '\0' here.\r
+    //\r
+    NodeVal = StrHexToUintn (TempStr);\r
+\r
+    IsId[Index] = (UINT8) NodeVal;\r
+\r
+    IsIdStr = IsIdStr + 2;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Convert IsId from binary to string format.\r
+\r
+  @param[out]      String  The buffer to store the converted string.\r
+  @param[in]       IsId    The buffer to store IsId.\r
+\r
+  @retval EFI_SUCCESS              The string converted successfully.\r
+  @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConvertIsIdToString (\r
+  OUT CHAR16                         *String,\r
+  IN  UINT8                          *IsId\r
+  )\r
+{\r
+  UINT8                          Index;\r
+  UINTN                          Number;\r
+\r
+  if ((String == NULL) || (IsId == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  for (Index = 0; Index < 6; Index++) {\r
+    if (IsId[Index] <= 0xF) {\r
+      Number = UnicodeSPrint (\r
+                 String,\r
+                 2 * ISID_CONFIGURABLE_STORAGE,\r
+                 L"0%X",\r
+                 (UINTN) IsId[Index]\r
+                 );\r
+    } else {\r
+      Number = UnicodeSPrint (\r
+                 String,\r
+                 2 * ISID_CONFIGURABLE_STORAGE,\r
+                 L"%X",\r
+                 (UINTN) IsId[Index]\r
+                 );\r
+\r
+    }\r
+\r
+    String = String + Number;\r
+  }\r
+\r
+  *String = L'\0';\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get the attempt config data from global structure by the ConfigIndex.\r
+\r
+  @param[in]  AttemptConfigIndex     The unique index indicates the attempt.\r
+\r
+  @return       Pointer to the attempt config data.\r
+  @retval NULL  The attempt configuration data cannot be found.\r
+\r
+**/\r
+ISCSI_ATTEMPT_CONFIG_NVDATA *\r
+IScsiConfigGetAttemptByConfigIndex (\r
+  IN UINT8                     AttemptConfigIndex\r
+  )\r
+{\r
+  LIST_ENTRY                   *Entry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {\r
+    Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+    if (Attempt->AttemptConfigIndex == AttemptConfigIndex) {\r
+      return Attempt;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Get the existing attempt config data from global structure by the NicIndex.\r
+\r
+  @param[in]  NewAttempt         The created new attempt\r
+  @param[in]  IScsiMode          The IScsi Mode of the new attempt, Enabled or\r
+                                 Enabled for MPIO.\r
+\r
+  @return                        Pointer to the existing attempt config data which\r
+                                 has the same NICIndex as the new created attempt.\r
+  @retval     NULL               The attempt with NicIndex does not exist.\r
+\r
+**/\r
+ISCSI_ATTEMPT_CONFIG_NVDATA *\r
+IScsiConfigGetAttemptByNic (\r
+  IN ISCSI_ATTEMPT_CONFIG_NVDATA *NewAttempt,\r
+  IN UINT8                       IScsiMode\r
+  )\r
+{\r
+  LIST_ENTRY                   *Entry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {\r
+    Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+    if (Attempt != NewAttempt && Attempt->NicIndex == NewAttempt->NicIndex &&\r
+        Attempt->SessionConfigData.Enabled == IScsiMode) {\r
+      return Attempt;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Convert the iSCSI configuration data into the IFR data.\r
+\r
+  @param[in]       Attempt                The iSCSI attempt config data.\r
+  @param[in, out]  IfrNvData              The IFR nv data.\r
+\r
+**/\r
+VOID\r
+IScsiConvertAttemptConfigDataToIfrNvData (\r
+  IN ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt,\r
+  IN OUT ISCSI_CONFIG_IFR_NVDATA  *IfrNvData\r
+  )\r
+{\r
+  ISCSI_SESSION_CONFIG_NVDATA   *SessionConfigData;\r
+  ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;\r
+  EFI_IP_ADDRESS                Ip;\r
+\r
+  //\r
+  // Normal session configuration parameters.\r
+  //\r
+  SessionConfigData                 = &Attempt->SessionConfigData;\r
+  IfrNvData->Enabled                = SessionConfigData->Enabled;\r
+  IfrNvData->IpMode                 = SessionConfigData->IpMode;\r
+\r
+  IfrNvData->InitiatorInfoFromDhcp  = SessionConfigData->InitiatorInfoFromDhcp;\r
+  IfrNvData->TargetInfoFromDhcp     = SessionConfigData->TargetInfoFromDhcp;\r
+  IfrNvData->TargetPort             = SessionConfigData->TargetPort;\r
+\r
+  if (IfrNvData->IpMode == IP_MODE_IP4) {\r
+    CopyMem (&Ip.v4, &SessionConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS));\r
+    IScsiIpToStr (&Ip, FALSE, IfrNvData->LocalIp);\r
+    CopyMem (&Ip.v4, &SessionConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    IScsiIpToStr (&Ip, FALSE, IfrNvData->SubnetMask);\r
+    CopyMem (&Ip.v4, &SessionConfigData->Gateway, sizeof (EFI_IPv4_ADDRESS));\r
+    IScsiIpToStr (&Ip, FALSE, IfrNvData->Gateway);\r
+    CopyMem (&Ip.v4, &SessionConfigData->TargetIp, sizeof (EFI_IPv4_ADDRESS));\r
+    IScsiIpToStr (&Ip, FALSE, IfrNvData->TargetIp);\r
+  } else if (IfrNvData->IpMode == IP_MODE_IP6) {\r
+    ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));\r
+    IP6_COPY_ADDRESS (&Ip.v6, &SessionConfigData->TargetIp);\r
+    IScsiIpToStr (&Ip, TRUE, IfrNvData->TargetIp);\r
+  }\r
+\r
+  AsciiStrToUnicodeStr (SessionConfigData->TargetName, IfrNvData->TargetName);\r
+  IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun);\r
+  IScsiConvertIsIdToString (IfrNvData->IsId, SessionConfigData->IsId);\r
+\r
+  IfrNvData->ConnectRetryCount = SessionConfigData->ConnectRetryCount;\r
+  IfrNvData->ConnectTimeout    = SessionConfigData->ConnectTimeout;\r
+\r
+  //\r
+  // Authentication parameters.\r
+  //\r
+  IfrNvData->AuthenticationType = Attempt->AuthenticationType;\r
+\r
+  if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {\r
+    AuthConfigData      = &Attempt->AuthConfigData.CHAP;\r
+    IfrNvData->CHAPType = AuthConfigData->CHAPType;\r
+    AsciiStrToUnicodeStr (AuthConfigData->CHAPName, IfrNvData->CHAPName);\r
+    AsciiStrToUnicodeStr (AuthConfigData->CHAPSecret, IfrNvData->CHAPSecret);\r
+    AsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPName, IfrNvData->ReverseCHAPName);\r
+    AsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPSecret, IfrNvData->ReverseCHAPSecret);\r
+  }\r
+\r
+  //\r
+  // Other parameters.\r
+  //\r
+  AsciiStrToUnicodeStr (Attempt->AttemptName, IfrNvData->AttemptName);\r
+}\r
+\r
+\r
+/**\r
+  Convert the IFR data to iSCSI configuration data.\r
+\r
+  @param[in]       IfrNvData              The IFR nv data.\r
+  @param[in, out]  Attempt                The iSCSI attempt config data.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Any input or configured parameter is invalid.\r
+  @retval EFI_NOT_FOUND          Cannot find the corresponding variable.\r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConvertIfrNvDataToAttemptConfigData (\r
+  IN ISCSI_CONFIG_IFR_NVDATA          *IfrNvData,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt\r
+  )\r
+{\r
+  EFI_IP_ADDRESS              HostIp;\r
+  EFI_IP_ADDRESS              SubnetMask;\r
+  EFI_IP_ADDRESS              Gateway;\r
+  CHAR16                      *MacString;\r
+  CHAR16                      *AttemptName1;\r
+  CHAR16                      *AttemptName2;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *ExistAttempt;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *SameNicAttempt;\r
+  CHAR16                      IScsiMode[64];\r
+  CHAR16                      IpMode[64];\r
+  ISCSI_NIC_INFO              *NicInfo;\r
+  EFI_INPUT_KEY               Key;\r
+\r
+  if (IfrNvData == NULL || Attempt == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Update those fields which don't have INTERACTIVE attribute.\r
+  //\r
+  Attempt->SessionConfigData.ConnectRetryCount     = IfrNvData->ConnectRetryCount;\r
+  Attempt->SessionConfigData.ConnectTimeout        = IfrNvData->ConnectTimeout;\r
+  Attempt->SessionConfigData.IpMode                = IfrNvData->IpMode;\r
+\r
+  if (IfrNvData->IpMode < IP_MODE_AUTOCONFIG) {\r
+    Attempt->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;\r
+    Attempt->SessionConfigData.TargetPort            = IfrNvData->TargetPort;\r
+\r
+    if (Attempt->SessionConfigData.TargetPort == 0) {\r
+      Attempt->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;\r
+    }\r
+\r
+    Attempt->SessionConfigData.TargetInfoFromDhcp = IfrNvData->TargetInfoFromDhcp;\r
+  }\r
+\r
+  Attempt->AuthenticationType = IfrNvData->AuthenticationType;\r
+\r
+  if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {\r
+    Attempt->AuthConfigData.CHAP.CHAPType = IfrNvData->CHAPType;\r
+  }\r
+\r
+  //\r
+  // Only do full parameter validation if iSCSI is enabled on this device.\r
+  //\r
+  if (IfrNvData->Enabled != ISCSI_DISABLED) {\r
+    if (Attempt->SessionConfigData.ConnectTimeout < CONNECT_MIN_TIMEOUT) {\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        L"Connection Establishing Timeout is less than minimum value 100ms.",\r
+        NULL\r
+        );\r
+      \r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Validate the address configuration of the Initiator if DHCP isn't\r
+    // deployed.\r
+    //\r
+    if (!Attempt->SessionConfigData.InitiatorInfoFromDhcp) {\r
+      CopyMem (&HostIp.v4, &Attempt->SessionConfigData.LocalIp, sizeof (HostIp.v4));\r
+      CopyMem (&SubnetMask.v4, &Attempt->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));\r
+      CopyMem (&Gateway.v4, &Attempt->SessionConfigData.Gateway, sizeof (Gateway.v4));\r
+\r
+      if ((Gateway.Addr[0] != 0)) {\r
+        if (SubnetMask.Addr[0] == 0) {\r
+          CreatePopUp (\r
+            EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+            &Key,\r
+            L"Gateway address is set but subnet mask is zero.",\r
+            NULL\r
+            );\r
+          \r
+          return EFI_INVALID_PARAMETER;\r
+        } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) {\r
+          CreatePopUp (\r
+            EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+            &Key,\r
+            L"Local IP and Gateway are not in the same subnet.",\r
+            NULL\r
+            );\r
+          \r
+          return EFI_INVALID_PARAMETER;\r
+        }\r
+      }\r
+    }\r
+    //\r
+    // Validate target configuration if DHCP isn't deployed.\r
+    //\r
+    if (!Attempt->SessionConfigData.TargetInfoFromDhcp && Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {\r
+      if (!IpIsUnicast (&Attempt->SessionConfigData.TargetIp, IfrNvData->IpMode)) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Target IP is invalid!",\r
+          NULL\r
+          );\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+    //\r
+    // Validate the authentication info.\r
+    //\r
+    if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {\r
+      if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"CHAP Name or CHAP Secret is invalid!",\r
+          NULL\r
+          );\r
+        \r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+\r
+      if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) &&\r
+          ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0'))\r
+          ) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Reverse CHAP Name or Reverse CHAP Secret is invalid!",\r
+          NULL\r
+          );        \r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Check whether this attempt uses NIC which is already used by existing attempt.\r
+    //\r
+    SameNicAttempt = IScsiConfigGetAttemptByNic (Attempt, IfrNvData->Enabled);\r
+    if (SameNicAttempt != NULL) {\r
+      AttemptName1 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16));\r
+      if (AttemptName1 == NULL) {\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }\r
+\r
+      AttemptName2 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16));\r
+      if (AttemptName2 == NULL) {\r
+        FreePool (AttemptName1);\r
+        return EFI_OUT_OF_RESOURCES;\r
+      }      \r
+      \r
+      AsciiStrToUnicodeStr (Attempt->AttemptName, AttemptName1);\r
+      if (StrLen (AttemptName1) > ATTEMPT_NAME_SIZE) {\r
+        CopyMem (&AttemptName1[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));\r
+      }\r
+\r
+      AsciiStrToUnicodeStr (SameNicAttempt->AttemptName, AttemptName2);\r
+      if (StrLen (AttemptName2) > ATTEMPT_NAME_SIZE) {\r
+        CopyMem (&AttemptName2[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));\r
+      }\r
+\r
+      UnicodeSPrint (\r
+        mPrivate->PortString,\r
+        (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+        L"Warning! Attempt \"%s\" uses same NIC as Attempt \"%s\".",\r
+        AttemptName1,\r
+        AttemptName2\r
+        );\r
+\r
+      CreatePopUp (\r
+        EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+        &Key,\r
+        mPrivate->PortString,\r
+        NULL\r
+        );       \r
+\r
+      FreePool (AttemptName1);\r
+      FreePool (AttemptName2);\r
+    }\r
+  }\r
+\r
+  //\r
+  // Check whether this attempt is an existing one.\r
+  //\r
+  ExistAttempt = IScsiConfigGetAttemptByConfigIndex (Attempt->AttemptConfigIndex);\r
+  if (ExistAttempt != NULL) {\r
+    ASSERT (ExistAttempt == Attempt);\r
+\r
+    if (IfrNvData->Enabled == ISCSI_DISABLED &&\r
+        Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {\r
+\r
+      //\r
+      // User updates the Attempt from "Enabled"/"Enabled for MPIO" to "Disabled".\r
+      //\r
+      if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+        if (mPrivate->MpioCount < 1) {\r
+          return EFI_ABORTED;\r
+        }\r
+\r
+        if (--mPrivate->MpioCount == 0) {\r
+          mPrivate->EnableMpio = FALSE;\r
+        }\r
+      } else if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) {\r
+        if (mPrivate->SinglePathCount < 1) {\r
+          return EFI_ABORTED;\r
+        }\r
+        mPrivate->SinglePathCount--;\r
+      }\r
+\r
+    } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO &&\r
+               Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) {\r
+      //\r
+      // User updates the Attempt from "Enabled" to "Enabled for MPIO".\r
+      //\r
+      if (mPrivate->SinglePathCount < 1) {\r
+        return EFI_ABORTED;\r
+      }\r
+\r
+      mPrivate->EnableMpio = TRUE;\r
+      mPrivate->MpioCount++;\r
+      mPrivate->SinglePathCount--;\r
+\r
+    } else if (IfrNvData->Enabled == ISCSI_ENABLED &&\r
+               Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+      //\r
+      // User updates the Attempt from "Enabled for MPIO" to "Enabled".\r
+      //\r
+      if (mPrivate->MpioCount < 1) {\r
+        return EFI_ABORTED;\r
+      }\r
+\r
+      if (--mPrivate->MpioCount == 0) {\r
+        mPrivate->EnableMpio = FALSE;\r
+      }\r
+      mPrivate->SinglePathCount++;\r
+\r
+    } else if (IfrNvData->Enabled != ISCSI_DISABLED &&\r
+               Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) {\r
+      //\r
+      // User updates the Attempt from "Disabled" to "Enabled"/"Enabled for MPIO".\r
+      //\r
+      if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+        mPrivate->EnableMpio = TRUE;\r
+        mPrivate->MpioCount++;\r
+\r
+      } else if (IfrNvData->Enabled == ISCSI_ENABLED) {\r
+        mPrivate->SinglePathCount++;\r
+      }\r
+    }\r
+\r
+  } else if (ExistAttempt == NULL && IfrNvData->Enabled != ISCSI_DISABLED) {\r
+    if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+      //\r
+      // This new Attempt is enabled for MPIO; enable the multipath mode.\r
+      //\r
+      mPrivate->EnableMpio = TRUE;\r
+      mPrivate->MpioCount++;\r
+    } else if (IfrNvData->Enabled == ISCSI_ENABLED) {\r
+      mPrivate->SinglePathCount++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Update the iSCSI Mode data and record it in attempt help info.\r
+  //\r
+  Attempt->SessionConfigData.Enabled = IfrNvData->Enabled;\r
+  if (IfrNvData->Enabled == ISCSI_DISABLED) {\r
+    UnicodeSPrint (IScsiMode, 64, L"Disabled");\r
+  } else if (IfrNvData->Enabled == ISCSI_ENABLED) {\r
+    UnicodeSPrint (IScsiMode, 64, L"Enabled");\r
+  } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+    UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");\r
+  }\r
+\r
+  if (IfrNvData->IpMode == IP_MODE_IP4) {\r
+    UnicodeSPrint (IpMode, 64, L"IP4");\r
+  } else if (IfrNvData->IpMode == IP_MODE_IP6) {\r
+    UnicodeSPrint (IpMode, 64, L"IP6");\r
+  } else if (IfrNvData->IpMode == IP_MODE_AUTOCONFIG) {\r
+    UnicodeSPrint (IpMode, 64, L"Autoconfigure");\r
+  }\r
+\r
+  NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex);\r
+  if (NicInfo == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  MacString = (CHAR16 *) AllocateZeroPool (ISCSI_MAX_MAC_STRING_LEN * sizeof (CHAR16));\r
+  if (MacString == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  AsciiStrToUnicodeStr (Attempt->MacString, MacString);\r
+\r
+  UnicodeSPrint (\r
+    mPrivate->PortString,\r
+    (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+    L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",\r
+    MacString,\r
+    NicInfo->BusNumber,\r
+    NicInfo->DeviceNumber,\r
+    NicInfo->FunctionNumber,\r
+    IScsiMode,\r
+    IpMode\r
+    );\r
+\r
+  Attempt->AttemptTitleHelpToken = HiiSetString (\r
+                                     mCallbackInfo->RegisteredHandle,\r
+                                     Attempt->AttemptTitleHelpToken,\r
+                                     mPrivate->PortString,\r
+                                     NULL\r
+                                     );\r
+  if (Attempt->AttemptTitleHelpToken == 0) {\r
+    FreePool (MacString);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Record the user configuration information in NVR.\r
+  //\r
+  UnicodeSPrint (\r
+    mPrivate->PortString,\r
+    (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+    L"%s%d",\r
+    MacString,\r
+    (UINTN) Attempt->AttemptConfigIndex\r
+    );\r
+\r
+  FreePool (MacString);\r
+\r
+  return gRT->SetVariable (\r
+                mPrivate->PortString,\r
+                &gEfiIScsiInitiatorNameProtocolGuid,\r
+                ISCSI_CONFIG_VAR_ATTR,\r
+                sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),\r
+                Attempt\r
+                );\r
+}\r
+\r
+/**\r
+  Create Hii Extend Label OpCode as the start opcode and end opcode. It is \r
+  a help function.\r
+\r
+  @param[in]  StartLabelNumber   The number of start label.\r
+  @param[out] StartOpCodeHandle  Points to the start opcode handle.\r
+  @param[out] StartLabel         Points to the created start opcode.\r
+  @param[out] EndOpCodeHandle    Points to the end opcode handle.\r
+  @param[out] EndLabel           Points to the created end opcode.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Do not have sufficient resource to finish this\r
+                                 operation.\r
+  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.                                 \r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCreateOpCode (\r
+  IN  UINT16                        StartLabelNumber,\r
+  OUT VOID                          **StartOpCodeHandle,\r
+  OUT EFI_IFR_GUID_LABEL            **StartLabel,\r
+  OUT VOID                          **EndOpCodeHandle,\r
+  OUT EFI_IFR_GUID_LABEL            **EndLabel\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_IFR_GUID_LABEL                *InternalStartLabel;\r
+  EFI_IFR_GUID_LABEL                *InternalEndLabel;\r
+\r
+  if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *StartOpCodeHandle = NULL;\r
+  *EndOpCodeHandle   = NULL;\r
+  Status             = EFI_OUT_OF_RESOURCES;\r
+\r
+  //\r
+  // Initialize the container for dynamic opcodes.\r
+  //\r
+  *StartOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+  if (*StartOpCodeHandle == NULL) {\r
+    return Status;\r
+  }\r
+\r
+  *EndOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+  if (*EndOpCodeHandle == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Create Hii Extend Label OpCode as the start opcode.\r
+  //\r
+  InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (\r
+                                                *StartOpCodeHandle,\r
+                                                &gEfiIfrTianoGuid,\r
+                                                NULL,\r
+                                                sizeof (EFI_IFR_GUID_LABEL)\r
+                                                );\r
+  if (InternalStartLabel == NULL) {\r
+    goto Exit;\r
+  }\r
+  \r
+  InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+  InternalStartLabel->Number       = StartLabelNumber;\r
+\r
+  //\r
+  // Create Hii Extend Label OpCode as the end opcode.\r
+  //\r
+  InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (\r
+                                              *EndOpCodeHandle,\r
+                                              &gEfiIfrTianoGuid,\r
+                                              NULL,\r
+                                              sizeof (EFI_IFR_GUID_LABEL)\r
+                                              );\r
+  if (InternalEndLabel == NULL) {\r
+    goto Exit;\r
+  }\r
+\r
+  InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;\r
+  InternalEndLabel->Number       = LABEL_END;\r
+\r
+  *StartLabel = InternalStartLabel;\r
+  *EndLabel   = InternalEndLabel;\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Exit:\r
+\r
+  if (*StartOpCodeHandle != NULL) {\r
+    HiiFreeOpCodeHandle (*StartOpCodeHandle);\r
+  }\r
+\r
+  if (*EndOpCodeHandle != NULL) {\r
+    HiiFreeOpCodeHandle (*EndOpCodeHandle);\r
+  }\r
+  \r
+  return Status;\r
+}\r
+\r
+/**\r
+  Callback function when user presses "Add an Attempt".\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigAddAttempt (\r
+  VOID\r
+  )\r
+{\r
+  LIST_ENTRY                    *Entry;\r
+  ISCSI_NIC_INFO                *NicInfo;\r
+  EFI_STRING_ID                 PortTitleToken;\r
+  EFI_STRING_ID                 PortTitleHelpToken;\r
+  CHAR16                        MacString[ISCSI_MAX_MAC_STRING_LEN];\r
+  EFI_STATUS                    Status;\r
+  VOID                          *StartOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL            *StartLabel;\r
+  VOID                          *EndOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL            *EndLabel;\r
+\r
+  Status = IScsiCreateOpCode (\r
+             MAC_ENTRY_LABEL,\r
+             &StartOpCodeHandle,\r
+             &StartLabel,\r
+             &EndOpCodeHandle,\r
+             &EndLabel\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Ask user to select a MAC for this attempt.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {\r
+    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);\r
+    IScsiMacAddrToStr (\r
+      &NicInfo->PermanentAddress,\r
+      NicInfo->HwAddressSize,\r
+      NicInfo->VlanId,\r
+      MacString\r
+      );\r
+\r
+    UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"Port %s", MacString);\r
+    PortTitleToken = HiiSetString (\r
+                       mCallbackInfo->RegisteredHandle,\r
+                       0,\r
+                       mPrivate->PortString,\r
+                       NULL\r
+                       );\r
+    if (PortTitleToken == 0) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Exit;\r
+    }\r
+\r
+    UnicodeSPrint (\r
+      mPrivate->PortString,\r
+      (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+      L"PFA: Bus %d | Dev %d | Func %d",\r
+      NicInfo->BusNumber,\r
+      NicInfo->DeviceNumber,\r
+      NicInfo->FunctionNumber\r
+      );\r
+    PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, mPrivate->PortString, NULL);\r
+    if (PortTitleHelpToken == 0) {\r
+      Status = EFI_INVALID_PARAMETER;\r
+      goto Exit;      \r
+    }\r
+\r
+    HiiCreateGotoOpCode (\r
+      StartOpCodeHandle,                      // Container for dynamic created opcodes\r
+      FORMID_ATTEMPT_FORM,\r
+      PortTitleToken,\r
+      PortTitleHelpToken,\r
+      EFI_IFR_FLAG_CALLBACK,                  // Question flag\r
+      (UINT16) (KEY_MAC_ENTRY_BASE + NicInfo->NicIndex)\r
+      );\r
+  }\r
+\r
+  Status = HiiUpdateForm (\r
+             mCallbackInfo->RegisteredHandle, // HII handle\r
+             &mVendorGuid,                    // Formset GUID\r
+             FORMID_MAC_FORM,                 // Form ID\r
+             StartOpCodeHandle,               // Label for where to insert opcodes\r
+             EndOpCodeHandle                  // Replace data\r
+             );\r
+\r
+Exit:\r
+  HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+  HiiFreeOpCodeHandle (EndOpCodeHandle);\r
+  \r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Update the MAIN form to display the configured attempts.\r
+\r
+**/\r
+VOID\r
+IScsiConfigUpdateAttempt (\r
+  VOID\r
+  )\r
+{\r
+  CHAR16                        AttemptName[ATTEMPT_NAME_MAX_SIZE];\r
+  LIST_ENTRY                    *Entry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA   *AttemptConfigData;\r
+  VOID                          *StartOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL            *StartLabel;\r
+  VOID                          *EndOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL            *EndLabel;\r
+  EFI_STATUS                    Status;\r
+\r
+  Status = IScsiCreateOpCode (\r
+             ATTEMPT_ENTRY_LABEL,\r
+             &StartOpCodeHandle,\r
+             &StartLabel,\r
+             &EndOpCodeHandle,\r
+             &EndLabel\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return ;\r
+  }\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {\r
+    AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+\r
+    AsciiStrToUnicodeStr (AttemptConfigData->AttemptName, AttemptName);\r
+    UnicodeSPrint (mPrivate->PortString, (UINTN) 128, L"Attempt %s", AttemptName);\r
+    AttemptConfigData->AttemptTitleToken = HiiSetString (\r
+                                             mCallbackInfo->RegisteredHandle,\r
+                                             0,\r
+                                             mPrivate->PortString,\r
+                                             NULL\r
+                                             );\r
+    if (AttemptConfigData->AttemptTitleToken == 0) {\r
+      return ;\r
+    }\r
+\r
+    HiiCreateGotoOpCode (\r
+      StartOpCodeHandle,                         // Container for dynamic created opcodes\r
+      FORMID_ATTEMPT_FORM,                       // Form ID\r
+      AttemptConfigData->AttemptTitleToken,      // Prompt text\r
+      AttemptConfigData->AttemptTitleHelpToken,  // Help text\r
+      EFI_IFR_FLAG_CALLBACK,                     // Question flag\r
+      (UINT16) (KEY_ATTEMPT_ENTRY_BASE + AttemptConfigData->AttemptConfigIndex)   // Question ID\r
+      );\r
+  }\r
+\r
+  HiiUpdateForm (\r
+    mCallbackInfo->RegisteredHandle, // HII handle\r
+    &mVendorGuid,                    // Formset GUID\r
+    FORMID_MAIN_FORM,                // Form ID\r
+    StartOpCodeHandle,               // Label for where to insert opcodes\r
+    EndOpCodeHandle                  // Replace data\r
+  );    \r
+\r
+  HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+  HiiFreeOpCodeHandle (EndOpCodeHandle);\r
+}\r
+\r
+\r
+/**\r
+  Callback function when user presses "Commit Changes and Exit" in Delete Attempts.\r
+\r
+  @param[in]  IfrNvData          The IFR NV data.\r
+\r
+  @retval EFI_NOT_FOUND          Cannot find the corresponding variable.\r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+  @retval EFI_ABOTRED            This operation is aborted cause of error\r
+                                 configuration.\r
+  @retval EFI_OUT_OF_RESOURCES   Fail to finish the operation due to lack of\r
+                                 resources.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigDeleteAttempts (\r
+  IN ISCSI_CONFIG_IFR_NVDATA  *IfrNvData\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINTN                       Index;\r
+  UINTN                       NewIndex;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  UINT8                       *AttemptConfigOrder;\r
+  UINTN                       AttemptConfigOrderSize;\r
+  UINT8                       *AttemptNewOrder;\r
+  UINT32                      Attribute;\r
+  UINTN                       Total;\r
+  UINTN                       NewTotal;\r
+  LIST_ENTRY                  *Entry;\r
+  LIST_ENTRY                  *NextEntry;\r
+  CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];\r
+\r
+  AttemptConfigOrder = IScsiGetVariableAndSize (\r
+                         L"AttemptOrder",\r
+                         &mVendorGuid,\r
+                         &AttemptConfigOrderSize\r
+                         );\r
+  if ((AttemptConfigOrder == NULL) || (AttemptConfigOrderSize == 0)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  AttemptNewOrder = AllocateZeroPool (AttemptConfigOrderSize);\r
+  if (AttemptNewOrder == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Total    = AttemptConfigOrderSize / sizeof (UINT8);\r
+  NewTotal = Total;\r
+  Index    = 0;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {\r
+    if (IfrNvData->DeleteAttemptList[Index] == 0) {\r
+      Index++;\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Delete the attempt.\r
+    //\r
+\r
+    AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+    if (AttemptConfigData == NULL) {\r
+      Status = EFI_NOT_FOUND;\r
+      goto Error;\r
+    }\r
+\r
+    //\r
+    // Remove this attempt from UI configured attempt list.\r
+    //\r
+    RemoveEntryList (&AttemptConfigData->Link);\r
+    mPrivate->AttemptCount--;\r
+\r
+    if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+      if (mPrivate->MpioCount < 1) {\r
+        Status = EFI_ABORTED;\r
+        goto Error;\r
+      }\r
+\r
+      //\r
+      // No more attempt is enabled for MPIO. Transit the iSCSI mode to single path.\r
+      //\r
+      if (--mPrivate->MpioCount == 0) {\r
+        mPrivate->EnableMpio = FALSE;\r
+      }\r
+    } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {\r
+      if (mPrivate->SinglePathCount < 1) {\r
+        Status = EFI_ABORTED;\r
+        goto Error;\r
+      }\r
+\r
+      mPrivate->SinglePathCount--;\r
+    }\r
+\r
+    AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);\r
+\r
+    UnicodeSPrint (\r
+      mPrivate->PortString,\r
+      (UINTN) 128,\r
+      L"%s%d",\r
+      MacString,\r
+      (UINTN) AttemptConfigData->AttemptConfigIndex\r
+      );\r
+\r
+    gRT->SetVariable (\r
+           mPrivate->PortString,\r
+           &gEfiIScsiInitiatorNameProtocolGuid,\r
+           0,\r
+           0,\r
+           NULL\r
+           );\r
+\r
+    //\r
+    // Mark the attempt order in NVR to be deleted - 0.\r
+    //\r
+    for (NewIndex = 0; NewIndex < Total; NewIndex++) {\r
+      if (AttemptConfigOrder[NewIndex] == AttemptConfigData->AttemptConfigIndex) {\r
+        AttemptConfigOrder[NewIndex] = 0;\r
+        break;\r
+      }\r
+    }\r
+\r
+    NewTotal--;\r
+    FreePool (AttemptConfigData);\r
+\r
+    //\r
+    // Check next Attempt.\r
+    //\r
+    Index++;\r
+  }\r
+\r
+  //\r
+  // Construct AttemptNewOrder.\r
+  //\r
+  for (Index = 0, NewIndex = 0; Index < Total; Index++) {\r
+    if (AttemptConfigOrder[Index] != 0) {\r
+      AttemptNewOrder[NewIndex] = AttemptConfigOrder[Index];\r
+      NewIndex++;\r
+    }\r
+  }\r
+\r
+  Attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS\r
+              | EFI_VARIABLE_NON_VOLATILE;\r
+\r
+  //\r
+  // Update AttemptOrder in NVR.\r
+  //\r
+  Status = gRT->SetVariable (\r
+                  L"AttemptOrder",\r
+                  &mVendorGuid,\r
+                  Attribute,\r
+                  NewTotal * sizeof (UINT8),\r
+                  AttemptNewOrder\r
+                  );\r
+\r
+Error:\r
+  FreePool (AttemptConfigOrder);\r
+  FreePool (AttemptNewOrder);\r
+  \r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Callback function when user presses "Delete Attempts".\r
+\r
+  @param[in]  IfrNvData          The IFR nv data.\r
+\r
+  @retval EFI_INVALID_PARAMETER  Any parameter is invalid.\r
+  @retval EFI_BUFFER_TOO_SMALL   The buffer in UpdateData is too small.\r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigDisplayDeleteAttempts (\r
+  IN ISCSI_CONFIG_IFR_NVDATA  *IfrNvData\r
+  )\r
+{\r
+\r
+  UINT8                       *AttemptConfigOrder;\r
+  UINTN                       AttemptConfigOrderSize;\r
+  LIST_ENTRY                  *Entry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  UINT8                       Index;\r
+  VOID                        *StartOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL          *StartLabel;\r
+  VOID                        *EndOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL          *EndLabel;\r
+  EFI_STATUS                  Status;\r
+\r
+  Status = IScsiCreateOpCode (\r
+             DELETE_ENTRY_LABEL,\r
+             &StartOpCodeHandle,\r
+             &StartLabel,\r
+             &EndOpCodeHandle,\r
+             &EndLabel\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  AttemptConfigOrder = IScsiGetVariableAndSize (\r
+                         L"AttemptOrder",\r
+                         &mVendorGuid,\r
+                         &AttemptConfigOrderSize\r
+                         );\r
+  if (AttemptConfigOrder != NULL) {\r
+    //\r
+    // Create the check box opcode to be deleted.\r
+    //\r
+    Index = 0;\r
+\r
+    NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {\r
+      AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+      IfrNvData->DeleteAttemptList[Index] = 0x00;\r
+\r
+      HiiCreateCheckBoxOpCode(\r
+        StartOpCodeHandle,\r
+        (EFI_QUESTION_ID) (ATTEMPT_DEL_QUESTION_ID + Index),\r
+        CONFIGURATION_VARSTORE_ID,\r
+        (UINT16) (ATTEMPT_DEL_VAR_OFFSET + Index),\r
+        AttemptConfigData->AttemptTitleToken,\r
+        AttemptConfigData->AttemptTitleHelpToken,\r
+        0,\r
+        0,\r
+        NULL\r
+        );\r
+\r
+      Index++;\r
+\r
+      if (Index == ISCSI_MAX_ATTEMPTS_NUM) {\r
+        break;\r
+      }\r
+    }\r
+\r
+    FreePool (AttemptConfigOrder);\r
+  }\r
+\r
+  Status = HiiUpdateForm (\r
+             mCallbackInfo->RegisteredHandle, // HII handle\r
+             &mVendorGuid,                    // Formset GUID\r
+             FORMID_DELETE_FORM,              // Form ID\r
+             StartOpCodeHandle,               // Label for where to insert opcodes\r
+             EndOpCodeHandle                  // Replace data\r
+             );    \r
+\r
+  HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+  HiiFreeOpCodeHandle (EndOpCodeHandle);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Callback function when user presses "Change Attempt Order".\r
+\r
+  @retval EFI_INVALID_PARAMETER  Any parameter is invalid.\r
+  @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this\r
+                                 operation.\r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigDisplayOrderAttempts (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINT8                       Index;\r
+  LIST_ENTRY                  *Entry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  VOID                        *StartOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL          *StartLabel;\r
+  VOID                        *EndOpCodeHandle;\r
+  EFI_IFR_GUID_LABEL          *EndLabel;\r
+  VOID                        *OptionsOpCodeHandle;  \r
+  \r
+  Status = IScsiCreateOpCode (\r
+             ORDER_ENTRY_LABEL,\r
+             &StartOpCodeHandle,\r
+             &StartLabel,\r
+             &EndOpCodeHandle,\r
+             &EndLabel\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  OptionsOpCodeHandle = NULL;\r
+\r
+  //\r
+  // If no attempt to be ordered, update the original form and exit.\r
+  //\r
+  if (mPrivate->AttemptCount == 0) {\r
+    goto Exit;\r
+  }\r
+\r
+  //\r
+  // Create Option OpCode.\r
+  //\r
+  OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();\r
+  if (OptionsOpCodeHandle == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  Index = 0;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {\r
+    AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+    HiiCreateOneOfOptionOpCode (\r
+      OptionsOpCodeHandle,\r
+      AttemptConfigData->AttemptTitleToken,\r
+      0,\r
+      EFI_IFR_NUMERIC_SIZE_1,\r
+      AttemptConfigData->AttemptConfigIndex\r
+      );\r
+    Index++;\r
+  }\r
+\r
+  ASSERT (Index == mPrivate->AttemptCount);\r
+\r
+  HiiCreateOrderedListOpCode (\r
+    StartOpCodeHandle,                          // Container for dynamic created opcodes\r
+    DYNAMIC_ORDERED_LIST_QUESTION_ID,           // Question ID\r
+    CONFIGURATION_VARSTORE_ID,                  // VarStore ID\r
+    DYNAMIC_ORDERED_LIST_VAR_OFFSET,            // Offset in Buffer Storage\r
+    STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),     // Question prompt text        \r
+    STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),     // Question help text       \r
+    0,                                          // Question flag\r
+    EFI_IFR_UNIQUE_SET,                         // Ordered list flag, e.g. EFI_IFR_UNIQUE_SET\r
+    EFI_IFR_NUMERIC_SIZE_1,                     // Data type of Question value\r
+    ISCSI_MAX_ATTEMPTS_NUM,                     // Maximum container\r
+    OptionsOpCodeHandle,                        // Option Opcode list                        \r
+    NULL                                        // Default Opcode is NULL                               \r
+    );\r
+\r
+Exit:\r
+  Status = HiiUpdateForm (\r
+             mCallbackInfo->RegisteredHandle, // HII handle\r
+             &mVendorGuid,                    // Formset GUID\r
+             FORMID_ORDER_FORM,               // Form ID\r
+             StartOpCodeHandle,               // Label for where to insert opcodes\r
+             EndOpCodeHandle                  // Replace data\r
+             );             \r
+\r
+Error:\r
+  HiiFreeOpCodeHandle (StartOpCodeHandle);\r
+  HiiFreeOpCodeHandle (EndOpCodeHandle);  \r
+  if (OptionsOpCodeHandle != NULL) {\r
+    HiiFreeOpCodeHandle (OptionsOpCodeHandle);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Callback function when user presses "Commit Changes and Exit" in Change Attempt Order.\r
+\r
+  @param[in]  IfrNvData          The IFR nv data.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this\r
+                                 operation.\r
+  @retval EFI_NOT_FOUND          Cannot find the corresponding variable.\r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigOrderAttempts (\r
+  IN ISCSI_CONFIG_IFR_NVDATA  *IfrNvData\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINTN                       Index;\r
+  UINTN                       Indexj;\r
+  UINT8                       AttemptConfigIndex;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  UINT8                       *AttemptConfigOrder;\r
+  UINT8                       *AttemptConfigOrderTmp;\r
+  UINTN                       AttemptConfigOrderSize;\r
+\r
+  AttemptConfigOrder = IScsiGetVariableAndSize (\r
+                         L"AttemptOrder",\r
+                         &mVendorGuid,\r
+                         &AttemptConfigOrderSize\r
+                         );\r
+  if (AttemptConfigOrder == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  AttemptConfigOrderTmp = AllocateZeroPool (AttemptConfigOrderSize);\r
+  if (AttemptConfigOrderTmp == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Exit;\r
+  }\r
+\r
+  for (Index = 0; Index < ISCSI_MAX_ATTEMPTS_NUM; Index++) {\r
+    //\r
+    // The real content ends with 0.\r
+    //\r
+    if (IfrNvData->DynamicOrderedList[Index] == 0) {\r
+      break;\r
+    }\r
+\r
+    AttemptConfigIndex = IfrNvData->DynamicOrderedList[Index];\r
+    AttemptConfigData  = IScsiConfigGetAttemptByConfigIndex (AttemptConfigIndex);\r
+    if (AttemptConfigData == NULL) {\r
+      Status = EFI_NOT_FOUND;\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // Reorder the Attempt List.\r
+    //\r
+    RemoveEntryList (&AttemptConfigData->Link);\r
+    InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);\r
+\r
+    AttemptConfigOrderTmp[Index] = AttemptConfigIndex;\r
+\r
+    //\r
+    // Mark it to be deleted - 0.\r
+    //\r
+    for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) {\r
+      if (AttemptConfigOrder[Indexj] == AttemptConfigIndex) {\r
+        AttemptConfigOrder[Indexj] = 0;\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // Adjust the attempt order in NVR.\r
+  //\r
+  for (; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {\r
+    for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) {\r
+      if (AttemptConfigOrder[Indexj] != 0) {\r
+        AttemptConfigOrderTmp[Index] = AttemptConfigOrder[Indexj];\r
+        AttemptConfigOrder[Indexj]   = 0;\r
+        continue;\r
+      }\r
+    }\r
+  }\r
+\r
+  Status = gRT->SetVariable (\r
+                  L"AttemptOrder",\r
+                  &mVendorGuid,\r
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                  AttemptConfigOrderSize,\r
+                  AttemptConfigOrderTmp\r
+                  );\r
+\r
+Exit:\r
+  if (AttemptConfigOrderTmp != NULL) {\r
+    FreePool (AttemptConfigOrderTmp);\r
+  }\r
+\r
+  FreePool (AttemptConfigOrder);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Callback function when a user presses "Attempt *" or when a user selects a NIC to\r
+  create the new attempt.\r
+\r
+  @param[in]  KeyValue           A unique value which is sent to the original\r
+                                 exporting driver so that it can identify the type\r
+                                 of data to expect.\r
+  @param[in]  IfrNvData          The IFR nv data.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this\r
+                                 operation.\r
+  @retval EFI_NOT_FOUND          Cannot find the corresponding variable.\r
+  @retval EFI_SUCCESS            The operation is completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigProcessDefault (\r
+  IN  EFI_QUESTION_ID              KeyValue,\r
+  IN  ISCSI_CONFIG_IFR_NVDATA      *IfrNvData\r
+  )\r
+{\r
+  BOOLEAN                     NewAttempt;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  ISCSI_SESSION_CONFIG_NVDATA *ConfigData;\r
+  UINT8                       CurrentAttemptConfigIndex;\r
+  ISCSI_NIC_INFO              *NicInfo;\r
+  UINT8                       NicIndex;\r
+  CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];\r
+  UINT8                       *AttemptConfigOrder;\r
+  UINTN                       AttemptConfigOrderSize;\r
+  UINTN                       TotalNumber;\r
+  UINT8                       *AttemptOrderTmp;\r
+  UINTN                       Index;\r
+  EFI_STATUS                  Status;\r
+\r
+  NewAttempt = FALSE;\r
+\r
+  if ((KeyValue >= KEY_MAC_ENTRY_BASE) &&\r
+      (KeyValue <= (UINT16) (mPrivate->MaxNic + KEY_MAC_ENTRY_BASE))) {\r
+    //\r
+    // User has pressed "Add an Attempt" and then selects a NIC.\r
+    //\r
+    NewAttempt = TRUE;\r
+  } else if ((KeyValue >= KEY_ATTEMPT_ENTRY_BASE) &&\r
+             (KeyValue < (ISCSI_MAX_ATTEMPTS_NUM + KEY_ATTEMPT_ENTRY_BASE))) {\r
+\r
+    //\r
+    // User has pressed "Attempt *".\r
+    //\r
+    NewAttempt = FALSE;\r
+  } else {\r
+    //\r
+    // Don't process anything.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (NewAttempt) {\r
+    //\r
+    // Determine which NIC user has selected for the new created attempt.\r
+    //\r
+    NicIndex = (UINT8) (KeyValue - KEY_MAC_ENTRY_BASE);\r
+    NicInfo = IScsiGetNicInfoByIndex (NicIndex);\r
+    if (NicInfo == NULL) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+    \r
+    //\r
+    // Create the new attempt and save to NVR.\r
+    //\r
+\r
+    AttemptConfigData = AllocateZeroPool (sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA));\r
+    if (AttemptConfigData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    ConfigData                    = &AttemptConfigData->SessionConfigData;\r
+    ConfigData->TargetPort        = ISCSI_WELL_KNOWN_PORT;\r
+    ConfigData->ConnectTimeout    = CONNECT_DEFAULT_TIMEOUT;\r
+    ConfigData->ConnectRetryCount = CONNECT_MIN_RETRY;\r
+\r
+    AttemptConfigData->AuthenticationType           = ISCSI_AUTH_TYPE_CHAP;\r
+    AttemptConfigData->AuthConfigData.CHAP.CHAPType = ISCSI_CHAP_UNI;\r
+\r
+    //\r
+    // Get current order number for this attempt.\r
+    //\r
+    AttemptConfigOrder = IScsiGetVariableAndSize (\r
+                           L"AttemptOrder",\r
+                           &mVendorGuid,\r
+                           &AttemptConfigOrderSize\r
+                           );\r
+\r
+    TotalNumber = AttemptConfigOrderSize / sizeof (UINT8);\r
+\r
+    if (AttemptConfigOrder == NULL) {\r
+      CurrentAttemptConfigIndex = 1;\r
+    } else {\r
+      //\r
+      // Get the max attempt config index.\r
+      //\r
+      CurrentAttemptConfigIndex = AttemptConfigOrder[0];\r
+      for (Index = 1; Index < TotalNumber; Index++) {\r
+        if (CurrentAttemptConfigIndex < AttemptConfigOrder[Index]) {\r
+          CurrentAttemptConfigIndex = AttemptConfigOrder[Index];\r
+        }\r
+      }\r
+\r
+      CurrentAttemptConfigIndex++;\r
+    }\r
+\r
+    TotalNumber++;\r
+\r
+    //\r
+    // Append the new created attempt order to the end.\r
+    //\r
+    AttemptOrderTmp = AllocateZeroPool (TotalNumber * sizeof (UINT8));\r
+    if (AttemptOrderTmp == NULL) {\r
+      FreePool (AttemptConfigData);\r
+      if (AttemptConfigOrder != NULL) {\r
+        FreePool (AttemptConfigOrder);\r
+      }\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    if (AttemptConfigOrder != NULL) {\r
+      CopyMem (AttemptOrderTmp, AttemptConfigOrder, AttemptConfigOrderSize);\r
+      FreePool (AttemptConfigOrder);      \r
+    }\r
+\r
+    AttemptOrderTmp[TotalNumber - 1] = CurrentAttemptConfigIndex;    \r
+    AttemptConfigOrder               = AttemptOrderTmp;\r
+    AttemptConfigOrderSize           = TotalNumber * sizeof (UINT8);\r
+\r
+    Status = gRT->SetVariable (\r
+                    L"AttemptOrder",\r
+                    &mVendorGuid,\r
+                    EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+                    AttemptConfigOrderSize,\r
+                    AttemptConfigOrder\r
+                    );\r
+    FreePool (AttemptConfigOrder);\r
+    if (EFI_ERROR (Status)) {\r
+      FreePool (AttemptConfigData);\r
+      return Status;\r
+    }\r
+\r
+    //\r
+    // Record the mapping between attempt order and attempt's configdata.\r
+    //\r
+    AttemptConfigData->AttemptConfigIndex  = CurrentAttemptConfigIndex;\r
+    InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);\r
+    mPrivate->AttemptCount++;\r
+\r
+    //\r
+    // Record the MAC info in Config Data.\r
+    //\r
+    IScsiMacAddrToStr (\r
+      &NicInfo->PermanentAddress,\r
+      NicInfo->HwAddressSize,\r
+      NicInfo->VlanId,\r
+      MacString\r
+      );\r
+\r
+    UnicodeStrToAsciiStr (MacString, AttemptConfigData->MacString);\r
+    AttemptConfigData->NicIndex = NicIndex;\r
+\r
+    //\r
+    // Generate OUI-format ISID based on MAC address.\r
+    //\r
+    CopyMem (AttemptConfigData->SessionConfigData.IsId, &NicInfo->PermanentAddress, 6);\r
+    AttemptConfigData->SessionConfigData.IsId[0] = \r
+      (UINT8) (AttemptConfigData->SessionConfigData.IsId[0] & 0x3F);\r
+\r
+    //\r
+    // Add the help info for the new attempt.\r
+    //\r
+    UnicodeSPrint (\r
+      mPrivate->PortString,\r
+      (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+      L"MAC: %s, PFA: Bus %d | Dev %d | Func %d",\r
+      MacString,\r
+      NicInfo->BusNumber,\r
+      NicInfo->DeviceNumber,\r
+      NicInfo->FunctionNumber\r
+      );\r
+\r
+    AttemptConfigData->AttemptTitleHelpToken  = HiiSetString (\r
+                                                  mCallbackInfo->RegisteredHandle,\r
+                                                  0,\r
+                                                  mPrivate->PortString,\r
+                                                  NULL\r
+                                                  );\r
+    if (AttemptConfigData->AttemptTitleHelpToken == 0) {\r
+      FreePool (AttemptConfigData);\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    //\r
+    // Set the attempt name to default.\r
+    //\r
+    UnicodeSPrint (\r
+      mPrivate->PortString,\r
+      (UINTN) 128,\r
+      L"%d",\r
+      (UINTN) AttemptConfigData->AttemptConfigIndex\r
+      );\r
+    UnicodeStrToAsciiStr (mPrivate->PortString, AttemptConfigData->AttemptName);\r
+\r
+  } else {\r
+    //\r
+    // Determine which Attempt user has selected to configure.\r
+    // Get the attempt configuration data.\r
+    //\r
+    CurrentAttemptConfigIndex = (UINT8) (KeyValue - KEY_ATTEMPT_ENTRY_BASE);\r
+\r
+    AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (CurrentAttemptConfigIndex);\r
+    if (AttemptConfigData == NULL) {\r
+      DEBUG ((DEBUG_ERROR, "Corresponding configuration data can not be retrieved!\n"));\r
+      return EFI_NOT_FOUND;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Clear the old IFR data to avoid sharing it with other attempts.\r
+  //\r
+  if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {\r
+    ZeroMem (IfrNvData->CHAPName, sizeof (IfrNvData->CHAPName));\r
+    ZeroMem (IfrNvData->CHAPSecret, sizeof (IfrNvData->CHAPSecret));\r
+    ZeroMem (IfrNvData->ReverseCHAPName, sizeof (IfrNvData->ReverseCHAPName));\r
+    ZeroMem (IfrNvData->ReverseCHAPSecret, sizeof (IfrNvData->ReverseCHAPSecret));\r
+  }\r
+  \r
+  IScsiConvertAttemptConfigDataToIfrNvData (AttemptConfigData, IfrNvData);\r
+\r
+  mCallbackInfo->Current = AttemptConfigData;\r
+\r
+  IScsiConfigUpdateAttempt ();\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+   \r
+  This function allows the caller to request the current\r
+  configuration for one or more named elements. The resulting\r
+  string is in <ConfigAltResp> format. Also, any and all alternative\r
+  configuration strings shall be appended to the end of the\r
+  current configuration string. If they are, they must appear\r
+  after the current configuration. They must contain the same\r
+  routing (GUID, NAME, PATH) as the current configuration string.\r
+  They must have an additional description indicating the type of\r
+  alternative configuration the string represents,\r
+  "ALTCFG=<StringToken>". That <StringToken> (when\r
+  converted from Hex UNICODE to binary) is a reference to a\r
+  string in the associated string pack.\r
+\r
+  @param[in]  This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+\r
+  @param[in]  Request    A null-terminated Unicode string in\r
+                         <ConfigRequest> format. Note that this\r
+                         includes the routing information as well as\r
+                         the configurable name / value pairs. It is\r
+                         invalid for this string to be in\r
+                         <MultiConfigRequest> format.\r
+\r
+  @param[out] Progress   On return, points to a character in the\r
+                         Request string. Points to the string's null\r
+                         terminator if request was successful. Points\r
+                         to the most recent "&" before the first\r
+                         failing name / value pair (or the beginning\r
+                         of the string if the failure is in the first\r
+                         name / value pair) if the request was not successful.                        \r
+\r
+  @param[out] Results    A null-terminated Unicode string in\r
+                         <ConfigAltResp> format which has all values\r
+                         filled in for the names in the Request string.\r
+                         String to be allocated by the called function.\r
+\r
+  @retval EFI_SUCCESS             The Results string is filled with the\r
+                                  values corresponding to all requested\r
+                                  names.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+\r
+  @retval EFI_INVALID_PARAMETER   For example, passing in a NULL\r
+                                  for the Request parameter\r
+                                  would result in this type of\r
+                                  error. In this case, the\r
+                                  Progress parameter would be\r
+                                  set to NULL. \r
+\r
+  @retval EFI_NOT_FOUND           Routing data doesn't match any\r
+                                  known driver. Progress set to the\r
+                                  first character in the routing header.\r
+                                  Note: There is no requirement that the\r
+                                  driver validate the routing data. It\r
+                                  must skip the <ConfigHdr> in order to\r
+                                  process the names.\r
+\r
+  @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set\r
+                                  to most recent "&" before the\r
+                                  error or the beginning of the\r
+                                  string.\r
+\r
+  @retval EFI_INVALID_PARAMETER   Unknown name. Progress points\r
+                                  to the & before the name in\r
+                                  question.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiFormExtractConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Request,\r
+  OUT EFI_STRING                             *Progress,\r
+  OUT EFI_STRING                             *Results\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  CHAR8                            *InitiatorName;\r
+  UINTN                            BufferSize;\r
+  ISCSI_CONFIG_IFR_NVDATA          *IfrNvData;\r
+  ISCSI_FORM_CALLBACK_INFO         *Private;\r
+  EFI_STRING                       ConfigRequestHdr;\r
+  EFI_STRING                       ConfigRequest;\r
+  BOOLEAN                          AllocatedRequest;\r
+  UINTN                            Size;\r
+\r
+  if (This == NULL || Progress == NULL || Results == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *Progress = Request;\r
+  if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &mVendorGuid, mVendorStorageName)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  ConfigRequestHdr = NULL;\r
+  ConfigRequest    = NULL;\r
+  AllocatedRequest = FALSE;\r
+  Size             = 0;\r
+\r
+  Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);\r
+  IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));\r
+  if (IfrNvData == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  \r
+  if (Private->Current != NULL) {\r
+    IScsiConvertAttemptConfigDataToIfrNvData (Private->Current, IfrNvData);\r
+  }\r
+\r
+  BufferSize    = ISCSI_NAME_MAX_SIZE;\r
+  InitiatorName = (CHAR8 *) AllocateZeroPool (BufferSize);\r
+  if (InitiatorName == NULL) {\r
+    FreePool (IfrNvData);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+    \r
+  Status = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);\r
+  if (EFI_ERROR (Status)) {\r
+    IfrNvData->InitiatorName[0] = L'\0';\r
+  } else {\r
+    AsciiStrToUnicodeStr (InitiatorName, IfrNvData->InitiatorName);\r
+  }\r
+\r
+  //\r
+  // Convert buffer data to <ConfigResp> by helper function BlockToConfig().\r
+  //\r
+  BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);\r
+  ConfigRequest = Request;\r
+  if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {\r
+    //\r
+    // Request has no request element, construct full request string.\r
+    // Allocate and fill a buffer large enough to hold the <ConfigHdr> template\r
+    // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator\r
+    //\r
+    ConfigRequestHdr = HiiConstructConfigHdr (&mVendorGuid, mVendorStorageName, Private->DriverHandle);\r
+    Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);\r
+    ConfigRequest = AllocateZeroPool (Size);\r
+    ASSERT (ConfigRequest != NULL);\r
+    AllocatedRequest = TRUE;\r
+    UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);\r
+    FreePool (ConfigRequestHdr);\r
+  }\r
+\r
+  Status = gHiiConfigRouting->BlockToConfig (\r
+                                gHiiConfigRouting,\r
+                                ConfigRequest,\r
+                                (UINT8 *) IfrNvData,\r
+                                BufferSize,\r
+                                Results,\r
+                                Progress\r
+                                );\r
+  FreePool (IfrNvData);\r
+  FreePool (InitiatorName);\r
+\r
+  //\r
+  // Free the allocated config request string.\r
+  //\r
+  if (AllocatedRequest) {\r
+    FreePool (ConfigRequest);\r
+    ConfigRequest = NULL;\r
+  }\r
+  //\r
+  // Set Progress string to the original request string.\r
+  //\r
+  if (Request == NULL) {\r
+    *Progress = NULL;\r
+  } else if (StrStr (Request, L"OFFSET") == NULL) {\r
+    *Progress = Request + StrLen (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+   \r
+  This function applies changes in a driver's configuration.\r
+  Input is a Configuration, which has the routing data for this\r
+  driver followed by name / value configuration pairs. The driver\r
+  must apply those pairs to its configurable storage. If the\r
+  driver's configuration is stored in a linear block of data\r
+  and the driver's name / value pairs are in <BlockConfig>\r
+  format, it may use the ConfigToBlock helper function (above) to\r
+  simplify the job.\r
+\r
+  @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+\r
+  @param[in]  Configuration  A null-terminated Unicode string in\r
+                             <ConfigString> format. \r
+  \r
+  @param[out] Progress       A pointer to a string filled in with the\r
+                             offset of the most recent '&' before the\r
+                             first failing name / value pair (or the\r
+                             beginning of the string if the failure\r
+                             is in the first name / value pair) or\r
+                             the terminating NULL if all was\r
+                             successful.\r
+\r
+  @retval EFI_SUCCESS             The results have been distributed or are\r
+                                  awaiting distribution.\r
+  \r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+  \r
+  @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the\r
+                                  Results parameter would result\r
+                                  in this type of error.\r
+  \r
+  @retval EFI_NOT_FOUND           Target for the specified routing data\r
+                                  was not found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiFormRouteConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Configuration,\r
+  OUT EFI_STRING                             *Progress\r
+  )\r
+{\r
+  if (This == NULL || Configuration == NULL || Progress == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check routing data in <ConfigHdr>.\r
+  // Note: if only one Storage is used, then this checking could be skipped.\r
+  //\r
+  if (!HiiIsConfigHdrMatch (Configuration, &mVendorGuid, mVendorStorageName)) {\r
+    *Progress = Configuration;\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  *Progress = Configuration + StrLen (Configuration);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+   \r
+  This function is called to provide results data to the driver.\r
+  This data consists of a unique key that is used to identify\r
+  which data is either being passed back or being asked for.\r
+\r
+  @param[in]       This          Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in]       Action        Specifies the type of action taken by the browser.\r
+  @param[in]       QuestionId    A unique value which is sent to the original\r
+                                 exporting driver so that it can identify the type\r
+                                 of data to expect. The format of the data tends to \r
+                                 vary based on the opcode that generated the callback.\r
+  @param[in]       Type          The type of value for the question.\r
+  @param[in, out]  Value         A pointer to the data being sent to the original\r
+                                 exporting driver.\r
+  @param[out]      ActionRequest On return, points to the action requested by the\r
+                                 callback function.\r
+\r
+  @retval EFI_SUCCESS            The callback successfully handled the action.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the\r
+                                 variable and its data.\r
+  @retval EFI_DEVICE_ERROR       The variable could not be saved.\r
+  @retval EFI_UNSUPPORTED        The specified Action is not supported by the\r
+                                 callback.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiFormCallback (\r
+  IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN        EFI_BROWSER_ACTION               Action,\r
+  IN        EFI_QUESTION_ID                  QuestionId,\r
+  IN        UINT8                            Type,\r
+  IN OUT    EFI_IFR_TYPE_VALUE               *Value,\r
+  OUT       EFI_BROWSER_ACTION_REQUEST       *ActionRequest\r
+  )\r
+{\r
+  ISCSI_FORM_CALLBACK_INFO    *Private;\r
+  UINTN                       BufferSize;\r
+  CHAR8                       *IScsiName;\r
+  CHAR8                       IpString[IP_STR_MAX_SIZE];\r
+  CHAR8                       LunString[ISCSI_LUN_STR_MAX_LEN];\r
+  UINT64                      Lun;\r
+  EFI_IP_ADDRESS              HostIp;\r
+  EFI_IP_ADDRESS              SubnetMask;\r
+  EFI_IP_ADDRESS              Gateway;\r
+  ISCSI_CONFIG_IFR_NVDATA     *IfrNvData;\r
+  ISCSI_CONFIG_IFR_NVDATA     OldIfrNvData;\r
+  EFI_STATUS                  Status;\r
+  CHAR16                      AttemptName[ATTEMPT_NAME_SIZE + 4];\r
+  EFI_INPUT_KEY               Key;\r
+\r
+  if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)) {\r
+    //\r
+    // Do nothing for UEFI OPEN/CLOSE Action\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  if (Action == EFI_BROWSER_ACTION_CHANGING) {\r
+    if (This == NULL || Value == NULL || ActionRequest == NULL) {\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+\r
+    Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);\r
+\r
+    //\r
+    // Retrieve uncommitted data from Browser\r
+    //\r
+\r
+    BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);\r
+    IfrNvData = AllocateZeroPool (BufferSize);\r
+    if (IfrNvData == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    IScsiName = (CHAR8 *) AllocateZeroPool (ISCSI_NAME_MAX_SIZE);\r
+    if (IScsiName == NULL) {\r
+      FreePool (IfrNvData);\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    Status = EFI_SUCCESS;\r
+\r
+    ZeroMem (&OldIfrNvData, BufferSize);\r
+\r
+    HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);\r
+\r
+    CopyMem (&OldIfrNvData, IfrNvData, BufferSize);\r
+\r
+    switch (QuestionId) {\r
+    case KEY_INITIATOR_NAME:\r
+      UnicodeStrToAsciiStr (IfrNvData->InitiatorName, IScsiName);\r
+      BufferSize  = AsciiStrSize (IScsiName);\r
+\r
+      Status      = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName);\r
+      if (EFI_ERROR (Status)) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Invalid iSCSI Name!",\r
+          NULL\r
+          );      \r
+      }\r
+\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+      break;\r
+\r
+    case KEY_ADD_ATTEMPT:\r
+      Status = IScsiConfigAddAttempt ();\r
+      break;\r
+\r
+    case KEY_DELETE_ATTEMPT:\r
+      CopyMem (\r
+        OldIfrNvData.DeleteAttemptList,\r
+        IfrNvData->DeleteAttemptList,\r
+        sizeof (IfrNvData->DeleteAttemptList)\r
+        );\r
+      Status = IScsiConfigDisplayDeleteAttempts (IfrNvData);\r
+      break;\r
+\r
+    case KEY_SAVE_DELETE_ATTEMPT:\r
+      //\r
+      // Delete the Attempt Order from NVR\r
+      //\r
+      Status = IScsiConfigDeleteAttempts (IfrNvData);\r
+      if (EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+\r
+      IScsiConfigUpdateAttempt ();\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+      break;\r
+\r
+    case KEY_IGNORE_DELETE_ATTEMPT:\r
+      CopyMem (\r
+        IfrNvData->DeleteAttemptList,\r
+        OldIfrNvData.DeleteAttemptList,\r
+        sizeof (IfrNvData->DeleteAttemptList)\r
+        );\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+      break;\r
+\r
+    case KEY_ORDER_ATTEMPT_CONFIG:\r
+      //\r
+      // Order the attempt according to user input.\r
+      //\r
+      CopyMem (\r
+        OldIfrNvData.DynamicOrderedList,\r
+        IfrNvData->DynamicOrderedList,\r
+        sizeof (IfrNvData->DynamicOrderedList)\r
+        );\r
+      IScsiConfigDisplayOrderAttempts ();\r
+      break;\r
+\r
+    case KEY_SAVE_ORDER_CHANGES:\r
+      //\r
+      // Sync the Attempt Order to NVR.\r
+      //\r
+      Status = IScsiConfigOrderAttempts (IfrNvData);\r
+      if (EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+\r
+      IScsiConfigUpdateAttempt ();\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+      break;\r
+\r
+    case KEY_IGNORE_ORDER_CHANGES:\r
+      CopyMem (\r
+        IfrNvData->DynamicOrderedList,\r
+        OldIfrNvData.DynamicOrderedList,\r
+        sizeof (IfrNvData->DynamicOrderedList)\r
+        );\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+      break;\r
+\r
+    case KEY_ATTEMPT_NAME:\r
+      if (StrLen (IfrNvData->AttemptName) > ATTEMPT_NAME_SIZE) {\r
+        CopyMem (AttemptName, IfrNvData->AttemptName, ATTEMPT_NAME_SIZE * sizeof (CHAR16));\r
+        CopyMem (&AttemptName[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));\r
+      } else {\r
+        CopyMem (\r
+          AttemptName,\r
+          IfrNvData->AttemptName,\r
+          (StrLen (IfrNvData->AttemptName) + 1) * sizeof (CHAR16)\r
+          );\r
+      }\r
+\r
+      UnicodeStrToAsciiStr (IfrNvData->AttemptName, Private->Current->AttemptName);\r
+\r
+      IScsiConfigUpdateAttempt ();\r
+\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+      break;\r
+\r
+    case KEY_IP_MODE:\r
+      switch (Value->u8) {\r
+      case IP_MODE_IP6:\r
+        ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));\r
+        IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, TRUE, IfrNvData->TargetIp);\r
+        Private->Current->AutoConfigureMode = 0;\r
+        break;\r
+\r
+      case IP_MODE_IP4:\r
+        ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));\r
+        IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, FALSE, IfrNvData->TargetIp);\r
+        Private->Current->AutoConfigureMode = 0;\r
+\r
+        break;\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_LOCAL_IP:\r
+      Status = NetLibStrToIp4 (IfrNvData->LocalIp, &HostIp.v4);\r
+      if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Invalid IP address!",\r
+          NULL\r
+          ); \r
+        \r
+        Status = EFI_INVALID_PARAMETER;\r
+      } else {\r
+        CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_SUBNET_MASK:\r
+      Status = NetLibStrToIp4 (IfrNvData->SubnetMask, &SubnetMask.v4);\r
+      if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Invalid Subnet Mask!",\r
+          NULL\r
+          ); \r
+        \r
+        Status = EFI_INVALID_PARAMETER;\r
+      } else {\r
+        CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_GATE_WAY:\r
+      Status = NetLibStrToIp4 (IfrNvData->Gateway, &Gateway.v4);\r
+      if (EFI_ERROR (Status) || ((Gateway.Addr[0] != 0) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 0))) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Invalid Gateway!",\r
+          NULL\r
+          );       \r
+        Status = EFI_INVALID_PARAMETER;\r
+      } else {\r
+        CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_TARGET_IP:\r
+      UnicodeStrToAsciiStr (IfrNvData->TargetIp, IpString);\r
+      Status = IScsiAsciiStrToIp (IpString, IfrNvData->IpMode, &HostIp);\r
+      if (EFI_ERROR (Status) || !IpIsUnicast (&HostIp, IfrNvData->IpMode)) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Invalid IP address!",\r
+          NULL\r
+          );       \r
+        Status = EFI_INVALID_PARAMETER;\r
+      } else {\r
+        CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp, sizeof (HostIp));\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_TARGET_NAME:\r
+      UnicodeStrToAsciiStr (IfrNvData->TargetName, IScsiName);\r
+      Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName));\r
+      if (EFI_ERROR (Status)) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Invalid iSCSI Name!",\r
+          NULL\r
+          );       \r
+      } else {\r
+        AsciiStrCpy (Private->Current->SessionConfigData.TargetName, IScsiName);\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_DHCP_ENABLE:\r
+      if (IfrNvData->InitiatorInfoFromDhcp == 0) {\r
+        IfrNvData->TargetInfoFromDhcp = 0;\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_BOOT_LUN:\r
+      UnicodeStrToAsciiStr (IfrNvData->BootLun, LunString);\r
+      Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);\r
+      if (EFI_ERROR (Status)) {\r
+        CreatePopUp (\r
+          EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,\r
+          &Key,\r
+          L"Invalid LUN string!",\r
+          NULL\r
+          );       \r
+      } else {\r
+        CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun));\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_AUTH_TYPE:\r
+      switch (Value->u8) {\r
+      case ISCSI_AUTH_TYPE_CHAP:\r
+        IfrNvData->CHAPType = ISCSI_CHAP_UNI;\r
+        break;\r
+      default:\r
+        break;\r
+      }\r
+\r
+      break;\r
+\r
+    case KEY_CHAP_NAME:\r
+      UnicodeStrToAsciiStr (\r
+        IfrNvData->CHAPName,\r
+        Private->Current->AuthConfigData.CHAP.CHAPName\r
+        );\r
+      break;\r
+\r
+    case KEY_CHAP_SECRET:\r
+      UnicodeStrToAsciiStr (\r
+        IfrNvData->CHAPSecret,\r
+        Private->Current->AuthConfigData.CHAP.CHAPSecret\r
+        );\r
+      break;\r
+\r
+    case KEY_REVERSE_CHAP_NAME:\r
+      UnicodeStrToAsciiStr (\r
+        IfrNvData->ReverseCHAPName,\r
+        Private->Current->AuthConfigData.CHAP.ReverseCHAPName\r
+        );\r
+      break;\r
+\r
+    case KEY_REVERSE_CHAP_SECRET:\r
+      UnicodeStrToAsciiStr (\r
+        IfrNvData->ReverseCHAPSecret,\r
+        Private->Current->AuthConfigData.CHAP.ReverseCHAPSecret\r
+        );\r
+      break;\r
+\r
+    case KEY_CONFIG_ISID:\r
+      IScsiParseIsIdFromString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);\r
+      IScsiConvertIsIdToString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);\r
+\r
+      break;\r
+\r
+    case KEY_SAVE_ATTEMPT_CONFIG:\r
+      Status = IScsiConvertIfrNvDataToAttemptConfigData (IfrNvData, Private->Current);\r
+      if (EFI_ERROR (Status)) {\r
+        break;\r
+      }\r
+\r
+      *ActionRequest = EFI_BROWSER_ACTION_REQUEST_SUBMIT;\r
+      break;\r
+\r
+    default:\r
+      Status = IScsiConfigProcessDefault (QuestionId, IfrNvData);\r
+      break;\r
+    }\r
+\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // Pass changed uncommitted data back to Form Browser.\r
+      //\r
+      BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);\r
+      HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL);\r
+    }\r
+\r
+    FreePool (IfrNvData);\r
+    FreePool (IScsiName);\r
+\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // All other action return unsupported.\r
+  //\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+/**\r
+  Initialize the iSCSI configuration form.\r
+\r
+  @param[in]  DriverBindingHandle The iSCSI driverbinding handle.\r
+\r
+  @retval EFI_SUCCESS             The iSCSI configuration form is initialized.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigFormInit (\r
+  IN EFI_HANDLE  DriverBindingHandle\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  ISCSI_FORM_CALLBACK_INFO    *CallbackInfo;\r
+\r
+  CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO));\r
+  if (CallbackInfo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  CallbackInfo->Signature   = ISCSI_FORM_CALLBACK_INFO_SIGNATURE;\r
+  CallbackInfo->Current     = NULL;\r
+\r
+  CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig;\r
+  CallbackInfo->ConfigAccess.RouteConfig   = IScsiFormRouteConfig;\r
+  CallbackInfo->ConfigAccess.Callback      = IScsiFormCallback;\r
+\r
+  //\r
+  // Install Device Path Protocol and Config Access protocol to driver handle.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &CallbackInfo->DriverHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  &mIScsiHiiVendorDevicePath,\r
+                  &gEfiHiiConfigAccessProtocolGuid,\r
+                  &CallbackInfo->ConfigAccess,\r
+                  NULL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+  \r
+  //\r
+  // Publish our HII data.\r
+  //\r
+  CallbackInfo->RegisteredHandle = HiiAddPackages (\r
+                                     &mVendorGuid,\r
+                                     CallbackInfo->DriverHandle,\r
+                                     IScsiDxeStrings,\r
+                                     IScsiConfigVfrBin,\r
+                                     NULL\r
+                                     );\r
+  if (CallbackInfo->RegisteredHandle == NULL) {\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           &CallbackInfo->DriverHandle,\r
+           &gEfiDevicePathProtocolGuid,\r
+           &mIScsiHiiVendorDevicePath,\r
+           &gEfiHiiConfigAccessProtocolGuid,\r
+           &CallbackInfo->ConfigAccess,\r
+           NULL\r
+           );\r
+    FreePool(CallbackInfo);\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  mCallbackInfo = CallbackInfo;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Unload the iSCSI configuration form, this includes: delete all the iSCSI\r
+  configuration entries, uninstall the form callback protocol, and\r
+  free the resources used.\r
+\r
+  @param[in]  DriverBindingHandle The iSCSI driverbinding handle.\r
+\r
+  @retval EFI_SUCCESS             The iSCSI configuration form is unloaded.\r
+  @retval Others                  Failed to unload the form.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigFormUnload (\r
+  IN EFI_HANDLE  DriverBindingHandle\r
+  )\r
+{\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  ISCSI_NIC_INFO              *NicInfo;\r
+  LIST_ENTRY                  *Entry;\r
+  EFI_STATUS                  Status;\r
+\r
+  while (!IsListEmpty (&mPrivate->AttemptConfigs)) {\r
+    Entry = NetListRemoveHead (&mPrivate->AttemptConfigs);\r
+    AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+    FreePool (AttemptConfigData);\r
+    mPrivate->AttemptCount--;\r
+  }\r
+\r
+  ASSERT (mPrivate->AttemptCount == 0);\r
+\r
+  while (!IsListEmpty (&mPrivate->NicInfoList)) {\r
+    Entry = NetListRemoveHead (&mPrivate->NicInfoList);\r
+    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);\r
+    FreePool (NicInfo);\r
+    mPrivate->NicCount--;\r
+  }\r
+\r
+  ASSERT (mPrivate->NicCount == 0);\r
+\r
+  FreePool (mPrivate);\r
+  mPrivate = NULL;\r
+\r
+  //\r
+  // Remove HII package list.\r
+  //\r
+  HiiRemovePackages (mCallbackInfo->RegisteredHandle);\r
+\r
+  //\r
+  // Uninstall Device Path Protocol and Config Access protocol.\r
+  //\r
+  Status = gBS->UninstallMultipleProtocolInterfaces (\r
+                  mCallbackInfo->DriverHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  &mIScsiHiiVendorDevicePath,\r
+                  &gEfiHiiConfigAccessProtocolGuid,\r
+                  &mCallbackInfo->ConfigAccess,\r
+                  NULL\r
+                  );\r
+\r
+  FreePool (mCallbackInfo);\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiConfig.h b/NetworkPkg/IScsiDxe/IScsiConfig.h
new file mode 100644 (file)
index 0000000..df0c753
--- /dev/null
@@ -0,0 +1,158 @@
+/** @file\r
+  The header file of functions for configuring or getting the parameters\r
+  relating to iSCSI.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_CONFIG_H_\r
+#define _ISCSI_CONFIG_H_\r
+\r
+#include "IScsiConfigNVDataStruc.h"\r
+\r
+typedef struct _ISCSI_FORM_CALLBACK_INFO ISCSI_FORM_CALLBACK_INFO;\r
+\r
+extern UINT8                       IScsiConfigVfrBin[];\r
+extern UINT8                       IScsiDxeStrings[];\r
+extern ISCSI_FORM_CALLBACK_INFO    *mCallbackInfo;\r
+extern EFI_GUID                    mVendorGuid;\r
+\r
+\r
+#define VAR_OFFSET(Field)    \\r
+  ((UINT16) ((UINTN) &(((ISCSI_CONFIG_IFR_NVDATA *) 0)->Field)))\r
+\r
+#define QUESTION_ID(Field)   \\r
+  ((UINT16) (VAR_OFFSET (Field) + CONFIG_OPTION_OFFSET))\r
+\r
+\r
+#define DYNAMIC_ONE_OF_VAR_OFFSET           VAR_OFFSET  (Enabled)\r
+#define DYNAMIC_ORDERED_LIST_QUESTION_ID    QUESTION_ID (DynamicOrderedList)\r
+#define DYNAMIC_ORDERED_LIST_VAR_OFFSET     VAR_OFFSET  (DynamicOrderedList)\r
+#define ATTEMPT_DEL_QUESTION_ID             QUESTION_ID (DeleteAttemptList)\r
+#define ATTEMPT_DEL_VAR_OFFSET              VAR_OFFSET  (DeleteAttemptList)\r
+\r
+//\r
+// sizeof (EFI_MAC_ADDRESS) * 3\r
+//\r
+#define ISCSI_MAX_MAC_STRING_LEN            96\r
+\r
+#define ISCSI_INITATOR_NAME_VAR_NAME        L"I_NAME"\r
+\r
+#define ISCSI_CONFIG_VAR_ATTR               (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE)\r
+\r
+#define ISCSI_FORM_CALLBACK_INFO_SIGNATURE  SIGNATURE_32 ('I', 'f', 'c', 'i')\r
+\r
+#define ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK(Callback) \\r
+  CR ( \\r
+  Callback, \\r
+  ISCSI_FORM_CALLBACK_INFO, \\r
+  ConfigAccess, \\r
+  ISCSI_FORM_CALLBACK_INFO_SIGNATURE \\r
+  )\r
+\r
+#pragma pack(1)\r
+struct _ISCSI_ATTEMPT_CONFIG_NVDATA {\r
+  LIST_ENTRY                       Link;\r
+  UINT8                            NicIndex;\r
+  UINT8                            AttemptConfigIndex;\r
+  BOOLEAN                          DhcpSuccess;\r
+  BOOLEAN                          ValidiBFTPath;\r
+  BOOLEAN                          ValidPath;\r
+  UINT8                            AutoConfigureMode;\r
+  EFI_STRING_ID                    AttemptTitleToken;\r
+  EFI_STRING_ID                    AttemptTitleHelpToken;\r
+  CHAR8                            AttemptName[ATTEMPT_NAME_MAX_SIZE];\r
+  CHAR8                            MacString[ISCSI_MAX_MAC_STRING_LEN];\r
+  EFI_IP_ADDRESS                   PrimaryDns;\r
+  EFI_IP_ADDRESS                   SecondaryDns;\r
+  EFI_IP_ADDRESS                   DhcpServer;\r
+  ISCSI_SESSION_CONFIG_NVDATA      SessionConfigData;\r
+  UINT8                            AuthenticationType;\r
+  union {\r
+    ISCSI_CHAP_AUTH_CONFIG_NVDATA  CHAP;\r
+  } AuthConfigData;\r
+\r
+};\r
+\r
+///\r
+/// HII specific Vendor Device Path definition.\r
+///\r
+typedef struct {\r
+  VENDOR_DEVICE_PATH               VendorDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL         End;\r
+} HII_VENDOR_DEVICE_PATH;\r
+\r
+#pragma pack()\r
+\r
+struct _ISCSI_FORM_CALLBACK_INFO {\r
+  UINT32                           Signature;\r
+  EFI_HANDLE                       DriverHandle;\r
+  EFI_HII_CONFIG_ACCESS_PROTOCOL   ConfigAccess;\r
+  UINT16                           *KeyList;\r
+  VOID                             *FormBuffer;\r
+  EFI_HII_HANDLE                   RegisteredHandle;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA      *Current;\r
+};\r
+\r
+/**\r
+  Initialize the iSCSI configuration form.\r
+\r
+  @param[in]  DriverBindingHandle The iSCSI driverbinding handle.\r
+\r
+  @retval EFI_SUCCESS             The iSCSI configuration form is initialized.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigFormInit (\r
+  IN EFI_HANDLE  DriverBindingHandle\r
+  );\r
+\r
+/**\r
+  Unload the iSCSI configuration form, this includes: delete all the iSCSI\r
+  configuration entries, uninstall the form callback protocol, and\r
+  free the resources used.\r
+\r
+  @param[in]  DriverBindingHandle The iSCSI driverbinding handle.\r
+\r
+  @retval EFI_SUCCESS             The iSCSI configuration form is unloaded.\r
+  @retval Others                  Failed to unload the form.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConfigFormUnload (\r
+  IN EFI_HANDLE  DriverBindingHandle\r
+  );\r
+\r
+/**\r
+  Update the MAIN form to display the configured attempts.\r
+\r
+**/\r
+VOID\r
+IScsiConfigUpdateAttempt (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Get the attempt config data from global structure by the ConfigIndex.\r
+\r
+  @param[in]  AttemptConfigIndex     The unique index indicates the attempt.\r
+\r
+  @return       Pointer to the attempt config data.\r
+  @retval NULL  The attempt configuration data can not be found.\r
+\r
+**/\r
+ISCSI_ATTEMPT_CONFIG_NVDATA *\r
+IScsiConfigGetAttemptByConfigIndex (\r
+  IN UINT8                     AttemptConfigIndex\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h b/NetworkPkg/IScsiDxe/IScsiConfigNVDataStruc.h
new file mode 100644 (file)
index 0000000..211b4ab
--- /dev/null
@@ -0,0 +1,194 @@
+/** @file\r
+  Define NVData structures used by the iSCSI configuration component.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_NVDATASTRUC_H_\r
+#define _ISCSI_NVDATASTRUC_H_\r
+\r
+#define ISCSI_CONFIG_GUID \\r
+  { \\r
+    0x6456ed61, 0x3579, 0x41c9, { 0x8a, 0x26, 0x0a, 0x0b, 0xd6, 0x2b, 0x78, 0xfc } \\r
+  }\r
+\r
+#define VAR_EQ_TEST_NAME          0x100\r
+#define CONFIGURATION_VARSTORE_ID 0x6666\r
+\r
+#define FORMID_MAIN_FORM          1\r
+#define FORMID_MAC_FORM           2\r
+#define FORMID_ATTEMPT_FORM       3\r
+#define FORMID_ORDER_FORM         4\r
+#define FORMID_DELETE_FORM        5\r
+\r
+#define ISCSI_NAME_IFR_MIN_SIZE   4\r
+#define ISCSI_NAME_IFR_MAX_SIZE   223\r
+#define ISCSI_NAME_MAX_SIZE       224\r
+\r
+#define ATTEMPT_NAME_MAX_SIZE     96\r
+#define ATTEMPT_NAME_SIZE         10\r
+\r
+#define CONNECT_MIN_RETRY         0\r
+#define CONNECT_MAX_RETRY         16\r
+\r
+#define CONNECT_MIN_TIMEOUT       100\r
+#define CONNECT_MAX_TIMEOUT       20000\r
+#define CONNECT_DEFAULT_TIMEOUT   1000\r
+\r
+#define ISCSI_MAX_ATTEMPTS_NUM    255\r
+\r
+#define ISCSI_DISABLED            0\r
+#define ISCSI_ENABLED             1\r
+#define ISCSI_ENABLED_FOR_MPIO    2\r
+\r
+#define IP_MODE_IP4               0\r
+#define IP_MODE_IP6               1\r
+#define IP_MODE_AUTOCONFIG        2\r
+\r
+#define ISCSI_AUTH_TYPE_NONE      0\r
+#define ISCSI_AUTH_TYPE_CHAP      1\r
+#define ISCSI_AUTH_TYPE_KRB       2\r
+\r
+#define IP4_MIN_SIZE              7\r
+#define IP4_MAX_SIZE              15\r
+#define IP4_STR_MAX_SIZE          16\r
+\r
+//\r
+// Macros used for an IPv4 or an IPv6 address.\r
+//\r
+#define IP_MIN_SIZE               2\r
+#define IP_MAX_SIZE               39\r
+#define IP_STR_MAX_SIZE           40\r
+\r
+#define LUN_MIN_SIZE              1\r
+#define LUN_MAX_SIZE              20\r
+\r
+#define ISCSI_CHAP_UNI            1\r
+#define ISCSI_CHAP_MUTUAL         2\r
+\r
+#define TARGET_PORT_MIN_NUM       0\r
+#define TARGET_PORT_MAX_NUM       65535\r
+#define LABEL_END                 0xffff\r
+\r
+#define KEY_INITIATOR_NAME        0x101\r
+#define KEY_DHCP_ENABLE           0x102\r
+#define KEY_LOCAL_IP              0x103\r
+#define KEY_SUBNET_MASK           0x104\r
+#define KEY_GATE_WAY              0x105\r
+#define KEY_TARGET_IP             0x106\r
+#define KEY_CHAP_NAME             0x107\r
+#define KEY_CHAP_SECRET           0x108\r
+#define KEY_REVERSE_CHAP_NAME     0x109\r
+#define KEY_REVERSE_CHAP_SECRET   0x10a\r
+#define KEY_SAVE_CHANGES          0x10b\r
+#define KEY_TARGET_NAME           0x10c\r
+#define KEY_BOOT_LUN              0x10d\r
+\r
+#define KEY_ADD_ATTEMPT           0x10e\r
+#define KEY_SAVE_ATTEMPT_CONFIG   0x10f\r
+#define KEY_ORDER_ATTEMPT_CONFIG  0x110\r
+#define KEY_SAVE_ORDER_CHANGES    0x111\r
+#define KEY_IGNORE_ORDER_CHANGES  0x112\r
+#define KEY_ATTEMPT_NAME          0x113\r
+#define KEY_SAVE_DELETE_ATTEMPT   0x114\r
+#define KEY_IGNORE_DELETE_ATTEMPT 0x115\r
+#define KEY_DELETE_ATTEMPT        0x116\r
+\r
+#define KEY_KERBEROS_USER_NAME    0x117\r
+#define KEY_KERBEROS_USER_SECRET  0x118\r
+#define KEY_KERBEROS_KDC_NAME     0x119\r
+#define KEY_KERBEROS_KDC_REALM    0x11a\r
+#define KEY_KERBEROS_KDC_IP_ADDR  0x11b\r
+\r
+#define KEY_IP_MODE               0x11c\r
+#define KEY_AUTH_TYPE             0x11d\r
+#define KEY_CONFIG_ISID           0x11e\r
+\r
+#define ATTEMPT_ENTRY_LABEL       0x9000\r
+#define KEY_ATTEMPT_ENTRY_BASE    0xa000\r
+#define KEY_DE_ATTEMPT_ENTRY_BASE 0xb000\r
+\r
+#define KEY_DEVICE_ENTRY_BASE     0x1000\r
+#define KEY_MAC_ENTRY_BASE        0x2000\r
+#define MAC_ENTRY_LABEL           0x3000\r
+#define ORDER_ENTRY_LABEL         0x4000\r
+#define DELETE_ENTRY_LABEL        0x5000\r
+#define CONFIG_OPTION_OFFSET      0x9000\r
+\r
+#define ISCSI_LUN_STR_MAX_LEN     21\r
+#define ISCSI_CHAP_SECRET_MIN_LEN 12\r
+#define ISCSI_CHAP_SECRET_MAX_LEN 16\r
+//\r
+// ISCSI_CHAP_SECRET_STORAGE = ISCSI_CHAP_SECRET_MAX_LEN + sizeof (NULL-Terminator)\r
+//\r
+#define ISCSI_CHAP_SECRET_STORAGE 17\r
+#define ISCSI_CHAP_NAME_MAX_LEN   126\r
+#define ISCSI_CHAP_NAME_STORAGE   127\r
+\r
+#define KERBEROS_SECRET_MIN_LEN   12\r
+#define KERBEROS_SECRET_MAX_LEN   16\r
+#define KERBEROS_SECRET_STORAGE   17\r
+#define KERBEROS_NAME_MAX_LEN     96\r
+#define KERBEROS_KDC_PORT_MIN_NUM 0\r
+#define KERBEROS_KDC_PORT_MAX_NUM 65535\r
+\r
+#define ISID_CONFIGURABLE_MIN_LEN 6\r
+#define ISID_CONFIGURABLE_MAX_LEN 12\r
+#define ISID_CONFIGURABLE_STORAGE 13\r
+\r
+#pragma pack(1)\r
+typedef struct _ISCSI_CONFIG_IFR_NVDATA {\r
+  CHAR16  InitiatorName[ISCSI_NAME_MAX_SIZE];\r
+  CHAR16  AttemptName[ATTEMPT_NAME_MAX_SIZE];\r
+\r
+  UINT8   Enabled;\r
+  UINT8   IpMode;\r
+\r
+  UINT8   ConnectRetryCount;\r
+  UINT8   Padding1;\r
+  UINT16  ConnectTimeout; // Timeout value in milliseconds.\r
+\r
+  UINT8   InitiatorInfoFromDhcp;\r
+  UINT8   TargetInfoFromDhcp;\r
+  CHAR16  LocalIp[IP4_STR_MAX_SIZE];\r
+  CHAR16  SubnetMask[IP4_STR_MAX_SIZE];\r
+  CHAR16  Gateway[IP4_STR_MAX_SIZE];\r
+\r
+  CHAR16  TargetName[ISCSI_NAME_MAX_SIZE];\r
+  CHAR16  TargetIp[IP_STR_MAX_SIZE];\r
+  UINT16  TargetPort;\r
+  CHAR16  BootLun[ISCSI_LUN_STR_MAX_LEN];\r
+\r
+  UINT8   AuthenticationType;\r
+\r
+  UINT8   CHAPType;\r
+  CHAR16  CHAPName[ISCSI_CHAP_NAME_STORAGE];\r
+  CHAR16  CHAPSecret[ISCSI_CHAP_SECRET_STORAGE];\r
+  CHAR16  ReverseCHAPName[ISCSI_CHAP_NAME_STORAGE];\r
+  CHAR16  ReverseCHAPSecret[ISCSI_CHAP_SECRET_STORAGE];\r
+\r
+  BOOLEAN MutualRequired;\r
+  UINT8   Padding2;\r
+  CHAR16  KerberosUserName[KERBEROS_NAME_MAX_LEN];\r
+  CHAR16  KerberosUserSecret[KERBEROS_SECRET_STORAGE];\r
+  CHAR16  KerberosKDCName[KERBEROS_NAME_MAX_LEN];\r
+  CHAR16  KerberosKDCRealm[KERBEROS_NAME_MAX_LEN];\r
+  CHAR16  KerberosKDCIp[IP_STR_MAX_SIZE];\r
+  UINT16  KerberosKDCPort;\r
+\r
+  UINT8   DynamicOrderedList[ISCSI_MAX_ATTEMPTS_NUM];\r
+  UINT8   DeleteAttemptList[ISCSI_MAX_ATTEMPTS_NUM];\r
+\r
+  CHAR16  IsId[ISID_CONFIGURABLE_STORAGE];\r
+} ISCSI_CONFIG_IFR_NVDATA;\r
+#pragma pack()\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni b/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni
new file mode 100644 (file)
index 0000000..8df9cef
Binary files /dev/null and b/NetworkPkg/IScsiDxe/IScsiConfigStrings.uni differ
diff --git a/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr b/NetworkPkg/IScsiDxe/IScsiConfigVfr.vfr
new file mode 100644 (file)
index 0000000..94b00ee
--- /dev/null
@@ -0,0 +1,429 @@
+/** @file\r
+  VFR file used by the iSCSI configuration component.\r
+  \r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiConfigNVDataStruc.h"\r
+\r
+#define EFI_NETWORK_DEVICE_CLASS  0x04\r
+\r
+formset\r
+  guid     = ISCSI_CONFIG_GUID,\r
+  title    = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_TITLE),\r
+  help     = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_HELP),\r
+  class    = EFI_NETWORK_DEVICE_CLASS,\r
+  subclass = 0x03,\r
+\r
+  varstore ISCSI_CONFIG_IFR_NVDATA,\r
+    varid = CONFIGURATION_VARSTORE_ID,\r
+    name = ISCSI_CONFIG_IFR_NVDATA,\r
+    guid = ISCSI_CONFIG_GUID;\r
+\r
+  form formid = FORMID_MAIN_FORM,\r
+    title  = STRING_TOKEN(STR_ISCSI_MAIN_FORM_TITLE);\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.InitiatorName,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME),\r
+            help    = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_INITIATOR_NAME,\r
+            minsize = ISCSI_NAME_IFR_MIN_SIZE,\r
+            maxsize = ISCSI_NAME_IFR_MAX_SIZE,\r
+    endstring;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    goto FORMID_MAC_FORM,\r
+         prompt = STRING_TOKEN(STR_ADD_ATTEMPT_ENTRY),\r
+         help   = STRING_TOKEN(STR_ADD_ATTEMPT_ENTRY),\r
+         flags  = INTERACTIVE,\r
+         key    = KEY_ADD_ATTEMPT;\r
+\r
+    label ATTEMPT_ENTRY_LABEL;\r
+    label LABEL_END;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    goto FORMID_DELETE_FORM,\r
+      prompt = STRING_TOKEN (STR_DEL_ATTEMPT_ENTRY),\r
+      help   = STRING_TOKEN (STR_DEL_ATTEMPT_ENTRY_HELP),\r
+      flags  = INTERACTIVE,\r
+      key    = KEY_DELETE_ATTEMPT;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    goto FORMID_ORDER_FORM,\r
+      prompt = STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),\r
+      help   = STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),\r
+      flags  = INTERACTIVE,\r
+      key    = KEY_ORDER_ATTEMPT_CONFIG;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+  endform;\r
+\r
+  form formid = FORMID_MAC_FORM,\r
+    title  = STRING_TOKEN(STR_ISCSI_MAC_FORM_TITLE);\r
+\r
+    label MAC_ENTRY_LABEL;\r
+    label LABEL_END;\r
+\r
+  endform;\r
+\r
+  form formid = FORMID_ORDER_FORM,\r
+    title  = STRING_TOKEN(STR_ORDER_ATTEMPT_ENTRY);\r
+\r
+    label ORDER_ENTRY_LABEL;\r
+    label LABEL_END;\r
+\r
+    goto FORMID_MAIN_FORM,\r
+      prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+      help   = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+      flags  = INTERACTIVE,\r
+      key    = KEY_SAVE_ORDER_CHANGES;\r
+      \r
+    goto FORMID_MAIN_FORM,\r
+      prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+      help   = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+      flags  = INTERACTIVE,\r
+      key    = KEY_IGNORE_ORDER_CHANGES;\r
+\r
+  endform;\r
+\r
+  form formid = FORMID_DELETE_FORM,\r
+    title  = STRING_TOKEN(STR_DEL_ATTEMPT_ENTRY);\r
+\r
+    label DELETE_ENTRY_LABEL;\r
+    label LABEL_END;\r
+\r
+    goto FORMID_MAIN_FORM,\r
+      prompt = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+      help   = STRING_TOKEN (STR_SAVE_AND_EXIT),\r
+      flags  = INTERACTIVE,\r
+      key    = KEY_SAVE_DELETE_ATTEMPT;\r
+      \r
+    goto FORMID_MAIN_FORM,\r
+      prompt = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+      help   = STRING_TOKEN (STR_NO_SAVE_AND_EXIT),\r
+      flags  = INTERACTIVE,\r
+      key    = KEY_IGNORE_DELETE_ATTEMPT;\r
+      \r
+  endform;    \r
+\r
+  form formid = FORMID_ATTEMPT_FORM,\r
+    title  = STRING_TOKEN(STR_ISCSI_ATTEMPT_FORM_TITLE);\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.AttemptName,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_ATTEMPT_NAME),\r
+            help    = STRING_TOKEN(STR_ISCSI_ATTEMPT_NAME_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_ATTEMPT_NAME,\r
+            minsize = 0,\r
+            maxsize = ATTEMPT_NAME_MAX_SIZE,\r
+    endstring;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    oneof varid  = ISCSI_CONFIG_IFR_NVDATA.Enabled,\r
+          prompt = STRING_TOKEN(STR_ISCSI_MODE_PROMPT),\r
+          help   = STRING_TOKEN(STR_ISCSI_MODE_HELP),\r
+          option text = STRING_TOKEN(STR_ISCSI_MODE_DISABLED),         value = ISCSI_DISABLED,         flags = DEFAULT;\r
+          option text = STRING_TOKEN(STR_ISCSI_MODE_ENABLED),          value = ISCSI_ENABLED,          flags = 0;\r
+          option text = STRING_TOKEN(STR_ISCSI_MODE_ENABLED_FOR_MPIO), value = ISCSI_ENABLED_FOR_MPIO, flags = 0;\r
+    endoneof;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    oneof varid  = ISCSI_CONFIG_IFR_NVDATA.IpMode,\r
+          questionid = KEY_IP_MODE,\r
+          prompt = STRING_TOKEN(STR_IP_MODE_PROMPT),\r
+          help   = STRING_TOKEN(STR_IP_MODE_HELP),\r
+          option text = STRING_TOKEN(STR_IP_MODE_IP4),        value = IP_MODE_IP4,        flags = INTERACTIVE;\r
+          option text = STRING_TOKEN(STR_IP_MODE_IP6),        value = IP_MODE_IP6,        flags = INTERACTIVE;\r
+          option text = STRING_TOKEN(STR_IP_MODE_AUTOCONFIG), value = IP_MODE_AUTOCONFIG, flags = INTERACTIVE;\r
+    endoneof;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    numeric varid   = ISCSI_CONFIG_IFR_NVDATA.ConnectRetryCount,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_CONFIG_RETRY),\r
+            help    = STRING_TOKEN(STR_ISCSI_CONFIG_RETRY_HELP),\r
+            flags   = 0,\r
+            minimum = CONNECT_MIN_RETRY,\r
+            maximum = CONNECT_MAX_RETRY,\r
+            step    = 0,\r
+    endnumeric;  \r
+    \r
+    numeric varid   = ISCSI_CONFIG_IFR_NVDATA.ConnectTimeout,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_CONFIG_TIMEOUT),\r
+            help    = STRING_TOKEN(STR_ISCSI_CONFIG_TIMEOUT_HELP),\r
+            flags   = 0,\r
+            minimum = CONNECT_MIN_TIMEOUT,\r
+            maximum = CONNECT_MAX_TIMEOUT,\r
+            step    = 0,\r
+            default = CONNECT_DEFAULT_TIMEOUT,\r
+    endnumeric;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.IsId, \r
+            prompt  = STRING_TOKEN(STR_ISCSI_CONFIG_ISID),\r
+            help    = STRING_TOKEN(STR_ISCSI_CONFIG_ISID_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_CONFIG_ISID,\r
+            minsize = ISID_CONFIGURABLE_MIN_LEN,\r
+            maxsize = ISID_CONFIGURABLE_MAX_LEN,\r
+    endstring;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;\r
+    checkbox varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp,\r
+            prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),\r
+            help   = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),\r
+            flags  = INTERACTIVE,\r
+            key    = KEY_DHCP_ENABLE,\r
+    endcheckbox;\r
+    endif;\r
+\r
+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x01 OR\r
+               ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_IP6 OR \r
+               ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.LocalIp,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_LOCAL_IP_ADDRESS),\r
+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_LOCAL_IP,\r
+            minsize = IP4_MIN_SIZE,\r
+            maxsize = IP4_MAX_SIZE,\r
+    endstring;\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.SubnetMask,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_LOCAL_MASK),\r
+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_SUBNET_MASK,\r
+            minsize = IP4_MIN_SIZE,\r
+            maxsize = IP4_MAX_SIZE,\r
+    endstring;\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.Gateway,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_LOCAL_GATEWAY),\r
+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_GATE_WAY,\r
+            minsize = IP4_MIN_SIZE,\r
+            maxsize = IP4_MAX_SIZE,\r
+    endstring;\r
+    \r
+    endif;\r
+\r
+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+    endif;     \r
+\r
+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG OR\r
+               ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x00;\r
+    checkbox varid  = ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp,\r
+             prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),\r
+             help   = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),\r
+             flags  = 0,\r
+    endcheckbox;\r
+    endif;\r
+\r
+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG OR\r
+               ideqval ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp == 0x01;\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.TargetName,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_TARGET_NAME),\r
+            help    = STRING_TOKEN(STR_ISCSI_TARGET_NAME_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_TARGET_NAME,\r
+            minsize = ISCSI_NAME_IFR_MIN_SIZE,\r
+            maxsize = ISCSI_NAME_IFR_MAX_SIZE,\r
+    endstring;\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.TargetIp,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_TARGET_IP_ADDRESS),\r
+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_TARGET_IP,\r
+            minsize = IP_MIN_SIZE,\r
+            maxsize = IP_MAX_SIZE,\r
+    endstring;\r
+\r
+    numeric varid   = ISCSI_CONFIG_IFR_NVDATA.TargetPort,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_TARGET_PORT),\r
+            help    = STRING_TOKEN(STR_ISCSI_TARGET_PORT),\r
+            flags   = 0,\r
+            minimum = TARGET_PORT_MIN_NUM,\r
+            maximum = TARGET_PORT_MAX_NUM,\r
+            step    = 0,\r
+    endnumeric;\r
+\r
+    string varid    = ISCSI_CONFIG_IFR_NVDATA.BootLun,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_BOOT_LUN),\r
+            help    = STRING_TOKEN(STR_ISCSI_BOOT_LUN_HELP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_BOOT_LUN,\r
+            minsize = LUN_MIN_SIZE,\r
+            maxsize = LUN_MAX_SIZE,\r
+    endstring;\r
+    \r
+    endif;\r
+\r
+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.IpMode == IP_MODE_AUTOCONFIG;\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+    endif; \r
+\r
+    oneof varid  = ISCSI_CONFIG_IFR_NVDATA.AuthenticationType,\r
+          questionid = KEY_AUTH_TYPE,      \r
+          prompt = STRING_TOKEN(STR_AUTHEN_TYPE_PROMPT),\r
+          help   = STRING_TOKEN(STR_AUTHEN_TYPE_HELP),\r
+          option text = STRING_TOKEN(STR_AUTHEN_TYPE_CHAP),     value = ISCSI_AUTH_TYPE_CHAP, flags = 0;\r
+          option text = STRING_TOKEN(STR_AUTHEN_TYPE_NONE),     value = ISCSI_AUTH_TYPE_NONE, flags = DEFAULT;\r
+    endoneof;\r
+\r
+    suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP;    \r
+    oneof varid  = ISCSI_CONFIG_IFR_NVDATA.CHAPType,\r
+          prompt = STRING_TOKEN(STR_CHAP_TYPE_PROMPT),\r
+          help   = STRING_TOKEN(STR_CHAP_TYPE_HELP),\r
+          option text = STRING_TOKEN(STR_CHAP_TYPE_UNI),    value = ISCSI_CHAP_UNI,    flags = 0;\r
+          option text = STRING_TOKEN(STR_CHAP_TYPE_MUTUAL), value = ISCSI_CHAP_MUTUAL, flags = DEFAULT;\r
+    endoneof;\r
+    endif;\r
+\r
+    suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP;\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.CHAPName,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_CHAP_NAME),\r
+            help    = STRING_TOKEN(STR_ISCSI_CHAP_NAME),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_CHAP_NAME,\r
+            minsize = 0,\r
+            maxsize = ISCSI_CHAP_NAME_MAX_LEN,\r
+    endstring;\r
+\r
+    string  varid    = ISCSI_CONFIG_IFR_NVDATA.CHAPSecret,\r
+            prompt   = STRING_TOKEN(STR_ISCSI_CHAP_SECRET),\r
+            help     = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),\r
+            flags    = INTERACTIVE,\r
+            key      = KEY_CHAP_SECRET,\r
+            minsize  = ISCSI_CHAP_SECRET_MIN_LEN,\r
+            maxsize  = ISCSI_CHAP_SECRET_MAX_LEN,\r
+    endstring;\r
+\r
+    endif;\r
+\r
+    suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_CHAP OR\r
+               NOT ideqval ISCSI_CONFIG_IFR_NVDATA.CHAPType == ISCSI_CHAP_MUTUAL;\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPName,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),\r
+            help    = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_REVERSE_CHAP_NAME,\r
+            minsize = 0,\r
+            maxsize = ISCSI_CHAP_NAME_MAX_LEN,\r
+    endstring;\r
+\r
+    string  varid    = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPSecret,\r
+            prompt   = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_SECRET),\r
+            help     = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),\r
+            flags    = INTERACTIVE,\r
+            key      = KEY_REVERSE_CHAP_SECRET,\r
+            minsize  = ISCSI_CHAP_SECRET_MIN_LEN,\r
+            maxsize  = ISCSI_CHAP_SECRET_MAX_LEN,\r
+    endstring;\r
+\r
+    endif;\r
+\r
+    suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.AuthenticationType == ISCSI_AUTH_TYPE_KRB;\r
+\r
+    checkbox varid = ISCSI_CONFIG_IFR_NVDATA.MutualRequired,\r
+            prompt = STRING_TOKEN(STR_ISCSI_MUTUAL_REQUIRED),\r
+            help   = STRING_TOKEN(STR_ISCSI_MUTUAL_REQUIRED_HELP),\r
+            flags  = 0,\r
+    endcheckbox;\r
+    \r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.KerberosUserName,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_NAME),\r
+            help    = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_NAME),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_KERBEROS_USER_NAME,\r
+            minsize = 0,\r
+            maxsize = KERBEROS_NAME_MAX_LEN,\r
+    endstring;\r
+\r
+    string  varid    = ISCSI_CONFIG_IFR_NVDATA.KerberosUserSecret,\r
+            prompt   = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_SECRET),\r
+            help     = STRING_TOKEN(STR_ISCSI_KERBEROS_USER_SECRET),\r
+            flags    = INTERACTIVE,\r
+            key      = KEY_KERBEROS_USER_SECRET,\r
+            minsize  = KERBEROS_SECRET_MIN_LEN,\r
+            maxsize  = KERBEROS_SECRET_MAX_LEN,\r
+    endstring;\r
+    \r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCName,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_NAME),\r
+            help    = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_NAME),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_KERBEROS_KDC_NAME,\r
+            minsize = 0,\r
+            maxsize = KERBEROS_NAME_MAX_LEN,\r
+    endstring;\r
+\r
+    string  varid    = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCRealm,\r
+            prompt   = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_REALM),\r
+            help     = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_REALM),\r
+            flags    = INTERACTIVE,\r
+            key      = KEY_KERBEROS_KDC_REALM,\r
+            minsize  = 0,\r
+            maxsize  = KERBEROS_NAME_MAX_LEN,\r
+    endstring;\r
+\r
+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCIp,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_IP),\r
+            help    = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_IP),\r
+            flags   = INTERACTIVE,\r
+            key     = KEY_KERBEROS_KDC_IP_ADDR,\r
+            minsize = IP_MIN_SIZE,\r
+            maxsize = IP_MAX_SIZE,\r
+    endstring;\r
+\r
+    numeric varid   = ISCSI_CONFIG_IFR_NVDATA.KerberosKDCPort,\r
+            prompt  = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_PORT),\r
+            help    = STRING_TOKEN(STR_ISCSI_KERBEROS_KDC_PORT),\r
+            flags   = 0,\r
+            minimum = KERBEROS_KDC_PORT_MIN_NUM,\r
+            maximum = KERBEROS_KDC_PORT_MAX_NUM,\r
+            step    = 0,\r
+    endnumeric;\r
+    \r
+    endif;\r
+\r
+    subtitle text = STRING_TOKEN(STR_NULL);\r
+\r
+    goto FORMID_ATTEMPT_FORM,\r
+    prompt = STRING_TOKEN (STR_SAVE_CHANGES),\r
+    help   = STRING_TOKEN (STR_SAVE_CHANGES),\r
+    flags  = INTERACTIVE,\r
+    key    = KEY_SAVE_ATTEMPT_CONFIG;\r
+\r
+    goto FORMID_MAIN_FORM,\r
+    prompt = STRING_TOKEN (STR_RETURN_MAIN_FORM),\r
+    help   = STRING_TOKEN (STR_RETURN_MAIN_FORM),\r
+    flags  = 0;\r
+\r
+  endform;\r
+\r
+endformset;\r
+\r
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp.c b/NetworkPkg/IScsiDxe/IScsiDhcp.c
new file mode 100644 (file)
index 0000000..3706256
--- /dev/null
@@ -0,0 +1,498 @@
+/** @file\r
+  iSCSI DHCP4 related configuration routines.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+\r
+/**\r
+  Extract the Root Path option and get the required target information.\r
+\r
+  @param[in]        RootPath         The RootPath.\r
+  @param[in]        Length           Length of the RootPath option payload.\r
+  @param[in, out]   ConfigData       The iSCSI attempt configuration data read\r
+                                     from a nonvolatile device.\r
+\r
+  @retval EFI_SUCCESS           All required information is extracted from the RootPath option.\r
+  @retval EFI_NOT_FOUND         The RootPath is not an iSCSI RootPath.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_INVALID_PARAMETER The RootPath is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiDhcpExtractRootPath (\r
+  IN      CHAR8                        *RootPath,\r
+  IN      UINT8                        Length,\r
+  IN OUT  ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINT8                       IScsiRootPathIdLen;\r
+  CHAR8                       *TmpStr;\r
+  ISCSI_ROOT_PATH_FIELD       Fields[RP_FIELD_IDX_MAX];\r
+  ISCSI_ROOT_PATH_FIELD       *Field;\r
+  UINT32                      FieldIndex;\r
+  UINT8                       Index;\r
+  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;\r
+  EFI_IP_ADDRESS              Ip;\r
+  UINT8                       IpMode;\r
+\r
+  ConfigNvData = &ConfigData->SessionConfigData;\r
+\r
+  //\r
+  // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>\r
+  //\r
+  IScsiRootPathIdLen = (UINT8) AsciiStrLen (ISCSI_ROOT_PATH_ID);\r
+\r
+  if ((Length <= IScsiRootPathIdLen) || (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // Skip the iSCSI RootPath ID "iscsi:".\r
+  //\r
+  RootPath += IScsiRootPathIdLen;\r
+  Length  = (UINT8) (Length - IScsiRootPathIdLen);\r
+\r
+  TmpStr  = (CHAR8 *) AllocatePool (Length + 1);\r
+  if (TmpStr == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  CopyMem (TmpStr, RootPath, Length);\r
+  TmpStr[Length]  = '\0';\r
+\r
+  Index           = 0;\r
+  FieldIndex      = RP_FIELD_IDX_SERVERNAME;\r
+  ZeroMem (&Fields[0], sizeof (Fields));\r
+\r
+  //\r
+  // Extract the fields in the Root Path option string.\r
+  //\r
+  for (FieldIndex = RP_FIELD_IDX_SERVERNAME; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {\r
+    if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
+      Fields[FieldIndex].Str = &TmpStr[Index];\r
+    }\r
+\r
+    while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {\r
+      Index++;\r
+    }\r
+\r
+    if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
+      if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {\r
+        TmpStr[Index] = '\0';\r
+        Index++;\r
+      }\r
+\r
+      if (Fields[FieldIndex].Str != NULL) {\r
+        Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);\r
+      }\r
+    }\r
+  }\r
+\r
+  if (FieldIndex != RP_FIELD_IDX_MAX) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||\r
+      (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||\r
+      (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)\r
+      ) {\r
+\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Get the IP address of the target.\r
+  //\r
+  Field   = &Fields[RP_FIELD_IDX_SERVERNAME];\r
+\r
+  if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {\r
+    IpMode = ConfigNvData->IpMode;\r
+  } else {\r
+    IpMode = ConfigData->AutoConfigureMode;\r
+  }\r
+\r
+  Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);\r
+  CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Check the protocol type.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_PROTOCOL];\r
+  if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Get the port of the iSCSI target.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_PORT];\r
+  if (Field->Str != NULL) {\r
+    ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);\r
+  } else {\r
+    ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;\r
+  }\r
+  //\r
+  // Get the LUN.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_LUN];\r
+  if (Field->Str != NULL) {\r
+    Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  } else {\r
+    ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));\r
+  }\r
+  //\r
+  // Get the target iSCSI Name.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_TARGETNAME];\r
+\r
+  if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Validate the iSCSI name.\r
+  //\r
+  Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  AsciiStrCpy (ConfigNvData->TargetName, Field->Str);\r
+\r
+ON_EXIT:\r
+\r
+  FreePool (TmpStr);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  The callback function registerd to the DHCP4 instance that is used to select\r
+  the qualified DHCP OFFER.\r
+  \r
+  @param[in]  This         The DHCP4 protocol.\r
+  @param[in]  Context      The context set when configuring the DHCP4 protocol.\r
+  @param[in]  CurrentState The current state of the DHCP4 protocol.\r
+  @param[in]  Dhcp4Event   The event occurs in the current state.\r
+  @param[in]  Packet       The DHCP packet that is to be sent or was already received. \r
+  @param[out] NewPacket    The packet used to replace the above Packet.\r
+  \r
+  @retval EFI_SUCCESS      Either the DHCP OFFER is qualified or we're not intereseted\r
+                           in the Dhcp4Event.\r
+  @retval EFI_NOT_READY    The DHCP OFFER packet doesn't match our requirements.\r
+  @retval Others           Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDhcpSelectOffer (\r
+  IN  EFI_DHCP4_PROTOCOL  *This,\r
+  IN  VOID                *Context,\r
+  IN  EFI_DHCP4_STATE     CurrentState,\r
+  IN  EFI_DHCP4_EVENT     Dhcp4Event,\r
+  IN  EFI_DHCP4_PACKET    *Packet, OPTIONAL\r
+  OUT EFI_DHCP4_PACKET    **NewPacket OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  UINT32                  OptionCount;\r
+  EFI_DHCP4_PACKET_OPTION **OptionList;\r
+  UINT32                  Index;\r
+\r
+  if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  OptionCount = 0;\r
+\r
+  Status      = This->Parse (This, Packet, &OptionCount, NULL);\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));\r
+  if (OptionList == NULL) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  Status = This->Parse (This, Packet, &OptionCount, OptionList);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (OptionList);\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  for (Index = 0; Index < OptionCount; Index++) {\r
+    if (OptionList[Index]->OpCode != DHCP4_TAG_ROOT_PATH) {\r
+      continue;\r
+    }\r
+\r
+    Status = IScsiDhcpExtractRootPath (\r
+               (CHAR8 *) &OptionList[Index]->Data[0],\r
+               OptionList[Index]->Length,\r
+               (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context\r
+               );\r
+\r
+    break;\r
+  }\r
+\r
+  if ((Index == OptionCount)) {\r
+    Status = EFI_NOT_READY;\r
+  }\r
+\r
+  FreePool (OptionList);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Parse the DHCP ACK to get the address configuration and DNS information.\r
+\r
+  @param[in]       Dhcp4        The DHCP4 protocol.\r
+  @param[in, out]  ConfigData   The session configuration data.\r
+\r
+  @retval EFI_SUCCESS           The DNS information is got from the DHCP ACK.\r
+  @retval EFI_NO_MAPPING        DHCP failed to acquire address and other information.\r
+  @retval EFI_INVALID_PARAMETER The DHCP ACK's DNS option is malformatted.\r
+  @retval EFI_DEVICE_ERROR      Other errors as indicated.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiParseDhcpAck (\r
+  IN     EFI_DHCP4_PROTOCOL          *Dhcp4,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  EFI_DHCP4_MODE_DATA           Dhcp4ModeData;\r
+  UINT32                        OptionCount;\r
+  EFI_DHCP4_PACKET_OPTION       **OptionList;\r
+  UINT32                        Index;\r
+  ISCSI_SESSION_CONFIG_NVDATA   *NvData;\r
+\r
+  Status = Dhcp4->GetModeData (Dhcp4, &Dhcp4ModeData);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (Dhcp4ModeData.State != Dhcp4Bound) {\r
+    return EFI_NO_MAPPING;\r
+  }\r
+\r
+  NvData = &ConfigData->SessionConfigData;\r
+\r
+  CopyMem (&NvData->LocalIp, &Dhcp4ModeData.ClientAddress, sizeof (EFI_IPv4_ADDRESS));\r
+  CopyMem (&NvData->SubnetMask, &Dhcp4ModeData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+  CopyMem (&NvData->Gateway, &Dhcp4ModeData.RouterAddress, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+  OptionCount = 0;\r
+  OptionList  = NULL;\r
+\r
+  Status      = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));\r
+  if (OptionList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = Dhcp4->Parse (Dhcp4, Dhcp4ModeData.ReplyPacket, &OptionCount, OptionList);\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (OptionList);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  for (Index = 0; Index < OptionCount; Index++) {\r
+    //\r
+    // Get DNS server addresses and DHCP server address from this offer.\r
+    //\r
+    if (OptionList[Index]->OpCode == DHCP4_TAG_DNS) {\r
+\r
+      if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {\r
+        Status = EFI_INVALID_PARAMETER;\r
+        break;\r
+      }\r
+      //\r
+      // Primary DNS server address.\r
+      //\r
+      CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));\r
+\r
+      if (OptionList[Index]->Length > 4) {\r
+        //\r
+        // Secondary DNS server address.\r
+        //\r
+        CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[4], sizeof (EFI_IPv4_ADDRESS));\r
+      }\r
+    } else if (OptionList[Index]->OpCode == DHCP4_TAG_SERVER_ID) {\r
+      if (OptionList[Index]->Length != 4) {\r
+        Status = EFI_INVALID_PARAMETER;\r
+        break;\r
+      }\r
+\r
+      CopyMem (&ConfigData->DhcpServer, &OptionList[Index]->Data[0], sizeof (EFI_IPv4_ADDRESS));\r
+    }\r
+  }\r
+\r
+  FreePool (OptionList);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Parse the DHCP ACK to get the address configuration and DNS information.\r
+\r
+  @param[in]       Image            The handle of the driver image.\r
+  @param[in]       Controller       The handle of the controller.\r
+  @param[in, out]  ConfigData       The attempt configuration data.\r
+\r
+  @retval EFI_SUCCESS           The DNS information is got from the DHCP ACK.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_NO_MEDIA          There was a media error.\r
+  @retval Others                Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiDoDhcp (\r
+  IN     EFI_HANDLE                  Image,\r
+  IN     EFI_HANDLE                  Controller,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  )\r
+{\r
+  EFI_HANDLE                    Dhcp4Handle;\r
+  EFI_DHCP4_PROTOCOL            *Dhcp4;\r
+  EFI_STATUS                    Status;\r
+  EFI_DHCP4_PACKET_OPTION       *ParaList;\r
+  EFI_DHCP4_CONFIG_DATA         Dhcp4ConfigData;\r
+  ISCSI_SESSION_CONFIG_NVDATA   *NvData;\r
+  BOOLEAN                       MediaPresent;\r
+\r
+  Dhcp4Handle = NULL;\r
+  Dhcp4       = NULL;\r
+  ParaList    = NULL;\r
+\r
+  //\r
+  // Check media status before doing DHCP.\r
+  //\r
+  MediaPresent = TRUE;\r
+  NetLibDetectMedia (Controller, &MediaPresent);\r
+  if (!MediaPresent) {\r
+    return EFI_NO_MEDIA;\r
+  }\r
+\r
+  //\r
+  // Create a DHCP4 child instance and get the protocol.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             Controller,\r
+             Image,\r
+             &gEfiDhcp4ServiceBindingProtocolGuid,\r
+             &Dhcp4Handle\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Dhcp4Handle,\r
+                  &gEfiDhcp4ProtocolGuid,\r
+                  (VOID **) &Dhcp4,\r
+                  Image,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  NvData   = &ConfigData->SessionConfigData;\r
+\r
+  ParaList = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 3);\r
+  if (ParaList == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Ask the server to reply with Netmask, Router, DNS, and RootPath options.\r
+  //\r
+  ParaList->OpCode  = DHCP4_TAG_PARA_LIST;\r
+  ParaList->Length  = (UINT8) (NvData->TargetInfoFromDhcp ? 4 : 3);\r
+  ParaList->Data[0] = DHCP4_TAG_NETMASK;\r
+  ParaList->Data[1] = DHCP4_TAG_ROUTER;\r
+  ParaList->Data[2] = DHCP4_TAG_DNS;\r
+  ParaList->Data[3] = DHCP4_TAG_ROOT_PATH;\r
+\r
+  ZeroMem (&Dhcp4ConfigData, sizeof (EFI_DHCP4_CONFIG_DATA));\r
+  Dhcp4ConfigData.OptionCount = 1;\r
+  Dhcp4ConfigData.OptionList  = &ParaList;\r
+\r
+  if (NvData->TargetInfoFromDhcp) {\r
+    //\r
+    // Use callback to select an offer that contains target information.\r
+    //\r
+    Dhcp4ConfigData.Dhcp4Callback   = IScsiDhcpSelectOffer;\r
+    Dhcp4ConfigData.CallbackContext = ConfigData;\r
+  }\r
+\r
+  Status = Dhcp4->Configure (Dhcp4, &Dhcp4ConfigData);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Status = Dhcp4->Start (Dhcp4, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Parse the ACK to get required information.\r
+  //\r
+  Status = IScsiParseDhcpAck (Dhcp4, ConfigData);\r
+\r
+ON_EXIT:\r
+\r
+  if (ParaList != NULL) {\r
+    FreePool (ParaList);\r
+  }\r
+\r
+  if (Dhcp4 != NULL) {\r
+    Dhcp4->Stop (Dhcp4);\r
+    Dhcp4->Configure (Dhcp4, NULL);\r
+\r
+    gBS->CloseProtocol (\r
+           Dhcp4Handle,\r
+           &gEfiDhcp4ProtocolGuid,\r
+           Image,\r
+           Controller\r
+           );\r
+  }\r
+\r
+  NetLibDestroyServiceChild (\r
+    Controller,\r
+    Image,\r
+    &gEfiDhcp4ServiceBindingProtocolGuid,\r
+    Dhcp4Handle\r
+    );\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp.h b/NetworkPkg/IScsiDxe/IScsiDhcp.h
new file mode 100644 (file)
index 0000000..3165100
--- /dev/null
@@ -0,0 +1,62 @@
+/** @file\r
+  The head file of iSCSI DHCP4 related configuration routines.\r
+\r
+Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_DHCP_H_\r
+#define _ISCSI_DHCP_H_\r
+\r
+\r
+#define DHCP4_TAG_PARA_LIST             55\r
+#define DHCP4_TAG_NETMASK               1\r
+#define DHCP4_TAG_ROUTER                3\r
+#define DHCP4_TAG_DNS                   6\r
+#define DHCP4_TAG_SERVER_ID             54\r
+#define DHCP4_TAG_ROOT_PATH             17\r
+#define ISCSI_ROOT_PATH_ID              "iscsi:"\r
+#define ISCSI_ROOT_PATH_FIELD_DELIMITER ':'\r
+\r
+#define RP_FIELD_IDX_SERVERNAME         0\r
+#define RP_FIELD_IDX_PROTOCOL           1\r
+#define RP_FIELD_IDX_PORT               2\r
+#define RP_FIELD_IDX_LUN                3\r
+#define RP_FIELD_IDX_TARGETNAME         4\r
+#define RP_FIELD_IDX_MAX                5\r
+\r
+typedef struct _ISCSI_ATTEMPT_CONFIG_NVDATA ISCSI_ATTEMPT_CONFIG_NVDATA;\r
+\r
+typedef struct _ISCSI_ROOT_PATH_FIELD {\r
+  CHAR8 *Str;\r
+  UINT8 Len;\r
+} ISCSI_ROOT_PATH_FIELD;\r
+\r
+/**\r
+  Parse the DHCP ACK to get the address configuration and DNS information.\r
+\r
+  @param[in]       Image            The handle of the driver image.\r
+  @param[in]       Controller       The handle of the controller.\r
+  @param[in, out]  ConfigData       The attempt configuration data.\r
+\r
+  @retval EFI_SUCCESS           The DNS information is got from the DHCP ACK.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_NO_MEDIA          There was a media error.\r
+  @retval Others                Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiDoDhcp (\r
+  IN     EFI_HANDLE                  Image,\r
+  IN     EFI_HANDLE                  Controller,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp6.c b/NetworkPkg/IScsiDxe/IScsiDhcp6.c
new file mode 100644 (file)
index 0000000..2bf102b
--- /dev/null
@@ -0,0 +1,505 @@
+/** @file\r
+  iSCSI DHCP6 related configuration routines.\r
+\r
+Copyright (c) 2009 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+\r
+/**\r
+  Extract the Root Path option and get the required target information from\r
+  Boot File Uniform Resource Locator (URL) Option.\r
+\r
+  @param[in]       RootPath      The RootPath string.\r
+  @param[in]       Length        Length of the RootPath option payload.\r
+  @param[in, out]  ConfigData    The iSCSI session configuration data read from\r
+                                 nonvolatile device.\r
+\r
+  @retval EFI_SUCCESS            All required information is extracted from the\r
+                                 RootPath option.\r
+  @retval EFI_NOT_FOUND          The RootPath is not an iSCSI RootPath.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.\r
+  @retval EFI_INVALID_PARAMETER  The RootPath is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiDhcp6ExtractRootPath (\r
+  IN     CHAR8                        *RootPath,\r
+  IN     UINT16                       Length,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINT16                      IScsiRootPathIdLen;\r
+  CHAR8                       *TmpStr;\r
+  ISCSI_ROOT_PATH_FIELD       Fields[RP_FIELD_IDX_MAX];\r
+  ISCSI_ROOT_PATH_FIELD       *Field;\r
+  UINT32                      FieldIndex;\r
+  UINT8                       Index;\r
+  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;\r
+  EFI_IP_ADDRESS              Ip;\r
+  UINT8                       IpMode;  \r
+\r
+  ConfigNvData = &ConfigData->SessionConfigData;\r
+\r
+  //\r
+  // "iscsi:"<servername>":"<protocol>":"<port>":"<LUN>":"<targetname>\r
+  //\r
+  IScsiRootPathIdLen = (UINT16) AsciiStrLen (ISCSI_ROOT_PATH_ID);\r
+\r
+  if ((Length <= IScsiRootPathIdLen) ||\r
+      (CompareMem (RootPath, ISCSI_ROOT_PATH_ID, IScsiRootPathIdLen) != 0)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  //\r
+  // Skip the iSCSI RootPath ID "iscsi:".\r
+  //\r
+  RootPath = RootPath + IScsiRootPathIdLen;\r
+  Length   = (UINT16) (Length - IScsiRootPathIdLen);\r
+\r
+  TmpStr   = (CHAR8 *) AllocatePool (Length + 1);\r
+  if (TmpStr == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  CopyMem (TmpStr, RootPath, Length);\r
+  TmpStr[Length]  = '\0';\r
+\r
+  Index           = 0;\r
+  FieldIndex      = 0;\r
+  ZeroMem (&Fields[0], sizeof (Fields));\r
+\r
+  //\r
+  // Extract SERVERNAME field in the Root Path option.\r
+  //\r
+  if (TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_START_DELIMITER) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  } else {\r
+    Index++;\r
+  }\r
+\r
+  Fields[RP_FIELD_IDX_SERVERNAME].Str = &TmpStr[Index];\r
+\r
+  while ((TmpStr[Index] != ISCSI_ROOT_PATH_ADDR_END_DELIMITER) && (Index < Length)) {\r
+    Index++;\r
+  }\r
+\r
+  //\r
+  // Skip ']' and ':'.\r
+  //\r
+  TmpStr[Index] = '\0';\r
+  Index += 2;\r
+\r
+  Fields[RP_FIELD_IDX_SERVERNAME].Len = (UINT8) AsciiStrLen (Fields[RP_FIELD_IDX_SERVERNAME].Str);\r
+\r
+  //\r
+  // Extract others fields in the Root Path option string.\r
+  //\r
+  for (FieldIndex = 1; (FieldIndex < RP_FIELD_IDX_MAX) && (Index < Length); FieldIndex++) {\r
+\r
+    if (TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
+      Fields[FieldIndex].Str = &TmpStr[Index];\r
+    }\r
+\r
+    while ((TmpStr[Index] != ISCSI_ROOT_PATH_FIELD_DELIMITER) && (Index < Length)) {\r
+      Index++;\r
+    }\r
+\r
+    if (TmpStr[Index] == ISCSI_ROOT_PATH_FIELD_DELIMITER) {\r
+      if (FieldIndex != RP_FIELD_IDX_TARGETNAME) {\r
+        TmpStr[Index] = '\0';\r
+        Index++;\r
+      }\r
+\r
+      if (Fields[FieldIndex].Str != NULL) {\r
+        Fields[FieldIndex].Len = (UINT8) AsciiStrLen (Fields[FieldIndex].Str);\r
+      }\r
+    }\r
+  }\r
+\r
+  if (FieldIndex != RP_FIELD_IDX_MAX) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if ((Fields[RP_FIELD_IDX_SERVERNAME].Str == NULL) ||\r
+      (Fields[RP_FIELD_IDX_TARGETNAME].Str == NULL) ||\r
+      (Fields[RP_FIELD_IDX_PROTOCOL].Len > 1)\r
+      ) {\r
+\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Get the IP address of the target.\r
+  //\r
+  Field   = &Fields[RP_FIELD_IDX_SERVERNAME];  \r
+  if (ConfigNvData->IpMode < IP_MODE_AUTOCONFIG) {\r
+    IpMode = ConfigNvData->IpMode;\r
+  } else {\r
+    IpMode = ConfigData->AutoConfigureMode;\r
+  }\r
+\r
+  Status = IScsiAsciiStrToIp (Field->Str, IpMode, &Ip);\r
+  CopyMem (&ConfigNvData->TargetIp, &Ip, sizeof (EFI_IP_ADDRESS));\r
+\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Check the protocol type.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_PROTOCOL];\r
+  if ((Field->Str != NULL) && ((*(Field->Str) - '0') != EFI_IP_PROTO_TCP)) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Get the port of the iSCSI target.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_PORT];\r
+  if (Field->Str != NULL) {\r
+    ConfigNvData->TargetPort = (UINT16) AsciiStrDecimalToUintn (Field->Str);\r
+  } else {\r
+    ConfigNvData->TargetPort = ISCSI_WELL_KNOWN_PORT;\r
+  }\r
+  //\r
+  // Get the LUN.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_LUN];\r
+  if (Field->Str != NULL) {\r
+    Status = IScsiAsciiStrToLun (Field->Str, ConfigNvData->BootLun);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  } else {\r
+    ZeroMem (ConfigNvData->BootLun, sizeof (ConfigNvData->BootLun));\r
+  }\r
+  //\r
+  // Get the target iSCSI Name.\r
+  //\r
+  Field = &Fields[RP_FIELD_IDX_TARGETNAME];\r
+\r
+  if (AsciiStrLen (Field->Str) > ISCSI_NAME_MAX_SIZE - 1) {\r
+    Status = EFI_INVALID_PARAMETER;\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Validate the iSCSI name.\r
+  //\r
+  Status = IScsiNormalizeName (Field->Str, AsciiStrLen (Field->Str));\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  AsciiStrCpy (ConfigNvData->TargetName, Field->Str);\r
+\r
+ON_EXIT:\r
+\r
+  FreePool (TmpStr);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol \r
+  instance to intercept events that occurs in the DHCPv6 Information Request\r
+  exchange process.\r
+\r
+  @param[in]  This              Pointer to the EFI_DHCP6_PROTOCOL instance that \r
+                                is used to configure this  callback function.\r
+  @param[in]  Context           Pointer to the context that is initialized in\r
+                                the EFI_DHCP6_PROTOCOL.InfoRequest().\r
+  @param[in]  Packet            Pointer to Reply packet that has been received.\r
+                                The EFI DHCPv6 Protocol instance is responsible\r
+                                for freeing the buffer.\r
+\r
+  @retval EFI_SUCCESS           Tell the EFI DHCPv6 Protocol instance to finish\r
+                                Information Request exchange process.\r
+  @retval EFI_NOT_READY         Tell the EFI DHCPv6 Protocol instance to continue\r
+                                Information Request exchange process.\r
+  @retval EFI_ABORTED           Tell the EFI DHCPv6 Protocol instance to abort\r
+                                the Information Request exchange process.\r
+  @retval EFI_UNSUPPORTED       Tell the EFI DHCPv6 Protocol instance to finish\r
+                                the Information Request exchange process because some\r
+                                request information are not received.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDhcp6ParseReply (\r
+  IN EFI_DHCP6_PROTOCOL          *This,\r
+  IN VOID                        *Context,\r
+  IN EFI_DHCP6_PACKET            *Packet\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  UINT32                      Index;\r
+  UINT32                      OptionCount;\r
+  EFI_DHCP6_PACKET_OPTION     *BootFileOpt;\r
+  EFI_DHCP6_PACKET_OPTION     **OptionList;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;\r
\r
+  OptionCount = 0;\r
+  BootFileOpt = NULL;\r
+  \r
+  Status      = This->Parse (This, Packet, &OptionCount, NULL);\r
+  if (Status != EFI_BUFFER_TOO_SMALL) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));\r
+  if (OptionList == NULL) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  Status = This->Parse (This, Packet, &OptionCount, OptionList);\r
+  if (EFI_ERROR (Status)) {\r
+    Status = EFI_NOT_READY;\r
+    goto Exit;\r
+  }\r
+\r
+  ConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) Context;\r
+\r
+  for (Index = 0; Index < OptionCount; Index++) {\r
+    OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);\r
+    OptionList[Index]->OpLen  = NTOHS (OptionList[Index]->OpLen);\r
+\r
+    //\r
+    // Get DNS server addresses from this reply packet.\r
+    //\r
+    if (OptionList[Index]->OpCode == DHCP6_OPT_DNS_SERVERS) {\r
+\r
+      if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {\r
+        Status = EFI_INVALID_PARAMETER;\r
+        goto Exit;\r
+      }\r
+      //\r
+      // Primary DNS server address.\r
+      //\r
+      CopyMem (&ConfigData->PrimaryDns, &OptionList[Index]->Data[0], sizeof (EFI_IPv6_ADDRESS));\r
+\r
+      if (OptionList[Index]->OpLen > 16) {\r
+        //\r
+        // Secondary DNS server address\r
+        //\r
+        CopyMem (&ConfigData->SecondaryDns, &OptionList[Index]->Data[16], sizeof (EFI_IPv6_ADDRESS));\r
+      }\r
+\r
+    } else if (OptionList[Index]->OpCode == DHCP6_OPT_BOOT_FILE_URL) {\r
+      //\r
+      // The server sends this option to inform the client about an URL to a boot file.\r
+      //\r
+      BootFileOpt = OptionList[Index];\r
+    }\r
+  }\r
+\r
+  if (BootFileOpt == NULL) {\r
+    Status = EFI_UNSUPPORTED;\r
+    goto Exit;\r
+  }\r
+  \r
+  //\r
+  // Get iSCSI root path from Boot File Uniform Resource Locator (URL) Option\r
+  //\r
+  Status = IScsiDhcp6ExtractRootPath (\r
+             (CHAR8 *) BootFileOpt->Data,\r
+             BootFileOpt->OpLen,\r
+             ConfigData\r
+             );\r
+\r
+Exit:\r
+\r
+  FreePool (OptionList);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Parse the DHCP ACK to get the address configuration and DNS information.\r
+\r
+  @param[in]       Image         The handle of the driver image.\r
+  @param[in]       Controller    The handle of the controller;\r
+  @param[in, out]  ConfigData    The attempt configuration data.\r
+\r
+  @retval EFI_SUCCESS            The DNS information is got from the DHCP ACK.\r
+  @retval EFI_NO_MAPPING         DHCP failed to acquire address and other\r
+                                 information.\r
+  @retval EFI_INVALID_PARAMETER  The DHCP ACK's DNS option is malformatted.\r
+  @retval EFI_DEVICE_ERROR       Some unexpected error occurred.\r
+  @retval EFI_OUT_OF_RESOURCES   There is no sufficient resource to finish the\r
+                                 operation.\r
+  @retval EFI_NO_MEDIA           There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiDoDhcp6 (\r
+  IN     EFI_HANDLE                  Image,\r
+  IN     EFI_HANDLE                  Controller,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  )\r
+{\r
+  EFI_HANDLE                Dhcp6Handle;\r
+  EFI_DHCP6_PROTOCOL        *Dhcp6;\r
+  EFI_STATUS                Status;\r
+  EFI_STATUS                TimerStatus;\r
+  EFI_DHCP6_PACKET_OPTION   *Oro;\r
+  EFI_DHCP6_RETRANSMISSION  InfoReqReXmit;\r
+  EFI_EVENT                 Timer;\r
+  BOOLEAN                   MediaPresent;\r
+\r
+  //\r
+  // Check media status before doing DHCP.\r
+  //\r
+  MediaPresent = TRUE;\r
+  NetLibDetectMedia (Controller, &MediaPresent);\r
+  if (!MediaPresent) {\r
+    return EFI_NO_MEDIA;\r
+  }\r
+\r
+  //\r
+  // iSCSI will only request target info from DHCPv6 server.\r
+  //\r
+  if (!ConfigData->SessionConfigData.TargetInfoFromDhcp) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Dhcp6Handle = NULL;\r
+  Dhcp6       = NULL;\r
+  Oro         = NULL;\r
+  Timer       = NULL;\r
+\r
+  //\r
+  // Create a DHCP6 child instance and get the protocol.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             Controller,\r
+             Image,\r
+             &gEfiDhcp6ServiceBindingProtocolGuid,\r
+             &Dhcp6Handle\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Dhcp6Handle,\r
+                  &gEfiDhcp6ProtocolGuid,\r
+                  (VOID **) &Dhcp6,\r
+                  Image,\r
+                  Controller,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 3);\r
+  if (Oro == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Ask the server to reply with DNS and Boot File URL options by info request.\r
+  // All members in EFI_DHCP6_PACKET_OPTION are in network order.\r
+  //\r
+  Oro->OpCode  = HTONS (DHCP6_OPT_REQUEST_OPTION);\r
+  Oro->OpLen   = HTONS (2 * 2);\r
+  Oro->Data[1] = DHCP6_OPT_DNS_SERVERS;\r
+  Oro->Data[3] = DHCP6_OPT_BOOT_FILE_URL;\r
+\r
+  InfoReqReXmit.Irt = 4;\r
+  InfoReqReXmit.Mrc = 1;\r
+  InfoReqReXmit.Mrt = 10;\r
+  InfoReqReXmit.Mrd = 30;\r
+\r
+  Status = Dhcp6->InfoRequest (\r
+                    Dhcp6,\r
+                    TRUE,\r
+                    Oro,\r
+                    0,\r
+                    NULL,\r
+                    &InfoReqReXmit,\r
+                    NULL,\r
+                    IScsiDhcp6ParseReply,\r
+                    ConfigData\r
+                    );\r
+  if (Status == EFI_NO_MAPPING) {\r
+    Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = gBS->SetTimer (\r
+                    Timer,\r
+                    TimerRelative,\r
+                    ISCSI_GET_MAPPING_TIMEOUT\r
+                    );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    do {\r
+\r
+      TimerStatus = gBS->CheckEvent (Timer);\r
+\r
+      if (!EFI_ERROR (TimerStatus)) {\r
+        Status = Dhcp6->InfoRequest (\r
+                          Dhcp6,\r
+                          TRUE,\r
+                          Oro,\r
+                          0,\r
+                          NULL,\r
+                          &InfoReqReXmit,\r
+                          NULL,\r
+                          IScsiDhcp6ParseReply,\r
+                          ConfigData\r
+                          );\r
+      }\r
+\r
+    } while (TimerStatus == EFI_NOT_READY);\r
+\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (Oro != NULL) {\r
+    FreePool (Oro);\r
+  }  \r
+\r
+  if (Timer != NULL) {\r
+    gBS->CloseEvent (Timer);\r
+  }\r
+\r
+  if (Dhcp6 != NULL) {\r
+    gBS->CloseProtocol (\r
+           Dhcp6Handle,\r
+           &gEfiDhcp6ProtocolGuid,\r
+           Image,\r
+           Controller\r
+           );\r
+  }\r
+\r
+  NetLibDestroyServiceChild (\r
+    Controller,\r
+    Image,\r
+    &gEfiDhcp6ServiceBindingProtocolGuid,\r
+    Dhcp6Handle\r
+    );\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/IScsiDxe/IScsiDhcp6.h b/NetworkPkg/IScsiDxe/IScsiDhcp6.h
new file mode 100644 (file)
index 0000000..fe3dfb7
--- /dev/null
@@ -0,0 +1,79 @@
+/** @file\r
+  The header file of iSCSI DHCP6 related configuration routines.\r
+\r
+Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_DHCP6_H_\r
+#define _ISCSI_DHCP6_H_\r
+\r
+#define DHCP6_OPT_REQUEST_OPTION 6\r
+#define DHCP6_OPT_VENDOR_INFO    17\r
+#define DHCP6_OPT_DNS_SERVERS    23\r
+///\r
+/// Assigned by IANA, RFC 5970\r
+///\r
+#define DHCP6_OPT_BOOT_FILE_URL  59\r
+\r
+#define ISCSI_ROOT_PATH_ID                   "iscsi:"\r
+#define ISCSI_ROOT_PATH_FIELD_DELIMITER      ':'\r
+#define ISCSI_ROOT_PATH_ADDR_START_DELIMITER '['\r
+#define ISCSI_ROOT_PATH_ADDR_END_DELIMITER   ']'\r
+\r
+\r
+/**\r
+  Extract the Root Path option and get the required target information from\r
+  Boot File Uniform Resource Locator (URL) Option.\r
+\r
+  @param[in]       RootPath      The RootPath string.\r
+  @param[in]       Length        Length of the RootPath option payload.\r
+  @param[in, out]  ConfigData    The iSCSI session configuration data read from\r
+                                 nonvolatile device.\r
+\r
+  @retval EFI_SUCCESS            All required information is extracted from the\r
+                                 RootPath option.\r
+  @retval EFI_NOT_FOUND          The RootPath is not an iSCSI RootPath.\r
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.\r
+  @retval EFI_INVALID_PARAMETER  The RootPath is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiDhcp6ExtractRootPath (\r
+  IN     CHAR8                        *RootPath,\r
+  IN     UINT16                       Length,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  );\r
+\r
+/**\r
+  Parse the DHCP ACK to get the address configuration and DNS information.\r
+\r
+  @param[in]       Image         The handle of the driver image.\r
+  @param[in]       Controller    The handle of the controller;\r
+  @param[in, out]  ConfigData    The attempt configuration data.\r
+\r
+  @retval EFI_SUCCESS            The DNS information is got from the DHCP ACK.\r
+  @retval EFI_NO_MAPPING         DHCP failed to acquire address and other\r
+                                 information.\r
+  @retval EFI_INVALID_PARAMETER  The DHCP ACK's DNS option is malformatted.\r
+  @retval EFI_DEVICE_ERROR       Some unexpected error happened.\r
+  @retval EFI_OUT_OF_RESOURCES   There is no sufficient resource to finish the\r
+                                 operation.\r
+  @retval EFI_NO_MEDIA           There was a media error.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiDoDhcp6 (\r
+  IN     EFI_HANDLE                  Image,\r
+  IN     EFI_HANDLE                  Controller,\r
+  IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiDriver.c b/NetworkPkg/IScsiDxe/IScsiDriver.c
new file mode 100644 (file)
index 0000000..2d922ee
--- /dev/null
@@ -0,0 +1,1216 @@
+/** @file\r
+  The entry point of IScsi driver.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+EFI_DRIVER_BINDING_PROTOCOL gIScsiDriverBinding = {\r
+  IScsiDriverBindingSupported,\r
+  IScsiDriverBindingStart,\r
+  IScsiDriverBindingStop,\r
+  0xa,\r
+  NULL,\r
+  NULL\r
+};\r
+\r
+EFI_GUID                    mIScsiV4PrivateGuid = ISCSI_V4_PRIVATE_GUID;\r
+EFI_GUID                    mIScsiV6PrivateGuid = ISCSI_V6_PRIVATE_GUID;\r
+ISCSI_PRIVATE_DATA          *mPrivate           = NULL;\r
+\r
+/**\r
+  Tests to see if this driver supports the RemainingDevicePath.\r
+\r
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This \r
+                                   parameter is ignored by device drivers, and is optional for bus \r
+                                   drivers. For bus drivers, if this parameter is not NULL, then \r
+                                   the bus driver must determine if the bus controller specified \r
+                                   by ControllerHandle and the child controller specified \r
+                                   by RemainingDevicePath are both supported by this \r
+                                   bus driver.\r
+\r
+  @retval EFI_SUCCESS              The RemainingDevicePath is supported or NULL.\r
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+IScsiIsDevicePathSupported (\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_DEVICE_PATH_PROTOCOL  *CurrentDevicePath;\r
+\r
+  CurrentDevicePath = RemainingDevicePath;\r
+  if (CurrentDevicePath != NULL) {\r
+    while (!IsDevicePathEnd (CurrentDevicePath)) {\r
+      if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) {\r
+        return EFI_SUCCESS;\r
+      }\r
+\r
+      CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);\r
+    }\r
+\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Tests to see if this driver supports a given controller. If a child device is provided, \r
+  it further tests to see if this driver supports creating a handle for the specified child device.\r
+\r
+  This function checks to see if the driver specified by This supports the device specified by \r
+  ControllerHandle. Drivers typically use the device path attached to \r
+  ControllerHandle and/or the services from the bus I/O abstraction attached to \r
+  ControllerHandle to determine if the driver supports ControllerHandle. This function \r
+  may be called many times during platform initialization. In order to reduce boot times, the tests \r
+  performed by this function must be very small and take as little time as possible to execute. This \r
+  function must not change the state of any hardware devices, and this function must be aware that the \r
+  device specified by ControllerHandle may already be managed by the same driver or a \r
+  different driver. This function must match its calls to AllocatePages() with FreePages(), \r
+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().  \r
+  Since ControllerHandle may have been previously started by the same driver, if a protocol is \r
+  already in the opened state, then it must not be closed with CloseProtocol(). This is required \r
+  to guarantee the state of ControllerHandle is not modified by this function.\r
+\r
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle     The handle of the controller to test. This handle \r
+                                   must support a protocol interface that supplies \r
+                                   an I/O abstraction to the driver.\r
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This \r
+                                   parameter is ignored by device drivers, and is optional for bus \r
+                                   drivers. For bus drivers, if this parameter is not NULL, then \r
+                                   the bus driver must determine if the bus controller specified \r
+                                   by ControllerHandle and the child controller specified \r
+                                   by RemainingDevicePath are both supported by this \r
+                                   bus driver.\r
+\r
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is supported by the driver specified by This.\r
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already being managed by the driver\r
+                                   specified by This.\r
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already being managed by a different\r
+                                   driver or an application that requires exclusive access.\r
+                                   Currently not implemented.\r
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  BOOLEAN                   IsIscsi4Started;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &mIScsiV4PrivateGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    IsIscsi4Started = TRUE;\r
+  } else {\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiTcp4ServiceBindingProtocolGuid,\r
+                    NULL,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = IScsiIsDevicePathSupported (RemainingDevicePath);\r
+      if (!EFI_ERROR (Status)) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+\r
+    IsIscsi4Started = FALSE;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  &mIScsiV6PrivateGuid,\r
+                  NULL,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    if (IsIscsi4Started) {\r
+      return EFI_ALREADY_STARTED;\r
+    }\r
+  } else {\r
+    Status = gBS->OpenProtocol (\r
+                    ControllerHandle,\r
+                    &gEfiTcp6ServiceBindingProtocolGuid,\r
+                    NULL,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = IScsiIsDevicePathSupported (RemainingDevicePath);\r
+      if (!EFI_ERROR (Status)) {\r
+        return EFI_SUCCESS;\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+/**\r
+  Start to manage the controller. This is the worker function for\r
+  IScsiDriverBindingStart.\r
+\r
+  @param[in]  Image                Handle of the image.\r
+  @param[in]  ControllerHandle     Handle of the controller.\r
+  @param[in]  IpVersion            Ip4 or Ip6\r
+\r
+  @retval EFI_SUCCES            This driver supports this device.\r
+  @retval EFI_ALREADY_STARTED   This driver is already running on this device.\r
+  @retval EFI_INVALID_PARAMETER Any input parameter is invalid.\r
+  @retval EFI_NOT_FOUND         There is no sufficient information to establish\r
+                                the iScsi session.\r
+  @retval EFI_DEVICE_ERROR      Failed to get TCP connection device path.                              \r
+\r
+**/\r
+EFI_STATUS\r
+IScsiStart (\r
+  IN EFI_HANDLE                   Image,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN UINT8                        IpVersion\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  ISCSI_DRIVER_DATA               *Private;\r
+  LIST_ENTRY                      *Entry;\r
+  LIST_ENTRY                      *NextEntry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA     *AttemptConfigData;\r
+  ISCSI_SESSION                   *Session;\r
+  UINT8                           Index;\r
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *ExistIScsiExtScsiPassThru;\r
+  ISCSI_DRIVER_DATA               *ExistPrivate;\r
+  UINT8                           *AttemptConfigOrder;\r
+  UINTN                           AttemptConfigOrderSize;\r
+  UINT8                           BootSelected;\r
+  EFI_HANDLE                      *HandleBuffer;\r
+  UINTN                           NumberOfHandles;\r
+  EFI_DEVICE_PATH_PROTOCOL        *DevicePath;\r
+  EFI_GUID                        *IScsiPrivateGuid;\r
+  EFI_GUID                        *TcpServiceBindingGuid;\r
+  CHAR16                          MacString[ISCSI_MAX_MAC_STRING_LEN];\r
+  BOOLEAN                         NeedUpdate;\r
+  VOID                            *Interface;\r
+  EFI_GUID                        *ProtocolGuid;\r
+\r
+  //\r
+  // Test to see if iSCSI driver supports the given controller.\r
+  //\r
+\r
+  if (IpVersion == IP_VERSION_4) {\r
+    IScsiPrivateGuid      = &mIScsiV4PrivateGuid;\r
+    TcpServiceBindingGuid = &gEfiTcp4ServiceBindingProtocolGuid;\r
+    ProtocolGuid          = &gEfiTcp4ProtocolGuid;\r
+  } else if (IpVersion == IP_VERSION_6) {\r
+    IScsiPrivateGuid      = &mIScsiV6PrivateGuid;\r
+    TcpServiceBindingGuid = &gEfiTcp6ServiceBindingProtocolGuid;\r
+    ProtocolGuid          = &gEfiTcp6ProtocolGuid;\r
+  } else {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  IScsiPrivateGuid,\r
+                  NULL,\r
+                  Image,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    return EFI_ALREADY_STARTED;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ControllerHandle,\r
+                  TcpServiceBindingGuid,\r
+                  NULL,\r
+                  Image,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  //\r
+  // Record the incoming NIC info.\r
+  //\r
+  Status = IScsiAddNic (ControllerHandle);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Create the instance private data.\r
+  //\r
+  Private = IScsiCreateDriverData (Image, ControllerHandle);\r
+  if (Private == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  //\r
+  // Create a underlayer child instance, but not need to configure it. Just open ChildHandle\r
+  // via BY_DRIVER. That is, establishing the relationship between ControllerHandle and ChildHandle.\r
+  // Therefore, when DisconnectController(), especially VLAN virtual controller handle,\r
+  // IScsiDriverBindingStop() will be called.\r
+  //\r
+  Status = NetLibCreateServiceChild (\r
+             ControllerHandle,\r
+             Image,\r
+             TcpServiceBindingGuid,\r
+             &Private->ChildHandle\r
+             );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  Private->ChildHandle,\r
+                  ProtocolGuid,\r
+                  &Interface,\r
+                  Image,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_BY_DRIVER\r
+                  );\r
+                  \r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // Always install private protocol no matter what happens later. We need to \r
+  // keep the relationship between ControllerHandle and ChildHandle.\r
+  //\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &ControllerHandle,\r
+                  IScsiPrivateGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &Private->IScsiIdentifier\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+  \r
+  if (IpVersion == IP_VERSION_4) {\r
+    mPrivate->Ipv6Flag = FALSE;\r
+  } else {\r
+    mPrivate->Ipv6Flag = TRUE;\r
+  }\r
+\r
+  //\r
+  // Get the current iSCSI configuration data.\r
+  //\r
+  Status = IScsiGetConfigData (Private);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  //\r
+  // If there is already a successul attempt, check whether this attempt is the\r
+  // first "enabled for MPIO" attempt. If not, still try the first attempt.\r
+  // In single path mode, try all attempts.\r
+  //\r
+  ExistPrivate = NULL;\r
+  Status       = EFI_NOT_FOUND;\r
+\r
+  if (mPrivate->OneSessionEstablished && mPrivate->EnableMpio) {\r
+    AttemptConfigData = NULL;\r
+    NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {\r
+     AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+      if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (AttemptConfigData == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    if (AttemptConfigData->AttemptConfigIndex == mPrivate->BootSelectedIndex) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    //\r
+    // Uninstall the original ExtScsiPassThru first.\r
+    //\r
+\r
+    //\r
+    // Locate all ExtScsiPassThru protocol instances.\r
+    //\r
+    Status = gBS->LocateHandleBuffer (\r
+                    ByProtocol,\r
+                    &gEfiExtScsiPassThruProtocolGuid,\r
+                    NULL,\r
+                    &NumberOfHandles,\r
+                    &HandleBuffer\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    //\r
+    // Find ExtScsiPassThru protocol instance produced by this driver.\r
+    //\r
+    ExistIScsiExtScsiPassThru = NULL;\r
+    for (Index = 0; Index < NumberOfHandles && ExistIScsiExtScsiPassThru == NULL; Index++) {\r
+      Status = gBS->HandleProtocol (\r
+                      HandleBuffer[Index],\r
+                      &gEfiDevicePathProtocolGuid,\r
+                      (VOID **) &DevicePath\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
+        continue;\r
+      }\r
+\r
+      while (!IsDevicePathEnd (DevicePath)) {\r
+        if ((DevicePath->Type == MESSAGING_DEVICE_PATH) && (DevicePath->SubType == MSG_MAC_ADDR_DP)) {\r
+          //\r
+          // Get the ExtScsiPassThru protocol instance.\r
+          //\r
+          Status = gBS->HandleProtocol (\r
+                          HandleBuffer[Index],\r
+                          &gEfiExtScsiPassThruProtocolGuid,\r
+                          (VOID **) &ExistIScsiExtScsiPassThru\r
+                          );\r
+          ASSERT_EFI_ERROR (Status);\r
+          break;\r
+        }\r
+\r
+        DevicePath = NextDevicePathNode (DevicePath);\r
+      }\r
+    }\r
+\r
+    FreePool (HandleBuffer);\r
+\r
+    if (ExistIScsiExtScsiPassThru == NULL) {\r
+      Status = EFI_NOT_FOUND;\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    ExistPrivate = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (ExistIScsiExtScsiPassThru);\r
+\r
+    Status = gBS->UninstallProtocolInterface (\r
+                    ExistPrivate->ExtScsiPassThruHandle,\r
+                    &gEfiExtScsiPassThruProtocolGuid,\r
+                    &ExistPrivate->IScsiExtScsiPassThru\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Install the Ext SCSI PASS THRU protocol.\r
+  //\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &Private->ExtScsiPassThruHandle,\r
+                  &gEfiExtScsiPassThruProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &Private->IScsiExtScsiPassThru\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  BootSelected = 0;\r
+\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {\r
+    AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+    //\r
+    // Don't process the attempt that does not associate with the current NIC or\r
+    // this attempt is disabled or established.\r
+    //\r
+    if (AttemptConfigData->NicIndex != mPrivate->CurrentNic ||\r
+        AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED ||\r
+        AttemptConfigData->ValidPath) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // In multipath mode, don't process attempts configured for single path.\r
+    // In default single path mode, don't process attempts configured for multipath.\r
+    //\r
+    if ((mPrivate->EnableMpio &&\r
+         AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) ||\r
+        (!mPrivate->EnableMpio &&\r
+         AttemptConfigData->SessionConfigData.Enabled != ISCSI_ENABLED)) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Don't process the attempt that fails to get the init/target information from DHCP.\r
+    //\r
+    if (AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp &&\r
+        !AttemptConfigData->DhcpSuccess) {\r
+      if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {\r
+        mPrivate->ValidSinglePathCount--;\r
+      }\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Don't process the autoconfigure path if it is already established.\r
+    //\r
+    if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&\r
+        AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_SUCCESS) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Don't process the attempt if its IP mode is not in the current IP version.\r
+    //\r
+    if (!mPrivate->Ipv6Flag) {\r
+      if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {\r
+        continue;\r
+      }\r
+      if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&\r
+          AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {\r
+        continue;\r
+      }\r
+    } else {\r
+      if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {\r
+        continue;\r
+      }\r
+      if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&\r
+          AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {\r
+        continue;\r
+      }\r
+    }\r
+\r
+    //\r
+    // Fill in the Session and init it.\r
+    //\r
+    Session = (ISCSI_SESSION *) AllocateZeroPool (sizeof (ISCSI_SESSION));\r
+    if (Session == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    Session->Private    = Private;\r
+    Session->ConfigData = AttemptConfigData;\r
+    Session->AuthType   = AttemptConfigData->AuthenticationType;\r
+\r
+    AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);\r
+    UnicodeSPrint (\r
+      mPrivate->PortString,\r
+      (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+      L"%s%d",\r
+      MacString,\r
+      (UINTN) AttemptConfigData->AttemptConfigIndex\r
+      );\r
+\r
+    if (Session->AuthType == ISCSI_AUTH_TYPE_CHAP) {\r
+      Session->AuthData.CHAP.AuthConfig = &AttemptConfigData->AuthConfigData.CHAP;\r
+    }\r
+\r
+    IScsiSessionInit (Session, FALSE);\r
+\r
+    //\r
+    // Try to login and create an iSCSI session according to the configuration.\r
+    //\r
+    Status = IScsiSessionLogin (Session);\r
+    if (Status == EFI_MEDIA_CHANGED) {\r
+      //\r
+      // The specified target is not available, and the redirection information is\r
+      // received. Login the session again with the updated target address.\r
+      //\r
+      Status = IScsiSessionLogin (Session);\r
+    } else if (Status == EFI_NOT_READY) {\r
+      Status = IScsiSessionReLogin (Session);\r
+    }\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // In Single path mode, only the successful attempt will be recorded in iBFT;\r
+      // in multi-path mode, all the attempt entries in MPIO will be recorded in iBFT.\r
+      //\r
+      if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {\r
+        mPrivate->ValidSinglePathCount--;\r
+      }\r
+\r
+      FreePool (Session);\r
+\r
+    } else {\r
+      AttemptConfigData->ValidPath = TRUE;\r
+\r
+      //\r
+      // Do not record the attempt in iBFT if it login with KRB5.\r
+      // TODO: record KRB5 attempt information in the iSCSI device path.\r
+      //\r
+      if (Session->AuthType == ISCSI_AUTH_TYPE_KRB) {\r
+        if (!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount > 0) {\r
+          mPrivate->ValidSinglePathCount--;\r
+        }\r
+\r
+        AttemptConfigData->ValidiBFTPath = FALSE;\r
+      } else {\r
+        AttemptConfigData->ValidiBFTPath = TRUE;\r
+      }\r
+\r
+      //\r
+      // IScsi session success. Update the attempt state to NVR.\r
+      //\r
+      if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {\r
+        AttemptConfigData->AutoConfigureMode = IP_MODE_AUTOCONFIG_SUCCESS;\r
+      }\r
+\r
+      gRT->SetVariable (\r
+             mPrivate->PortString,\r
+             &gEfiIScsiInitiatorNameProtocolGuid,\r
+             ISCSI_CONFIG_VAR_ATTR,\r
+             sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),\r
+             AttemptConfigData\r
+             );\r
+\r
+      //\r
+      // Select the first login session. Abort others.\r
+      //\r
+      if (Private->Session == NULL) {\r
+        Private->Session = Session;\r
+        BootSelected     = AttemptConfigData->AttemptConfigIndex;\r
+        //\r
+        // Don't validate other attempt in multipath mode if one is success.\r
+        //\r
+        if (mPrivate->EnableMpio) {\r
+          break;\r
+        }\r
+      } else {\r
+        IScsiSessionAbort (Session);\r
+        FreePool (Session);\r
+      }\r
+    }\r
+  }\r
+\r
+  //\r
+  // All attempts configured for this driver instance are not valid.\r
+  //\r
+  if (Private->Session == NULL) {\r
+    Status = gBS->UninstallProtocolInterface (\r
+                    Private->ExtScsiPassThruHandle,\r
+                    &gEfiExtScsiPassThruProtocolGuid,\r
+                    &Private->IScsiExtScsiPassThru\r
+                    );\r
+    ASSERT_EFI_ERROR (Status);\r
+    Private->ExtScsiPassThruHandle = NULL;\r
+\r
+    //\r
+    // Reinstall the original ExtScsiPassThru back.\r
+    //\r
+    if (mPrivate->OneSessionEstablished && ExistPrivate != NULL) {\r
+      Status = gBS->InstallProtocolInterface (\r
+                      &ExistPrivate->ExtScsiPassThruHandle,\r
+                      &gEfiExtScsiPassThruProtocolGuid,\r
+                      EFI_NATIVE_INTERFACE,\r
+                      &ExistPrivate->IScsiExtScsiPassThru\r
+                      );\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_ERROR;\r
+      }\r
+\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Status = EFI_NOT_FOUND;\r
+\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NeedUpdate = TRUE;\r
+  //\r
+  // More than one attempt successes.\r
+  //\r
+  if (Private->Session != NULL && mPrivate->OneSessionEstablished) {\r
+\r
+    AttemptConfigOrder = IScsiGetVariableAndSize (\r
+                           L"AttemptOrder",\r
+                           &mVendorGuid,\r
+                           &AttemptConfigOrderSize\r
+                           );\r
+    ASSERT (AttemptConfigOrder != NULL);\r
+    for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {\r
+      if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex ||\r
+          AttemptConfigOrder[Index] == BootSelected) {\r
+        break;\r
+      }\r
+    }\r
+\r
+    if (mPrivate->EnableMpio) {\r
+      //\r
+      // Use the attempt in earlier order. Abort the later one in MPIO.\r
+      //\r
+      if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) {\r
+        IScsiSessionAbort (Private->Session);\r
+        FreePool (Private->Session);\r
+        Private->Session = NULL;\r
+        gBS->UninstallProtocolInterface (\r
+               Private->ExtScsiPassThruHandle,\r
+               &gEfiExtScsiPassThruProtocolGuid,\r
+               &Private->IScsiExtScsiPassThru\r
+               );\r
+        Private->ExtScsiPassThruHandle = NULL;\r
+\r
+        //\r
+        // Reinstall the original ExtScsiPassThru back.\r
+        //\r
+        Status = gBS->InstallProtocolInterface (\r
+                        &ExistPrivate->ExtScsiPassThruHandle,\r
+                        &gEfiExtScsiPassThruProtocolGuid,\r
+                        EFI_NATIVE_INTERFACE,\r
+                        &ExistPrivate->IScsiExtScsiPassThru\r
+                        );\r
+        if (EFI_ERROR (Status)) {\r
+          goto ON_ERROR;\r
+        }\r
+\r
+        goto ON_EXIT;\r
+      } else {\r
+        ASSERT (AttemptConfigOrder[Index] == BootSelected);\r
+        mPrivate->BootSelectedIndex = BootSelected;\r
+        //\r
+        // Clear the resource in ExistPrivate.\r
+        //\r
+        gBS->UninstallProtocolInterface (\r
+               ExistPrivate->Controller,\r
+               IScsiPrivateGuid,\r
+               &ExistPrivate->IScsiIdentifier\r
+               ); \r
+        \r
+        IScsiRemoveNic (ExistPrivate->Controller);\r
+        if (ExistPrivate->Session != NULL) {\r
+          IScsiSessionAbort (ExistPrivate->Session);\r
+        }\r
+\r
+        IScsiCleanDriverData (ExistPrivate);\r
+      }\r
+    } else {\r
+      //\r
+      // Use the attempt in earlier order as boot selected in single path mode.\r
+      //\r
+      if (AttemptConfigOrder[Index] == mPrivate->BootSelectedIndex) {\r
+        NeedUpdate = FALSE;\r
+      }\r
+    }\r
+\r
+  }\r
+\r
+  if (NeedUpdate) {\r
+    mPrivate->OneSessionEstablished = TRUE;\r
+    mPrivate->BootSelectedIndex     = BootSelected;\r
+  }\r
+\r
+  //\r
+  // Duplicate the Session's tcp connection device path. The source port field\r
+  // will be set to zero as one iSCSI session is comprised of several iSCSI\r
+  // connections.\r
+  //\r
+  Private->DevicePath = IScsiGetTcpConnDevicePath (Private->Session);\r
+  if (Private->DevicePath == NULL) {\r
+    Status = EFI_DEVICE_ERROR;\r
+    goto ON_ERROR;\r
+  }\r
+  //\r
+  // Install the updated device path onto the ExtScsiPassThruHandle.\r
+  //\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &Private->ExtScsiPassThruHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  Private->DevicePath\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  //\r
+  // Update/Publish the iSCSI Boot Firmware Table.\r
+  //\r
+  if (mPrivate->BootSelectedIndex != 0) {\r
+    IScsiPublishIbft ();\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+ON_ERROR:\r
+\r
+  if (Private->Session != NULL) {\r
+    IScsiSessionAbort (Private->Session);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Starts a device controller or a bus controller.\r
+\r
+  The Start() function is designed to be invoked from the EFI boot service ConnectController().\r
+  As a result, much of the error checking on the parameters to Start() has been moved into this \r
+  common boot service. It is legal to call Start() from other locations, \r
+  but the following calling restrictions must be followed or the system behavior will not be deterministic.\r
+  1. ControllerHandle must be a valid EFI_HANDLE.\r
+  2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned\r
+     EFI_DEVICE_PATH_PROTOCOL.\r
+  3. Prior to calling Start(), the Supported() function for the driver specified by This must\r
+     have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.  \r
+\r
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle     The handle of the controller to start. This handle \r
+                                   must support a protocol interface that supplies \r
+                                   an I/O abstraction to the driver.\r
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This \r
+                                   parameter is ignored by device drivers, and is optional for bus \r
+                                   drivers. For a bus driver, if this parameter is NULL, then handles \r
+                                   for all the children of Controller are created by this driver.  \r
+                                   If this parameter is not NULL and the first Device Path Node is \r
+                                   not the End of Device Path Node, then only the handle for the \r
+                                   child device specified by the first Device Path Node of \r
+                                   RemainingDevicePath is created by this driver.\r
+                                   If the first Device Path Node of RemainingDevicePath is \r
+                                   the End of Device Path Node, no child handle is created by this\r
+                                   driver.\r
+\r
+  @retval EFI_SUCCESS              The device was started.\r
+  @retval EFI_DEVICE_ERROR         The device could not be started due to a device error.Currently not implemented.\r
+  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a lack of resources.\r
+  @retval Others                   The driver failed to start the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  )\r
+{\r
+  EFI_STATUS        V4Status;\r
+  EFI_STATUS        V6Status;\r
+\r
+  V4Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_4);\r
+  if (V4Status == EFI_ALREADY_STARTED) {\r
+    V4Status = EFI_SUCCESS;\r
+  }\r
+\r
+  V6Status = IScsiStart (This->DriverBindingHandle, ControllerHandle, IP_VERSION_6);\r
+  if (V6Status == EFI_ALREADY_STARTED) {\r
+    V6Status = EFI_SUCCESS;\r
+  }\r
+\r
+  if (!EFI_ERROR (V4Status) || !EFI_ERROR (V6Status)) {\r
+    return EFI_SUCCESS;\r
+  } else if (EFI_ERROR (V4Status)) {\r
+    return V4Status;\r
+  } else {\r
+    return V6Status;\r
+  }\r
+}\r
+\r
+/**\r
+  Stops a device controller or a bus controller.\r
+  \r
+  The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). \r
+  As a result, much of the error checking on the parameters to Stop() has been moved \r
+  into this common boot service. It is legal to call Stop() from other locations, \r
+  but the following calling restrictions must be followed or the system behavior will not be deterministic.\r
+  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this\r
+     same driver's Start() function.\r
+  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid\r
+     EFI_HANDLE. In addition, all of these handles must have been created in this driver's\r
+     Start() function, and the Start() function must have called OpenProtocol() on\r
+     ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
+  \r
+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must \r
+                                support a bus specific I/O protocol for the driver \r
+                                to use to stop the device.\r
+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.\r
+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL \r
+                                if NumberOfChildren is 0.\r
+\r
+  @retval EFI_SUCCESS           The device was stopped.\r
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN UINTN                        NumberOfChildren,\r
+  IN EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  )\r
+{\r
+  EFI_HANDLE                      IScsiController;\r
+  EFI_STATUS                      Status;\r
+  ISCSI_PRIVATE_PROTOCOL          *IScsiIdentifier;\r
+  ISCSI_DRIVER_DATA               *Private;\r
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;\r
+  ISCSI_CONNECTION                *Conn;\r
+  EFI_GUID                        *ProtocolGuid;\r
+  EFI_GUID                        *TcpServiceBindingGuid;\r
+  EFI_GUID                        *TcpProtocolGuid;\r
+\r
+\r
+  if (NumberOfChildren != 0) {\r
+    //\r
+    // We should have only one child.\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    ChildHandleBuffer[0],\r
+                    &gEfiExtScsiPassThruProtocolGuid,\r
+                    (VOID **) &PassThru,\r
+                    This->DriverBindingHandle,\r
+                    ControllerHandle,\r
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);\r
+    Conn    = NET_LIST_HEAD (&Private->Session->Conns, ISCSI_CONNECTION, Link);\r
+\r
+    //\r
+    // Previously the TCP protocol is opened BY_CHILD_CONTROLLER. Just close\r
+    // the protocol here, but do not uninstall the device path protocol and\r
+    // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle.\r
+    //\r
+    if (!Conn->Ipv6Flag) {\r
+      ProtocolGuid = &gEfiTcp4ProtocolGuid;\r
+    } else {\r
+      ProtocolGuid = &gEfiTcp6ProtocolGuid;\r
+    }\r
+\r
+    gBS->CloseProtocol (\r
+           Conn->TcpIo.Handle,\r
+           ProtocolGuid,\r
+           Private->Image,\r
+           Private->ExtScsiPassThruHandle\r
+           );\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // Get the handle of the controller we are controling.\r
+  //\r
+  IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid);\r
+  if (IScsiController != NULL) {\r
+    ProtocolGuid            = &mIScsiV4PrivateGuid;\r
+    TcpProtocolGuid         = &gEfiTcp4ProtocolGuid;\r
+    TcpServiceBindingGuid   = &gEfiTcp4ServiceBindingProtocolGuid;\r
+  } else {\r
+    IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp6ProtocolGuid);\r
+    ASSERT (IScsiController != NULL);\r
+    ProtocolGuid            = &mIScsiV6PrivateGuid;\r
+    TcpProtocolGuid         = &gEfiTcp6ProtocolGuid;\r
+    TcpServiceBindingGuid   = &gEfiTcp6ServiceBindingProtocolGuid;\r
+  }\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  IScsiController,\r
+                  ProtocolGuid,\r
+                  (VOID **) &IScsiIdentifier,\r
+                  This->DriverBindingHandle,\r
+                  ControllerHandle,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);\r
+  ASSERT (Private != NULL);\r
+\r
+  if (Private->ChildHandle != NULL) {\r
+    Status = gBS->CloseProtocol (\r
+                    Private->ChildHandle,\r
+                    TcpProtocolGuid,\r
+                    This->DriverBindingHandle,\r
+                    IScsiController\r
+                    );\r
+                    \r
+    ASSERT (!EFI_ERROR (Status));\r
+\r
+    Status = NetLibDestroyServiceChild (\r
+               IScsiController,\r
+               This->DriverBindingHandle,\r
+               TcpServiceBindingGuid,\r
+               Private->ChildHandle\r
+               );\r
+\r
+    ASSERT (!EFI_ERROR (Status));\r
+  }\r
+\r
+  gBS->UninstallProtocolInterface (\r
+         IScsiController,\r
+         ProtocolGuid,\r
+         &Private->IScsiIdentifier\r
+         ); \r
+\r
+  //\r
+  // Remove this NIC.\r
+  //\r
+  IScsiRemoveNic (IScsiController);\r
+\r
+  //\r
+  // Update the iSCSI Boot Firware Table.\r
+  //\r
+  IScsiPublishIbft ();\r
+\r
+  if (Private->Session != NULL) {\r
+    IScsiSessionAbort (Private->Session);\r
+  }\r
+\r
+  IScsiCleanDriverData (Private);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Unload the iSCSI driver.\r
+\r
+  @param[in]  ImageHandle          The handle of the driver image.\r
+\r
+  @retval     EFI_SUCCESS          The driver is unloaded.\r
+  @retval     EFI_DEVICE_ERROR     An unexpected error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiUnload (\r
+  IN EFI_HANDLE  ImageHandle\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINTN       DeviceHandleCount;\r
+  EFI_HANDLE  *DeviceHandleBuffer;\r
+  UINTN       Index;\r
+\r
+  //\r
+  // Try to disonnect the driver from the devices it's controlling.\r
+  //\r
+  Status = gBS->LocateHandleBuffer (\r
+                  AllHandles,\r
+                  NULL,\r
+                  NULL,\r
+                  &DeviceHandleCount,\r
+                  &DeviceHandleBuffer\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    for (Index = 0; Index < DeviceHandleCount; Index++) {\r
+      Status = gBS->DisconnectController (\r
+                      DeviceHandleBuffer[Index],\r
+                      ImageHandle,\r
+                      NULL\r
+                      );\r
+    }\r
+\r
+    if (DeviceHandleBuffer != NULL) {\r
+      FreePool (DeviceHandleBuffer);\r
+    }\r
+  }\r
+  //\r
+  // Unload the iSCSI configuration form.\r
+  //\r
+  IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);\r
+\r
+  //\r
+  // Uninstall the protocols installed by iSCSI driver.\r
+  //\r
+  gBS->UninstallMultipleProtocolInterfaces (\r
+         ImageHandle,\r
+         &gEfiAuthenticationInfoProtocolGuid,\r
+         &gIScsiAuthenticationInfo,\r
+         NULL\r
+         );\r
+  \r
+  return gBS->UninstallMultipleProtocolInterfaces (\r
+                ImageHandle,\r
+                &gEfiDriverBindingProtocolGuid,\r
+                &gIScsiDriverBinding,\r
+                &gEfiComponentName2ProtocolGuid,\r
+                &gIScsiComponentName2,\r
+                &gEfiComponentNameProtocolGuid,\r
+                &gIScsiComponentName,\r
+                &gEfiIScsiInitiatorNameProtocolGuid,\r
+                &gIScsiInitiatorName,\r
+                NULL\r
+                );\r
+}\r
+\r
+/**\r
+  This is the declaration of an EFI image entry point. This entry point is\r
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including\r
+  both device drivers and bus drivers.\r
+  \r
+  The entry point for iSCSI driver which initializes the global variables and\r
+  installs the driver binding, component name protocol, iSCSI initiator name\r
+  protocol and Authentication Info protocol on its image.\r
+  \r
+  @param[in]  ImageHandle       The firmware allocated handle for the UEFI image.\r
+  @param[in]  SystemTable       A pointer to the EFI System Table.\r
+\r
+  @retval EFI_SUCCESS           The operation completed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be completed due to a lack of resources.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDriverEntryPoint (\r
+  IN EFI_HANDLE         ImageHandle,\r
+  IN EFI_SYSTEM_TABLE   *SystemTable\r
+  )\r
+{\r
+  EFI_STATUS                         Status;\r
+  EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *IScsiInitiatorName;\r
+  EFI_AUTHENTICATION_INFO_PROTOCOL   *AuthenticationInfo;\r
+\r
+  //\r
+  // There should be only one EFI_ISCSI_INITIATOR_NAME_PROTOCOL.\r
+  //\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiIScsiInitiatorNameProtocolGuid,\r
+                  NULL,\r
+                  (VOID **) &IScsiInitiatorName\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    return EFI_ACCESS_DENIED;\r
+  }\r
+\r
+  //\r
+  // Initialize the EFI Driver Library.\r
+  //\r
+  Status = EfiLibInstallDriverBindingComponentName2 (\r
+             ImageHandle,\r
+             SystemTable,\r
+             &gIScsiDriverBinding,\r
+             ImageHandle,\r
+             &gIScsiComponentName,\r
+             &gIScsiComponentName2\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Install the iSCSI Initiator Name Protocol.\r
+  //\r
+  Status = gBS->InstallProtocolInterface (\r
+                  &ImageHandle,\r
+                  &gEfiIScsiInitiatorNameProtocolGuid,\r
+                  EFI_NATIVE_INTERFACE,\r
+                  &gIScsiInitiatorName\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error1;\r
+  } \r
+\r
+  //\r
+  // Create the private data structures.\r
+  //\r
+  mPrivate = AllocateZeroPool (sizeof (ISCSI_PRIVATE_DATA));\r
+  if (mPrivate == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error2;\r
+  }\r
+\r
+  InitializeListHead (&mPrivate->NicInfoList);\r
+  InitializeListHead (&mPrivate->AttemptConfigs);\r
+\r
+  //\r
+  // Initialize the configuration form of iSCSI.\r
+  //\r
+  Status = IScsiConfigFormInit (gIScsiDriverBinding.DriverBindingHandle);\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error3;\r
+  }\r
+\r
+  //\r
+  // There should be only one EFI_AUTHENTICATION_INFO_PROTOCOL. If already exists,\r
+  // do not produce the protocol instance.\r
+  //\r
+  Status = gBS->LocateProtocol (\r
+                  &gEfiAuthenticationInfoProtocolGuid,\r
+                  NULL,\r
+                  (VOID **) &AuthenticationInfo\r
+                  );\r
+  if (Status == EFI_NOT_FOUND) {\r
+    Status = gBS->InstallProtocolInterface (\r
+                    &ImageHandle,\r
+                    &gEfiAuthenticationInfoProtocolGuid,\r
+                    EFI_NATIVE_INTERFACE,\r
+                    &gIScsiAuthenticationInfo\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error4;\r
+    }    \r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+\r
+Error4:\r
+  IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);\r
+\r
+Error3:\r
+  FreePool (mPrivate);\r
+\r
+Error2:\r
+  gBS->UninstallMultipleProtocolInterfaces (\r
+         ImageHandle,\r
+         &gEfiIScsiInitiatorNameProtocolGuid,\r
+         &gIScsiInitiatorName,\r
+         NULL\r
+         );\r
+\r
+Error1:\r
+  gBS->UninstallMultipleProtocolInterfaces (\r
+         ImageHandle,\r
+         &gEfiDriverBindingProtocolGuid,\r
+         &gIScsiDriverBinding,\r
+         &gEfiComponentName2ProtocolGuid,\r
+         &gIScsiComponentName2,\r
+         &gEfiComponentNameProtocolGuid,\r
+         &gIScsiComponentName,\r
+         NULL\r
+         );\r
+\r
+  return Status;\r
+}\r
+\r
diff --git a/NetworkPkg/IScsiDxe/IScsiDriver.h b/NetworkPkg/IScsiDxe/IScsiDriver.h
new file mode 100644 (file)
index 0000000..ad6b83a
--- /dev/null
@@ -0,0 +1,678 @@
+/** @file\r
+  The header file of IScsiDriver.c.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_DRIVER_H_\r
+#define _ISCSI_DRIVER_H_\r
+\r
+#define ISCSI_V4_PRIVATE_GUID \\r
+  { \\r
+    0xfa3cde4c, 0x87c2, 0x427d, { 0xae, 0xde, 0x7d, 0xd0, 0x96, 0xc8, 0x8c, 0x58 } \\r
+  }\r
+\r
+#define ISCSI_V6_PRIVATE_GUID \\r
+  { \\r
+    0x28be27e5, 0x66cc, 0x4a31, { 0xa3, 0x15, 0xdb, 0x14, 0xc3, 0x74, 0x4d, 0x85 } \\r
+  }\r
+\r
+#define ISCSI_INITIATOR_NAME_VAR_NAME L"I_NAME"\r
+\r
+#define IP_MODE_AUTOCONFIG_IP4     3\r
+#define IP_MODE_AUTOCONFIG_IP6     4\r
+#define IP_MODE_AUTOCONFIG_SUCCESS 5\r
+\r
+extern EFI_COMPONENT_NAME2_PROTOCOL       gIScsiComponentName2;\r
+extern EFI_COMPONENT_NAME_PROTOCOL        gIScsiComponentName;\r
+extern EFI_ISCSI_INITIATOR_NAME_PROTOCOL  gIScsiInitiatorName;\r
+extern EFI_AUTHENTICATION_INFO_PROTOCOL   gIScsiAuthenticationInfo;\r
+extern EFI_EXT_SCSI_PASS_THRU_PROTOCOL    gIScsiExtScsiPassThruProtocolTemplate;\r
+\r
+typedef struct {\r
+  CHAR16          PortString[ISCSI_NAME_IFR_MAX_SIZE];\r
+  LIST_ENTRY      NicInfoList;\r
+  UINT8           NicCount;\r
+  UINT8           CurrentNic;\r
+  UINT8           MaxNic;\r
+  BOOLEAN         Ipv6Flag;\r
+  BOOLEAN         OneSessionEstablished;\r
+  BOOLEAN         EnableMpio;\r
+  UINT8           MpioCount;            // The number of attempts in MPIO.\r
+  UINT8           Krb5MpioCount;        // The number of attempts login with KRB5 in MPIO.\r
+  UINT8           SinglePathCount;      // The number of single path attempts.\r
+  UINT8           ValidSinglePathCount; // The number of valid single path attempts.\r
+  UINT8           BootSelectedIndex;\r
+  UINT8           AttemptCount;\r
+  LIST_ENTRY      AttemptConfigs;       // User configured Attempt list.\r
+  CHAR8           InitiatorName[ISCSI_NAME_MAX_SIZE];\r
+  UINTN           InitiatorNameLength;\r
+} ISCSI_PRIVATE_DATA;\r
+\r
+extern ISCSI_PRIVATE_DATA                 *mPrivate;\r
+\r
+typedef struct {\r
+  LIST_ENTRY      Link;\r
+  UINT32          HwAddressSize;\r
+  EFI_MAC_ADDRESS PermanentAddress;\r
+  UINT8           NicIndex;\r
+  UINT16          VlanId;\r
+  UINTN           BusNumber;\r
+  UINTN           DeviceNumber;\r
+  UINTN           FunctionNumber;\r
+} ISCSI_NIC_INFO;\r
+\r
+typedef struct _ISCSI_PRIVATE_PROTOCOL {\r
+  UINT32  Reserved;\r
+} ISCSI_PRIVATE_PROTOCOL;\r
+\r
+//\r
+// EFI Driver Binding Protocol for iSCSI driver.\r
+//\r
+\r
+/**\r
+  Tests to see if this driver supports a given controller. If a child device is provided, \r
+  it tests to see if this driver supports creating a handle for the specified child device.\r
+\r
+  This function checks to see if the driver specified by This supports the device specified by \r
+  ControllerHandle. Drivers typically use the device path attached to \r
+  ControllerHandle and/or the services from the bus I/O abstraction attached to \r
+  ControllerHandle to determine if the driver supports ControllerHandle. This function \r
+  may be called many times during platform initialization. In order to reduce boot times, the tests \r
+  performed by this function must be very small and take as little time as possible to execute. This \r
+  function must not change the state of any hardware devices, and this function must be aware that the \r
+  device specified by ControllerHandle may already be managed by the same driver or a \r
+  different driver. This function must match its calls to AllocatePages() with FreePages(), \r
+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().  \r
+  Since ControllerHandle may have been previously started by the same driver, if a protocol is \r
+  already in the opened state, then it must not be closed with CloseProtocol(). This is required \r
+  to guarantee the state of ControllerHandle is not modified by this function.\r
+\r
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle     The handle of the controller to test. This handle \r
+                                   must support a protocol interface that supplies \r
+                                   an I/O abstraction to the driver.\r
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This \r
+                                   parameter is ignored by device drivers, and is optional for bus \r
+                                   drivers. For bus drivers, if this parameter is not NULL, then \r
+                                   the bus driver must determine if the bus controller specified \r
+                                   by ControllerHandle and the child controller specified \r
+                                   by RemainingDevicePath are both supported by this \r
+                                   bus driver.\r
+\r
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is supported by the driver specified by This.\r
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already managed by the driver\r
+                                   specified by This.\r
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is already managed by a different\r
+                                   driver or an application that requires exclusive access.\r
+                                   Currently not implemented.\r
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and\r
+                                   RemainingDevicePath is not supported by the driver specified by This.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDriverBindingSupported (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Starts a device controller or a bus controller.\r
+\r
+  The Start() function is designed to be invoked from the EFI boot service ConnectController().\r
+  As a result, much of the error checking on the parameters to Start() has been moved into this \r
+  common boot service. It is legal to call Start() from other locations, \r
+  but the following calling restrictions must be followed or the system behavior will not be deterministic.\r
+  1. ControllerHandle must be a valid EFI_HANDLE.\r
+  2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned\r
+     EFI_DEVICE_PATH_PROTOCOL.\r
+  3. Prior to calling Start(), the Supported() function for the driver specified by This must\r
+     have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.  \r
+\r
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle     The handle of the controller to start. This handle \r
+                                   must support a protocol interface that supplies \r
+                                   an I/O abstraction to the driver.\r
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This \r
+                                   parameter is ignored by device drivers, and is optional for bus \r
+                                   drivers. For a bus driver, if this parameter is NULL, then handles \r
+                                   for all the children of Controller are created by this driver.  \r
+                                   If this parameter is not NULL and the first Device Path Node is \r
+                                   not the End of Device Path Node, then only the handle for the \r
+                                   child device specified by the first Device Path Node of \r
+                                   RemainingDevicePath is created by this driver.\r
+                                   If the first Device Path Node of RemainingDevicePath is \r
+                                   the End of Device Path Node, no child handle is created by this\r
+                                   driver.\r
+\r
+  @retval EFI_SUCCESS              The device was started.\r
+  @retval EFI_DEVICE_ERROR         The device could not be started due to a device error. Currently not implemented.\r
+  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a lack of resources.\r
+  @retval Others                   The driver failed to start the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDriverBindingStart (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL\r
+  );\r
+\r
+/**\r
+  Stops a device controller or a bus controller.\r
+  \r
+  The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). \r
+  As a result, much of the error checking on the parameters to Stop() has been moved \r
+  into this common boot service. It is legal to call Stop() from other locations, \r
+  but the following calling restrictions must be followed or the system behavior will not be deterministic.\r
+  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this\r
+     same driver's Start() function.\r
+  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid\r
+     EFI_HANDLE. In addition, all of these handles must have been created in this driver's\r
+     Start() function, and the Start() function must have called OpenProtocol() on\r
+     ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.\r
+  \r
+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.\r
+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must \r
+                                support a bus specific I/O protocol for the driver \r
+                                to use to stop the device.\r
+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.\r
+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL \r
+                                if NumberOfChildren is 0.\r
+\r
+  @retval EFI_SUCCESS           The device was stopped.\r
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiDriverBindingStop (\r
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,\r
+  IN EFI_HANDLE                   ControllerHandle,\r
+  IN UINTN                        NumberOfChildren,\r
+  IN EFI_HANDLE                   *ChildHandleBuffer OPTIONAL\r
+  );\r
+\r
+//\r
+// EFI Component Name(2) Protocol for iSCSI driver.\r
+//\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user readable name of the driver.\r
+\r
+  This function retrieves the user readable name of a driver in the form of a\r
+  Unicode string. If the driver specified by This has a user readable name in\r
+  the language specified by Language, then a pointer to the driver name is\r
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified\r
+  by This does not support the language specified by Language,\r
+  then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language. This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is up\r
+                                to the driver writer. Language is specified\r
+                                in RFC 4646 or ISO 639-2 language code format.\r
+\r
+  @param[out]  DriverName       A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                driver specified by This in the language\r
+                                specified by Language.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by\r
+                                This and the language specified by Language was\r
+                                returned in DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiComponentNameGetDriverName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,\r
+  IN  CHAR8                         *Language,\r
+  OUT CHAR16                        **DriverName\r
+  );\r
+\r
+/**\r
+  Retrieves a Unicode string that is the user readable name of the controller\r
+  that is being managed by a driver.\r
+\r
+  This function retrieves the user readable name of the controller specified by\r
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the\r
+  driver specified by This has a user readable name in the language specified by\r
+  Language, then a pointer to the controller name is returned in ControllerName,\r
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently\r
+  managing the controller specified by ControllerHandle and ChildHandle,\r
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not\r
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.\r
+\r
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or\r
+                                EFI_COMPONENT_NAME_PROTOCOL instance.\r
+\r
+  @param[in]  ControllerHandle  The handle of a controller that the driver\r
+                                specified by This is managing.  This handle\r
+                                specifies the controller whose name is to be\r
+                                returned.\r
+\r
+  @param[in]  ChildHandle       The handle of the child controller to retrieve\r
+                                the name of.  This is an optional parameter that\r
+                                may be NULL.  It will be NULL for device\r
+                                drivers.  It will also be NULL for a bus drivers\r
+                                that wish to retrieve the name of the bus\r
+                                controller.  It will not be NULL for a bus\r
+                                driver that wishes to retrieve the name of a\r
+                                child controller.\r
+\r
+  @param[in]  Language          A pointer to a Null-terminated ASCII string\r
+                                array indicating the language.  This is the\r
+                                language of the driver name that the caller is\r
+                                requesting, and it must match one of the\r
+                                languages specified in SupportedLanguages. The\r
+                                number of languages supported by a driver is \r
+                                determined by the driver writer. Language is \r
+                                specified inRFC 4646 or ISO 639-2 language code \r
+                                format.\r
+                                \r
+  @param[out]  ControllerName   A pointer to the Unicode string to return.\r
+                                This Unicode string is the name of the\r
+                                controller specified by ControllerHandle and\r
+                                ChildHandle in the language specified by\r
+                                Language from the point of view of the driver\r
+                                specified by This.\r
+\r
+  @retval EFI_SUCCESS           The Unicode string for the user readable name in\r
+                                the language specified by Language for the\r
+                                driver specified by This was returned in\r
+                                DriverName.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid\r
+                                EFI_HANDLE.\r
+\r
+  @retval EFI_INVALID_PARAMETER Language is NULL.\r
+\r
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently\r
+                                managing the controller specified by\r
+                                ControllerHandle and ChildHandle.\r
+\r
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support\r
+                                the language specified by Language.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiComponentNameGetControllerName (\r
+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,\r
+  IN  EFI_HANDLE                    ControllerHandle,\r
+  IN  EFI_HANDLE                    ChildHandle        OPTIONAL,\r
+  IN  CHAR8                         *Language,\r
+  OUT CHAR16                        **ControllerName\r
+  );\r
+\r
+//\r
+// EFI iSCSI Initiator Name Protocol for iSCSI driver.\r
+//\r
+\r
+/**\r
+  Retrieves the current set value of iSCSI Initiator Name.\r
+\r
+  @param[in]       This          Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL\r
+                                 instance.\r
+  @param[in, out]  BufferSize    Size of the buffer in bytes pointed to by Buffer /\r
+                                 Actual size of the variable data buffer.\r
+  @param[out]      Buffer        Pointer to the buffer for data to be read.\r
+\r
+  @retval EFI_SUCCESS            Data was successfully retrieved into the provided\r
+                                 buffer and the BufferSize was sufficient to handle\r
+                                 the iSCSI initiator name.\r
+  @retval EFI_BUFFER_TOO_SMALL   BufferSize is too small for the result. BufferSize\r
+                                 will be updated with the size required to complete\r
+                                 the request. Buffer will not be affected.\r
+  @retval EFI_INVALID_PARAMETER  BufferSize is NULL. BufferSize and Buffer will not\r
+                                 be affected.\r
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL. BufferSize and Buffer will not be\r
+                                 affected.\r
+  @retval EFI_DEVICE_ERROR       The iSCSI initiator name could not be retrieved\r
+                                 due to a hardware error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiGetInitiatorName (\r
+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,\r
+  IN OUT UINTN                              *BufferSize,\r
+  OUT    VOID                               *Buffer\r
+  );\r
+\r
+/**\r
+  Sets the iSSI Initiator Name.\r
+\r
+  @param[in]       This          Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL\r
+                                 instance.\r
+  @param[in, out]  BufferSize    Size of the buffer in bytes pointed to by Buffer.\r
+  @param[in]       Buffer        Pointer to the buffer for data to be written.\r
+\r
+  @retval EFI_SUCCESS            Data was successfully stored by the protocol.\r
+  @retval EFI_UNSUPPORTED        Platform policies do not allow for data to be\r
+                                 written.\r
+  @retval EFI_INVALID_PARAMETER  BufferSize exceeds the maximum allowed limit.\r
+                                 BufferSize will be updated with the maximum size\r
+                                 required to complete the request.\r
+  @retval EFI_INVALID_PARAMETER  Buffersize is NULL. BufferSize and Buffer will not\r
+                                 be affected.\r
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL. BufferSize and Buffer will not be\r
+                                 affected.\r
+  @retval EFI_DEVICE_ERROR       The data could not be stored due to a hardware\r
+                                 error.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the data\r
+  @retval EFI_PROTOCOL_ERROR     Input iSCSI initiator name does not adhere to RFC\r
+                                 3720\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiSetInitiatorName (\r
+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,\r
+  IN OUT UINTN                              *BufferSize,\r
+  IN     VOID                               *Buffer\r
+  );\r
+\r
+//\r
+// EFI_AUTHENTICATION_INFO_PROTOCOL for iSCSI driver.\r
+//\r
+\r
+/**\r
+  Retrieves the authentication information associated with a particular controller handle.\r
+\r
+  @param[in]  This              Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.\r
+  @param[in]  ControllerHandle  Handle to the Controller.\r
+  @param[out] Buffer            Pointer to the authentication information. This function is\r
+                                responsible for allocating the buffer and it is the caller's\r
+                                responsibility to free buffer when the caller is finished with buffer.\r
+\r
+  @retval EFI_DEVICE_ERROR      The authentication information could not be\r
+                                retrieved due to a hardware error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiGetAuthenticationInfo (\r
+  IN  EFI_AUTHENTICATION_INFO_PROTOCOL *This,\r
+  IN  EFI_HANDLE                       ControllerHandle,\r
+  OUT VOID                             **Buffer\r
+  );\r
+\r
+/**\r
+  Set the authentication information for a given controller handle.\r
+\r
+  @param[in]  This             Pointer to the EFI_AUTHENTICATION_INFO_PROTOCOL.\r
+  @param[in]  ControllerHandle Handle to the Controller.\r
+  @param[in]  Buffer           Pointer to the authentication information.\r
+\r
+  @retval EFI_UNSUPPORTED      If the platform policies do not allow setting of\r
+                               the authentication information.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiSetAuthenticationInfo (\r
+  IN EFI_AUTHENTICATION_INFO_PROTOCOL  *This,\r
+  IN EFI_HANDLE                        ControllerHandle,\r
+  IN VOID                              *Buffer\r
+  );\r
+\r
+//\r
+// EFI_EXT_SCSI_PASS_THRU_PROTOCOL for iSCSI driver.\r
+//\r
+\r
+/**\r
+  Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel.\r
+  This function supports both blocking I/O and nonblocking I/O. The blocking I/O\r
+  functionality is required, and the nonblocking I/O functionality is optional.  \r
+\r
+  @param[in]       This    A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.\r
+  @param[in]       Target  The Target is an array of size TARGET_MAX_BYTES and it\r
+                           represents the id of the SCSI device to send the SCSI\r
+                           Request Packet. Each transport driver may choose to\r
+                           utilize a subset of this size to suit the needs\r
+                           of transport target representation. For example, a \r
+                           Fibre Channel driver may use only 8 bytes (WWN)\r
+                           to represent an FC target.\r
+  @param[in]       Lun     The LUN of the SCSI device to send the SCSI Request Packet.\r
+  @param[in, out]  Packet  A pointer to the SCSI Request Packet to send to the\r
+                           SCSI device specified by Target and Lun.                  \r
+  @param[in]       Event   If nonblocking I/O is not supported then Event is ignored,\r
+                           and blocking I/O is performed. If Event is NULL, then\r
+                           blocking I/O is performed. If Event is not NULL and non\r
+                           blocking I/O is supported, then nonblocking I/O is performed,\r
+                           and Event will be signaled when the SCSI Request Packet\r
+                           completes.\r
+\r
+  @retval EFI_SUCCESS           The SCSI Request Packet was sent by the host. For\r
+                                bi-directional commands, InTransferLength bytes \r
+                                were transferred from InDataBuffer.\r
+                                For write and bi-directional commands, OutTransferLength\r
+                                bytes were transferred by OutDataBuffer.\r
+  @retval EFI_BAD_BUFFER_SIZE   The SCSI Request Packet was not executed.\r
+                                The number of bytes that could be transferred is\r
+                                returned in InTransferLength. For write and\r
+                                bi-directional commands, OutTransferLength bytes\r
+                                were transferred by OutDataBuffer.\r
+  @retval EFI_NOT_READY         The SCSI Request Packet could not be sent because\r
+                                there are too many SCSI Request Packets already\r
+                                queued. The caller may retry later.\r
+  @retval EFI_DEVICE_ERROR      A device error occurred while attempting to send\r
+                                the SCSI Request Packet.                                \r
+  @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket\r
+                                are invalid.\r
+  @retval EFI_UNSUPPORTED       The command described by the SCSI Request Packet\r
+                                is not supported by the host adapter.\r
+                                This includes the case of Bi-directional SCSI\r
+                                commands not supported by the implementation.\r
+                                The SCSI Request Packet was not sent,\r
+                                so no additional status information is available.\r
+  @retval EFI_TIMEOUT           A timeout occurred while waiting for the SCSI\r
+                                Request Packet to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruFunction (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                          *This,\r
+  IN UINT8                                                    *Target,\r
+  IN UINT64                                                   Lun,\r
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET           *Packet,\r
+  IN EFI_EVENT                                                Event     OPTIONAL\r
+  );\r
+\r
+/**\r
+  Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on\r
+  a SCSI channel. These can either be the list SCSI devices that are actually\r
+  present on the SCSI channel, or the list of legal Target Ids and LUNs for the\r
+  SCSI channel. Regardless, the caller of this function must probe the Target ID      \r
+  and LUN returned to see if a SCSI device is actually present at that location    \r
+  on the SCSI channel. \r
+\r
+  @param[in]       This          The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.\r
+  @param[in, out]  Target        On input, a pointer to the Target ID of a SCSI\r
+                                 device present on the SCSI channel.  On output, a\r
+                                 pointer to the Target ID of the next SCSI device\r
+                                 present on a SCSI channel.  An input value of\r
+                                 0xFFFFFFFF retrieves the Target ID of the first\r
+                                 SCSI device present on a SCSI channel.\r
+  @param[in, out]  Lun           On input, a pointer to the LUN of a SCSI device\r
+                                 present on the SCSI channel. On output, a pointer\r
+                                 to the LUN of the next SCSI device present on a\r
+                                 SCSI channel.\r
+\r
+  @retval EFI_SUCCESS            The Target ID and Lun of the next SCSI device  on\r
+                                 the SCSI channel was returned in Target and Lun.\r
+  @retval EFI_NOT_FOUND          There are no more SCSI devices on this SCSI\r
+                                 channel.\r
+  @retval EFI_INVALID_PARAMETER  Target is not 0xFFFFFFFF,and Target and Lun were\r
+                                 not returned on a previous call to\r
+                                 GetNextDevice().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruGetNextTargetLun (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN OUT UINT8                        **Target,\r
+  IN OUT UINT64                       *Lun\r
+  );\r
+\r
+/**\r
+  Allocate and build a device path node for a SCSI device on a SCSI channel.\r
+\r
+  @param[in]       This          Protocol instance pointer.\r
+  @param[in]       Target        The Target ID of the SCSI device for which a\r
+                                 device path node is to be allocated and built.\r
+  @param[in]       Lun           The LUN of the SCSI device for which a device\r
+                                 path node is to be allocated and built.\r
+  @param[in, out]  DevicePath    A pointer to a single device path node that\r
+                                 describes the SCSI device specified by  Target and\r
+                                 Lun. This function is responsible  for allocating\r
+                                 the buffer DevicePath with the boot service\r
+                                 AllocatePool().  It is the caller's\r
+                                 responsibility to free DevicePath when the caller\r
+                                 is finished with DevicePath.\r
+\r
+  @retval EFI_SUCCESS            The device path node that describes the SCSI\r
+                                 device specified by Target and Lun was allocated\r
+                                 and  returned in DevicePath.\r
+  @retval EFI_NOT_FOUND          The SCSI devices specified by Target and Lun does\r
+                                 not exist on the SCSI channel.\r
+  @retval EFI_INVALID_PARAMETER  DevicePath is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to allocate\r
+                                 DevicePath.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruBuildDevicePath (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN UINT8                            *Target,\r
+  IN UINT64                           Lun,\r
+  IN OUT EFI_DEVICE_PATH_PROTOCOL     **DevicePath\r
+  );\r
+\r
+/**\r
+  Translate a device path node to a Target ID and LUN.\r
+\r
+  @param[in]   This              Protocol instance pointer.\r
+  @param[in]   DevicePath        A pointer to the device path node that  describes\r
+                                 a SCSI device on the SCSI channel.\r
+  @param[out]  Target            A pointer to the Target ID of a SCSI device  on\r
+                                 the SCSI channel.\r
+  @param[out]  Lun               A pointer to the LUN of a SCSI device on  the SCSI\r
+                                 channel.\r
+\r
+  @retval EFI_SUCCESS            DevicePath was successfully translated to a\r
+                                 Target ID and LUN, and they were returned  in\r
+                                 Target and Lun.\r
+  @retval EFI_INVALID_PARAMETER  DevicePath/Target/Lun is NULL.\r
+  @retval EFI_UNSUPPORTED        This driver does not support the device path node\r
+                                 type in DevicePath.\r
+  @retval EFI_NOT_FOUND          A valid translation from DevicePath to a  Target\r
+                                 ID and LUN does not exist.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruGetTargetLun (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN EFI_DEVICE_PATH_PROTOCOL         *DevicePath,\r
+  OUT UINT8                           **Target,\r
+  OUT UINT64                          *Lun\r
+  );\r
+\r
+/**\r
+  Resets a SCSI channel.This operation resets all the SCSI devices connected to\r
+  the SCSI channel.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+\r
+  @retval EFI_UNSUPPORTED        It is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruResetChannel (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This\r
+  );\r
+\r
+/**\r
+  Resets a SCSI device that is connected to a SCSI channel.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  Target             The Target ID of the SCSI device to reset.\r
+  @param[in]  Lun                The LUN of the SCSI device to reset.\r
+\r
+  @retval EFI_UNSUPPORTED        It is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruResetTargetLun (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN UINT8                            *Target,\r
+  IN UINT64                           Lun\r
+  );\r
+\r
+/**\r
+  Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel.                         \r
+\r
+  @param[in]       This         A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL\r
+                                instance.\r
+  @param[in, out]  Target       (TARGET_MAX_BYTES) of a SCSI device present on\r
+                                the SCSI channel. On output, a pointer to the\r
+                                Target ID (an array of TARGET_MAX_BYTES) of the\r
+                                next SCSI device present on a SCSI channel.\r
+                                An input value of 0xF(all bytes in the array are 0xF)\r
+                                in the Target array retrieves the Target ID of the\r
+                                first SCSI device present on a SCSI channel.                 \r
+\r
+  @retval EFI_SUCCESS           The Target ID of the next SCSI device on the SCSI\r
+                                channel was returned in Target.\r
+  @retval EFI_INVALID_PARAMETER Target or Lun is NULL.\r
+  @retval EFI_TIMEOUT           Target array is not all 0xF, and Target was not\r
+                                returned on a previous call to GetNextTarget().\r
+  @retval EFI_NOT_FOUND         There are no more SCSI devices on this SCSI channel.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruGetNextTarget (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN OUT UINT8                        **Target\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiDxe.inf b/NetworkPkg/IScsiDxe/IScsiDxe.inf
new file mode 100644 (file)
index 0000000..24799fc
--- /dev/null
@@ -0,0 +1,106 @@
+## @file\r
+#  Component description file for IScsi module.\r
+#\r
+# Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions of the BSD License\r
+# which accompanies this distribution.  The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = IScsiDxe\r
+  FILE_GUID                      = 86CDDF93-4872-4597-8AF9-A35AE4D3725F\r
+  MODULE_TYPE                    = UEFI_DRIVER\r
+  VERSION_STRING                 = 1.0\r
+  ENTRY_POINT                    = IScsiDriverEntryPoint\r
+  UNLOAD_IMAGE                   = IScsiUnload\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF\r
+#\r
+#  DRIVER_BINDING                =  gIScsiDriverBinding\r
+#  COMPONENT_NAME                =  gIScsiComponentName\r
+#  COMPONENT_NAME2               =  gIScsiComponentName2\r
+#\r
+\r
+\r
+[Sources]\r
+  ComponentName.c\r
+  IScsiAuthenticationInfo.c\r
+  IScsiCHAP.h\r
+  IScsiCHAP.c\r
+  IScsiConfig.c\r
+  IScsiConfig.h\r
+  IScsiConfigNVDataStruc.h\r
+  IScsiConfigStrings.uni\r
+  IScsiConfigVfr.vfr\r
+  IScsiDhcp.c\r
+  IScsiDhcp.h\r
+  IScsiDhcp6.c\r
+  IScsiDhcp6.h\r
+  IScsiDriver.c\r
+  IScsiDriver.h\r
+  IScsiExtScsiPassThru.c\r
+  IScsiIbft.c\r
+  IScsiIbft.h\r
+  IScsiInitiatorName.c \r
+  IScsiImpl.h\r
+  IScsiMisc.c\r
+  IScsiMisc.h\r
+  IScsiProto.c\r
+  IScsiProto.h\r
+\r
+[Packages]\r
+  MdePkg/MdePkg.dec\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  CryptoPkg/CryptoPkg.dec\r
+\r
+[LibraryClasses]\r
+  BaseLib\r
+  BaseMemoryLib\r
+  DebugLib\r
+  DevicePathLib\r
+  HiiLib\r
+  MemoryAllocationLib\r
+  NetLib\r
+  TcpIoLib\r
+  PrintLib\r
+  UefiBootServicesTableLib\r
+  UefiDriverEntryPoint\r
+  UefiLib  \r
+  UefiRuntimeServicesTableLib\r
+  UefiHiiServicesLib\r
+  BaseCryptLib\r
+\r
+[Protocols]\r
+  gEfiAcpiTableProtocolGuid                     # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDriverBindingProtocolGuid                 # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiPciIoProtocolGuid                         # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDhcp4ProtocolGuid                         # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDhcp6ProtocolGuid                         # PROTOCOL ALWAYS_CONSUMED  \r
+  gEfiDhcp4ServiceBindingProtocolGuid           # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDhcp6ServiceBindingProtocolGuid           # PROTOCOL ALWAYS_CONSUMED  \r
+  gEfiTcp4ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiTcp6ProtocolGuid                          # PROTOCOL ALWAYS_CONSUMED  \r
+  gEfiTcp4ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiTcp6ServiceBindingProtocolGuid            # PROTOCOL ALWAYS_CONSUMED  \r
+  gEfiExtScsiPassThruProtocolGuid               # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiHiiConfigAccessProtocolGuid               # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiDevicePathProtocolGuid                    # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiIScsiInitiatorNameProtocolGuid            # PROTOCOL ALWAYS_CONSUMED\r
+  gEfiAuthenticationInfoProtocolGuid            # PROTOCOL ALWAYS_CONSUMED  \r
+\r
+[Guids]\r
+  gEfiEventExitBootServicesGuid\r
+  gEfiIfrTianoGuid                              ## CONSUMES ## GUID\r
+  gEfiAcpiTableGuid                             ## CONSUMES ## GUID\r
+  gEfiAcpi10TableGuid                           ## CONSUMES ## GUID\r
+  gEfiAcpi20TableGuid                           ## CONSUMES ## GUID\r
diff --git a/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c b/NetworkPkg/IScsiDxe/IScsiExtScsiPassThru.c
new file mode 100644 (file)
index 0000000..c4aed4c
--- /dev/null
@@ -0,0 +1,409 @@
+/** @file\r
+  The implementation of EFI_EXT_SCSI_PASS_THRU_PROTOCOL.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate = {\r
+  NULL,\r
+  IScsiExtScsiPassThruFunction,\r
+  IScsiExtScsiPassThruGetNextTargetLun,\r
+  IScsiExtScsiPassThruBuildDevicePath,\r
+  IScsiExtScsiPassThruGetTargetLun,\r
+  IScsiExtScsiPassThruResetChannel,\r
+  IScsiExtScsiPassThruResetTargetLun,\r
+  IScsiExtScsiPassThruGetNextTarget\r
+};\r
+\r
+\r
+/**\r
+  Sends a SCSI Request Packet to a SCSI device that is attached to the SCSI channel.\r
+  This function supports both blocking I/O and nonblocking I/O. The blocking I/O\r
+  functionality is required, and the nonblocking I/O functionality is optional.  \r
+\r
+  @param[in]       This    A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.\r
+  @param[in]       Target  The Target is an array of size TARGET_MAX_BYTES and it\r
+                           represents the id of the SCSI device to send the SCSI\r
+                           Request Packet. Each transport driver may choose to\r
+                           utilize a subset of this size to suit the needs\r
+                           of transport target representation. For example, a \r
+                           Fibre Channel driver may use only 8 bytes (WWN)\r
+                           to represent an FC target.\r
+  @param[in]       Lun     The LUN of the SCSI device to send the SCSI Request Packet.\r
+  @param[in, out]  Packet  A pointer to the SCSI Request Packet to send to the\r
+                           SCSI device specified by Target and Lun.                  \r
+  @param[in]       Event   If nonblocking I/O is not supported then Event is ignored,\r
+                           and blocking I/O is performed. If Event is NULL, then\r
+                           blocking I/O is performed. If Event is not NULL and non\r
+                           blocking I/O is supported, then nonblocking I/O is performed,\r
+                           and Event will be signaled when the SCSI Request Packet\r
+                           completes.\r
+\r
+  @retval EFI_SUCCESS           The SCSI Request Packet was sent by the host. For\r
+                                bi-directional commands, InTransferLength bytes \r
+                                were transferred from InDataBuffer.\r
+                                For write and bi-directional commands, OutTransferLength\r
+                                bytes were transferred by OutDataBuffer.\r
+  @retval EFI_BAD_BUFFER_SIZE   The SCSI Request Packet was not executed.\r
+                                The number of bytes that could be transferred is\r
+                                returned in InTransferLength. For write and\r
+                                bi-directional commands, OutTransferLength bytes\r
+                                were transferred by OutDataBuffer.\r
+  @retval EFI_NOT_READY         The SCSI Request Packet could not be sent because\r
+                                there are too many SCSI Request Packets already\r
+                                queued. The caller may retry later.\r
+  @retval EFI_DEVICE_ERROR      A device error occurred while attempting to send\r
+                                the SCSI Request Packet.                                \r
+  @retval EFI_INVALID_PARAMETER Target, Lun, or the contents of ScsiRequestPacket,\r
+                                are invalid.\r
+  @retval EFI_UNSUPPORTED       The command described by the SCSI Request Packet\r
+                                is not supported by the host adapter.\r
+                                This includes the case of Bi-directional SCSI\r
+                                commands not supported by the implementation.\r
+                                The SCSI Request Packet was not sent,\r
+                                so no additional status information is available.\r
+  @retval EFI_TIMEOUT           A timeout occurred while waiting for the SCSI\r
+                                Request Packet to execute.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruFunction (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                          *This,\r
+  IN UINT8                                                    *Target,\r
+  IN UINT64                                                   Lun,\r
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET           *Packet,\r
+  IN EFI_EVENT                                                Event     OPTIONAL\r
+  )\r
+{\r
+  if (Target[0] != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Packet == NULL) || (Packet->Cdb == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return IScsiExecuteScsiCommand (This, Target, Lun, Packet);\r
+}\r
+\r
+\r
+/**\r
+  Used to retrieve the list of legal Target IDs and LUNs for SCSI devices on\r
+  a SCSI channel. These can either be the list SCSI devices that are actually\r
+  present on the SCSI channel, or the list of legal Target Ids and LUNs for the\r
+  SCSI channel. Regardless, the caller of this function must probe the Target ID      \r
+  and LUN returned to see if a SCSI device is actually present at that location    \r
+  on the SCSI channel. \r
+\r
+  @param[in]       This          The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.\r
+  @param[in, out]  Target        On input, a pointer to the Target ID of a SCSI\r
+                                 device present on the SCSI channel.  On output, a\r
+                                 pointer to the Target ID of the next SCSI device\r
+                                 present on a SCSI channel.  An input value of\r
+                                 0xFFFFFFFF retrieves the Target ID of the first\r
+                                 SCSI device present on a SCSI channel.\r
+  @param[in, out]  Lun           On input, a pointer to the LUN of a SCSI device\r
+                                 present on the SCSI channel. On output, a pointer\r
+                                 to the LUN of the next SCSI device present on a\r
+                                 SCSI channel.\r
+\r
+  @retval EFI_SUCCESS            The Target ID and Lun of the next SCSI device on\r
+                                 the SCSI channel was returned in Target and Lun.\r
+  @retval EFI_NOT_FOUND          There are no more SCSI devices on this SCSI\r
+                                 channel.\r
+  @retval EFI_INVALID_PARAMETER  Target is not 0xFFFFFFFF,and Target and Lun were\r
+                                 not returned on a previous call to\r
+                                 GetNextDevice().\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruGetNextTargetLun (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN OUT UINT8                        **Target,\r
+  IN OUT UINT64                       *Lun\r
+  )\r
+{\r
+  ISCSI_DRIVER_DATA           *Private;\r
+  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;\r
+  UINT8                       TargetId[TARGET_MAX_BYTES];\r
+\r
+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);\r
+  ConfigNvData  = &Private->Session->ConfigData->SessionConfigData;\r
+\r
+  if ((*Target)[0] == 0 && (CompareMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)) == 0)) {\r
+    //\r
+    // Only one <Target, Lun> pair per iSCSI Driver instance.\r
+    //\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);\r
+  if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {\r
+    (*Target)[0] = 0;\r
+    CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+\r
+/**\r
+  Allocate and build a device path node for a SCSI device on a SCSI channel.\r
+\r
+  @param[in]       This          Protocol instance pointer.\r
+  @param[in]       Target        The Target ID of the SCSI device for which a\r
+                                 device path node is to be allocated and built.\r
+  @param[in]       Lun           The LUN of the SCSI device for which a device\r
+                                 path node is to be allocated and built.\r
+  @param[in, out]  DevicePath    A pointer to a single device path node that\r
+                                 describes the SCSI device specified by  Target and\r
+                                 Lun. This function is responsible  for allocating\r
+                                 the buffer DevicePath with the boot service\r
+                                 AllocatePool().  It is the caller's\r
+                                 responsibility to free DevicePath when the caller\r
+                                 is finished with DevicePath.\r
+\r
+  @retval EFI_SUCCESS            The device path node that describes the SCSI\r
+                                 device specified by Target and Lun was allocated\r
+                                 and  returned in DevicePath.\r
+  @retval EFI_NOT_FOUND          The SCSI devices specified by Target and Lun does\r
+                                 not exist on the SCSI channel.\r
+  @retval EFI_INVALID_PARAMETER  DevicePath is NULL.\r
+  @retval EFI_OUT_OF_RESOURCES   There are not enough resources to allocate\r
+                                 DevicePath.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruBuildDevicePath (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN UINT8                            *Target,\r
+  IN UINT64                           Lun,\r
+  IN OUT EFI_DEVICE_PATH_PROTOCOL     **DevicePath\r
+  )\r
+{\r
+  ISCSI_DRIVER_DATA             *Private;\r
+  ISCSI_SESSION                 *Session;\r
+  ISCSI_SESSION_CONFIG_NVDATA   *ConfigNvData;\r
+  ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;\r
+  EFI_DEV_PATH                  *Node;\r
+  UINTN                         DevPathNodeLen;\r
+\r
+  if ((DevicePath == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (Target[0] != 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);\r
+  Session       = Private->Session;\r
+  ConfigNvData  = &Session->ConfigData->SessionConfigData;\r
+  AuthConfig    = Session->AuthData.CHAP.AuthConfig;\r
+\r
+  if (CompareMem (&Lun, ConfigNvData->BootLun, sizeof (UINT64)) != 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  DevPathNodeLen  = sizeof (ISCSI_DEVICE_PATH) + AsciiStrLen (ConfigNvData->TargetName) + 1;\r
+  Node            = AllocateZeroPool (DevPathNodeLen);\r
+  if (Node == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;\r
+  Node->DevPath.SubType = MSG_ISCSI_DP;\r
+  SetDevicePathNodeLength (&Node->DevPath, DevPathNodeLen);\r
+\r
+  //\r
+  // 0 for TCP, others are reserved.\r
+  //\r
+  Node->Iscsi.NetworkProtocol = 0;\r
+\r
+  Node->Iscsi.LoginOption     = 0;\r
+\r
+  switch (Session->AuthType) {\r
+  case ISCSI_AUTH_TYPE_NONE:\r
+    Node->Iscsi.LoginOption |= 0x0800;\r
+    break;\r
+\r
+  case ISCSI_AUTH_TYPE_CHAP:\r
+    //\r
+    // Bit12: 0=CHAP_BI, 1=CHAP_UNI\r
+    //\r
+    if (AuthConfig->CHAPType == ISCSI_CHAP_UNI) {\r
+      Node->Iscsi.LoginOption |= 0x1000;\r
+    }\r
+    break;\r
+\r
+  default:\r
+    break;\r
+  }\r
+\r
+  CopyMem (&Node->Iscsi.Lun, ConfigNvData->BootLun, sizeof (UINT64));\r
+  Node->Iscsi.TargetPortalGroupTag = Session->TargetPortalGroupTag;\r
+  AsciiStrCpy ((CHAR8 *) Node + sizeof (ISCSI_DEVICE_PATH), ConfigNvData->TargetName);\r
+\r
+  *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Translate a device path node to a Target ID and LUN.\r
+\r
+  @param[in]   This              Protocol instance pointer.\r
+  @param[in]   DevicePath        A pointer to the device path node that  describes\r
+                                 a SCSI device on the SCSI channel.\r
+  @param[out]  Target            A pointer to the Target ID of a SCSI device on\r
+                                 the SCSI channel.\r
+  @param[out]  Lun               A pointer to the LUN of a SCSI device on the SCSI\r
+                                 channel.\r
+\r
+  @retval EFI_SUCCESS            DevicePath was successfully translated to a\r
+                                 Target ID and LUN, and they were returned  in\r
+                                 Target and Lun.\r
+  @retval EFI_INVALID_PARAMETER  DevicePath/Target/Lun is NULL.\r
+  @retval EFI_UNSUPPORTED        This driver does not support the device path  node\r
+                                 type in DevicePath.\r
+  @retval EFI_NOT_FOUND          A valid translation does not exist from DevicePath \r
+                                 to a TargetID and LUN.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruGetTargetLun (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN EFI_DEVICE_PATH_PROTOCOL         *DevicePath,\r
+  OUT UINT8                           **Target,\r
+  OUT UINT64                          *Lun\r
+  )\r
+{\r
+  ISCSI_DRIVER_DATA           *Private;\r
+  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;\r
+\r
+  if ((DevicePath == NULL) || (Target == NULL) || (Lun == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||\r
+      (DevicePath->SubType != MSG_ISCSI_DP) ||\r
+      (DevicePathNodeLength (DevicePath) <= sizeof (ISCSI_DEVICE_PATH))\r
+      ) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);\r
+  ConfigNvData  = &Private->Session->ConfigData->SessionConfigData;\r
+\r
+  SetMem (*Target, TARGET_MAX_BYTES, 0xFF);\r
+  (*Target)[0] = 0;\r
+\r
+  if (AsciiStrCmp (ConfigNvData->TargetName, (CHAR8 *) DevicePath + sizeof (ISCSI_DEVICE_PATH)) != 0) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+\r
+  CopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Resets a SCSI channel. This operation resets all the SCSI devices connected to\r
+  the SCSI channel.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+\r
+  @retval EFI_UNSUPPORTED        It is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruResetChannel (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+\r
+/**\r
+  Resets a SCSI device that is connected to a SCSI channel.\r
+\r
+  @param[in]  This               Protocol instance pointer.\r
+  @param[in]  Target             The Target ID of the SCSI device to reset.\r
+  @param[in]  Lun                The LUN of the SCSI device to reset.\r
+\r
+  @retval EFI_UNSUPPORTED        It is not supported.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruResetTargetLun (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN UINT8                            *Target,\r
+  IN UINT64                           Lun\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel.                         \r
+\r
+  @param[in]       This         A pointer to the EFI_EXT_SCSI_PASS_THRU_PROTOCOL\r
+                                instance.\r
+  @param[in, out]  Target       (TARGET_MAX_BYTES) of a SCSI device present on\r
+                                the SCSI channel. On output, a pointer to the\r
+                                Target ID (an array of TARGET_MAX_BYTES) of the\r
+                                next SCSI device present on a SCSI channel.\r
+                                An input value of 0xF(all bytes in the array are 0xF)\r
+                                in the Target array retrieves the Target ID of the\r
+                                first SCSI device present on a SCSI channel.                 \r
+\r
+  @retval EFI_SUCCESS           The Target ID of the next SCSI device on the SCSI\r
+                                channel was returned in Target.\r
+  @retval EFI_INVALID_PARAMETER Target or Lun is NULL.\r
+  @retval EFI_TIMEOUT           Target array is not all 0xF, and Target was not\r
+                                returned on a previous call to GetNextTarget().\r
+  @retval EFI_NOT_FOUND         There are no more SCSI devices on this SCSI channel.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiExtScsiPassThruGetNextTarget (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,\r
+  IN OUT UINT8                        **Target\r
+  )\r
+{\r
+  UINT8 TargetId[TARGET_MAX_BYTES];\r
+\r
+  SetMem (TargetId, TARGET_MAX_BYTES, 0xFF);\r
+\r
+  if (CompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {\r
+    (*Target)[0] = 0;\r
+    return EFI_SUCCESS;\r
+  } else if ((*Target)[0] == 0) {\r
+    return EFI_NOT_FOUND;\r
+  } else {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+}\r
+\r
diff --git a/NetworkPkg/IScsiDxe/IScsiIbft.c b/NetworkPkg/IScsiDxe/IScsiIbft.c
new file mode 100644 (file)
index 0000000..e90c982
--- /dev/null
@@ -0,0 +1,546 @@
+/** @file\r
+  Implementation for iSCSI Boot Firmware Table publication.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+BOOLEAN mIbftInstalled = FALSE;\r
+UINTN   mTableKey;\r
+\r
+/**\r
+  Initialize the header of the iSCSI Boot Firmware Table.\r
+  \r
+  @param[out]  Header     The header of the iSCSI Boot Firmware Table.\r
+  @param[in]   OemId      The OEM ID.\r
+  @param[in]   OemTableId The OEM table ID for the iBFT.\r
+\r
+**/\r
+VOID\r
+IScsiInitIbfTableHeader (\r
+  OUT EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER   *Header,\r
+  IN  UINT8                                       *OemId,\r
+  IN  UINT64                                      *OemTableId\r
+  )\r
+{\r
+  Header->Signature = EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE;\r
+  Header->Length    = IBFT_HEAP_OFFSET;\r
+  Header->Revision  = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_REVISION;\r
+  Header->Checksum  = 0;\r
+\r
+  CopyMem (Header->OemId, OemId, sizeof (Header->OemId));\r
+  CopyMem (&Header->OemTableId, OemTableId, sizeof (UINT64));\r
+}\r
+\r
+\r
+/**\r
+  Initialize the control section of the iSCSI Boot Firmware Table.\r
+\r
+  @param[in]  Table       The ACPI table.\r
+\r
+**/\r
+VOID\r
+IScsiInitControlSection (\r
+  IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER  *Table\r
+  )\r
+{\r
+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE  *Control;\r
+  UINTN                                                 NumOffset;\r
+\r
+  Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);\r
+\r
+  Control->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_ID;\r
+  Control->Header.Version     = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_VERSION;\r
+  Control->Header.Length      = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE);\r
+\r
+  //\r
+  // If in multipathing mode, enable the Boot Failover Flag.\r
+  // If in single path mode, disable it. Mix-model is not allowed.\r
+  //\r
+  // BUGBUG: if Boot Failover Flag is set to 1, the OS installer cannot\r
+  // find the iSCSI mapped disk. So still keep not set for single path mode.\r
+  //\r
+  if (mPrivate->EnableMpio) {\r
+    Control->Header.Flags = 0;\r
+    NumOffset = 2 * (mPrivate->MpioCount - mPrivate->Krb5MpioCount);\r
+  } else {\r
+    NumOffset = 2 * mPrivate->ValidSinglePathCount;\r
+  }\r
+\r
+  //\r
+  // Each attempt occupies two offsets: one for the NIC section;\r
+  // the other for the Target section.\r
+  //\r
+  if (NumOffset > 4) {\r
+    //\r
+    // Need expand the control section if more than 2 NIC/Target attempts\r
+    // exist.\r
+    //\r
+    Control->Header.Length = (UINT16) (Control->Header.Length + (NumOffset - 4) * sizeof (UINT16));\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Add one item into the heap.\r
+\r
+  @param[in, out]  Heap  On input, the current address of the heap. On output, the address of\r
+                         the heap after the item is added.\r
+  @param[in]       Data  The data to add into the heap.\r
+  @param[in]       Len   Length of the Data in byte.\r
+\r
+**/\r
+VOID\r
+IScsiAddHeapItem (\r
+  IN OUT UINT8  **Heap,\r
+  IN     VOID   *Data,\r
+  IN     UINTN  Len\r
+  )\r
+{\r
+  //\r
+  // Add one byte for the NULL delimiter.\r
+  //\r
+  *Heap -= Len + 1;\r
+\r
+  CopyMem (*Heap, Data, Len);\r
+  *(*Heap + Len) = 0;\r
+}\r
+\r
+\r
+/**\r
+  Fill the Initiator section of the iSCSI Boot Firmware Table.\r
+\r
+  @param[in]       Table    The ACPI table.\r
+  @param[in, out]  Heap     The heap.\r
+\r
+**/\r
+VOID\r
+IScsiFillInitiatorSection (\r
+  IN     EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER  *Table,\r
+  IN OUT UINT8                                      **Heap\r
+  )\r
+{\r
+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE    *Control;\r
+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE  *Initiator;\r
+\r
+  Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);\r
+\r
+  //\r
+  // Initiator section immediately follows the control section.\r
+  //\r
+  Initiator = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *)\r
+              ((UINT8 *) Control + IBFT_ROUNDUP (Control->Header.Length));\r
+\r
+  Control->InitiatorOffset = (UINT16) ((UINTN) Initiator - (UINTN) Table);\r
+\r
+  Initiator->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_ID;\r
+  Initiator->Header.Version     = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_VERSION;\r
+  Initiator->Header.Length      = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE);\r
+  Initiator->Header.Flags       = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BLOCK_VALID |\r
+                                  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BOOT_SELECTED;\r
+\r
+  //\r
+  // Fill the iSCSI Initiator Name into the heap.\r
+  //\r
+  IScsiAddHeapItem (Heap, mPrivate->InitiatorName, mPrivate->InitiatorNameLength - 1);\r
+\r
+  Initiator->IScsiNameLength  = (UINT16) (mPrivate->InitiatorNameLength - 1);\r
+  Initiator->IScsiNameOffset  = (UINT16) ((UINTN) *Heap - (UINTN) Table);\r
+}\r
+\r
+\r
+/**\r
+  Map the v4 IP address into v6 IP address.\r
+\r
+  @param[in]   V4 The v4 IP address.\r
+  @param[out]  V6 The v6 IP address.\r
+\r
+**/\r
+VOID\r
+IScsiMapV4ToV6Addr (\r
+  IN  EFI_IPv4_ADDRESS *V4,\r
+  OUT EFI_IPv6_ADDRESS *V6\r
+  )\r
+{\r
+  UINTN Index;\r
+\r
+  ZeroMem (V6, sizeof (EFI_IPv6_ADDRESS));\r
+\r
+  V6->Addr[10]  = 0xff;\r
+  V6->Addr[11]  = 0xff;\r
+\r
+  for (Index = 0; Index < 4; Index++) {\r
+    V6->Addr[12 + Index] = V4->Addr[Index];\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Fill the NIC and target sections in iSCSI Boot Firmware Table.\r
+\r
+  @param[in]       Table    The buffer of the ACPI table.\r
+  @param[in, out]  Heap     The heap buffer used to store the variable length\r
+                            parameters such as iSCSI name.\r
+\r
+**/\r
+VOID\r
+IScsiFillNICAndTargetSections (\r
+  IN     EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER  *Table,\r
+  IN OUT UINT8                                      **Heap\r
+  )\r
+{\r
+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE  *Control;\r
+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE      *Nic;\r
+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE   *Target;\r
+  ISCSI_SESSION_CONFIG_NVDATA                           *NvData;\r
+  ISCSI_CHAP_AUTH_CONFIG_NVDATA                         *AuthConfig;\r
+  UINT16                                                *SectionOffset;\r
+  UINTN                                                 Index;\r
+  UINT16                                                Length;\r
+  LIST_ENTRY                                            *Entry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA                           *Attempt;\r
+  ISCSI_NIC_INFO                                        *NicInfo;\r
+  BOOLEAN                                               Flag;\r
+\r
+  //\r
+  // Get the offset of the first Nic and Target section.\r
+  //\r
+  Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);\r
+  Nic     = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Table +\r
+          Control->InitiatorOffset + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE)));\r
+  Target  = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +\r
+          IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));\r
+\r
+  SectionOffset = &Control->NIC0Offset;\r
+\r
+  Index = 0;\r
+  Flag  = TRUE;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {\r
+    if (Index == 0) {\r
+      //\r
+      // First entry should be boot selected entry.\r
+      //\r
+      Attempt = IScsiConfigGetAttemptByConfigIndex (mPrivate->BootSelectedIndex);\r
+      if (Attempt == NULL) {\r
+        //\r
+        // First boot selected entry can not be found.\r
+        //\r
+        break;\r
+      }\r
+\r
+      ASSERT (Attempt->SessionConfigData.Enabled != ISCSI_DISABLED);\r
+\r
+    } else {\r
+      if (Index == 1 && Flag) {\r
+        Entry = mPrivate->AttemptConfigs.ForwardLink;\r
+        Flag = FALSE;\r
+      }\r
+\r
+      Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+      if (Attempt->AttemptConfigIndex == mPrivate->BootSelectedIndex) {\r
+        continue;\r
+      }\r
+    }\r
+\r
+    if (Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Krb5 attempt will not be recorded in iBFT.\r
+    //\r
+    if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_KRB) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // If multipath mode is enabled, only the attempts in MPIO will be recorded in iBFT.\r
+    //\r
+    if (mPrivate->EnableMpio && Attempt->SessionConfigData.Enabled != ISCSI_ENABLED_FOR_MPIO) {\r
+      continue;\r
+    }\r
+\r
+    //\r
+    // Only the valid attempts will be recorded.\r
+    //\r
+    if (!Attempt->ValidiBFTPath) {\r
+      continue;\r
+    }\r
+\r
+    NvData     = &Attempt->SessionConfigData;\r
+    AuthConfig = &Attempt->AuthConfigData.CHAP;\r
+\r
+    //\r
+    // Fill the Nic section.\r
+    //\r
+\r
+    Nic->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_ID;\r
+    Nic->Header.Version     = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_VERSION;\r
+    Nic->Header.Length      = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE);\r
+    Nic->Header.Index       = (UINT8) Index;\r
+    Nic->Header.Flags       = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BLOCK_VALID |\r
+                            EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_GLOBAL;\r
+\r
+    if (Index == 0) {\r
+      Nic->Header.Flags    |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BOOT_SELECTED;\r
+    }\r
+\r
+    if (NvData->InitiatorInfoFromDhcp) {\r
+      Nic->Origin = IpPrefixOriginDhcp;\r
+    } else {\r
+      Nic->Origin = IpPrefixOriginManual;\r
+    }\r
+\r
+    if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) {\r
+      //\r
+      // Get the subnet mask prefix length.\r
+      //\r
+      Nic->SubnetMaskPrefixLength = IScsiGetSubnetMaskPrefixLength (&NvData->SubnetMask);\r
+\r
+      //\r
+      // Map the various v4 addresses into v6 addresses.\r
+      //\r
+      IScsiMapV4ToV6Addr (&NvData->LocalIp, &Nic->Ip);\r
+      IScsiMapV4ToV6Addr (&NvData->Gateway, &Nic->Gateway);\r
+      IScsiMapV4ToV6Addr (&Attempt->PrimaryDns.v4, &Nic->PrimaryDns);\r
+      IScsiMapV4ToV6Addr (&Attempt->SecondaryDns.v4, &Nic->SecondaryDns);\r
+      IScsiMapV4ToV6Addr (&Attempt->DhcpServer.v4, &Nic->DhcpServer);\r
+\r
+    } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) {\r
+      //\r
+      // TODO: The subnet mask/local ip/gateway/dhcpserver for iBFT-IPv6 needs to be \r
+      // confirmed with spec owner.\r
+      //\r
+\r
+      CopyMem (&Nic->PrimaryDns, &Attempt->PrimaryDns, sizeof (EFI_IPv6_ADDRESS));\r
+      CopyMem (&Nic->SecondaryDns, &Attempt->SecondaryDns, sizeof (EFI_IPv6_ADDRESS));\r
+      //\r
+      // TODO: DHCP server address cannot be retrieved by DHCPv6 process since \r
+      // DHCP server option is removed.\r
+      //CopyMem (&Nic->DhcpServer, &Attempt->DhcpServer, sizeof (EFI_IPv6_ADDRESS));\r
+      //\r
+    } else {\r
+      ASSERT (FALSE);\r
+    }\r
+\r
+    //\r
+    // Get Nic Info: VLAN tag, Mac address, PCI location.\r
+    //\r
+    NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex);\r
+    ASSERT (NicInfo != NULL);\r
+\r
+    Nic->VLanTag = NicInfo->VlanId;\r
+    CopyMem (Nic->Mac, &NicInfo->PermanentAddress, sizeof (Nic->Mac));\r
+    Nic->PciLocation = (UINT16) ((NicInfo->BusNumber << 8)    |\r
+                                 (NicInfo->DeviceNumber << 3) | NicInfo->FunctionNumber);\r
+    *SectionOffset    = (UINT16) ((UINTN) Nic - (UINTN) Table);\r
+    SectionOffset++;\r
+\r
+    //\r
+    // Fill the Target section.\r
+    //\r
+\r
+    Target->Header.StructureId  = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_ID;\r
+    Target->Header.Version      = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_VERSION;\r
+    Target->Header.Length       = (UINT16) sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE);\r
+    Target->Header.Index        = (UINT8) Index;\r
+    Target->Header.Flags        = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BLOCK_VALID;\r
+\r
+    if (Index == 0) {\r
+      Target->Header.Flags     |= EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BOOT_SELECTED;\r
+    }\r
+\r
+    Target->Port                = NvData->TargetPort;\r
+\r
+    if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {\r
+      Target->CHAPType          = AuthConfig->CHAPType;\r
+    } else if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_NONE) {\r
+      Target->CHAPType          = ISCSI_AUTH_TYPE_NONE;\r
+    }\r
+\r
+    Target->NicIndex            = (UINT8) Index;\r
+\r
+    if (NvData->IpMode == IP_MODE_IP4 || NvData->IpMode == IP_MODE_AUTOCONFIG) {\r
+      IScsiMapV4ToV6Addr (&NvData->TargetIp.v4, &Target->Ip);\r
+    } else if (NvData->IpMode == IP_MODE_IP6 || NvData->IpMode == IP_MODE_AUTOCONFIG) {\r
+      CopyMem (&Target->Ip, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS));\r
+    } else {\r
+      ASSERT (FALSE);\r
+    }\r
+\r
+    CopyMem (Target->BootLun, NvData->BootLun, sizeof (Target->BootLun));\r
+\r
+    //\r
+    // Target iSCSI Name, CHAP name/secret, reverse CHAP name/secret.\r
+    //\r
+    Length = (UINT16) AsciiStrLen (NvData->TargetName);\r
+    IScsiAddHeapItem (Heap, NvData->TargetName, Length);\r
+\r
+    Target->IScsiNameLength = Length;\r
+    Target->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);\r
+\r
+    if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {\r
+      //\r
+      // CHAP Name\r
+      //\r
+      Length = (UINT16) AsciiStrLen (AuthConfig->CHAPName);\r
+      IScsiAddHeapItem (Heap, AuthConfig->CHAPName, Length);\r
+      Target->CHAPNameLength  = Length;\r
+      Target->CHAPNameOffset  = (UINT16) ((UINTN) *Heap - (UINTN) Table);\r
+\r
+      //\r
+      // CHAP Secret\r
+      //\r
+      Length = (UINT16) AsciiStrLen (AuthConfig->CHAPSecret);\r
+      IScsiAddHeapItem (Heap, AuthConfig->CHAPSecret, Length);\r
+      Target->CHAPSecretLength  = Length;\r
+      Target->CHAPSecretOffset  = (UINT16) ((UINTN) *Heap - (UINTN) Table);\r
+\r
+      if (Target->CHAPType == ISCSI_CHAP_MUTUAL) {\r
+        //\r
+        // Reverse CHAP Name.\r
+        //\r
+        Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPName);\r
+        IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPName, Length);\r
+        Target->ReverseCHAPNameLength = Length;\r
+        Target->ReverseCHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);\r
+\r
+        //\r
+        // Reverse CHAP Secret.\r
+        //\r
+        Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPSecret);\r
+        IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPSecret, Length);\r
+        Target->ReverseCHAPSecretLength = Length;\r
+        Target->ReverseCHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);\r
+      }\r
+    }\r
+\r
+    *SectionOffset = (UINT16) ((UINTN) Target - (UINTN) Table);\r
+    SectionOffset++;\r
+\r
+    //\r
+    // Advance to the next NIC/Target pair.\r
+    //\r
+    Nic    = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Target +\r
+           IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE)));\r
+    Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +\r
+           IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));\r
+\r
+    Index++;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Publish and remove the iSCSI Boot Firmware Table according to the iSCSI\r
+  session status.\r
+\r
+**/\r
+VOID\r
+IScsiPublishIbft (\r
+  IN VOID\r
+  )\r
+{\r
+  EFI_STATUS                                    Status;\r
+  EFI_ACPI_TABLE_PROTOCOL                       *AcpiTableProtocol;\r
+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER     *Table;\r
+  EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER  *Rsdp;\r
+  EFI_ACPI_DESCRIPTION_HEADER                   *Rsdt;\r
+  UINT8                                         *Heap;\r
+  UINT8                                         Checksum;\r
+  UINTN                                         Index;\r
+\r
+\r
+  Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (VOID **) &AcpiTableProtocol);\r
+  if (EFI_ERROR (Status)) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Find ACPI table RSD_PTR from the system table.\r
+  //\r
+  for (Index = 0, Rsdp = NULL; Index < gST->NumberOfTableEntries; Index++) {\r
+    if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi20TableGuid) ||\r
+      CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi10TableGuid) ||\r
+      CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpiTableGuid)\r
+      ) {\r
+      //\r
+      // A match was found.\r
+      //\r
+      Rsdp = (EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER *) gST->ConfigurationTable[Index].VendorTable;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (Rsdp == NULL) {\r
+    return ;\r
+  } else {\r
+    Rsdt = (EFI_ACPI_DESCRIPTION_HEADER *) (UINTN) Rsdp->RsdtAddress;\r
+  }\r
+\r
+  if (mIbftInstalled) {\r
+    Status = AcpiTableProtocol->UninstallAcpiTable (\r
+                                  AcpiTableProtocol,\r
+                                  mTableKey\r
+                                  );\r
+    if (EFI_ERROR (Status)) {\r
+      return ;\r
+    }\r
+    mIbftInstalled = FALSE;\r
+  }\r
+\r
+  //\r
+  // If there is no valid attempt configuration, just return.\r
+  //\r
+  if ((!mPrivate->EnableMpio && mPrivate->ValidSinglePathCount == 0) ||\r
+      (mPrivate->EnableMpio && mPrivate->MpioCount <= mPrivate->Krb5MpioCount)) {\r
+    return ;\r
+  }\r
+\r
+  //\r
+  // Allocate 4k bytes to hold the ACPI table.\r
+  //\r
+  Table = AllocateZeroPool (IBFT_MAX_SIZE);\r
+  if (Table == NULL) {\r
+    return ;\r
+  }\r
+\r
+  Heap = (UINT8 *) Table + IBFT_HEAP_OFFSET;\r
+\r
+  //\r
+  // Fill in the various section of the iSCSI Boot Firmware Table.\r
+  //\r
+  IScsiInitIbfTableHeader (Table, Rsdt->OemId, &Rsdt->OemTableId);\r
+  IScsiInitControlSection (Table);\r
+  IScsiFillInitiatorSection (Table, &Heap);\r
+  IScsiFillNICAndTargetSections (Table, &Heap);\r
+\r
+  Checksum = CalculateCheckSum8((UINT8 *)Table, Table->Length);\r
+  Table->Checksum = Checksum;\r
+\r
+  //\r
+  // Install or update the iBFT table.\r
+  //\r
+  Status = AcpiTableProtocol->InstallAcpiTable (\r
+                                AcpiTableProtocol,\r
+                                Table,\r
+                                Table->Length,\r
+                                &mTableKey\r
+                                );\r
+  if (EFI_ERROR(Status)) {\r
+    return;\r
+  }\r
+\r
+  mIbftInstalled = TRUE;\r
+  FreePool (Table);\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiIbft.h b/NetworkPkg/IScsiDxe/IScsiIbft.h
new file mode 100644 (file)
index 0000000..35d5216
--- /dev/null
@@ -0,0 +1,39 @@
+/** @file\r
+  Some extra definitions for iBFT.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_IBFT_H_\r
+#define _ISCSI_IBFT_H_\r
+\r
+#include <IndustryStandard/Acpi.h>\r
+#include <IndustryStandard/IScsiBootFirmwareTable.h> \r
+#include <Protocol/AcpiTable.h>\r
+#include <Protocol/PciIo.h>\r
+\r
+#define IBFT_TABLE_VAR_NAME L"iBFT"\r
+#define IBFT_MAX_SIZE       4096\r
+#define IBFT_HEAP_OFFSET    2048\r
+\r
+#define IBFT_ROUNDUP(size)  NET_ROUNDUP ((size), EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_STRUCTURE_ALIGNMENT)\r
+\r
+/**\r
+  Publish and remove the iSCSI Boot Firmware Table according to the iSCSI\r
+  session status.\r
+\r
+**/\r
+VOID\r
+IScsiPublishIbft (\r
+  IN VOID\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiImpl.h b/NetworkPkg/IScsiDxe/IScsiImpl.h
new file mode 100644 (file)
index 0000000..ccb83ed
--- /dev/null
@@ -0,0 +1,197 @@
+/** @file\r
+  The shared head file for iSCSI driver.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_IMPL_H_\r
+#define _ISCSI_IMPL_H_\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Protocol/ComponentName.h>\r
+#include <Protocol/ComponentName2.h>\r
+#include <Protocol/DriverBinding.h>\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/HiiConfigAccess.h>\r
+\r
+#include <Protocol/Dhcp4.h>\r
+#include <Protocol/Dhcp6.h>\r
+#include <Protocol/Tcp4.h>\r
+#include <Protocol/Tcp6.h>\r
+\r
+#include <Protocol/AuthenticationInfo.h>\r
+#include <Protocol/IScsiInitiatorName.h>\r
+#include <Protocol/ScsiPassThruExt.h>\r
+\r
+#include <Library/HiiLib.h>\r
+#include <Library/UefiHiiServicesLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/BaseLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiRuntimeServicesTableLib.h>\r
+#include <Library/UefiLib.h>\r
+#include <Library/DpcLib.h>\r
+#include <Library/NetLib.h>\r
+#include <Library/TcpIoLib.h>\r
+#include <Library/BaseCryptLib.h>\r
+\r
+#include <Guid/MdeModuleHii.h>\r
+#include <Guid/EventGroup.h>\r
+#include <Guid/Acpi.h>\r
+\r
+#include "IScsiConfigNVDataStruc.h"\r
+#include "IScsiDriver.h"\r
+#include "IScsiProto.h"\r
+#include "IScsiCHAP.h"\r
+#include "IScsiDhcp.h"\r
+#include "IScsiDhcp6.h"\r
+#include "IScsiIbft.h"\r
+#include "IScsiMisc.h"\r
+#include "IScsiConfig.h"\r
+\r
+#define ISCSI_AUTH_INITIAL        0\r
+\r
+#define ISCSI_SESSION_SIGNATURE   SIGNATURE_32 ('I', 'S', 'S', 'N')\r
+///\r
+/// 10 seconds\r
+///\r
+#define ISCSI_GET_MAPPING_TIMEOUT 100000000U\r
+///\r
+/// 3 seconds\r
+///\r
+#define ISCSI_WAIT_IPSEC_TIMEOUT  30000000U\r
+\r
+struct _ISCSI_SESSION {\r
+  UINT32                      Signature;\r
+\r
+  ISCSI_DRIVER_DATA           *Private;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *ConfigData;\r
+\r
+  UINT8                       AuthType;\r
+  union {\r
+    ISCSI_CHAP_AUTH_DATA      CHAP;\r
+  } AuthData;\r
+\r
+  UINT8                       State;\r
+\r
+  UINT8                       Isid[6];\r
+  UINT16                      Tsih;\r
+\r
+  UINT32                      CmdSN;\r
+  UINT32                      ExpCmdSN;\r
+  UINT32                      MaxCmdSN;\r
+\r
+  UINT32                      InitiatorTaskTag;\r
+  UINT16                      NextCid;\r
+\r
+  LIST_ENTRY                  Conns;\r
+  UINT32                      NumConns;\r
+\r
+  LIST_ENTRY                  TcbList;\r
+\r
+  //\r
+  // Session-wide parameters\r
+  //\r
+  UINT16                      TargetPortalGroupTag;\r
+  UINT32                      MaxConnections;\r
+  BOOLEAN                     InitialR2T;\r
+  BOOLEAN                     ImmediateData;\r
+  UINT32                      MaxBurstLength;\r
+  UINT32                      FirstBurstLength;\r
+  UINT32                      DefaultTime2Wait;\r
+  UINT32                      DefaultTime2Retain;\r
+  UINT16                      MaxOutstandingR2T;\r
+  BOOLEAN                     DataPDUInOrder;\r
+  BOOLEAN                     DataSequenceInOrder;\r
+  UINT8                       ErrorRecoveryLevel;\r
+};\r
+\r
+#define ISCSI_CONNECTION_SIGNATURE  SIGNATURE_32 ('I', 'S', 'C', 'N')\r
+\r
+struct _ISCSI_CONNECTION {\r
+  UINT32            Signature;\r
+  LIST_ENTRY        Link;\r
+\r
+  EFI_EVENT         TimeoutEvent;\r
+\r
+  ISCSI_SESSION     *Session;\r
+\r
+  UINT8             State;\r
+  UINT8             CurrentStage;\r
+  UINT8             NextStage;\r
+\r
+  UINT8             AuthStep;\r
+\r
+  BOOLEAN           PartialReqSent;\r
+  BOOLEAN           PartialRspRcvd;\r
+\r
+  BOOLEAN           TransitInitiated;\r
+  BOOLEAN           ParamNegotiated;\r
+\r
+  UINT16            Cid;\r
+  UINT32            ExpStatSN;\r
+\r
+  //\r
+  // Queues...\r
+  //\r
+  NET_BUF_QUEUE     RspQue;\r
+\r
+  BOOLEAN           Ipv6Flag;\r
+  TCP_IO            TcpIo;\r
+\r
+  //\r
+  // Connection-only parameters.\r
+  //\r
+  UINT32            MaxRecvDataSegmentLength;\r
+  ISCSI_DIGEST_TYPE HeaderDigest;\r
+  ISCSI_DIGEST_TYPE DataDigest;\r
+};\r
+\r
+#define ISCSI_DRIVER_DATA_SIGNATURE SIGNATURE_32 ('I', 'S', 'D', 'A')\r
+\r
+#define ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU(PassThru) \\r
+  CR ( \\r
+  PassThru, \\r
+  ISCSI_DRIVER_DATA, \\r
+  IScsiExtScsiPassThru, \\r
+  ISCSI_DRIVER_DATA_SIGNATURE \\r
+  )\r
+\r
+#define ISCSI_DRIVER_DATA_FROM_IDENTIFIER(Identifier) \\r
+  CR ( \\r
+  Identifier, \\r
+  ISCSI_DRIVER_DATA, \\r
+  IScsiIdentifier, \\r
+  ISCSI_DRIVER_DATA_SIGNATURE \\r
+  )\r
+\r
+struct _ISCSI_DRIVER_DATA {\r
+  UINT32                          Signature;\r
+  EFI_HANDLE                      Image;\r
+  EFI_HANDLE                      Controller;\r
+  ISCSI_PRIVATE_PROTOCOL          IScsiIdentifier;\r
+\r
+  EFI_EVENT                       ExitBootServiceEvent;\r
+\r
+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL IScsiExtScsiPassThru;\r
+  EFI_EXT_SCSI_PASS_THRU_MODE     ExtScsiPassThruMode;\r
+  EFI_HANDLE                      ExtScsiPassThruHandle;\r
+  EFI_DEVICE_PATH_PROTOCOL        *DevicePath;\r
+  EFI_HANDLE                      ChildHandle;  \r
+  ISCSI_SESSION                   *Session;\r
+};\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiInitiatorName.c b/NetworkPkg/IScsiDxe/IScsiInitiatorName.c
new file mode 100644 (file)
index 0000000..7a3934c
--- /dev/null
@@ -0,0 +1,136 @@
+/** @file\r
+  Implementation for EFI iSCSI Initiator Name Protocol.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+EFI_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName = {\r
+  IScsiGetInitiatorName,\r
+  IScsiSetInitiatorName\r
+};\r
+\r
+\r
+/**\r
+  Retrieves the current set value of iSCSI Initiator Name.\r
+\r
+  @param[in]       This          Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL\r
+                                 instance.\r
+  @param[in, out]  BufferSize    Size of the buffer in bytes pointed to by Buffer /\r
+                                 Actual size of the variable data buffer.\r
+  @param[out]      Buffer        Pointer to the buffer for data to be read.\r
+                                 The data is a null-terminated UTF-8 encoded string.\r
+                                 The maximum length is 223 characters, including the null-terminator.\r
+\r
+  @retval EFI_SUCCESS            Data was successfully retrieved into the provided\r
+                                 buffer and the BufferSize was sufficient to handle\r
+                                 the iSCSI initiator name.\r
+  @retval EFI_BUFFER_TOO_SMALL   BufferSize is too small for the result. BufferSize\r
+                                 will be updated with the size required to complete\r
+                                 the request. Buffer will not be affected.\r
+  @retval EFI_INVALID_PARAMETER  BufferSize is NULL. BufferSize and Buffer will not\r
+                                 be affected.\r
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL. BufferSize and Buffer will not be\r
+                                 affected.\r
+  @retval EFI_DEVICE_ERROR       The iSCSI initiator name could not be retrieved\r
+                                 due to a hardware error.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiGetInitiatorName (\r
+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,\r
+  IN OUT UINTN                              *BufferSize,\r
+  OUT    VOID                               *Buffer\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if ((BufferSize == NULL) || (Buffer == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = gRT->GetVariable (\r
+                  ISCSI_INITIATOR_NAME_VAR_NAME,\r
+                  &gEfiIScsiInitiatorNameProtocolGuid,\r
+                  NULL,\r
+                  BufferSize,\r
+                  Buffer\r
+                  );\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Sets the iSSI Initiator Name.\r
+\r
+  @param[in]       This          Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL\r
+                                 instance.\r
+  @param[in, out]  BufferSize    Size of the buffer in bytes pointed to by Buffer.\r
+  @param[in]       Buffer        Pointer to the buffer for data to be written.\r
+                                 The data is a null-terminated UTF-8 encoded string.\r
+                                 The maximum length is 223 characters, including the null-terminator.\r
+\r
+  @retval EFI_SUCCESS            Data was successfully stored by the protocol.\r
+  @retval EFI_UNSUPPORTED        Platform policies do not allow for data to be\r
+                                 written.\r
+  @retval EFI_INVALID_PARAMETER  BufferSize exceeds the maximum allowed limit.\r
+                                 BufferSize will be updated with the maximum size\r
+                                 required to complete the request.\r
+  @retval EFI_INVALID_PARAMETER  Buffersize is NULL. BufferSize and Buffer will not\r
+                                 be affected.\r
+  @retval EFI_INVALID_PARAMETER  Buffer is NULL. BufferSize and Buffer will not be\r
+                                 affected.\r
+  @retval EFI_DEVICE_ERROR       The data could not be stored due to a hardware\r
+                                 error.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the data\r
+  @retval EFI_PROTOCOL_ERROR     Input iSCSI initiator name does not adhere to RFC\r
+                                 3720\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+IScsiSetInitiatorName (\r
+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,\r
+  IN OUT UINTN                              *BufferSize,\r
+  IN     VOID                               *Buffer\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  if ((BufferSize == NULL) || (Buffer == NULL)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (*BufferSize > ISCSI_NAME_MAX_SIZE) {\r
+    *BufferSize = ISCSI_NAME_MAX_SIZE;\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  //\r
+  // Only support iqn iSCSI names.\r
+  //\r
+  Status = IScsiNormalizeName ((CHAR8 *) Buffer, *BufferSize - 1);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gRT->SetVariable (\r
+                  ISCSI_INITIATOR_NAME_VAR_NAME,\r
+                  &gEfiIScsiInitiatorNameProtocolGuid,\r
+                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+                  *BufferSize,\r
+                  Buffer\r
+                  );\r
+\r
+  return Status;\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiMisc.c b/NetworkPkg/IScsiDxe/IScsiMisc.c
new file mode 100644 (file)
index 0000000..a697659
--- /dev/null
@@ -0,0 +1,1320 @@
+/** @file\r
+  Miscellaneous routines for iSCSI driver.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8  IScsiHexString[] = "0123456789ABCDEFabcdef";\r
+\r
+/**\r
+  Removes (trims) specified leading and trailing characters from a string.\r
+\r
+  @param[in, out] Str   Pointer to the null-terminated string to be trimmed.\r
+                        On return, Str will hold the trimmed string. \r
+\r
+  @param[in]      CharC Character will be trimmed from str.\r
+\r
+**/\r
+VOID\r
+IScsiStrTrim (\r
+  IN OUT CHAR16   *Str,\r
+  IN     CHAR16   CharC\r
+  )\r
+{\r
+  CHAR16  *Pointer1;\r
+  CHAR16  *Pointer2;\r
+  \r
+  if (*Str == 0) {\r
+    return ;\r
+  }\r
+  \r
+  //\r
+  // Trim off the leading and trailing characters c\r
+  //\r
+  for (Pointer1 = Str; (*Pointer1 != 0) && (*Pointer1 == CharC); Pointer1++) {\r
+    ;\r
+  }\r
+  \r
+  Pointer2 = Str;\r
+  if (Pointer2 == Pointer1) {\r
+    while (*Pointer1 != 0) {\r
+      Pointer2++;\r
+      Pointer1++;\r
+    }\r
+  } else {\r
+    while (*Pointer1 != 0) {    \r
+    *Pointer2 = *Pointer1;    \r
+    Pointer1++;\r
+    Pointer2++;\r
+    }\r
+    *Pointer2 = 0;\r
+  }\r
+  \r
+  \r
+  for (Pointer1 = Str + StrLen(Str) - 1; Pointer1 >= Str && *Pointer1 == CharC; Pointer1--) {\r
+    ;\r
+  }\r
+  if  (Pointer1 !=  Str + StrLen(Str) - 1) { \r
+    *(Pointer1 + 1) = 0;\r
+  }\r
+}\r
+\r
+/**\r
+  Calculate the prefix length of the IPv4 subnet mask.\r
+\r
+  @param[in]  SubnetMask The IPv4 subnet mask.\r
+\r
+  @return     The prefix length of the subnet mask.\r
+  @retval 0   Other errors as indicated.\r
+\r
+**/\r
+UINT8\r
+IScsiGetSubnetMaskPrefixLength (\r
+  IN EFI_IPv4_ADDRESS  *SubnetMask\r
+  )\r
+{\r
+  UINT8   Len;\r
+  UINT32  ReverseMask;\r
+\r
+  //\r
+  // The SubnetMask is in network byte order.\r
+  //\r
+  ReverseMask = (SubnetMask->Addr[0] << 24) | (SubnetMask->Addr[1] << 16) | (SubnetMask->Addr[2] << 8) | (SubnetMask->Addr[3]);\r
+\r
+  //\r
+  // Reverse it.\r
+  //\r
+  ReverseMask = ~ReverseMask;\r
+\r
+  if ((ReverseMask & (ReverseMask + 1)) != 0) {\r
+    return 0;\r
+  }\r
+\r
+  Len = 0;\r
+\r
+  while (ReverseMask != 0) {\r
+    ReverseMask = ReverseMask >> 1;\r
+    Len++;\r
+  }\r
+\r
+  return (UINT8) (32 - Len);\r
+}\r
+\r
+\r
+/**\r
+  Convert the hexadecimal encoded LUN string into the 64-bit LUN.\r
+\r
+  @param[in]   Str             The hexadecimal encoded LUN string.\r
+  @param[out]  Lun             Storage to return the 64-bit LUN.\r
+\r
+  @retval EFI_SUCCESS            The 64-bit LUN is stored in Lun.\r
+  @retval EFI_INVALID_PARAMETER  The string is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiAsciiStrToLun (\r
+  IN  CHAR8  *Str,\r
+  OUT UINT8  *Lun\r
+  )\r
+{\r
+  UINTN   Index, IndexValue, IndexNum, SizeStr;\r
+  CHAR8   TemStr[2];\r
+  UINT8   TemValue;\r
+  UINT16  Value[4];\r
+  \r
+  ZeroMem (Lun, 8);\r
+  ZeroMem (TemStr, 2);\r
+  ZeroMem ((UINT8 *) Value, sizeof (Value));\r
+  SizeStr    = AsciiStrLen (Str);  \r
+  IndexValue = 0;\r
+  IndexNum   = 0;\r
+\r
+  for (Index = 0; Index < SizeStr; Index ++) {\r
+    TemStr[0] = Str[Index];\r
+    TemValue = (UINT8) AsciiStrHexToUint64 (TemStr);\r
+    if (TemValue == 0 && TemStr[0] != '0') {\r
+      if ((TemStr[0] != '-') || (IndexNum == 0)) {\r
+        //\r
+        // Invalid Lun Char.\r
+        //\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+    }\r
+    \r
+    if ((TemValue == 0) && (TemStr[0] == '-')) {\r
+      //\r
+      // Next Lun value.\r
+      //\r
+      if (++IndexValue >= 4) {\r
+        //\r
+        // Max 4 Lun value.\r
+        //\r
+        return EFI_INVALID_PARAMETER;\r
+      }\r
+      //\r
+      // Restart str index for the next lun value.\r
+      //\r
+      IndexNum = 0;\r
+      continue;\r
+    }\r
+    \r
+    if (++IndexNum > 4) {\r
+      //     \r
+      // Each Lun Str can't exceed size 4, because it will be as UINT16 value.\r
+      //\r
+      return EFI_INVALID_PARAMETER;\r
+    }\r
+    \r
+    //\r
+    // Combine UINT16 value.\r
+    //\r
+    Value[IndexValue] = (UINT16) ((Value[IndexValue] << 4) + TemValue);\r
+  }\r
\r
+  for (Index = 0; Index <= IndexValue; Index ++) {\r
+    *((UINT16 *) &Lun[Index * 2]) =  HTONS (Value[Index]);\r
+  }\r
+  \r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Convert the 64-bit LUN into the hexadecimal encoded LUN string.\r
+\r
+  @param[in]   Lun The 64-bit LUN.\r
+  @param[out]  Str The storage to return the hexadecimal encoded LUN string.\r
+\r
+**/\r
+VOID\r
+IScsiLunToUnicodeStr (\r
+  IN UINT8    *Lun,\r
+  OUT CHAR16  *Str\r
+  )\r
+{\r
+  UINTN   Index;\r
+  CHAR16  *TempStr;\r
+\r
+  TempStr = Str;\r
+\r
+  for (Index = 0; Index < 4; Index++) {\r
+\r
+    if ((Lun[2 * Index] | Lun[2 * Index + 1]) == 0) {\r
+      StrCpy (TempStr, L"0-");\r
+    } else {\r
+      TempStr[0]  = (CHAR16) IScsiHexString[Lun[2 * Index] >> 4];\r
+      TempStr[1]  = (CHAR16) IScsiHexString[Lun[2 * Index] & 0x0F];\r
+      TempStr[2]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] >> 4];\r
+      TempStr[3]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] & 0x0F];\r
+      TempStr[4]  = L'-';\r
+      TempStr[5]  = 0;\r
+\r
+      IScsiStrTrim (TempStr, L'0');\r
+    }\r
+\r
+    TempStr += StrLen (TempStr);\r
+  }\r
+\r
+  Str[StrLen (Str) - 1] = 0;\r
+\r
+  for (Index = StrLen (Str) - 1; Index > 1; Index = Index - 2) {\r
+    if ((Str[Index] == L'0') && (Str[Index - 1] == L'-')) {\r
+      Str[Index - 1] = 0;\r
+    } else {\r
+      break;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+  Convert the formatted IP address into the binary IP address.\r
+\r
+  @param[in]   Str               The UNICODE string.\r
+  @param[in]   IpMode            Indicates whether the IP address is v4 or v6.\r
+  @param[out]  Ip                The storage to return the ASCII string.\r
+\r
+  @retval EFI_SUCCESS            The binary IP address is returned in Ip.\r
+  @retval EFI_INVALID_PARAMETER  The IP string is malformatted or IpMode is\r
+                                 invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiAsciiStrToIp (\r
+  IN  CHAR8             *Str,\r
+  IN  UINT8             IpMode,\r
+  OUT EFI_IP_ADDRESS    *Ip\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+\r
+  if (IpMode == IP_MODE_IP4 || IpMode == IP_MODE_AUTOCONFIG_IP4) {\r
+    return NetLibAsciiStrToIp4 (Str, &Ip->v4);\r
+\r
+  } else if (IpMode == IP_MODE_IP6 || IpMode == IP_MODE_AUTOCONFIG_IP6) {\r
+    return NetLibAsciiStrToIp6 (Str, &Ip->v6);\r
+\r
+  } else if (IpMode == IP_MODE_AUTOCONFIG) {\r
+    Status = NetLibAsciiStrToIp4 (Str, &Ip->v4);\r
+    if (!EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    return NetLibAsciiStrToIp6 (Str, &Ip->v6);\r
+\r
+  }\r
+\r
+  return EFI_INVALID_PARAMETER;\r
+}\r
+\r
+/**\r
+  Convert the mac address into a hexadecimal encoded "-" seperated string.\r
+\r
+  @param[in]  Mac     The mac address.\r
+  @param[in]  Len     Length in bytes of the mac address.\r
+  @param[in]  VlanId  VLAN ID of the network device.\r
+  @param[out] Str     The storage to return the mac string.\r
+\r
+**/\r
+VOID\r
+IScsiMacAddrToStr (\r
+  IN  EFI_MAC_ADDRESS  *Mac,\r
+  IN  UINT32           Len,\r
+  IN  UINT16           VlanId,\r
+  OUT CHAR16           *Str\r
+  )\r
+{\r
+  UINT32  Index;\r
+  CHAR16  *String;\r
+\r
+  for (Index = 0; Index < Len; Index++) {\r
+    Str[3 * Index]      = (CHAR16) IScsiHexString[(Mac->Addr[Index] >> 4) & 0x0F];\r
+    Str[3 * Index + 1]  = (CHAR16) IScsiHexString[Mac->Addr[Index] & 0x0F];\r
+    Str[3 * Index + 2]  = L'-';\r
+  }\r
+\r
+  String = &Str[3 * Index - 1] ;\r
+  if (VlanId != 0) {\r
+    String += UnicodeSPrint (String, 6 * sizeof (CHAR16), L"\\%04x", (UINTN) VlanId);\r
+  }\r
+\r
+  *String = L'\0';\r
+}\r
+\r
+/**\r
+  Convert the binary encoded buffer into a hexadecimal encoded string.\r
+\r
+  @param[in]       BinBuffer   The buffer containing the binary data.\r
+  @param[in]       BinLength   Length of the binary buffer.\r
+  @param[in, out]  HexStr      Pointer to the string.\r
+  @param[in, out]  HexLength   The length of the string.\r
+\r
+  @retval EFI_SUCCESS          The binary data is converted to the hexadecimal string \r
+                               and the length of the string is updated.\r
+  @retval EFI_BUFFER_TOO_SMALL The string is too small.\r
+  @retval EFI_INVALID_PARAMETER The IP string is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiBinToHex (\r
+  IN     UINT8  *BinBuffer,\r
+  IN     UINT32 BinLength,\r
+  IN OUT CHAR8  *HexStr,\r
+  IN OUT UINT32 *HexLength\r
+  )\r
+{\r
+  UINTN Index;\r
+\r
+  if ((HexStr == NULL) || (BinBuffer == NULL) || (BinLength == 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if (((*HexLength) - 3) < BinLength * 2) {\r
+    *HexLength = BinLength * 2 + 3;\r
+    return EFI_BUFFER_TOO_SMALL;\r
+  }\r
+\r
+  *HexLength = BinLength * 2 + 3;\r
+  //\r
+  // Prefix for Hex String.\r
+  //\r
+  HexStr[0] = '0';\r
+  HexStr[1] = 'x';\r
+\r
+  for (Index = 0; Index < BinLength; Index++) {\r
+    HexStr[Index * 2 + 2] = IScsiHexString[BinBuffer[Index] >> 4];\r
+    HexStr[Index * 2 + 3] = IScsiHexString[BinBuffer[Index] & 0xf];\r
+  }\r
+\r
+  HexStr[Index * 2 + 2] = '\0';\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Convert the hexadecimal string into a binary encoded buffer.\r
+\r
+  @param[in, out]  BinBuffer   The binary buffer.\r
+  @param[in, out]  BinLength   Length of the binary buffer.\r
+  @param[in]       HexStr      The hexadecimal string.\r
+\r
+  @retval EFI_SUCCESS          The hexadecimal string is converted into a binary\r
+                               encoded buffer.\r
+  @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiHexToBin (\r
+  IN OUT UINT8  *BinBuffer,\r
+  IN OUT UINT32 *BinLength,\r
+  IN     CHAR8  *HexStr\r
+  )\r
+{\r
+  UINTN   Index;\r
+  UINTN   Length;\r
+  UINT8   Digit;\r
+  CHAR8   TemStr[2];\r
+  \r
+  ZeroMem (TemStr, sizeof (TemStr));\r
+\r
+  //\r
+  // Find out how many hex characters the string has.\r
+  //\r
+  if ((HexStr[0] == '0') && ((HexStr[1] == 'x') || (HexStr[1] == 'X'))) {\r
+    HexStr += 2;\r
+  }\r
+  \r
+  Length = AsciiStrLen (HexStr);\r
+\r
+  for (Index = 0; Index < Length; Index ++) {\r
+    TemStr[0] = HexStr[Index];\r
+    Digit = (UINT8) AsciiStrHexToUint64 (TemStr);\r
+    if (Digit == 0 && TemStr[0] != '0') {\r
+      //\r
+      // Invalid Lun Char.\r
+      //\r
+      break;\r
+    }\r
+    if ((Index & 1) == 0) {\r
+      BinBuffer [Index/2] = Digit;\r
+    } else {\r
+      BinBuffer [Index/2] = (UINT8) ((BinBuffer [Index/2] << 4) + Digit);\r
+    }\r
+  }\r
+  \r
+  *BinLength = (UINT32) ((Index + 1)/2);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Convert the decimal-constant string or hex-constant string into a numerical value.\r
+\r
+  @param[in] Str                    String in decimal or hex.\r
+\r
+  @return The numerical value.\r
+\r
+**/\r
+UINTN\r
+IScsiNetNtoi (\r
+  IN     CHAR8  *Str\r
+  )\r
+{\r
+  if ((Str[0] == '0') && ((Str[1] == 'x') || (Str[1] == 'X'))) {\r
+    Str += 2;\r
+\r
+    return AsciiStrHexToUintn (Str);\r
+  }\r
+\r
+  return AsciiStrDecimalToUintn (Str);\r
+}\r
+\r
+\r
+/**\r
+  Generate random numbers.\r
+\r
+  @param[in, out]  Rand       The buffer to contain random numbers.\r
+  @param[in]       RandLength The length of the Rand buffer.\r
+\r
+**/\r
+VOID\r
+IScsiGenRandom (\r
+  IN OUT UINT8  *Rand,\r
+  IN     UINTN  RandLength\r
+  )\r
+{\r
+  UINT32  Random;\r
+\r
+  while (RandLength > 0) {\r
+    Random  = NET_RANDOM (NetRandomInitSeed ());\r
+    *Rand++ = (UINT8) (Random);\r
+    RandLength--;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Record the NIC info in global structure.\r
+\r
+  @param[in]  Controller         The handle of the controller.\r
+\r
+  @retval EFI_SUCCESS            The operation is completed.\r
+  @retval EFI_OUT_OF_RESOURCES   Do not have sufficient resources to finish this\r
+                                 operation.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiAddNic (\r
+  IN EFI_HANDLE  Controller\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  ISCSI_NIC_INFO              *NicInfo;\r
+  LIST_ENTRY                  *Entry;\r
+  EFI_MAC_ADDRESS             MacAddr;\r
+  UINTN                       HwAddressSize;\r
+  UINT16                      VlanId;\r
+\r
+  //\r
+  // Get MAC address of this network device.\r
+  //\r
+  Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Get VLAN ID of this network device.\r
+  //\r
+  VlanId = NetLibGetVlanId (Controller);\r
+\r
+  //\r
+  // Check whether the NIC info already exists. Return directly if so.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {\r
+    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);\r
+    if (NicInfo->HwAddressSize == HwAddressSize &&\r
+        CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&\r
+        NicInfo->VlanId == VlanId) {\r
+      mPrivate->CurrentNic = NicInfo->NicIndex;\r
+      return EFI_SUCCESS;\r
+    }\r
+\r
+    if (mPrivate->MaxNic < NicInfo->NicIndex) {\r
+      mPrivate->MaxNic = NicInfo->NicIndex;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Record the NIC info in private structure.\r
+  //\r
+  NicInfo = AllocateZeroPool (sizeof (ISCSI_NIC_INFO));\r
+  if (NicInfo == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  CopyMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize);\r
+  NicInfo->HwAddressSize  = (UINT32) HwAddressSize;\r
+  NicInfo->VlanId         = VlanId;\r
+  NicInfo->NicIndex       = (UINT8) (mPrivate->MaxNic + 1);\r
+  mPrivate->MaxNic        = NicInfo->NicIndex;\r
+\r
+  //\r
+  // Get the PCI location.\r
+  //\r
+  IScsiGetNICPciLocation (\r
+    Controller,\r
+    &NicInfo->BusNumber,\r
+    &NicInfo->DeviceNumber,\r
+    &NicInfo->FunctionNumber\r
+    );\r
+\r
+  InsertTailList (&mPrivate->NicInfoList, &NicInfo->Link);\r
+  mPrivate->NicCount++;\r
+\r
+  mPrivate->CurrentNic = NicInfo->NicIndex;\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Delete the recorded NIC info from global structure. Also delete corresponding\r
+  attempts.\r
+\r
+  @param[in]  Controller         The handle of the controller.\r
+\r
+  @retval EFI_SUCCESS            The operation is completed.\r
+  @retval EFI_NOT_FOUND          The NIC info to be deleted is not recorded.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiRemoveNic (\r
+  IN EFI_HANDLE  Controller\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  ISCSI_NIC_INFO              *NicInfo;\r
+  LIST_ENTRY                  *Entry;\r
+  LIST_ENTRY                  *NextEntry;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  ISCSI_NIC_INFO              *ThisNic;\r
+  EFI_MAC_ADDRESS             MacAddr;\r
+  UINTN                       HwAddressSize;\r
+  UINT16                      VlanId;\r
+\r
+  //\r
+  // Get MAC address of this network device.\r
+  //\r
+  Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Get VLAN ID of this network device.\r
+  //\r
+  VlanId = NetLibGetVlanId (Controller);\r
+\r
+  //\r
+  // Check whether the NIC information exists.\r
+  //\r
+  ThisNic = NULL;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {\r
+    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);\r
+    if (NicInfo->HwAddressSize == HwAddressSize &&\r
+        CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&\r
+        NicInfo->VlanId == VlanId) {\r
+\r
+      ThisNic = NicInfo;\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (ThisNic == NULL) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  mPrivate->CurrentNic = ThisNic->NicIndex;\r
+\r
+  RemoveEntryList (&ThisNic->Link);\r
+  FreePool (ThisNic);\r
+  mPrivate->NicCount--;\r
+\r
+  //\r
+  // Remove all attempts related to this NIC.\r
+  //\r
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {\r
+    AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);\r
+    if (AttemptConfigData->NicIndex == mPrivate->CurrentNic) {\r
+      RemoveEntryList (&AttemptConfigData->Link);\r
+      mPrivate->AttemptCount--;\r
+\r
+      if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO && mPrivate->MpioCount > 0) {\r
+        if (--mPrivate->MpioCount == 0) {\r
+          mPrivate->EnableMpio = FALSE;\r
+        }\r
+\r
+        if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB && mPrivate->Krb5MpioCount > 0) {\r
+          mPrivate->Krb5MpioCount--;\r
+        }\r
+\r
+      } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED && mPrivate->SinglePathCount > 0) {\r
+        mPrivate->SinglePathCount--;\r
+\r
+        if (mPrivate->ValidSinglePathCount > 0) {\r
+          mPrivate->ValidSinglePathCount--;\r
+        }\r
+      }\r
+\r
+      FreePool (AttemptConfigData);\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Get the recorded NIC info from global structure by the Index.\r
+\r
+  @param[in]  NicIndex          The index indicates the position of NIC info.\r
+\r
+  @return Pointer to the NIC info, or NULL if not found.\r
+\r
+**/\r
+ISCSI_NIC_INFO *\r
+IScsiGetNicInfoByIndex (\r
+  IN UINT8      NicIndex\r
+  )\r
+{\r
+  LIST_ENTRY        *Entry;\r
+  ISCSI_NIC_INFO    *NicInfo;\r
+\r
+  NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {\r
+    NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);\r
+    if (NicInfo->NicIndex == NicIndex) {\r
+      return NicInfo;\r
+    }\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Get the NIC's PCI location and return it accroding to the composited\r
+  format defined in iSCSI Boot Firmware Table.\r
+\r
+  @param[in]   Controller        The handle of the controller.\r
+  @param[out]  Bus               The bus number.\r
+  @param[out]  Device            The device number.\r
+  @param[out]  Function          The function number.\r
+\r
+  @return      The composited representation of the NIC PCI location.\r
+\r
+**/\r
+UINT16\r
+IScsiGetNICPciLocation (\r
+  IN EFI_HANDLE  Controller,\r
+  OUT UINTN      *Bus,\r
+  OUT UINTN      *Device,\r
+  OUT UINTN      *Function\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
+  EFI_HANDLE                PciIoHandle;\r
+  EFI_PCI_IO_PROTOCOL       *PciIo;\r
+  UINTN                     Segment;\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  Controller,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &DevicePath\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return 0;\r
+  }\r
+\r
+  Status = gBS->LocateDevicePath (\r
+                  &gEfiPciIoProtocolGuid,\r
+                  &DevicePath,\r
+                  &PciIoHandle\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return 0;\r
+  }\r
+\r
+  Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo);\r
+  if (EFI_ERROR (Status)) {\r
+    return 0;\r
+  }\r
+\r
+  Status = PciIo->GetLocation (PciIo, &Segment, Bus, Device, Function);\r
+  if (EFI_ERROR (Status)) {\r
+    return 0;\r
+  }\r
+\r
+  return (UINT16) ((*Bus << 8) | (*Device << 3) | *Function);\r
+}\r
+\r
+\r
+/**\r
+  Read the EFI variable (VendorGuid/Name) and return a dynamically allocated\r
+  buffer, and the size of the buffer. If failure, return NULL.\r
+\r
+  @param[in]   Name                   String part of EFI variable name.\r
+  @param[in]   VendorGuid             GUID part of EFI variable name.\r
+  @param[out]  VariableSize           Returns the size of the EFI variable that was read.\r
+\r
+  @return Dynamically allocated memory that contains a copy of the EFI variable.\r
+  @return Caller is responsible freeing the buffer.\r
+  @retval NULL                   Variable was not read.\r
+\r
+**/\r
+VOID *\r
+IScsiGetVariableAndSize (\r
+  IN  CHAR16              *Name,\r
+  IN  EFI_GUID            *VendorGuid,\r
+  OUT UINTN               *VariableSize\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINTN       BufferSize;\r
+  VOID        *Buffer;\r
+\r
+  Buffer = NULL;\r
+\r
+  //\r
+  // Pass in a zero size buffer to find the required buffer size.\r
+  //\r
+  BufferSize  = 0;\r
+  Status      = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);\r
+  if (Status == EFI_BUFFER_TOO_SMALL) {\r
+    //\r
+    // Allocate the buffer to return\r
+    //\r
+    Buffer = AllocateZeroPool (BufferSize);\r
+    if (Buffer == NULL) {\r
+      return NULL;\r
+    }\r
+    //\r
+    // Read variable into the allocated buffer.\r
+    //\r
+    Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);\r
+    if (EFI_ERROR (Status)) {\r
+      BufferSize = 0;\r
+    }\r
+  }\r
+\r
+  *VariableSize = BufferSize;\r
+  return Buffer;\r
+}\r
+\r
+\r
+/**\r
+  Create the iSCSI driver data.\r
+\r
+  @param[in] Image      The handle of the driver image.\r
+  @param[in] Controller The handle of the controller.\r
+\r
+  @return The iSCSI driver data created.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+ISCSI_DRIVER_DATA *\r
+IScsiCreateDriverData (\r
+  IN EFI_HANDLE  Image,\r
+  IN EFI_HANDLE  Controller\r
+  )\r
+{\r
+  ISCSI_DRIVER_DATA *Private;\r
+  EFI_STATUS        Status;\r
+\r
+  Private = AllocateZeroPool (sizeof (ISCSI_DRIVER_DATA));\r
+  if (Private == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Private->Signature  = ISCSI_DRIVER_DATA_SIGNATURE;\r
+  Private->Image      = Image;\r
+  Private->Controller = Controller;\r
+  Private->Session    = NULL;\r
+\r
+  //\r
+  // Create an event to be signaled when the BS to RT transition is triggerd so\r
+  // as to abort the iSCSI session.\r
+  //\r
+  Status = gBS->CreateEventEx (\r
+                  EVT_NOTIFY_SIGNAL,\r
+                  TPL_CALLBACK,\r
+                  IScsiOnExitBootService,\r
+                  Private,\r
+                  &gEfiEventExitBootServicesGuid,\r
+                  &Private->ExitBootServiceEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Private);\r
+    return NULL;\r
+  }\r
+\r
+  Private->ExtScsiPassThruHandle = NULL;\r
+  CopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL));\r
+\r
+  //\r
+  // 0 is designated to the TargetId, so use another value for the AdapterId.\r
+  //\r
+  Private->ExtScsiPassThruMode.AdapterId  = 2;\r
+  Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;\r
+  Private->ExtScsiPassThruMode.IoAlign    = 4;\r
+  Private->IScsiExtScsiPassThru.Mode      = &Private->ExtScsiPassThruMode;\r
+\r
+  return Private;\r
+}\r
+\r
+\r
+/**\r
+  Clean the iSCSI driver data.\r
+\r
+  @param[in]  Private The iSCSI driver data.\r
+\r
+**/\r
+VOID\r
+IScsiCleanDriverData (\r
+  IN ISCSI_DRIVER_DATA  *Private\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+\r
+  if (Private->DevicePath != NULL) {\r
+    gBS->UninstallProtocolInterface (\r
+           Private->ExtScsiPassThruHandle,\r
+           &gEfiDevicePathProtocolGuid,\r
+           Private->DevicePath\r
+           );\r
+\r
+    FreePool (Private->DevicePath);\r
+  }\r
+\r
+  if (Private->ExtScsiPassThruHandle != NULL) {\r
+    Status = gBS->UninstallProtocolInterface (\r
+                    Private->ExtScsiPassThruHandle,\r
+                    &gEfiExtScsiPassThruProtocolGuid,\r
+                    &Private->IScsiExtScsiPassThru\r
+                    );\r
+    if (!EFI_ERROR (Status)) {\r
+      mPrivate->OneSessionEstablished = FALSE;\r
+    }\r
+  }\r
+\r
+  gBS->CloseEvent (Private->ExitBootServiceEvent);\r
+\r
+  FreePool (Private);\r
+}\r
+\r
+\r
+/**\r
+  Get the various configuration data.\r
+\r
+  @param[in]  Private   The iSCSI driver data.\r
+\r
+  @retval EFI_SUCCESS            The configuration data is retrieved.\r
+  @retval EFI_NOT_FOUND          This iSCSI driver is not configured yet.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiGetConfigData (\r
+  IN ISCSI_DRIVER_DATA  *Private\r
+  )\r
+{\r
+  EFI_STATUS                  Status;\r
+  CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];\r
+  UINTN                       Index;\r
+  ISCSI_NIC_INFO              *NicInfo;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;\r
+  ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp;\r
+  UINT8                       *AttemptConfigOrder;\r
+  UINTN                       AttemptConfigOrderSize;\r
+  CHAR16                      IScsiMode[64];\r
+  CHAR16                      IpMode[64];\r
+\r
+  //\r
+  // There should be at least one attempt configured.\r
+  //\r
+  AttemptConfigOrder = IScsiGetVariableAndSize (\r
+                         L"AttemptOrder",\r
+                         &mVendorGuid,\r
+                         &AttemptConfigOrderSize\r
+                         );\r
+  if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  //\r
+  // Get the iSCSI Initiator Name.\r
+  //\r
+  mPrivate->InitiatorNameLength  = ISCSI_NAME_MAX_SIZE;\r
+  Status = gIScsiInitiatorName.Get (\r
+                                 &gIScsiInitiatorName,\r
+                                 &mPrivate->InitiatorNameLength,\r
+                                 mPrivate->InitiatorName\r
+                                 );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Get the normal configuration.\r
+  //\r
+  for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {\r
+\r
+    //\r
+    // Check whether the attempt exists in AttemptConfig.\r
+    //\r
+    AttemptTmp = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);    \r
+    if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) {\r
+      continue;\r
+    } else if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled != ISCSI_DISABLED) {\r
+      //\r
+      // Check the autoconfig path to see whether it should be retried.\r
+      //\r
+      if (AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&\r
+          AttemptTmp->AutoConfigureMode != IP_MODE_AUTOCONFIG_SUCCESS) {\r
+        if (mPrivate->Ipv6Flag &&\r
+            AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {\r
+          //\r
+          // Autoconfigure for IP6 already attempted but failed. Do not try again.\r
+          //\r
+          continue;\r
+        } else if (!mPrivate->Ipv6Flag &&\r
+                   AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {\r
+          //\r
+          // Autoconfigure for IP4  already attempted but failed. Do not try again.\r
+          //\r
+          continue;\r
+        } else {\r
+          //\r
+          // Try another approach for this autoconfigure path.\r
+          //\r
+          AttemptTmp->AutoConfigureMode =\r
+            (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);\r
+          AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp = TRUE;\r
+          AttemptTmp->SessionConfigData.TargetInfoFromDhcp    = TRUE;\r
+          AttemptTmp->DhcpSuccess                             = FALSE;\r
+\r
+          //\r
+          // Get some information from the dhcp server.\r
+          //\r
+          if (!mPrivate->Ipv6Flag) {\r
+            Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);\r
+            if (!EFI_ERROR (Status)) {\r
+              AttemptTmp->DhcpSuccess = TRUE;\r
+            }\r
+          } else {\r
+            Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);\r
+            if (!EFI_ERROR (Status)) {\r
+              AttemptTmp->DhcpSuccess = TRUE;\r
+            }\r
+          }\r
+\r
+          //\r
+          // Refresh the state of this attempt to NVR.\r
+          //\r
+          AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);\r
+          UnicodeSPrint (\r
+            mPrivate->PortString,\r
+            (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+            L"%s%d",\r
+            MacString,\r
+            (UINTN) AttemptTmp->AttemptConfigIndex\r
+            );\r
+\r
+          gRT->SetVariable (\r
+                 mPrivate->PortString,\r
+                 &gEfiIScsiInitiatorNameProtocolGuid,\r
+                 ISCSI_CONFIG_VAR_ATTR,\r
+                 sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),\r
+                 AttemptTmp\r
+                 );\r
+\r
+          continue;\r
+        }\r
+      } else if (AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp && !AttemptTmp->ValidPath) {\r
+        //\r
+        // Get DHCP information for already added, but failed, attempt.\r
+        //\r
+        AttemptTmp->DhcpSuccess = FALSE;\r
+        if (!mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP4)) {\r
+          Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);\r
+          if (!EFI_ERROR (Status)) {\r
+            AttemptTmp->DhcpSuccess = TRUE;\r
+          }\r
+        } else if (mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP6)) {\r
+          Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);\r
+          if (!EFI_ERROR (Status)) {\r
+            AttemptTmp->DhcpSuccess = TRUE;\r
+          }\r
+        }\r
+\r
+        //\r
+        // Refresh the state of this attempt to NVR.\r
+        //\r
+        AsciiStrToUnicodeStr (AttemptTmp->MacString, MacString);\r
+        UnicodeSPrint (\r
+          mPrivate->PortString,\r
+          (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+          L"%s%d",\r
+          MacString,\r
+          (UINTN) AttemptTmp->AttemptConfigIndex\r
+          );\r
+\r
+        gRT->SetVariable (\r
+               mPrivate->PortString,\r
+               &gEfiIScsiInitiatorNameProtocolGuid,\r
+               ISCSI_CONFIG_VAR_ATTR,\r
+               sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),\r
+               AttemptTmp\r
+               );\r
+\r
+        continue;\r
+\r
+      } else {\r
+        continue;\r
+      }\r
+    }\r
+\r
+    //\r
+    // This attempt does not exist in AttemptConfig. Try to add a new one.\r
+    //\r
+\r
+    NicInfo = IScsiGetNicInfoByIndex (mPrivate->CurrentNic);\r
+    ASSERT (NicInfo != NULL);\r
+    IScsiMacAddrToStr (&NicInfo->PermanentAddress, NicInfo->HwAddressSize, NicInfo->VlanId, MacString);\r
+    UnicodeSPrint (\r
+      mPrivate->PortString,\r
+      (UINTN) 128,\r
+      L"%s%d",\r
+      MacString,\r
+      (UINTN) AttemptConfigOrder[Index]\r
+      );\r
+\r
+    AttemptConfigData = (ISCSI_ATTEMPT_CONFIG_NVDATA *) GetVariable (\r
+                                                          mPrivate->PortString,\r
+                                                          &gEfiIScsiInitiatorNameProtocolGuid\r
+                                                          );\r
+\r
+    if (AttemptConfigData == NULL) {\r
+      continue;\r
+    }\r
+\r
+    ASSERT (AttemptConfigOrder[Index] == AttemptConfigData->AttemptConfigIndex);\r
+\r
+    AttemptConfigData->NicIndex      = NicInfo->NicIndex;\r
+    AttemptConfigData->DhcpSuccess   = FALSE;\r
+    AttemptConfigData->ValidiBFTPath = (BOOLEAN) (mPrivate->EnableMpio ? TRUE : FALSE);\r
+    AttemptConfigData->ValidPath     = FALSE;\r
+\r
+    if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {\r
+      AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp = TRUE;\r
+      AttemptConfigData->SessionConfigData.TargetInfoFromDhcp    = TRUE;\r
+\r
+      AttemptConfigData->AutoConfigureMode =\r
+        (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);\r
+    }\r
+    \r
+    //\r
+    // Get some information from dhcp server.\r
+    //\r
+    if (AttemptConfigData->SessionConfigData.Enabled != ISCSI_DISABLED &&\r
+        AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp) {\r
+\r
+      if (!mPrivate->Ipv6Flag &&\r
+          (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4 ||\r
+           AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4)) {\r
+        Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptConfigData);\r
+        if (!EFI_ERROR (Status)) {\r
+          AttemptConfigData->DhcpSuccess = TRUE;\r
+        }\r
+      } else if (mPrivate->Ipv6Flag &&\r
+                (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6 ||\r
+                 AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6)) {\r
+        Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptConfigData);\r
+        if (!EFI_ERROR (Status)) {\r
+          AttemptConfigData->DhcpSuccess = TRUE;\r
+        }\r
+      }\r
+\r
+      //\r
+      // Refresh the state of this attempt to NVR.\r
+      //\r
+      AsciiStrToUnicodeStr (AttemptConfigData->MacString, MacString);\r
+      UnicodeSPrint (\r
+        mPrivate->PortString,\r
+        (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+        L"%s%d",\r
+        MacString,\r
+        (UINTN) AttemptConfigData->AttemptConfigIndex\r
+        );\r
+\r
+      gRT->SetVariable (\r
+             mPrivate->PortString,\r
+             &gEfiIScsiInitiatorNameProtocolGuid,\r
+             ISCSI_CONFIG_VAR_ATTR,\r
+             sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),\r
+             AttemptConfigData\r
+             );\r
+    }\r
+\r
+    //\r
+    // Update Attempt Help Info.\r
+    //\r
+\r
+    if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED) {\r
+      UnicodeSPrint (IScsiMode, 64, L"Disabled");\r
+    } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {\r
+      UnicodeSPrint (IScsiMode, 64, L"Enabled");\r
+    } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+      UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");\r
+    }\r
+\r
+    if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {\r
+      UnicodeSPrint (IpMode, 64, L"IP4");\r
+    } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {\r
+      UnicodeSPrint (IpMode, 64, L"IP6");\r
+    } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {\r
+      UnicodeSPrint (IpMode, 64, L"Autoconfigure");\r
+    }\r
+\r
+    UnicodeSPrint (\r
+      mPrivate->PortString,\r
+      (UINTN) ISCSI_NAME_IFR_MAX_SIZE,\r
+      L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",\r
+      MacString,\r
+      NicInfo->BusNumber,\r
+      NicInfo->DeviceNumber,\r
+      NicInfo->FunctionNumber,\r
+      IScsiMode,\r
+      IpMode\r
+      );\r
+\r
+    AttemptConfigData->AttemptTitleHelpToken = HiiSetString (\r
+                                                 mCallbackInfo->RegisteredHandle,\r
+                                                 0,\r
+                                                 mPrivate->PortString,\r
+                                                 NULL\r
+                                                 );\r
+    ASSERT (AttemptConfigData->AttemptTitleHelpToken != 0);\r
+\r
+    //\r
+    // Record the attempt in global link list.\r
+    //\r
+    InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);\r
+    mPrivate->AttemptCount++;\r
+\r
+    if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {\r
+      mPrivate->MpioCount++;\r
+      mPrivate->EnableMpio = TRUE;\r
+\r
+      if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB) {\r
+        mPrivate->Krb5MpioCount++;\r
+      }\r
+    } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {\r
+      mPrivate->SinglePathCount++;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Reorder the AttemptConfig by the configured order.\r
+  //\r
+  for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {\r
+    AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);\r
+    if (AttemptConfigData == NULL) {\r
+      continue;\r
+    }\r
+\r
+    RemoveEntryList (&AttemptConfigData->Link);\r
+    InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);\r
+  }\r
+\r
+  //\r
+  // Update the Main Form.\r
+  //\r
+  IScsiConfigUpdateAttempt ();\r
+\r
+  FreePool (AttemptConfigOrder);\r
+\r
+  //\r
+  //  There should be at least one attempt configuration.\r
+  //\r
+  if (!mPrivate->EnableMpio) {\r
+    if (mPrivate->SinglePathCount == 0) {\r
+      return EFI_NOT_FOUND;\r
+    }\r
+    mPrivate->ValidSinglePathCount = mPrivate->SinglePathCount;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Get the device path of the iSCSI tcp connection and update it.\r
+\r
+  @param  Session                The iSCSI session.\r
+\r
+  @return The updated device path.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+IScsiGetTcpConnDevicePath (\r
+  IN ISCSI_SESSION      *Session\r
+  )\r
+{\r
+  ISCSI_CONNECTION          *Conn;\r
+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;\r
+  EFI_STATUS                Status;\r
+  EFI_DEV_PATH              *DPathNode;\r
+\r
+  if (Session->State != SESSION_STATE_LOGGED_IN) {\r
+    return NULL;\r
+  }\r
+\r
+  Conn = NET_LIST_USER_STRUCT_S (\r
+           Session->Conns.ForwardLink,\r
+           ISCSI_CONNECTION,\r
+           Link,\r
+           ISCSI_CONNECTION_SIGNATURE\r
+           );\r
+\r
+  Status = gBS->HandleProtocol (\r
+                  Conn->TcpIo.Handle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  (VOID **) &DevicePath\r
+                  );  \r
+  if (EFI_ERROR (Status)) {\r
+    return NULL;\r
+  }\r
+  //\r
+  // Duplicate it.\r
+  //\r
+  DevicePath  = DuplicateDevicePath (DevicePath);\r
+  if (DevicePath == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  DPathNode   = (EFI_DEV_PATH *) DevicePath;\r
+\r
+  while (!IsDevicePathEnd (&DPathNode->DevPath)) {\r
+    if (DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) {\r
+      if (!Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP) {\r
+        DPathNode->Ipv4.LocalPort       = 0;\r
+        DPathNode->Ipv4.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;\r
+        break;\r
+      } else if (Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv6_DP) {\r
+        DPathNode->Ipv6.LocalPort       = 0;\r
+        DPathNode->Ipv6.StaticIpAddress = (BOOLEAN) !Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp;\r
+        break;\r
+      }\r
+    }\r
+\r
+    DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath);\r
+  }\r
+\r
+  return DevicePath;\r
+}\r
+\r
+\r
+/**\r
+  Abort the session when the transition from BS to RT is initiated.\r
+\r
+  @param[in]   Event  The event signaled.\r
+  @param[in]  Context The iSCSI driver data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IScsiOnExitBootService (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  )\r
+{\r
+  ISCSI_DRIVER_DATA *Private;\r
+\r
+  Private = (ISCSI_DRIVER_DATA *) Context;\r
+  gBS->CloseEvent (Private->ExitBootServiceEvent);\r
+\r
+  if (Private->Session != NULL) {\r
+    IScsiSessionAbort (Private->Session);\r
+  }\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiMisc.h b/NetworkPkg/IScsiDxe/IScsiMisc.h
new file mode 100644 (file)
index 0000000..8646f66
--- /dev/null
@@ -0,0 +1,343 @@
+/** @file\r
+  Miscellaneous definitions for iSCSI driver.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_MISC_H_\r
+#define _ISCSI_MISC_H_\r
+\r
+typedef struct _ISCSI_DRIVER_DATA ISCSI_DRIVER_DATA;\r
+\r
+#pragma pack(1)\r
+typedef struct _ISCSI_SESSION_CONFIG_NVDATA {\r
+  UINT16            TargetPort;\r
+  UINT8             Enabled;\r
+  UINT8             IpMode;\r
+\r
+  EFI_IPv4_ADDRESS  LocalIp;\r
+  EFI_IPv4_ADDRESS  SubnetMask;\r
+  EFI_IPv4_ADDRESS  Gateway;\r
+\r
+  BOOLEAN           InitiatorInfoFromDhcp;\r
+  BOOLEAN           TargetInfoFromDhcp;\r
+  CHAR8             TargetName[ISCSI_NAME_MAX_SIZE];\r
+  EFI_IP_ADDRESS    TargetIp;\r
+  UINT8             BootLun[8];\r
+\r
+  UINT16            ConnectTimeout; ///< timout value in milliseconds\r
+  UINT8             ConnectRetryCount;\r
+  UINT8             IsId[6];\r
+} ISCSI_SESSION_CONFIG_NVDATA;\r
+#pragma pack()\r
+\r
+/**\r
+  Calculate the prefix length of the IPv4 subnet mask.\r
+\r
+  @param[in]  SubnetMask The IPv4 subnet mask.\r
+\r
+  @return The prefix length of the subnet mask.\r
+  @retval 0 Other errors as indicated.\r
+\r
+**/\r
+UINT8\r
+IScsiGetSubnetMaskPrefixLength (\r
+  IN EFI_IPv4_ADDRESS  *SubnetMask\r
+  );\r
+\r
+/**\r
+  Convert the hexadecimal encoded LUN string into the 64-bit LUN. \r
+\r
+  @param[in]   Str             The hexadecimal encoded LUN string.\r
+  @param[out]  Lun             Storage to return the 64-bit LUN.\r
+\r
+  @retval EFI_SUCCESS           The 64-bit LUN is stored in Lun.\r
+  @retval EFI_INVALID_PARAMETER The string is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiAsciiStrToLun (\r
+  IN  CHAR8  *Str,\r
+  OUT UINT8  *Lun\r
+  );\r
+\r
+/**\r
+  Convert the 64-bit LUN into the hexadecimal encoded LUN string.\r
+\r
+  @param[in]   Lun    The 64-bit LUN.\r
+  @param[out]  String The storage to return the hexadecimal encoded LUN string.\r
+\r
+**/\r
+VOID\r
+IScsiLunToUnicodeStr (\r
+  IN UINT8    *Lun,\r
+  OUT CHAR16  *String\r
+  );\r
+\r
+/**\r
+  Convert the mac address into a hexadecimal encoded "-" seperated string.\r
+\r
+  @param[in]  Mac     The mac address.\r
+  @param[in]  Len     Length in bytes of the mac address.\r
+  @param[in]  VlanId  VLAN ID of the network device.\r
+  @param[out] Str     The storage to return the mac string.\r
+\r
+**/\r
+VOID\r
+IScsiMacAddrToStr (\r
+  IN  EFI_MAC_ADDRESS  *Mac,\r
+  IN  UINT32           Len,\r
+  IN  UINT16           VlanId,\r
+  OUT CHAR16           *Str\r
+  );\r
+\r
+/**\r
+  Convert the formatted IP address into the binary IP address.\r
+\r
+  @param[in]   Str               The UNICODE string.\r
+  @param[in]   IpMode            Indicates whether the IP address is v4 or v6.\r
+  @param[out]  Ip                The storage to return the ASCII string.\r
+\r
+  @retval EFI_SUCCESS            The binary IP address is returned in Ip.\r
+  @retval EFI_INVALID_PARAMETER  The IP string is malformatted or IpMode is\r
+                                 invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiAsciiStrToIp (\r
+  IN  CHAR8             *Str,\r
+  IN  UINT8             IpMode,\r
+  OUT EFI_IP_ADDRESS    *Ip\r
+  );\r
+\r
+/**\r
+  Convert the binary encoded buffer into a hexadecimal encoded string.\r
+\r
+  @param[in]       BinBuffer   The buffer containing the binary data.\r
+  @param[in]       BinLength   Length of the binary buffer.\r
+  @param[in, out]  HexStr      Pointer to the string.\r
+  @param[in, out]  HexLength   The length of the string.\r
+\r
+  @retval EFI_SUCCESS          The binary data is converted to the hexadecimal string \r
+                               and the length of the string is updated.\r
+  @retval EFI_BUFFER_TOO_SMALL The string is too small.\r
+  @retval EFI_INVALID_PARAMETER The IP string is malformatted.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiBinToHex (\r
+  IN     UINT8  *BinBuffer,\r
+  IN     UINT32 BinLength,\r
+  IN OUT CHAR8  *HexStr,\r
+  IN OUT UINT32 *HexLength\r
+  );\r
+\r
+/**\r
+  Convert the hexadecimal string into a binary encoded buffer.\r
+\r
+  @param[in, out]  BinBuffer   The binary buffer.\r
+  @param[in, out]  BinLength   Length of the binary buffer.\r
+  @param[in]       HexStr      The hexadecimal string.\r
+\r
+  @retval EFI_SUCCESS          The hexadecimal string is converted into a binary\r
+                               encoded buffer.\r
+  @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiHexToBin (\r
+  IN OUT UINT8  *BinBuffer,\r
+  IN OUT UINT32 *BinLength,\r
+  IN     CHAR8  *HexStr\r
+  );\r
+\r
+\r
+/**\r
+  Convert the decimal-constant string or hex-constant string into a numerical value.\r
+\r
+  @param[in] Str                    String in decimal or hex.\r
+\r
+  @return The numerical value.\r
+\r
+**/\r
+UINTN\r
+IScsiNetNtoi (\r
+  IN     CHAR8  *Str\r
+  );\r
+\r
+/**\r
+  Generate random numbers.\r
+\r
+  @param[in, out]  Rand       The buffer to contain random numbers.\r
+  @param[in]       RandLength The length of the Rand buffer.\r
+\r
+**/\r
+VOID\r
+IScsiGenRandom (\r
+  IN OUT UINT8  *Rand,\r
+  IN     UINTN  RandLength\r
+  );\r
+\r
+/**\r
+  Record the NIC information in a global structure.\r
+\r
+  @param[in]  Controller         The handle of the controller.\r
+\r
+  @retval EFI_SUCCESS            The operation is completed.\r
+  @retval EFI_OUT_OF_RESOURCES   Do not have sufficient resource to finish this\r
+                                 operation.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiAddNic (\r
+  IN EFI_HANDLE  Controller\r
+  );\r
+\r
+/**\r
+  Delete the recorded NIC information from a global structure. Also delete corresponding\r
+  attempts.\r
+\r
+  @param[in]  Controller         The handle of the controller.\r
+\r
+  @retval EFI_SUCCESS            The operation completed.\r
+  @retval EFI_NOT_FOUND          The NIC information to be deleted is not recorded.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiRemoveNic (\r
+  IN EFI_HANDLE  Controller\r
+  );\r
+\r
+/**\r
+  Get the recorded NIC information from a global structure by the Index.\r
+\r
+  @param[in]  NicIndex          The index indicates the position of NIC info.\r
+\r
+  @return Pointer to the NIC info or NULL if not found.\r
+\r
+**/\r
+ISCSI_NIC_INFO *\r
+IScsiGetNicInfoByIndex (\r
+  IN UINT8      NicIndex\r
+  );\r
+\r
+\r
+/**\r
+  Get the NIC's PCI location and return it accroding to the composited\r
+  format defined in iSCSI Boot Firmware Table.\r
+\r
+  @param[in]   Controller        The handle of the controller.\r
+  @param[out]  Bus               The bus number.\r
+  @param[out]  Device            The device number.\r
+  @param[out]  Function          The function number.\r
+\r
+  @return      The composited representation of the NIC PCI location.\r
+\r
+**/\r
+UINT16\r
+IScsiGetNICPciLocation (\r
+  IN EFI_HANDLE  Controller,\r
+  OUT UINTN      *Bus,\r
+  OUT UINTN      *Device,\r
+  OUT UINTN      *Function\r
+  );\r
+\r
+/**\r
+  Read the EFI variable (VendorGuid/Name) and return a dynamically allocated\r
+  buffer, and the size of the buffer. If failure, return NULL.\r
+\r
+  @param[in]   Name                   String part of EFI variable name.\r
+  @param[in]   VendorGuid             GUID part of EFI variable name.\r
+  @param[out]  VariableSize           Returns the size of the EFI variable that was read.\r
+\r
+  @return Dynamically allocated memory that contains a copy of the EFI variable.\r
+  @return Caller is responsible freeing the buffer.\r
+  @retval NULL                   Variable was not read.\r
+\r
+**/\r
+VOID *\r
+IScsiGetVariableAndSize (\r
+  IN  CHAR16              *Name,\r
+  IN  EFI_GUID            *VendorGuid,\r
+  OUT UINTN               *VariableSize\r
+  );\r
+\r
+/**\r
+  Create the iSCSI driver data.\r
+\r
+  @param[in] Image      The handle of the driver image.\r
+  @param[in] Controller The handle of the controller.\r
+\r
+  @return The iSCSI driver data created.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+ISCSI_DRIVER_DATA *\r
+IScsiCreateDriverData (\r
+  IN EFI_HANDLE  Image,\r
+  IN EFI_HANDLE  Controller\r
+  );\r
+\r
+/**\r
+  Clean the iSCSI driver data.\r
+\r
+  @param[in]  Private The iSCSI driver data.\r
+\r
+**/\r
+VOID\r
+IScsiCleanDriverData (\r
+  IN ISCSI_DRIVER_DATA  *Private\r
+  );\r
+\r
+/**\r
+  Get the various configuration data of this iSCSI instance.\r
+\r
+  @param[in]  Private   The iSCSI driver data.\r
+\r
+  @retval EFI_SUCCESS   Obtained the configuration of this instance.\r
+  @retval EFI_ABORTED   The operation was aborted.\r
+  @retval Others        Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiGetConfigData (\r
+  IN ISCSI_DRIVER_DATA  *Private\r
+  );\r
+\r
+/**\r
+  Get the device path of the iSCSI tcp connection and update it.\r
+\r
+  @param[in]  Session The iSCSI session data.\r
+\r
+  @return The updated device path.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+IScsiGetTcpConnDevicePath (\r
+  IN ISCSI_SESSION      *Session\r
+  );\r
+\r
+/**\r
+  Abort the session when the transition from BS to RT is initiated.\r
+\r
+  @param[in]   Event  The event signaled.\r
+  @param[in]  Context The iSCSI driver data.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IScsiOnExitBootService (\r
+  IN EFI_EVENT  Event,\r
+  IN VOID       *Context\r
+  );\r
+\r
+#endif\r
diff --git a/NetworkPkg/IScsiDxe/IScsiProto.c b/NetworkPkg/IScsiDxe/IScsiProto.c
new file mode 100644 (file)
index 0000000..2ab4da8
--- /dev/null
@@ -0,0 +1,2991 @@
+/** @file\r
+  The implementation of iSCSI protocol based on RFC3720.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "IScsiImpl.h"\r
+\r
+UINT32 mDataSegPad = 0;\r
+\r
+/**\r
+  Attach the iSCSI connection to the iSCSI session. \r
+\r
+  @param[in, out]  Session The iSCSI session.\r
+  @param[in, out]  Conn    The iSCSI connection.\r
+\r
+**/\r
+VOID\r
+IScsiAttatchConnection (\r
+  IN OUT ISCSI_SESSION     *Session,\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  InsertTailList (&Session->Conns, &Conn->Link);\r
+  Conn->Session = Session;\r
+  Session->NumConns++;\r
+}\r
+\r
+/**\r
+  Detach the iSCSI connection from the session it belongs to. \r
+\r
+  @param[in, out]  Conn The iSCSI connection.\r
+\r
+**/\r
+VOID\r
+IScsiDetatchConnection (\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  RemoveEntryList (&Conn->Link);\r
+  Conn->Session->NumConns--;\r
+  Conn->Session = NULL;\r
+}\r
+\r
+\r
+/**\r
+  Check the sequence number according to RFC3720. \r
+\r
+  @param[in, out]  ExpSN   The currently expected sequence number.\r
+  @param[in]       NewSN   The sequence number to check.\r
+\r
+  @retval EFI_SUCCESS         The check passed and the ExpSN is increased.\r
+  @retval EFI_NOT_READY       Response was sent due to a retransmission request.\r
+  @retval EFI_PROTOCOL_ERROR  Some kind of iSCSI protocol error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCheckSN (\r
+  IN OUT UINT32  *ExpSN,\r
+  IN UINT32      NewSN\r
+  )\r
+{\r
+  if (!ISCSI_SEQ_EQ (NewSN, *ExpSN)) {\r
+    if (ISCSI_SEQ_LT (NewSN, *ExpSN)) {\r
+      //\r
+      // Duplicate\r
+      //\r
+      return EFI_NOT_READY;\r
+    } else {\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+  } else {\r
+    //\r
+    // Advance the ExpSN\r
+    //\r
+    (*ExpSN)++;\r
+    return EFI_SUCCESS;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  Update the sequence numbers for the iSCSI command.\r
+\r
+  @param[in, out]  Session  The iSCSI session.\r
+  @param[in]       MaxCmdSN Maximum CmdSN from the target.\r
+  @param[in]       ExpCmdSN Next expected CmdSN from the target.\r
+\r
+**/\r
+VOID\r
+IScsiUpdateCmdSN (\r
+  IN OUT ISCSI_SESSION  *Session,\r
+  IN UINT32             MaxCmdSN,\r
+  IN UINT32             ExpCmdSN\r
+  )\r
+{\r
+  if (ISCSI_SEQ_LT (MaxCmdSN, ExpCmdSN - 1)) {\r
+    return ;\r
+  }\r
+\r
+  if (ISCSI_SEQ_GT (MaxCmdSN, Session->MaxCmdSN)) {\r
+    Session->MaxCmdSN = MaxCmdSN;\r
+  }\r
+\r
+  if (ISCSI_SEQ_GT (ExpCmdSN, Session->ExpCmdSN)) {\r
+    Session->ExpCmdSN = ExpCmdSN;\r
+  }\r
+}\r
+\r
+\r
+/**\r
+  This function does the iSCSI connection login.\r
+\r
+  @param[in, out]  Conn      The iSCSI connection to login.\r
+  @param           Timeout   The timeout value in millisecond.\r
+\r
+  @retval EFI_SUCCESS        The iSCSI connection is logged into the iSCSI target.\r
+  @retval EFI_TIMEOUT        Timeout occurred during the login procedure.\r
+  @retval Others             Other errors as indicated.  \r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConnLogin (\r
+  IN OUT ISCSI_CONNECTION    *Conn,\r
+  IN     UINT16              Timeout\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Start the timer, and wait Timeout seconds to establish the TCP connection.\r
+  //\r
+  Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout * TICKS_PER_MS);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Try to establish the tcp connection.\r
+  //\r
+  Status = TcpIoConnect (&Conn->TcpIo, Conn->TimeoutEvent);\r
+  gBS->SetTimer (Conn->TimeoutEvent, TimerCancel, 0);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Conn->State = CONN_STATE_IN_LOGIN;\r
+\r
+  //\r
+  // Connection is established, start the iSCSI Login.\r
+  //\r
+  do {\r
+    Status = IScsiSendLoginReq (Conn);\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+\r
+    Status = IScsiReceiveLoginRsp (Conn);\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  } while (Conn->CurrentStage != ISCSI_FULL_FEATURE_PHASE);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Reset the iSCSI connection.\r
+\r
+  @param[in, out]  Conn The iSCSI connection to reset.\r
+\r
+**/\r
+VOID\r
+IScsiConnReset (\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  TcpIoReset (&Conn->TcpIo);\r
+}\r
+\r
+\r
+/**\r
+  Create a TCP connection for the iSCSI session.\r
+\r
+  @param[in]  Session Points to the iSCSI session.\r
+\r
+  @return The newly created iSCSI connection.\r
+\r
+**/\r
+ISCSI_CONNECTION *\r
+IScsiCreateConnection (\r
+  IN ISCSI_SESSION      *Session\r
+  )\r
+{\r
+  ISCSI_DRIVER_DATA            *Private;\r
+  ISCSI_SESSION_CONFIG_NVDATA  *NvData;\r
+  ISCSI_CONNECTION             *Conn;\r
+  TCP_IO_CONFIG_DATA           TcpIoConfig;\r
+  TCP4_IO_CONFIG_DATA          *Tcp4IoConfig;\r
+  TCP6_IO_CONFIG_DATA          *Tcp6IoConfig;\r
+  EFI_STATUS                   Status;\r
+\r
+  Private = Session->Private;\r
+  NvData  = &Session->ConfigData->SessionConfigData;\r
+\r
+  Conn = AllocateZeroPool (sizeof (ISCSI_CONNECTION));\r
+  if (Conn == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  Conn->Signature       = ISCSI_CONNECTION_SIGNATURE;\r
+  Conn->State           = CONN_STATE_FREE;\r
+  Conn->CurrentStage    = ISCSI_SECURITY_NEGOTIATION;\r
+  Conn->NextStage       = ISCSI_LOGIN_OPERATIONAL_NEGOTIATION;\r
+  Conn->AuthStep        = ISCSI_AUTH_INITIAL;\r
+  Conn->ExpStatSN       = 0;\r
+  Conn->PartialReqSent  = FALSE;\r
+  Conn->PartialRspRcvd  = FALSE;\r
+  Conn->ParamNegotiated = FALSE;\r
+  Conn->Cid             = Session->NextCid++;\r
+  Conn->Ipv6Flag        = mPrivate->Ipv6Flag;\r
+\r
+  Status = gBS->CreateEvent (\r
+                  EVT_TIMER,\r
+                  TPL_CALLBACK,\r
+                  NULL,\r
+                  NULL,\r
+                  &Conn->TimeoutEvent\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    FreePool (Conn);\r
+    return NULL;\r
+  }\r
+\r
+  NetbufQueInit (&Conn->RspQue);\r
+\r
+  //\r
+  // Set the default connection-only parameters.\r
+  //\r
+  Conn->MaxRecvDataSegmentLength  = DEFAULT_MAX_RECV_DATA_SEG_LEN;\r
+  Conn->HeaderDigest              = IScsiDigestNone;\r
+  Conn->DataDigest                = IScsiDigestNone;\r
+\r
+  if (!Conn->Ipv6Flag) {\r
+    Tcp4IoConfig = &TcpIoConfig.Tcp4IoConfigData;\r
+    \r
+    CopyMem (&Tcp4IoConfig->LocalIp, &NvData->LocalIp, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Tcp4IoConfig->SubnetMask, &NvData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Tcp4IoConfig->Gateway, &NvData->Gateway, sizeof (EFI_IPv4_ADDRESS));\r
+    CopyMem (&Tcp4IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv4_ADDRESS));\r
+\r
+    Tcp4IoConfig->RemotePort = NvData->TargetPort;\r
+    Tcp4IoConfig->ActiveFlag = TRUE;\r
+\r
+  } else {\r
+    Tcp6IoConfig = &TcpIoConfig.Tcp6IoConfigData;\r
+  \r
+    CopyMem (&Tcp6IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS));\r
+    Tcp6IoConfig->RemotePort = NvData->TargetPort;\r
+    Tcp6IoConfig->ActiveFlag = TRUE;\r
+  }\r
+\r
+  //\r
+  // Create the TCP IO for this connection.\r
+  //\r
+  Status = TcpIoCreateSocket (\r
+             Private->Image,\r
+             Private->Controller,\r
+             (UINT8) (!Conn->Ipv6Flag ? TCP_VERSION_4: TCP_VERSION_6),\r
+             &TcpIoConfig,\r
+             &Conn->TcpIo\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseEvent (Conn->TimeoutEvent);\r
+    FreePool (Conn);\r
+    Conn = NULL;\r
+  }\r
+\r
+  return Conn;\r
+}\r
+\r
+\r
+/**\r
+  Destroy an iSCSI connection.\r
+\r
+  @param[in]  Conn The connection to destroy.\r
+\r
+**/\r
+VOID\r
+IScsiDestroyConnection (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  TcpIoDestroySocket (&Conn->TcpIo);\r
+\r
+  NetbufQueFlush (&Conn->RspQue);\r
+  gBS->CloseEvent (Conn->TimeoutEvent);\r
+  FreePool (Conn);\r
+}\r
+\r
+\r
+/**\r
+  Login the iSCSI session.\r
+\r
+  @param[in]  Session           The iSCSI session.\r
+\r
+  @retval EFI_SUCCESS           The iSCSI session login procedure finished.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_NO_MEDIA          There was a media error.\r
+  @retval Others                Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSessionLogin (\r
+  IN ISCSI_SESSION  *Session\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  ISCSI_CONNECTION  *Conn;\r
+  VOID              *Tcp;\r
+  EFI_GUID          *ProtocolGuid;\r
+  UINT8             RetryCount;\r
+  BOOLEAN           MediaPresent;\r
+\r
+  //\r
+  // Check media status before session login.\r
+  //\r
+  MediaPresent = TRUE;\r
+  NetLibDetectMedia (Session->Private->Controller, &MediaPresent);\r
+  if (!MediaPresent) {\r
+    return EFI_NO_MEDIA;\r
+  }\r
+\r
+  //\r
+  // Set session identifier\r
+  //\r
+  CopyMem (Session->Isid, Session->ConfigData->SessionConfigData.IsId, 6);\r
+\r
+  RetryCount = 0;\r
+\r
+  do {\r
+    //\r
+    // Create a connection for the session.\r
+    //\r
+    Conn = IScsiCreateConnection (Session);\r
+    if (Conn == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    IScsiAttatchConnection (Session, Conn);\r
+\r
+    //\r
+    // Login througth the newly created connection.\r
+    //\r
+    Status = IScsiConnLogin (Conn, Session->ConfigData->SessionConfigData.ConnectTimeout);\r
+    if (EFI_ERROR (Status)) {\r
+      IScsiConnReset (Conn);\r
+      IScsiDetatchConnection (Conn);\r
+      IScsiDestroyConnection (Conn);\r
+    }\r
+\r
+    if (Status != EFI_TIMEOUT) {\r
+      break;\r
+    }\r
+\r
+    RetryCount++;\r
+  } while (RetryCount <= Session->ConfigData->SessionConfigData.ConnectRetryCount);\r
+\r
+  if (!EFI_ERROR (Status)) {\r
+    Session->State = SESSION_STATE_LOGGED_IN;\r
+\r
+    if (!mPrivate->Ipv6Flag) {\r
+      ProtocolGuid = &gEfiTcp4ProtocolGuid;      \r
+    } else {\r
+      ProtocolGuid = &gEfiTcp6ProtocolGuid;\r
+    }\r
+\r
+    Status = gBS->OpenProtocol (\r
+                    Conn->TcpIo.Handle,\r
+                    ProtocolGuid,\r
+                    (VOID **) &Tcp,\r
+                    Session->Private->Image,\r
+                    Session->Private->ExtScsiPassThruHandle,\r
+                    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER                    \r
+                    );\r
+\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Wait for IPsec negotiation, then try to login the iSCSI session again.\r
+\r
+  @param[in]  Session           The iSCSI session.\r
+\r
+  @retval EFI_SUCCESS           The iSCSI session login procedure finished.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR    Some kind of iSCSI protocol error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSessionReLogin (\r
+  IN ISCSI_SESSION  *Session\r
+  )\r
+{\r
+\r
+  EFI_STATUS                Status;\r
+  EFI_STATUS                TimerStatus;\r
+  EFI_EVENT                 Timer;\r
+\r
+  Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = gBS->SetTimer (\r
+                  Timer,\r
+                  TimerRelative,\r
+                  ISCSI_WAIT_IPSEC_TIMEOUT\r
+                  );\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    gBS->CloseEvent (Timer);\r
+    return Status;\r
+  }\r
+\r
+  do {\r
+\r
+    TimerStatus = gBS->CheckEvent (Timer);\r
+\r
+    if (!EFI_ERROR (TimerStatus)) {\r
+      Status = IScsiSessionLogin (Session);\r
+    }\r
+\r
+  } while (TimerStatus == EFI_NOT_READY);\r
+\r
+  gBS->CloseEvent (Timer);\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Build and send the iSCSI login request to the iSCSI target according to\r
+  the current login stage.\r
+\r
+  @param[in]  Conn             The connection in the iSCSI login phase.\r
+\r
+  @retval EFI_SUCCESS          The iSCSI login request PDU is built and sent on this\r
+                               connection.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_DEVICE_ERROR     Some kind of device error occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSendLoginReq (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  NET_BUF     *Pdu;\r
+  EFI_STATUS  Status;\r
+\r
+  //\r
+  // Build the Login Request PDU.\r
+  //\r
+  Pdu = IScsiPrepareLoginReq (Conn);\r
+  if (Pdu == NULL) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Send it to the iSCSI target.\r
+  //\r
+  Status = TcpIoTransmit (&Conn->TcpIo, Pdu);\r
+\r
+  NetbufFree (Pdu);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Receive and process the iSCSI login response.\r
+\r
+  @param[in]  Conn             The connection in the iSCSI login phase.\r
+  \r
+  @retval EFI_SUCCESS          The iSCSI login response PDU is received and processed.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiReceiveLoginRsp (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  NET_BUF     *Pdu;\r
+\r
+  //\r
+  // Receive the iSCSI login response.\r
+  //\r
+  Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+  //\r
+  // A Login Response is received; process it.\r
+  //\r
+  Status = IScsiProcessLoginRsp (Conn, Pdu);\r
+\r
+  NetbufFree (Pdu);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.\r
+  The DataSegmentLength and the actual size of the net buffer containing this PDU will be\r
+  updated.\r
+\r
+  @param[in, out]  Pdu         The iSCSI PDU whose data segment the key-value pair will\r
+                               be added to.\r
+  @param[in]       Key         The key name string.\r
+  @param[in]       Value       The value string.\r
+\r
+  @retval EFI_SUCCESS          The key-value pair is added to the PDU's data segment and\r
+                               the correspondence length fields are updated.\r
+  @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value\r
+                               pair.\r
+**/\r
+EFI_STATUS\r
+IScsiAddKeyValuePair (\r
+  IN OUT NET_BUF      *Pdu,\r
+  IN CHAR8            *Key,\r
+  IN CHAR8            *Value\r
+  )\r
+{\r
+  UINT32              DataSegLen;\r
+  UINT32              KeyLen;\r
+  UINT32              ValueLen;\r
+  UINT32              TotalLen;\r
+  ISCSI_LOGIN_REQUEST *LoginReq;\r
+  CHAR8               *Data;\r
+\r
+  LoginReq    = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL);\r
+  DataSegLen  = NTOH24 (LoginReq->DataSegmentLength);\r
+\r
+  KeyLen      = (UINT32) AsciiStrLen (Key);\r
+  ValueLen    = (UINT32) AsciiStrLen (Value);\r
+\r
+  //\r
+  // 1 byte for the key value separator '=' and 1 byte for the null\r
+  // delimiter after the value.\r
+  //\r
+  TotalLen = KeyLen + 1 + ValueLen + 1;\r
+\r
+  //\r
+  // Allocate the space for the key-value pair.\r
+  //\r
+  Data = (CHAR8 *) NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL);\r
+  if (Data == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Add the key.\r
+  //\r
+  CopyMem (Data, Key, KeyLen);\r
+  Data += KeyLen;\r
+\r
+  *Data = '=';\r
+  Data++;\r
+\r
+  //\r
+  // Add the value.\r
+  //\r
+  CopyMem (Data, Value, ValueLen);\r
+  Data += ValueLen;\r
+\r
+  *Data = '\0';\r
+\r
+  //\r
+  // Update the DataSegmentLength\r
+  //\r
+  ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Prepare the iSCSI login request to be sent according to the current login status.\r
+\r
+  @param[in, out]  Conn The connection in the iSCSI login phase.\r
+\r
+  @return The pointer to the net buffer containing the iSCSI login request built.\r
+  @retval NULL     Other errors as indicated.\r
+\r
+**/\r
+NET_BUF *\r
+IScsiPrepareLoginReq (\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  ISCSI_SESSION       *Session;\r
+  NET_BUF             *Nbuf;\r
+  ISCSI_LOGIN_REQUEST *LoginReq;\r
+  EFI_STATUS          Status;\r
+\r
+  Session = Conn->Session;\r
+\r
+  Nbuf    = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN);\r
+  if (Nbuf == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufAllocSpace (Nbuf, sizeof (ISCSI_LOGIN_REQUEST), NET_BUF_TAIL);\r
+  ASSERT (LoginReq != NULL);\r
+  ZeroMem (LoginReq, sizeof (ISCSI_LOGIN_REQUEST));\r
+\r
+  //\r
+  // Init the login request pdu\r
+  //\r
+  ISCSI_SET_OPCODE (LoginReq, ISCSI_OPCODE_LOGIN_REQ, ISCSI_REQ_IMMEDIATE);\r
+  ISCSI_SET_STAGES (LoginReq, Conn->CurrentStage, Conn->NextStage);\r
+  LoginReq->VersionMax        = ISCSI_VERSION_MAX;\r
+  LoginReq->VersionMin        = ISCSI_VERSION_MIN;\r
+  LoginReq->Tsih              = HTONS (Session->Tsih);\r
+  LoginReq->InitiatorTaskTag  = HTONL (Session->InitiatorTaskTag);\r
+  LoginReq->Cid               = HTONS (Conn->Cid);\r
+  LoginReq->CmdSN             = HTONL (Session->CmdSN);\r
+\r
+  //\r
+  // For the first Login Request on a coonection this is ExpStatSN for the\r
+  // old connection, and this field is only valid if the Login Request restarts\r
+  // a connection.\r
+  // For subsequent Login Requests it is used to acknowledge the Login Responses\r
+  // with their increasing StatSN values.\r
+  //\r
+  LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN);\r
+  CopyMem (LoginReq->Isid, Session->Isid, sizeof (LoginReq->Isid));\r
+\r
+  if (Conn->PartialRspRcvd) {\r
+    //\r
+    // A partial response. The initiator must send an empty Login Request.\r
+    //\r
+    return Nbuf;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  switch (Conn->CurrentStage) {\r
+  case ISCSI_SECURITY_NEGOTIATION:\r
+    //\r
+    // Both none authentication and CHAP authentication share the CHAP path.\r
+    // \r
+    //\r
+    if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {\r
+      Status = IScsiCHAPToSendReq (Conn, Nbuf);\r
+    }\r
+\r
+    break;\r
+\r
+  case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:\r
+    //\r
+    // Only negotiate the paramter once.\r
+    //\r
+    if (!Conn->ParamNegotiated) {\r
+      IScsiFillOpParams (Conn, Nbuf);\r
+    }\r
+    \r
+    ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+    break;\r
+\r
+  default:\r
+    //\r
+    // An error occurs...\r
+    //\r
+    Status = EFI_DEVICE_ERROR;\r
+    break;\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    NetbufFree (Nbuf);\r
+    Nbuf = NULL;\r
+  } else {\r
+    //\r
+    // Pad the data segment if needed.\r
+    //\r
+    IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq));\r
+    //\r
+    // Check whether we will issue the stage transition signal?\r
+    //\r
+    Conn->TransitInitiated = ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);\r
+  }\r
+\r
+  return Nbuf;\r
+}\r
+\r
+\r
+/**\r
+  Process the iSCSI Login Response.\r
+\r
+  @param[in, out]  Conn The connection on which the iSCSI login response is received.\r
+  @param[in, out]  Pdu  The iSCSI login response PDU.\r
+\r
+  @retval EFI_SUCCESS        The iSCSI login response PDU is processed, and all checks are passed.\r
+  @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.\r
+  @retval EFI_MEDIA_CHANGED  Target is redirected.\r
+  @retval Others             Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiProcessLoginRsp (\r
+  IN OUT ISCSI_CONNECTION  *Conn,\r
+  IN OUT NET_BUF           *Pdu\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  ISCSI_SESSION         *Session;\r
+  ISCSI_LOGIN_RESPONSE  *LoginRsp;\r
+  BOOLEAN               Transit;\r
+  BOOLEAN               Continue;\r
+  UINT8                 CurrentStage;\r
+  UINT8                 NextStage;\r
+  UINT8                 *DataSeg;\r
+  UINT32                DataSegLen;\r
+\r
+  Status   = EFI_SUCCESS;\r
+  Session   = Conn->Session;\r
+\r
+  LoginRsp  = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);\r
+  if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) {\r
+    //\r
+    // It is not a Login Response.\r
+    //\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+  //\r
+  // Get the data segment, if any.\r
+  //\r
+  DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp);\r
+  if (DataSegLen != 0) {\r
+    DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL);\r
+  } else {\r
+    DataSeg = NULL;\r
+  }\r
+  //\r
+  // Check the status class in the login response PDU.\r
+  //\r
+  switch (LoginRsp->StatusClass) {\r
+  case ISCSI_LOGIN_STATUS_SUCCESS:\r
+    //\r
+    // Just break here; the response and the data segment will be processed later.\r
+    //\r
+    break;\r
+\r
+  case ISCSI_LOGIN_STATUS_REDIRECTION:\r
+    //\r
+    // The target may be moved to a different address.\r
+    //\r
+    if (DataSeg == NULL) {\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+    //\r
+    // Process the TargetAddress key-value strings in the data segment to update the\r
+    // target address info.\r
+    //\r
+    Status = IScsiUpdateTargetAddress (Session, (CHAR8 *) DataSeg, DataSegLen);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    //\r
+    // Session will be restarted on this error status because the Target is\r
+    // redirected by this Login Response.\r
+    //\r
+    return EFI_MEDIA_CHANGED;\r
+\r
+  default:\r
+    //\r
+    // Initiator Error, Target Error, or any other undefined error code.\r
+    //\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+  //\r
+  // The status is success; extract the wanted fields from the header segment.\r
+  //\r
+  Transit                     = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT);\r
+  Continue                    = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE);\r
+\r
+  CurrentStage                = ISCSI_GET_CURRENT_STAGE (LoginRsp);\r
+  NextStage                   = ISCSI_GET_NEXT_STAGE (LoginRsp);\r
+\r
+  LoginRsp->InitiatorTaskTag  = NTOHL (LoginRsp->InitiatorTaskTag);\r
+\r
+  if ((Transit && Continue) ||\r
+      (CurrentStage != Conn->CurrentStage) ||\r
+      (!Conn->TransitInitiated && Transit) ||\r
+      (Transit && (NextStage != Conn->NextStage)) ||\r
+      (CompareMem (Session->Isid, LoginRsp->Isid, sizeof (LoginRsp->Isid)) != 0) ||\r
+      (LoginRsp->InitiatorTaskTag != Session->InitiatorTaskTag)\r
+      ) {\r
+    //\r
+    // A Login Response with the C bit set to 1 MUST have the T bit set to 0.\r
+    // The CSG in the Login Response MUST be the same with the I-end of this connection.\r
+    // The T bit can't be 1 if the last Login Response sent by the initiator doesn't\r
+    // initiate the transistion.\r
+    // The NSG MUST be the same with the I-end of this connection if Transit is required.\r
+    // The ISID in the Login Response MUST be the same with this session.\r
+    //\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  LoginRsp->StatSN    = NTOHL (LoginRsp->StatSN);\r
+  LoginRsp->ExpCmdSN  = NTOHL (LoginRsp->ExpCmdSN);\r
+  LoginRsp->MaxCmdSN  = NTOHL (LoginRsp->MaxCmdSN);\r
+\r
+  if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->AuthStep == ISCSI_AUTH_INITIAL)) {\r
+    //\r
+    // If the Login Request is a leading Login Request, the target MUST use\r
+    // the value presented in CmdSN as the target value for ExpCmdSN.\r
+    //\r
+    if ((Session->State == SESSION_STATE_FREE) && (Session->CmdSN != LoginRsp->ExpCmdSN)) {\r
+      return EFI_PROTOCOL_ERROR;     \r
+    }\r
+\r
+    //\r
+    // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN\r
+    // and ExpCmdSN.\r
+    //\r
+    Conn->ExpStatSN   = LoginRsp->StatSN + 1;\r
+    Session->MaxCmdSN = LoginRsp->MaxCmdSN;\r
+    Session->ExpCmdSN = LoginRsp->ExpCmdSN;\r
+  } else {\r
+    //\r
+    // Check the StatSN of this PDU.\r
+    //\r
+    Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN);\r
+    if (!EFI_ERROR (Status)) {\r
+      //\r
+      // Update the MaxCmdSN and ExpCmdSN.\r
+      //\r
+      IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN);\r
+    } else {\r
+      return Status;\r
+    }\r
+  }\r
+  //\r
+  // Trim off the header segment.\r
+  //\r
+  NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD);\r
+\r
+  //\r
+  // Queue this login response first in case it's a partial response so that\r
+  // later when the full response list is received we can combine these scattered\r
+  // responses' data segment and then process it.\r
+  //\r
+  NET_GET_REF (Pdu);\r
+  NetbufQueAppend (&Conn->RspQue, Pdu);\r
+\r
+  Conn->PartialRspRcvd = Continue;\r
+  if (Continue) {\r
+    //\r
+    // It is a partial response; must wait for another or more Request/Response\r
+    // conversations to get the full response.\r
+    //\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  switch (CurrentStage) {\r
+  case ISCSI_SECURITY_NEGOTIATION:\r
+    //\r
+    // In security negotiation stage, let CHAP module handle it.\r
+    //\r
+    if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {\r
+      Status = IScsiCHAPOnRspReceived (Conn);\r
+    }\r
+    break;\r
+\r
+  case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:\r
+    //\r
+    // Response received with negotiation response on iSCSI parameters: check them.\r
+    //\r
+    Status = IScsiCheckOpParams (Conn);\r
+    if (!EFI_ERROR (Status)) {\r
+      Conn->ParamNegotiated = TRUE;\r
+    }\r
+\r
+    break;\r
+\r
+  default:\r
+    //\r
+    // Should never get here.\r
+    //\r
+    Status = EFI_PROTOCOL_ERROR;\r
+    break;\r
+  }\r
+\r
+  if (Transit && (Status == EFI_SUCCESS)) {\r
+    //\r
+    // Do the state transition.\r
+    //\r
+    Conn->CurrentStage = Conn->NextStage;\r
+\r
+    if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) {\r
+      Conn->NextStage = ISCSI_FULL_FEATURE_PHASE;\r
+    } else {\r
+      //\r
+      // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;\r
+      // get the TSIH from the Login Response.\r
+      //\r
+      Session->Tsih = NTOHS (LoginRsp->Tsih);\r
+    }\r
+  }\r
+  //\r
+  // Flush the response(s) received.\r
+  //\r
+  NetbufQueFlush (&Conn->RspQue);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Updated the target information according the data received in the iSCSI\r
+  login response with an target redirection status.\r
+\r
+  @param[in, out] Session      The iSCSI session.\r
+  @param[in]      Data         The data segment that should contain the\r
+                               TargetAddress key-value list.\r
+  @param[in]      Len          Length of the data.\r
+  \r
+  @retval EFI_SUCCESS          The target address is updated.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_NOT_FOUND        The TargetAddress key is not found.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiUpdateTargetAddress (\r
+  IN OUT ISCSI_SESSION         *Session,\r
+  IN     CHAR8                 *Data,\r
+  IN     UINT32                Len\r
+  )\r
+{\r
+  LIST_ENTRY      *KeyValueList;\r
+  CHAR8           *TargetAddress;\r
+  CHAR8           *IpStr;\r
+  EFI_STATUS      Status;\r
+  UINTN           Number;\r
+  UINT8           IpMode;\r
+\r
+  KeyValueList = IScsiBuildKeyValueList (Data, Len);\r
+  if (KeyValueList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = EFI_NOT_FOUND;\r
+\r
+  while (TRUE) {\r
+    TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);\r
+    if (TargetAddress == NULL) {\r
+      break;\r
+    }\r
+\r
+    if (!NET_IS_DIGIT (TargetAddress[0])) {\r
+      //\r
+      // The domainname of the target may be presented in three formats: a DNS host name,\r
+      // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted\r
+      // IPv4 address.\r
+      //\r
+      continue;\r
+    }\r
+\r
+    IpStr = TargetAddress;\r
+\r
+    while ((*TargetAddress != 0) && (*TargetAddress != ':') && (*TargetAddress != ',')) {\r
+      //\r
+      // NULL, ':', or ',' ends the IPv4 string.\r
+      //\r
+      TargetAddress++;\r
+    }\r
+\r
+    if (*TargetAddress == ',') {\r
+      //\r
+      // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent\r
+      // as the result of a redirection.\r
+      //\r
+      continue;\r
+    } else if (*TargetAddress == ':') {\r
+      *TargetAddress = '\0';\r
+\r
+      TargetAddress++;\r
+\r
+      Number = AsciiStrDecimalToUintn (TargetAddress);\r
+      if (Number > 0xFFFF) {\r
+        continue;\r
+      } else {\r
+        Session->ConfigData->SessionConfigData.TargetPort = (UINT16) Number;\r
+      }\r
+    } else {\r
+      //\r
+      // The string only contains the IPv4 address. Use the well-known port.\r
+      //\r
+      Session->ConfigData->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;\r
+    }\r
+    //\r
+    // Update the target IP address.\r
+    //\r
+    if (Session->ConfigData->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {\r
+      IpMode = Session->ConfigData->SessionConfigData.IpMode;\r
+    } else {\r
+      IpMode = Session->ConfigData->AutoConfigureMode;\r
+    }\r
+\r
+    Status = IScsiAsciiStrToIp (\r
+               IpStr,\r
+               IpMode,\r
+               &Session->ConfigData->SessionConfigData.TargetIp\r
+               );\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    } else {\r
+      break;\r
+    }\r
+  }\r
+\r
+  IScsiFreeKeyValueList (KeyValueList);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  The callback function to free the net buffer list.\r
+\r
+  @param[in]  Arg The opaque parameter.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IScsiFreeNbufList (\r
+  VOID *Arg\r
+  )\r
+{\r
+  ASSERT (Arg != NULL);\r
+\r
+  NetbufFreeList ((LIST_ENTRY *) Arg);\r
+  FreePool (Arg);\r
+}\r
+\r
+\r
+/**\r
+  The callback function called in NetBufFree; it does nothing.\r
+\r
+  @param[in]   Arg  The opaque parameter.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IScsiNbufExtFree (\r
+  VOID *Arg\r
+  )\r
+{\r
+}\r
+\r
+\r
+/**\r
+  Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and\r
+  an optional data segment. The two parts will be put into two blocks of buffers in the\r
+  net buffer. The digest check will be conducted in this function if needed and the digests\r
+  will be trimmed from the PDU buffer.\r
+\r
+  @param[in]  Conn         The iSCSI connection to receive data from.\r
+  @param[out] Pdu          The received iSCSI pdu.\r
+  @param[in]  Context      The context used to describe information on the caller provided\r
+                           buffer to receive data segment of the iSCSI pdu. It is optional.\r
+  @param[in]  HeaderDigest Whether there will be header digest received.\r
+  @param[in]  DataDigest   Whether there will be data digest.\r
+  @param[in]  TimeoutEvent The timeout event. It is optional.\r
+\r
+  @retval EFI_SUCCESS          An iSCSI pdu is received.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol error occurred.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiReceivePdu (\r
+  IN ISCSI_CONNECTION                      *Conn,\r
+  OUT NET_BUF                              **Pdu,\r
+  IN ISCSI_IN_BUFFER_CONTEXT               *Context, OPTIONAL\r
+  IN BOOLEAN                               HeaderDigest,\r
+  IN BOOLEAN                               DataDigest,\r
+  IN EFI_EVENT                             TimeoutEvent OPTIONAL\r
+  )\r
+{\r
+  LIST_ENTRY      *NbufList;\r
+  UINT32          Len;\r
+  NET_BUF         *PduHdr;\r
+  UINT8           *Header;\r
+  EFI_STATUS      Status;\r
+  UINT32          PadLen;\r
+  UINT32          InDataOffset;\r
+  NET_FRAGMENT    Fragment[2];\r
+  UINT32          FragmentCount;\r
+  NET_BUF         *DataSeg;\r
+  UINT32          PadAndCRC32[2];\r
+\r
+  NbufList = AllocatePool (sizeof (LIST_ENTRY));\r
+  if (NbufList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  InitializeListHead (NbufList);\r
+\r
+  //\r
+  // The header digest will be received together with the PDU header, if exists.\r
+  //\r
+  Len     = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0);\r
+  PduHdr  = NetbufAlloc (Len);\r
+  if (PduHdr == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);\r
+  ASSERT (Header != NULL);\r
+  InsertTailList (NbufList, &PduHdr->List);\r
+\r
+  //\r
+  // First step, receive the BHS of the PDU.\r
+  //\r
+  Status = TcpIoReceive (&Conn->TcpIo, PduHdr, FALSE, TimeoutEvent);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (HeaderDigest) {\r
+    //\r
+    // TODO: check the header-digest.\r
+    //\r
+    //\r
+    // Trim off the digest.\r
+    //\r
+    NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL);\r
+  }\r
+\r
+  Len = ISCSI_GET_DATASEG_LEN (Header);\r
+  if (Len == 0) {\r
+    //\r
+    // No data segment.\r
+    //\r
+    goto FORM_PDU;\r
+  }\r
+  //\r
+  // Get the length of the padding bytes of the data segment.\r
+  //\r
+  PadLen = ISCSI_GET_PAD_LEN (Len);\r
+\r
+  switch (ISCSI_GET_OPCODE (Header)) {\r
+  case ISCSI_OPCODE_SCSI_DATA_IN:\r
+    //\r
+    // To reduce memory copy overhead, try to use the buffer described by Context \r
+    // if the PDU is an iSCSI SCSI data.\r
+    //\r
+    InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header);\r
+    if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) {\r
+      Status = EFI_PROTOCOL_ERROR;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    Fragment[0].Len   = Len;\r
+    Fragment[0].Bulk  = Context->InData + InDataOffset;\r
+\r
+    if (DataDigest || (PadLen != 0)) {\r
+      //\r
+      // The data segment is padded. Use two fragments to receive it:\r
+      // the first to receive the useful data; the second to receive the padding.\r
+      //\r
+      Fragment[1].Len   = PadLen + (DataDigest ? sizeof (UINT32) : 0);\r
+      Fragment[1].Bulk  = (UINT8 *)PadAndCRC32 + (4 - PadLen);\r
+\r
+      FragmentCount     = 2;\r
+    } else {\r
+      FragmentCount = 1;\r
+    }\r
+\r
+    DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);\r
+    if (DataSeg == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    break;\r
+\r
+  case ISCSI_OPCODE_SCSI_RSP:\r
+  case ISCSI_OPCODE_NOP_IN:\r
+  case ISCSI_OPCODE_LOGIN_RSP:\r
+  case ISCSI_OPCODE_TEXT_RSP:\r
+  case ISCSI_OPCODE_ASYNC_MSG:\r
+  case ISCSI_OPCODE_REJECT:\r
+  case ISCSI_OPCODE_VENDOR_T0:\r
+  case ISCSI_OPCODE_VENDOR_T1:\r
+  case ISCSI_OPCODE_VENDOR_T2:\r
+    //\r
+    // Allocate buffer to receive the data segment.\r
+    //\r
+    Len += PadLen + (DataDigest ? sizeof (UINT32) : 0);\r
+    DataSeg = NetbufAlloc (Len);\r
+    if (DataSeg == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);\r
+    break;\r
+\r
+  default:\r
+    Status = EFI_PROTOCOL_ERROR;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  InsertTailList (NbufList, &DataSeg->List);\r
+\r
+  //\r
+  // Receive the data segment with the data digest, if any.\r
+  //\r
+  Status = TcpIoReceive (&Conn->TcpIo, DataSeg, FALSE, TimeoutEvent);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (DataDigest) {\r
+    //\r
+    // TODO: Check the data digest.\r
+    //\r
+    NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL);\r
+  }\r
+\r
+  if (PadLen != 0) {\r
+    //\r
+    // Trim off the padding bytes in the data segment.\r
+    //\r
+    NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL);\r
+  }\r
+\r
+FORM_PDU:\r
+  //\r
+  // Form the pdu from a list of pdu segments.\r
+  //\r
+  *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);\r
+  if (*Pdu == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    //\r
+    // Free the Nbufs in this NbufList and the NbufList itself.\r
+    //\r
+    IScsiFreeNbufList (NbufList);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Check and get the result of the prameter negotiation.\r
+\r
+  @param[in, out]  Conn          The connection in iSCSI login.\r
+\r
+  @retval EFI_SUCCESS          The parmeter check is passed and negotiation is finished.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol error occurred.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCheckOpParams (\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  )\r
+{\r
+  EFI_STATUS      Status;\r
+  LIST_ENTRY      *KeyValueList;\r
+  CHAR8           *Data;\r
+  UINT32          Len;\r
+  ISCSI_SESSION   *Session;\r
+  CHAR8           *Value;\r
+  UINTN           NumericValue;\r
+\r
+  ASSERT (Conn->RspQue.BufNum != 0);\r
+\r
+  Session = Conn->Session;\r
+\r
+  Len     = Conn->RspQue.BufSize;\r
+  Data    = AllocatePool (Len);\r
+  if (Data == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  NetbufQueCopy (&Conn->RspQue, 0, Len, (UINT8 *) Data);\r
+\r
+  Status = EFI_PROTOCOL_ERROR;\r
+\r
+  //\r
+  // Extract the Key-Value pairs into a list.\r
+  //\r
+  KeyValueList = IScsiBuildKeyValueList (Data, Len);\r
+  if (KeyValueList == NULL) {\r
+    FreePool (Data);\r
+    return Status;\r
+  }\r
+  //\r
+  // HeaderDigest\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (AsciiStrCmp (Value, "CRC32") == 0) {\r
+    if (Conn->HeaderDigest != IScsiDigestCRC32) {\r
+      goto ON_ERROR;\r
+    }\r
+  } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {\r
+    Conn->HeaderDigest = IScsiDigestNone;\r
+  } else {\r
+    goto ON_ERROR;\r
+  }\r
+  //\r
+  // DataDigest\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  if (AsciiStrCmp (Value, "CRC32") == 0) {\r
+    if (Conn->DataDigest != IScsiDigestCRC32) {\r
+      goto ON_ERROR;\r
+    }\r
+  } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {\r
+    Conn->DataDigest = IScsiDigestNone;\r
+  } else {\r
+    goto ON_ERROR;\r
+  }\r
+  //\r
+  // ErrorRecoveryLevel: result fuction is Minimum.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NumericValue = IScsiNetNtoi (Value);\r
+  if (NumericValue > 2) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue);\r
+\r
+  //\r
+  // InitialR2T: result function is OR.\r
+  //\r
+  if (!Session->InitialR2T) {\r
+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);\r
+    if (Value == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    Session->InitialR2T = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);\r
+  }\r
+\r
+  //\r
+  // ImmediateData: result function is AND.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Session->ImmediateData = (BOOLEAN) (Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0));\r
+\r
+  //\r
+  // MaxRecvDataSegmentLength is declarative.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH);\r
+  if (Value != NULL) {\r
+    Conn->MaxRecvDataSegmentLength = (UINT32) IScsiNetNtoi (Value);\r
+  }\r
+  //\r
+  // MaxBurstLength: result funtion is Mininum.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NumericValue            = IScsiNetNtoi (Value);\r
+  Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue);\r
+\r
+  //\r
+  // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and\r
+  // ImmediateData=No.\r
+  //\r
+  if (!(Session->InitialR2T && !Session->ImmediateData)) {\r
+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);\r
+    if (Value == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    NumericValue              = IScsiNetNtoi (Value);\r
+    Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue);\r
+  }\r
+\r
+  //\r
+  // MaxConnections: result function is Minimum.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NumericValue = IScsiNetNtoi (Value);\r
+  if ((NumericValue == 0) || (NumericValue > 65535)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue);\r
+\r
+  //\r
+  // DataPDUInOrder: result function is OR.\r
+  //\r
+  if (!Session->DataPDUInOrder) {\r
+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);\r
+    if (Value == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    Session->DataPDUInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);\r
+  }\r
+\r
+  //\r
+  // DataSequenceInorder: result function is OR.\r
+  //\r
+  if (!Session->DataSequenceInOrder) {\r
+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);\r
+    if (Value == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    Session->DataSequenceInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);\r
+  }\r
+\r
+  //\r
+  // DefaultTime2Wait: result function is Maximum.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NumericValue = IScsiNetNtoi (Value);\r
+  if (NumericValue == 0) {\r
+    Session->DefaultTime2Wait = 0;\r
+  } else if (NumericValue > 3600) {\r
+    goto ON_ERROR;\r
+  } else {\r
+    Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue);\r
+  }\r
+  //\r
+  // DefaultTime2Retain: result function is Minimum.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NumericValue = IScsiNetNtoi (Value);\r
+  if (NumericValue == 0) {\r
+    Session->DefaultTime2Retain = 0;\r
+  } else if (NumericValue > 3600) {\r
+    goto ON_ERROR;\r
+  } else {\r
+    Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue);\r
+  }\r
+  //\r
+  // MaxOutstandingR2T: result function is Minimum.\r
+  //\r
+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T);\r
+  if (Value == NULL) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  NumericValue = IScsiNetNtoi (Value);\r
+  if ((NumericValue == 0) || (NumericValue > 65535)) {\r
+    goto ON_ERROR;\r
+  }\r
+\r
+  Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue);\r
+\r
+  //\r
+  // Remove declarative key-value pairs, if any.\r
+  //\r
+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE);\r
+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS);\r
+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);\r
+\r
+  \r
+  //\r
+  // Remove the key-value that may not needed for result function is OR.\r
+  //\r
+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);\r
+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);\r
+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);\r
+\r
+\r
+  if (IsListEmpty (KeyValueList)) {\r
+    //\r
+    // Succeed if no more keys in the list.\r
+    //\r
+    Status = EFI_SUCCESS;\r
+  }\r
+\r
+ON_ERROR:\r
+\r
+  IScsiFreeKeyValueList (KeyValueList);\r
+\r
+  FreePool (Data);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Fill the oprational parameters.\r
+\r
+  @param[in]       Conn    The connection in iSCSI login.\r
+  @param[in, out]  Pdu     The iSCSI login request PDU to fill the parameters.\r
+\r
+**/\r
+VOID\r
+IScsiFillOpParams (\r
+  IN     ISCSI_CONNECTION  *Conn,\r
+  IN OUT NET_BUF           *Pdu\r
+  )\r
+{\r
+  ISCSI_SESSION *Session;\r
+  CHAR8         Value[256];\r
+\r
+  Session = Conn->Session;\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);\r
+\r
+  AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);\r
+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);\r
+}\r
+\r
+\r
+/**\r
+  Pad the iSCSI AHS or data segment to an integer number of 4 byte words.\r
+\r
+  @param[in, out]  Pdu         The iSCSI pdu which contains segments to pad.\r
+  @param[in]       Len         The length of the last segment in the PDU.\r
+\r
+  @retval EFI_SUCCESS          The segment is padded or there is no need to pad it.\r
+  @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the\r
+                               padding bytes.\r
+**/\r
+EFI_STATUS\r
+IScsiPadSegment (\r
+  IN OUT NET_BUF      *Pdu,\r
+  IN     UINT32       Len\r
+  )\r
+{\r
+  UINT32  PadLen;\r
+  UINT8   *Data;\r
+\r
+  PadLen = ISCSI_GET_PAD_LEN (Len);\r
+\r
+  if (PadLen != 0) {\r
+    Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);\r
+    if (Data == NULL) {\r
+      return EFI_OUT_OF_RESOURCES;\r
+    }\r
+\r
+    ZeroMem (Data, PadLen);\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Build a key-value list from the data segment.\r
+\r
+  @param[in]  Data The data segment containing the key-value pairs.\r
+  @param[in]  Len  Length of the data segment.\r
+\r
+  @return The key-value list.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+LIST_ENTRY *\r
+IScsiBuildKeyValueList (\r
+  IN CHAR8  *Data,\r
+  IN UINT32 Len\r
+  )\r
+{\r
+  LIST_ENTRY            *ListHead;\r
+  ISCSI_KEY_VALUE_PAIR  *KeyValuePair;\r
+\r
+  ListHead = AllocatePool (sizeof (LIST_ENTRY));\r
+  if (ListHead == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (ListHead);\r
+\r
+  while (Len > 0) {\r
+    KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));\r
+    if (KeyValuePair == NULL) {\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    InitializeListHead (&KeyValuePair->List);\r
+\r
+    KeyValuePair->Key = Data;\r
+\r
+    while ((Len > 0) && (*Data != '=')) {\r
+      Len--;\r
+      Data++;\r
+    }\r
+\r
+    if (*Data == '=') {\r
+      *Data = '\0';\r
+\r
+      Data++;\r
+      Len--;\r
+    } else {\r
+      FreePool (KeyValuePair);\r
+      goto ON_ERROR;\r
+    }\r
+\r
+    KeyValuePair->Value = Data;\r
+\r
+    InsertTailList (ListHead, &KeyValuePair->List);;\r
+\r
+    Data += AsciiStrLen (KeyValuePair->Value) + 1;\r
+    Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;\r
+  }\r
+\r
+  return ListHead;\r
+\r
+ON_ERROR:\r
+\r
+  IScsiFreeKeyValueList (ListHead);\r
+\r
+  return NULL;\r
+}\r
+\r
+\r
+/**\r
+  Get the value string by the key name from the key-value list. If found,\r
+  the key-value entry will be removed from the list.\r
+\r
+  @param[in, out]  KeyValueList  The key-value list.\r
+  @param[in]       Key           The key name to find.\r
+\r
+  @return The value string.\r
+  @retval NULL The key value pair cannot be found.\r
+\r
+**/\r
+CHAR8 *\r
+IScsiGetValueByKeyFromList (\r
+  IN OUT LIST_ENTRY     *KeyValueList,\r
+  IN     CHAR8          *Key\r
+  )\r
+{\r
+  LIST_ENTRY            *Entry;\r
+  ISCSI_KEY_VALUE_PAIR  *KeyValuePair;\r
+  CHAR8                 *Value;\r
+\r
+  Value = NULL;\r
+\r
+  NET_LIST_FOR_EACH (Entry, KeyValueList) {\r
+    KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);\r
+\r
+    if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {\r
+      Value = KeyValuePair->Value;\r
+\r
+      RemoveEntryList (&KeyValuePair->List);\r
+      FreePool (KeyValuePair);\r
+      break;\r
+    }\r
+  }\r
+\r
+  return Value;\r
+}\r
+\r
+\r
+/**\r
+  Free the key-value list.\r
+\r
+  @param[in]  KeyValueList The key-value list.\r
+\r
+**/\r
+VOID\r
+IScsiFreeKeyValueList (\r
+  IN LIST_ENTRY      *KeyValueList\r
+  )\r
+{\r
+  LIST_ENTRY            *Entry;\r
+  ISCSI_KEY_VALUE_PAIR  *KeyValuePair;\r
+\r
+  while (!IsListEmpty (KeyValueList)) {\r
+    Entry         = NetListRemoveHead (KeyValueList);\r
+    KeyValuePair  = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);\r
+\r
+    FreePool (KeyValuePair);\r
+  }\r
+\r
+  FreePool (KeyValueList);\r
+}\r
+\r
+\r
+/**\r
+  Normalize the iSCSI name according to RFC.\r
+\r
+  @param[in, out]  Name       The iSCSI name.\r
+  @param[in]       Len        Length of the iSCSI name.\r
+\r
+  @retval EFI_SUCCESS        The iSCSI name is valid and normalized.\r
+  @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiNormalizeName (\r
+  IN OUT CHAR8      *Name,\r
+  IN     UINTN      Len\r
+  )\r
+{\r
+  UINTN Index;\r
+\r
+  for (Index = 0; Index < Len; Index++) {\r
+    if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {\r
+      //\r
+      // Convert the upper-case characters to lower-case ones.\r
+      //\r
+      Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');\r
+    }\r
+\r
+    if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&\r
+        !NET_IS_DIGIT (Name[Index]) &&\r
+        (Name[Index] != '-') &&\r
+        (Name[Index] != '.') &&\r
+        (Name[Index] != ':')\r
+        ) {\r
+      //\r
+      // ASCII dash, dot, colon lower-case characters and digit characters\r
+      // are allowed.\r
+      //\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+  }\r
+\r
+  if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {\r
+    //\r
+    // Only IQN format is accepted now.\r
+    //\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Create an iSCSI task control block.\r
+\r
+  @param[in]   Conn           The connection on which the task control block will be created.\r
+  @param[out]  Tcb            The newly created task control block.\r
+\r
+  @retval EFI_SUCCESS          The task control block is created.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_NOT_READY        The target cannot accept new commands.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiNewTcb (\r
+  IN  ISCSI_CONNECTION  *Conn,\r
+  OUT ISCSI_TCB         **Tcb\r
+  )\r
+{\r
+  ISCSI_SESSION *Session;\r
+  ISCSI_TCB     *NewTcb;\r
+\r
+  ASSERT (Tcb != NULL);\r
+\r
+  Session = Conn->Session;\r
+\r
+  if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));\r
+  if (NewTcb == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  InitializeListHead (&NewTcb->Link);\r
+\r
+  NewTcb->SoFarInOrder      = TRUE;\r
+  NewTcb->InitiatorTaskTag  = Session->InitiatorTaskTag;\r
+  NewTcb->CmdSN             = Session->CmdSN;\r
+  NewTcb->Conn              = Conn;\r
+\r
+  InsertTailList (&Session->TcbList, &NewTcb->Link);\r
+\r
+  //\r
+  // Advance the initiator task tag.\r
+  //\r
+  Session->InitiatorTaskTag++;\r
+  Session->CmdSN++;\r
+\r
+  *Tcb = NewTcb;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Delete the tcb from the connection and destroy it.\r
+\r
+  @param[in]  Tcb The tcb to delete.\r
+\r
+**/\r
+VOID\r
+IScsiDelTcb (\r
+  IN ISCSI_TCB  *Tcb\r
+  )\r
+{\r
+  RemoveEntryList (&Tcb->Link);\r
+\r
+  FreePool (Tcb);\r
+}\r
+\r
+\r
+/**\r
+  Find the task control block by the initator task tag.\r
+\r
+  @param[in]  TcbList         The tcb list.\r
+  @param[in]  InitiatorTaskTag The initiator task tag.\r
+\r
+  @return The task control block found.\r
+  @retval NULL The task control block cannot be found.\r
+\r
+**/\r
+ISCSI_TCB *\r
+IScsiFindTcbByITT (\r
+  IN LIST_ENTRY      *TcbList,\r
+  IN UINT32          InitiatorTaskTag\r
+  )\r
+{\r
+  ISCSI_TCB       *Tcb;\r
+  LIST_ENTRY      *Entry;\r
+\r
+  Tcb = NULL;\r
+\r
+  NET_LIST_FOR_EACH (Entry, TcbList) {\r
+    Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);\r
+\r
+    if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  return Tcb;\r
+}\r
+\r
+\r
+/**\r
+  Create a data segment, pad it, and calculate the CRC if needed.\r
+\r
+  @param[in]  Data       The data to fill into the data segment.\r
+  @param[in]  Len        Length of the data.\r
+  @param[in]  DataDigest Whether to calculate CRC for this data segment.\r
+\r
+  @return The net buffer wrapping the data segment.\r
+\r
+**/\r
+NET_BUF *\r
+IScsiNewDataSegment (\r
+  IN UINT8    *Data,\r
+  IN UINT32   Len,\r
+  IN BOOLEAN  DataDigest\r
+  )\r
+{\r
+  NET_FRAGMENT  Fragment[2];\r
+  UINT32        FragmentCount;\r
+  UINT32        PadLen;\r
+  NET_BUF       *DataSeg;\r
+\r
+  Fragment[0].Len   = Len;\r
+  Fragment[0].Bulk  = Data;\r
+\r
+  PadLen            = ISCSI_GET_PAD_LEN (Len);\r
+  if (PadLen != 0) {\r
+    Fragment[1].Len   = PadLen;\r
+    Fragment[1].Bulk  = (UINT8 *) &mDataSegPad;\r
+\r
+    FragmentCount     = 2;\r
+  } else {\r
+    FragmentCount = 1;\r
+  }\r
+\r
+  DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);\r
+\r
+  return DataSeg;\r
+}\r
+\r
+\r
+/**\r
+  Create a iSCSI SCSI command PDU to encapsulate the command issued\r
+  by SCSI through the EXT SCSI PASS THRU Protocol.\r
+\r
+  @param[in]  Packet The EXT SCSI PASS THRU request packet containing the SCSI command.\r
+  @param[in]  Lun    The LUN.\r
+  @param[in]  Tcb    The tcb assocated with this SCSI command.\r
+\r
+  @return The  created iSCSI SCSI command PDU.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+NET_BUF *\r
+IScsiNewScsiCmdPdu (\r
+  IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
+  IN UINT64                                     Lun,\r
+  IN ISCSI_TCB                                  *Tcb\r
+  )\r
+{\r
+  LIST_ENTRY                      *NbufList;\r
+  NET_BUF                         *Pdu;\r
+  NET_BUF                         *PduHeader;\r
+  NET_BUF                         *DataSeg;\r
+  SCSI_COMMAND                    *ScsiCmd;\r
+  UINT8                           AHSLength;\r
+  UINT32                          Length;\r
+  ISCSI_ADDITIONAL_HEADER         *Header;\r
+  ISCSI_BI_EXP_READ_DATA_LEN_AHS  *BiExpReadDataLenAHS;\r
+  ISCSI_SESSION                   *Session;\r
+  UINT32                          ImmediateDataLen;\r
+\r
+  AHSLength = 0;\r
+\r
+  if (Packet->DataDirection == DataBi) {\r
+    //\r
+    // Bidirectional Read/Write command, the bidirectional expected\r
+    // read data length AHS is required.\r
+    //\r
+    AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);\r
+  }\r
+\r
+  if (Packet->CdbLength > 16) {\r
+    //\r
+    // The CDB exceeds 16 bytes. An extended CDB AHS is required.\r
+    //\r
+    AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER));\r
+  }\r
+\r
+  Length    = sizeof (SCSI_COMMAND) + AHSLength;\r
+  PduHeader = NetbufAlloc (Length);\r
+  if (PduHeader == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);\r
+  if (ScsiCmd == NULL) {\r
+    NetbufFree (PduHeader);\r
+    return NULL;\r
+  }    \r
+  Header  = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);\r
+\r
+  ZeroMem (ScsiCmd, Length);\r
+\r
+  ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);\r
+  ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);\r
+\r
+  //\r
+  // Set the READ/WRITE flags according to the IO type of this request.\r
+  //\r
+  switch (Packet->DataDirection) {\r
+  case DataIn:\r
+    ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);\r
+    ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);\r
+    break;\r
+\r
+  case DataOut:\r
+    ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);\r
+    ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);\r
+    break;\r
+\r
+  case DataBi:\r
+    ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);\r
+    ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);\r
+\r
+    //\r
+    // Fill the bidirectional expected read data length AHS.\r
+    //\r
+    BiExpReadDataLenAHS                     = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;\r
+    Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);\r
+\r
+    BiExpReadDataLenAHS->Length = NTOHS (5);\r
+    BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;\r
+    BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);\r
+\r
+    break;\r
+  }\r
+\r
+  ScsiCmd->TotalAHSLength = AHSLength;\r
+  CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));\r
+  ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);\r
+  ScsiCmd->CmdSN            = NTOHL (Tcb->CmdSN);\r
+  ScsiCmd->ExpStatSN        = NTOHL (Tcb->Conn->ExpStatSN);\r
+\r
+  CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));\r
+\r
+  if (Packet->CdbLength > 16) {\r
+    Header->Length  = NTOHS ((UINT16) (Packet->CdbLength - 15));\r
+    Header->Type    = ISCSI_AHS_TYPE_EXT_CDB;\r
+\r
+    CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);\r
+  }\r
+\r
+  Pdu               = PduHeader;\r
+  Session           = Tcb->Conn->Session;\r
+  ImmediateDataLen  = 0;\r
+\r
+  if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {\r
+    //\r
+    // Send immediate data in this SCSI Command PDU. The length of the immeidate\r
+    // data is the minimum of FirstBurstLength, the data length to be xfered, and\r
+    // the MaxRecvdataSegmentLength on this connection.\r
+    //\r
+    ImmediateDataLen  = MIN (Session->FirstBurstLength, Packet->OutTransferLength);\r
+    ImmediateDataLen  = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);\r
+\r
+    //\r
+    // Update the data segment length in the PDU header.\r
+    //\r
+    ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);\r
+\r
+    //\r
+    // Create the data segment.\r
+    //\r
+    DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);\r
+    if (DataSeg == NULL) {\r
+      NetbufFree (PduHeader);\r
+      Pdu = NULL;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    NbufList = AllocatePool (sizeof (LIST_ENTRY));\r
+    if (NbufList == NULL) {\r
+      NetbufFree (PduHeader);\r
+      NetbufFree (DataSeg);\r
+\r
+      Pdu = NULL;\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    InitializeListHead (NbufList);\r
+    InsertTailList (NbufList, &PduHeader->List);\r
+    InsertTailList (NbufList, &DataSeg->List);\r
+\r
+    Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);\r
+    if (Pdu == NULL) {\r
+      IScsiFreeNbufList (NbufList);\r
+    }\r
+  }\r
+\r
+  if (Session->InitialR2T ||\r
+      (ImmediateDataLen == Session->FirstBurstLength) ||\r
+      (ImmediateDataLen == Packet->OutTransferLength)\r
+      ) {\r
+    //\r
+    // Unsolicited data out sequence is not allowed,\r
+    // or FirstBustLength data is already sent out by immediate data,\r
+    // or all the OUT data accompany this SCSI packet are sent as\r
+    // immediate data. The final flag should be set on this SCSI Command\r
+    // PDU.\r
+    //\r
+    ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  return Pdu;\r
+}\r
+\r
+\r
+/**\r
+  Create a new iSCSI SCSI Data Out PDU.\r
+\r
+  @param[in]  Data   The data to put into the Data Out PDU.\r
+  @param[in]  Len    Length of the data.\r
+  @param[in]  DataSN The DataSN of the Data Out PDU.\r
+  @param[in]  Tcb    The task control block of this Data Out PDU.\r
+  @param[in]  Lun    The LUN.\r
+\r
+  @return The net buffer wrapping the Data Out PDU.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+NET_BUF *\r
+IScsiNewDataOutPdu (\r
+  IN UINT8      *Data,\r
+  IN UINT32     Len,\r
+  IN UINT32     DataSN,\r
+  IN ISCSI_TCB  *Tcb,\r
+  IN UINT64     Lun\r
+  )\r
+{\r
+  LIST_ENTRY          *NbufList;\r
+  NET_BUF             *PduHdr;\r
+  NET_BUF             *DataSeg;\r
+  NET_BUF             *Pdu;\r
+  ISCSI_SCSI_DATA_OUT *DataOutHdr;\r
+  ISCSI_XFER_CONTEXT  *XferContext;\r
+\r
+  NbufList = AllocatePool (sizeof (LIST_ENTRY));\r
+  if (NbufList == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (NbufList);\r
+\r
+  //\r
+  // Allocate memory for the BHS.\r
+  //\r
+  PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));\r
+  if (PduHdr == NULL) {\r
+    FreePool (NbufList);\r
+    return NULL;\r
+  }\r
+  //\r
+  // Insert the BHS into the buffer list.\r
+  //\r
+  InsertTailList (NbufList, &PduHdr->List);\r
+\r
+  DataOutHdr  = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);\r
+  ASSERT (DataOutHdr != NULL);\r
+  XferContext = &Tcb->XferContext;\r
+\r
+  ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));\r
+\r
+  //\r
+  // Set the flags and fields of the Data Out PDU BHS.\r
+  //\r
+  ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);\r
+  ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);\r
+\r
+  DataOutHdr->InitiatorTaskTag  = HTONL (Tcb->InitiatorTaskTag);\r
+  DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);\r
+  DataOutHdr->ExpStatSN         = HTONL (Tcb->Conn->ExpStatSN);\r
+  DataOutHdr->DataSN            = HTONL (DataSN);\r
+  DataOutHdr->BufferOffset      = HTONL (XferContext->Offset);\r
+\r
+  if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {\r
+    CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));\r
+  }\r
+  //\r
+  // Build the data segment for this Data Out PDU.\r
+  //\r
+  DataSeg = IScsiNewDataSegment (Data, Len, FALSE);\r
+  if (DataSeg == NULL) {\r
+    IScsiFreeNbufList (NbufList);\r
+    return NULL;\r
+  }\r
+  //\r
+  // Put the data segment into the buffer list and combine it with the BHS\r
+  // into a full Data Out PDU.\r
+  //\r
+  InsertTailList (NbufList, &DataSeg->List);\r
+  Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);\r
+  if (Pdu == NULL) {\r
+    IScsiFreeNbufList (NbufList);\r
+  }\r
+\r
+  return Pdu;\r
+}\r
+\r
+\r
+/**\r
+  Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.\r
+\r
+  @param[in]  Data The data  which will be carried by the sequence of iSCSI SCSI Data Out PDUs.\r
+  @param[in]  Tcb  The task control block of the data to send out.\r
+  @param[in]  Lun  The LUN the data will be sent to.\r
+\r
+  @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+LIST_ENTRY *\r
+IScsiGenerateDataOutPduSequence (\r
+  IN UINT8      *Data,\r
+  IN ISCSI_TCB  *Tcb,\r
+  IN UINT64     Lun\r
+  )\r
+{\r
+  LIST_ENTRY          *PduList;\r
+  UINT32              DataSN;\r
+  UINT32              DataLen;\r
+  NET_BUF             *DataOutPdu;\r
+  ISCSI_CONNECTION    *Conn;\r
+  ISCSI_XFER_CONTEXT  *XferContext;\r
+\r
+  PduList = AllocatePool (sizeof (LIST_ENTRY));\r
+  if (PduList == NULL) {\r
+    return NULL;\r
+  }\r
+\r
+  InitializeListHead (PduList);\r
+\r
+  DataSN      = 0;\r
+  Conn        = Tcb->Conn;\r
+  DataOutPdu  = NULL;\r
+  XferContext = &Tcb->XferContext;\r
+\r
+  while (XferContext->DesiredLength > 0) {\r
+    //\r
+    // Determine the length of data this Data Out PDU can carry.\r
+    //\r
+    DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);\r
+\r
+    //\r
+    // Create a Data Out PDU.\r
+    //\r
+    DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);\r
+    if (DataOutPdu == NULL) {\r
+      IScsiFreeNbufList (PduList);\r
+      PduList = NULL;\r
+\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    InsertTailList (PduList, &DataOutPdu->List);\r
+\r
+    //\r
+    // Update the context and DataSN.\r
+    //\r
+    Data += DataLen;\r
+    XferContext->Offset += DataLen;\r
+    XferContext->DesiredLength -= DataLen;\r
+    DataSN++;\r
+  }\r
+  //\r
+  // Set the F bit for the last data out PDU in this sequence.\r
+  //\r
+  ISCSI_SET_FLAG (NetbufGetByte (DataOutPdu, 0, NULL), ISCSI_BHS_FLAG_FINAL);\r
+\r
+ON_EXIT:\r
+\r
+  return PduList;\r
+}\r
+\r
+/**\r
+  Send the Data in a sequence of Data Out PDUs one by one.\r
+\r
+  @param[in]  Data            The data to carry by Data Out PDUs.\r
+  @param[in]  Lun             The LUN the data will be sent to.\r
+  @param[in]  Tcb             The task control block.\r
+\r
+  @retval EFI_SUCCES           The data is sent out to the LUN.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSendDataOutPduSequence (\r
+  IN UINT8      *Data,\r
+  IN UINT64     Lun,\r
+  IN ISCSI_TCB  *Tcb\r
+  )\r
+{\r
+  LIST_ENTRY      *DataOutPduList;\r
+  LIST_ENTRY      *Entry;\r
+  NET_BUF         *Pdu;\r
+  EFI_STATUS      Status;\r
+\r
+  //\r
+  // Generate the Data Out PDU sequence.\r
+  //\r
+  DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);\r
+  if (DataOutPduList == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  Status = EFI_SUCCESS;\r
+\r
+  //\r
+  // Send the Data Out PDU's one by one.\r
+  //\r
+  NET_LIST_FOR_EACH (Entry, DataOutPduList) {\r
+    Pdu     = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
+\r
+    Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  IScsiFreeNbufList (DataOutPduList);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Process the received iSCSI SCSI Data In PDU.\r
+\r
+  @param[in]        Pdu      The Data In PDU received.\r
+  @param[in]        Tcb      The task control block.\r
+  @param[in, out]   Packet   The EXT SCSI PASS THRU request packet.\r
+\r
+  @retval EFI_SUCCES           The check on the Data IN PDU is passed and some update\r
+                               actions are taken.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol errror occurred.\r
+  @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiOnDataInRcvd (\r
+  IN NET_BUF                                         *Pdu,\r
+  IN ISCSI_TCB                                       *Tcb,\r
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet\r
+  )\r
+{\r
+  ISCSI_SCSI_DATA_IN  *DataInHdr;\r
+  EFI_STATUS          Status;\r
+\r
+  DataInHdr                   = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);\r
+\r
+  DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);\r
+  DataInHdr->ExpCmdSN         = NTOHL (DataInHdr->ExpCmdSN);\r
+  DataInHdr->MaxCmdSN         = NTOHL (DataInHdr->MaxCmdSN);\r
+  DataInHdr->DataSN           = NTOHL (DataInHdr->DataSN);\r
+\r
+  //\r
+  // Check the DataSN.\r
+  //\r
+  Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+  //\r
+  // Update the command related sequence numbers.\r
+  //\r
+  IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);\r
+\r
+  if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {\r
+    if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {\r
+      //\r
+      // The S bit is on but the F bit is off.\r
+      //\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+\r
+    Tcb->StatusXferd = TRUE;\r
+\r
+    if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {\r
+      //\r
+      // Underflow and Overflow are mutual flags.\r
+      //\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+    //\r
+    // S bit is on, the StatSN is valid.\r
+    //\r
+    Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+\r
+    Packet->HostAdapterStatus = 0;\r
+    Packet->TargetStatus      = DataInHdr->Status;\r
+\r
+    if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {\r
+      Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);\r
+      Status = EFI_BAD_BUFFER_SIZE;\r
+    }\r
+\r
+    if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {\r
+      Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Process the received iSCSI R2T PDU.\r
+\r
+  @param[in]       Pdu       The R2T PDU received.\r
+  @param[in]       Tcb       The task control block.\r
+  @param[in]       Lun       The Lun.\r
+  @param[in, out]  Packet    The EXT SCSI PASS THRU request packet.\r
+\r
+  @retval EFI_SUCCES         The R2T PDU is valid and the solicited data is sent out.\r
+  @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.\r
+  @retval Others             Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiOnR2TRcvd (\r
+  IN NET_BUF                                         *Pdu,\r
+  IN ISCSI_TCB                                       *Tcb,\r
+  IN UINT64                                          Lun,\r
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet\r
+  )\r
+{\r
+  ISCSI_READY_TO_TRANSFER *R2THdr;\r
+  EFI_STATUS              Status;\r
+  ISCSI_XFER_CONTEXT      *XferContext;\r
+  UINT8                   *Data;\r
+\r
+  R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);\r
+\r
+  R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);\r
+  R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);\r
+  R2THdr->StatSN = NTOHL (R2THdr->StatSN);\r
+  R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum);\r
+  R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);\r
+  R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);\r
+\r
+  if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {\r
+    return EFI_PROTOCOL_ERROR;;\r
+  }\r
+  //\r
+  // Check the sequence number.\r
+  //\r
+  Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  XferContext                     = &Tcb->XferContext;\r
+  XferContext->TargetTransferTag  = R2THdr->TargetTransferTag;\r
+  XferContext->Offset             = R2THdr->BufferOffset;\r
+  XferContext->DesiredLength      = R2THdr->DesiredDataTransferLength;\r
+\r
+  if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||\r
+      (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)\r
+      ) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+  //\r
+  // Send the data solicited by this R2T.\r
+  //\r
+  Data    = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;\r
+  Status  = IScsiSendDataOutPduSequence (Data, Lun, Tcb);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Process the received iSCSI SCSI Response PDU.\r
+\r
+  @param[in]       Pdu      The Response PDU received.\r
+  @param[in]       Tcb      The task control block.\r
+  @param[in, out]  Packet   The EXT SCSI PASS THRU request packet.\r
+\r
+  @retval EFI_SUCCES         The Response PDU is processed.\r
+  @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.\r
+  @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.\r
+  @retval Others             Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiOnScsiRspRcvd (\r
+  IN NET_BUF                                         *Pdu,\r
+  IN ISCSI_TCB                                       *Tcb,\r
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet\r
+  )\r
+{\r
+  SCSI_RESPONSE     *ScsiRspHdr;\r
+  ISCSI_SENSE_DATA  *SenseData;\r
+  EFI_STATUS        Status;\r
+  UINT32            DataSegLen;\r
+\r
+  ScsiRspHdr                    = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);\r
+\r
+  ScsiRspHdr->InitiatorTaskTag  = NTOHL (ScsiRspHdr->InitiatorTaskTag);\r
+  if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  ScsiRspHdr->StatSN  = NTOHL (ScsiRspHdr->StatSN);\r
+\r
+  Status              = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  ScsiRspHdr->MaxCmdSN  = NTOHL (ScsiRspHdr->MaxCmdSN);\r
+  ScsiRspHdr->ExpCmdSN  = NTOHL (ScsiRspHdr->ExpCmdSN);\r
+  IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);\r
+\r
+  Tcb->StatusXferd          = TRUE;\r
+\r
+  Packet->HostAdapterStatus = ScsiRspHdr->Response;\r
+  if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  Packet->TargetStatus = ScsiRspHdr->Status;\r
+\r
+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||\r
+      ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)\r
+        ) {\r
+    return EFI_PROTOCOL_ERROR;\r
+  }\r
+\r
+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {\r
+    Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {\r
+    Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);\r
+  }\r
+\r
+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {\r
+    if (Packet->DataDirection == DataIn) {\r
+      Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);\r
+    } else {\r
+      Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);\r
+    }\r
+\r
+    Status = EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {\r
+    if (Packet->DataDirection == DataIn) {\r
+      Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);\r
+    } else {\r
+      Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);\r
+    }\r
+  }\r
+\r
+  DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);\r
+  if (DataSegLen != 0) {\r
+    SenseData               = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);\r
+\r
+    SenseData->Length       = NTOHS (SenseData->Length);\r
+\r
+    Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);\r
+    if (Packet->SenseDataLength != 0) {\r
+      CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);\r
+    }\r
+  } else {\r
+    Packet->SenseDataLength = 0;\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Process the received NOP In PDU.\r
+\r
+  @param[in]  Pdu            The NOP In PDU received.\r
+  @param[in]  Tcb            The task control block.\r
+\r
+  @retval EFI_SUCCES         The NOP In PDU is processed and the related sequence\r
+                             numbers are updated.\r
+  @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiOnNopInRcvd (\r
+  IN NET_BUF    *Pdu,\r
+  IN ISCSI_TCB  *Tcb\r
+  )\r
+{\r
+  ISCSI_NOP_IN  *NopInHdr;\r
+  EFI_STATUS    Status;\r
+\r
+  NopInHdr            = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);\r
+\r
+  NopInHdr->StatSN    = NTOHL (NopInHdr->StatSN);\r
+  NopInHdr->ExpCmdSN  = NTOHL (NopInHdr->ExpCmdSN);\r
+  NopInHdr->MaxCmdSN  = NTOHL (NopInHdr->MaxCmdSN);\r
+\r
+  if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {\r
+    if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {\r
+      return EFI_PROTOCOL_ERROR;\r
+    }\r
+  } else {\r
+    Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+  }\r
+\r
+  IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+  Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.\r
+\r
+  @param[in]       PassThru  The EXT SCSI PASS THRU protocol.\r
+  @param[in]       Target    The target ID.\r
+  @param[in]       Lun       The LUN.\r
+  @param[in, out]  Packet    The request packet containing IO request, SCSI command\r
+                             buffer and buffers to read/write.\r
+                             \r
+  @retval EFI_SUCCES           The SCSI command is executed and the result is updated to \r
+                               the Packet.\r
+  @retval EFI_DEVICE_ERROR     Session state was not as required.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiExecuteScsiCommand (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                 *PassThru,\r
+  IN UINT8                                           *Target,\r
+  IN UINT64                                          Lun,\r
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet\r
+  )\r
+{\r
+  EFI_STATUS              Status;\r
+  ISCSI_DRIVER_DATA       *Private;\r
+  ISCSI_SESSION           *Session;\r
+  EFI_EVENT               TimeoutEvent;\r
+  ISCSI_CONNECTION        *Conn;\r
+  ISCSI_TCB               *Tcb;\r
+  NET_BUF                 *Pdu;\r
+  ISCSI_XFER_CONTEXT      *XferContext;\r
+  UINT8                   *Data;\r
+  ISCSI_IN_BUFFER_CONTEXT InBufferContext;\r
+  UINT64                  Timeout;\r
+  UINT8                   *PduHdr;\r
+\r
+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);\r
+  Session       = Private->Session;\r
+  Status        = EFI_SUCCESS;\r
+  Tcb           = NULL;\r
+  TimeoutEvent  = NULL;\r
+  Timeout       = 0;\r
+\r
+  if (Session->State != SESSION_STATE_LOGGED_IN) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Conn = NET_LIST_USER_STRUCT_S (\r
+           Session->Conns.ForwardLink,\r
+           ISCSI_CONNECTION,\r
+           Link,\r
+           ISCSI_CONNECTION_SIGNATURE\r
+           );\r
+\r
+  if (Packet->Timeout != 0) {\r
+    Timeout = MultU64x32 (Packet->Timeout, 4);\r
+  }\r
+\r
+  Status = IScsiNewTcb (Conn, &Tcb);\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+  //\r
+  // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.\r
+  //\r
+  Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);\r
+  if (Pdu == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  XferContext         = &Tcb->XferContext;\r
+  PduHdr              = NetbufGetByte (Pdu, 0, NULL);\r
+  XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);\r
+\r
+  //\r
+  // Transmit the SCSI Command PDU.\r
+  //\r
+  Status = TcpIoTransmit (&Conn->TcpIo, Pdu);\r
+\r
+  NetbufFree (Pdu);\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  if (!Session->InitialR2T &&\r
+      (XferContext->Offset < Session->FirstBurstLength) &&\r
+      (XferContext->Offset < Packet->OutTransferLength)\r
+      ) {\r
+    //\r
+    // Unsolicited Data-Out sequence is allowed. There is remaining SCSI\r
+    // OUT data, and the limit of FirstBurstLength is not reached.\r
+    //\r
+    XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;\r
+    XferContext->DesiredLength = MIN (\r
+                                   Session->FirstBurstLength,\r
+                                   Packet->OutTransferLength - XferContext->Offset\r
+                                   );\r
+\r
+    Data    = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;\r
+    Status  = IScsiSendDataOutPduSequence (Data, Lun, Tcb);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+  }\r
+\r
+  InBufferContext.InData    = (UINT8 *) Packet->InDataBuffer;\r
+  InBufferContext.InDataLen = Packet->InTransferLength;\r
+\r
+  while (!Tcb->StatusXferd) {\r
+    //\r
+    // Start the timeout timer.\r
+    //\r
+    if (Timeout != 0) {\r
+      Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);\r
+      if (EFI_ERROR (Status)) {\r
+        goto ON_EXIT;\r
+      }\r
+\r
+      TimeoutEvent = Conn->TimeoutEvent;\r
+    }\r
+\r
+    //\r
+    // Try to receive PDU from target.\r
+    //\r
+    Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);\r
+    if (EFI_ERROR (Status)) {\r
+      goto ON_EXIT;\r
+    }\r
+\r
+    switch (ISCSI_GET_OPCODE (NetbufGetByte (Pdu, 0, NULL))) {\r
+    case ISCSI_OPCODE_SCSI_DATA_IN:\r
+      Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);\r
+      break;\r
+\r
+    case ISCSI_OPCODE_R2T:\r
+      Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);\r
+      break;\r
+\r
+    case ISCSI_OPCODE_SCSI_RSP:\r
+      Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);\r
+      break;\r
+\r
+    case ISCSI_OPCODE_NOP_IN:\r
+      Status = IScsiOnNopInRcvd (Pdu, Tcb);\r
+      break;\r
+\r
+    case ISCSI_OPCODE_VENDOR_T0:\r
+    case ISCSI_OPCODE_VENDOR_T1:\r
+    case ISCSI_OPCODE_VENDOR_T2:\r
+      //\r
+      // These messages are vendor specific. Skip them.\r
+      //\r
+      break;\r
+\r
+    default:\r
+      Status = EFI_PROTOCOL_ERROR;\r
+      break;\r
+    }\r
+\r
+    NetbufFree (Pdu);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      break;\r
+    }\r
+  }\r
+\r
+ON_EXIT:\r
+\r
+  if (TimeoutEvent != NULL) {\r
+    gBS->SetTimer (TimeoutEvent, TimerCancel, 0);\r
+  }\r
+\r
+  if (Tcb != NULL) {\r
+    IScsiDelTcb (Tcb);\r
+  }\r
+\r
+  if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {\r
+    //\r
+    // Reinstate the session.\r
+    //\r
+    if (EFI_ERROR (IScsiSessionReinstatement (Session))) {\r
+      Status = EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Reinstate the session on some error.\r
+\r
+  @param[in]  Session           The iSCSI session\r
+\r
+  @retval EFI_SUCCESS           The session is reinstated from some error.\r
+  @retval Other                 Reinstatement failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSessionReinstatement (\r
+  IN ISCSI_SESSION  *Session\r
+  )\r
+{\r
+  EFI_STATUS    Status;\r
+\r
+  ASSERT (Session->State == SESSION_STATE_LOGGED_IN);\r
+\r
+  //\r
+  // Abort the session and re-init it.\r
+  //\r
+  IScsiSessionAbort (Session);\r
+  IScsiSessionInit (Session, TRUE);\r
+\r
+  //\r
+  // Login again.\r
+  //\r
+  Status = IScsiSessionLogin (Session);\r
+\r
+  return Status;\r
+}\r
+\r
+\r
+/**\r
+  Initialize some session parameters before login.\r
+\r
+  @param[in, out]  Session  The iSCSI session.\r
+  @param[in]       Recovery Whether the request is from a fresh new start or recovery.\r
+\r
+**/\r
+VOID\r
+IScsiSessionInit (\r
+  IN OUT ISCSI_SESSION  *Session,\r
+  IN BOOLEAN            Recovery\r
+  )\r
+{\r
+  if (!Recovery) {\r
+    Session->Signature  = ISCSI_SESSION_SIGNATURE;\r
+    Session->State      = SESSION_STATE_FREE;\r
+\r
+    InitializeListHead (&Session->Conns);\r
+    InitializeListHead (&Session->TcbList);\r
+  }\r
+\r
+  Session->Tsih                 = 0;\r
+\r
+  Session->CmdSN                = 1;\r
+  Session->InitiatorTaskTag     = 1;\r
+  Session->NextCid              = 1;\r
+\r
+  Session->TargetPortalGroupTag = 0;\r
+  Session->MaxConnections       = ISCSI_MAX_CONNS_PER_SESSION;\r
+  Session->InitialR2T           = FALSE;\r
+  Session->ImmediateData        = TRUE;\r
+  Session->MaxBurstLength       = 262144;\r
+  Session->FirstBurstLength     = MAX_RECV_DATA_SEG_LEN_IN_FFP;\r
+  Session->DefaultTime2Wait     = 2;\r
+  Session->DefaultTime2Retain   = 20;\r
+  Session->MaxOutstandingR2T    = DEFAULT_MAX_OUTSTANDING_R2T;\r
+  Session->DataPDUInOrder       = TRUE;\r
+  Session->DataSequenceInOrder  = TRUE;\r
+  Session->ErrorRecoveryLevel   = 0;\r
+}\r
+\r
+\r
+/**\r
+  Abort the iSCSI session. That is, reset all the connection(s), and free the\r
+  resources.\r
+\r
+  @param[in, out]  Session The iSCSI session.\r
+\r
+**/\r
+VOID\r
+IScsiSessionAbort (\r
+  IN OUT ISCSI_SESSION  *Session\r
+  )\r
+{\r
+  ISCSI_CONNECTION  *Conn;\r
+  EFI_GUID          *ProtocolGuid;\r
+\r
+  if (Session->State != SESSION_STATE_LOGGED_IN) {\r
+    return ;\r
+  }\r
+\r
+  ASSERT (!IsListEmpty (&Session->Conns));\r
+\r
+  while (!IsListEmpty (&Session->Conns)) {\r
+    Conn = NET_LIST_USER_STRUCT_S (\r
+             Session->Conns.ForwardLink,\r
+             ISCSI_CONNECTION,\r
+             Link,\r
+             ISCSI_CONNECTION_SIGNATURE\r
+             );\r
+    if (!Conn->Ipv6Flag) {\r
+      ProtocolGuid = &gEfiTcp4ProtocolGuid;\r
+    } else {\r
+      ProtocolGuid = &gEfiTcp6ProtocolGuid;    \r
+    }\r
+\r
+    gBS->CloseProtocol (\r
+           Conn->TcpIo.Handle,\r
+           ProtocolGuid,\r
+           Session->Private->Image,\r
+           Session->Private->ExtScsiPassThruHandle\r
+           );\r
+\r
+    IScsiConnReset (Conn);\r
+\r
+    IScsiDetatchConnection (Conn);\r
+    IScsiDestroyConnection (Conn);\r
+  }\r
+\r
+  Session->State = SESSION_STATE_FAILED;\r
+\r
+  return ;\r
+}\r
diff --git a/NetworkPkg/IScsiDxe/IScsiProto.h b/NetworkPkg/IScsiDxe/IScsiProto.h
new file mode 100644 (file)
index 0000000..80c62b7
--- /dev/null
@@ -0,0 +1,1035 @@
+/** @file\r
+  The header file of iSCSI Protocol that defines many specific data structures.\r
+\r
+Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _ISCSI_PROTO_H_\r
+#define _ISCSI_PROTO_H_\r
+\r
+//\r
+// RFC 1982 Serial Number Arithmetic, SERIAL_BITS = 32\r
+//\r
+#define ISCSI_SEQ_EQ(s1, s2)  ((s1) == (s2))\r
+#define ISCSI_SEQ_LT(s1, s2) \\r
+    ( \\r
+      (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) < ((UINT32) 1 << 31)) || \\r
+      (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) > ((UINT32) 1 << 31)) \\r
+    )\r
+#define ISCSI_SEQ_GT(s1, s2) \\r
+    ( \\r
+      (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) > ((UINT32) 1 << 31)) || \\r
+      (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) < ((UINT32) 1 << 31)) \\r
+    )\r
+\r
+#define ISCSI_WELL_KNOWN_PORT                   3260\r
+#define ISCSI_MAX_CONNS_PER_SESSION             1\r
+\r
+#define DEFAULT_MAX_RECV_DATA_SEG_LEN           8192\r
+#define MAX_RECV_DATA_SEG_LEN_IN_FFP            65536\r
+#define DEFAULT_MAX_OUTSTANDING_R2T             1\r
+\r
+#define ISCSI_VERSION_MAX                       0x00\r
+#define ISCSI_VERSION_MIN                       0x00\r
+\r
+#define ISCSI_KEY_AUTH_METHOD                   "AuthMethod"\r
+#define ISCSI_KEY_HEADER_DIGEST                 "HeaderDigest"\r
+#define ISCSI_KEY_DATA_DIGEST                   "DataDigest"\r
+#define ISCSI_KEY_MAX_CONNECTIONS               "MaxConnections"\r
+#define ISCSI_KEY_TARGET_NAME                   "TargetName"\r
+#define ISCSI_KEY_INITIATOR_NAME                "InitiatorName"\r
+#define ISCSI_KEY_TARGET_ALIAS                  "TargetAlias"\r
+#define ISCSI_KEY_INITIATOR_ALIAS               "InitiatorAlias"\r
+#define ISCSI_KEY_TARGET_ADDRESS                "TargetAddress"\r
+#define ISCSI_KEY_INITIAL_R2T                   "InitialR2T"\r
+#define ISCSI_KEY_IMMEDIATE_DATA                "ImmediateData"\r
+#define ISCSI_KEY_TARGET_PORTAL_GROUP_TAG       "TargetPortalGroupTag"\r
+#define ISCSI_KEY_MAX_BURST_LENGTH              "MaxBurstLength"\r
+#define ISCSI_KEY_FIRST_BURST_LENGTH            "FirstBurstLength"\r
+#define ISCSI_KEY_DEFAULT_TIME2WAIT             "DefaultTime2Wait"\r
+#define ISCSI_KEY_DEFAULT_TIME2RETAIN           "DefaultTime2Retain"\r
+#define ISCSI_KEY_MAX_OUTSTANDING_R2T           "MaxOutstandingR2T"\r
+#define ISCSI_KEY_DATA_PDU_IN_ORDER             "DataPDUInOrder"\r
+#define ISCSI_KEY_DATA_SEQUENCE_IN_ORDER        "DataSequenceInOrder"\r
+#define ISCSI_KEY_ERROR_RECOVERY_LEVEL          "ErrorRecoveryLevel"\r
+#define ISCSI_KEY_SESSION_TYPE                  "SessionType"\r
+#define ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH  "MaxRecvDataSegmentLength"\r
+\r
+#define ISCSI_KEY_VALUE_NONE                    "None"\r
+\r
+///\r
+/// connection state for initiator\r
+///\r
+\r
+#define CONN_STATE_FREE                         0\r
+#define CONN_STATE_XPT_WAIT                     1\r
+#define CONN_STATE_IN_LOGIN                     2\r
+#define CONN_STATE_LOGGED_IN                    3\r
+#define CONN_STATE_IN_LOGOUT                    4\r
+#define CONN_STATE_LOGOUT_REQUESTED             5\r
+#define CONN_STATE_CLEANUP_WAIT                 6\r
+#define CONN_STATE_IN_CLEANUP                   7\r
+\r
+///\r
+/// session state for initiator\r
+///\r
+#define SESSION_STATE_FREE                      0\r
+#define SESSION_STATE_LOGGED_IN                 1\r
+#define SESSION_STATE_FAILED                    2\r
+\r
+#define ISCSI_RESERVED_TAG                      0xffffffff\r
+\r
+#define ISCSI_REQ_IMMEDIATE                     0x40\r
+#define ISCSI_OPCODE_MASK                       0x3F\r
+\r
+#define ISCSI_SET_OPCODE(PduHdr, Op, Flgs)  ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) = ((Op) | (Flgs)))\r
+#define ISCSI_GET_OPCODE(PduHdr)            ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) & ISCSI_OPCODE_MASK)\r
+#define ISCSI_CHECK_OPCODE(PduHdr, Op)      ((((PduHdr)->OpCode) & ISCSI_OPCODE_MASK) == (Op))\r
+#define ISCSI_IMMEDIATE_ON(PduHdr)          ((PduHdr)->OpCode & ISCSI_REQ_IMMEDIATE)\r
+#define ISCSI_SET_FLAG(PduHdr, Flag)        (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags |= (BOOLEAN)(Flag))\r
+#define ISCSI_CLEAR_FLAG(PduHdr, Flag)      (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags &= ~(Flag))\r
+#define ISCSI_FLAG_ON(PduHdr, Flag)         ((BOOLEAN) ((((ISCSI_BASIC_HEADER *) (PduHdr))->Flags & (Flag)) == (Flag)))\r
+#define ISCSI_SET_STAGES(PduHdr, Cur, Nxt)  ((PduHdr)->Flags = (UINT8) ((PduHdr)->Flags | ((Cur) << 2 | (Nxt))))\r
+#define ISCSI_GET_CURRENT_STAGE(PduHdr)     ((UINT8) (((PduHdr)->Flags >> 2) & 0x3))\r
+#define ISCSI_GET_NEXT_STAGE(PduHdr)        ((UINT8) (((PduHdr)->Flags) & 0x3))\r
+\r
+#define ISCSI_GET_PAD_LEN(DataLen)          ((~(DataLen) + 1) & 0x3)\r
+#define ISCSI_ROUNDUP(DataLen)              (((DataLen) + 3) &~(0x3))\r
+\r
+#define HTON24(Dst, Src) \\r
+  do { \\r
+    (Dst)[0]  = (UINT8) ((UINT8) ((Src) >> 16) & 0xFF); \\r
+    (Dst)[1]  = (UINT8) ((UINT8) ((Src) >> 8) & 0xFF); \\r
+    (Dst)[2]  = (UINT8) ((UINT8) (Src) & 0xFF); \\r
+  } while (0);\r
+\r
+#define NTOH24(src)                         (((src)[0] << 16) | ((src)[1] << 8) | ((src)[2]))\r
+\r
+#define ISCSI_GET_DATASEG_LEN(PduHdr)       NTOH24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength)\r
+#define ISCSI_SET_DATASEG_LEN(PduHdr, Len)  HTON24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength, (Len))\r
+#define ISCSI_GET_BUFFER_OFFSET(PduHdr)     NTOHL  (((ISCSI_SCSI_DATA_IN *) (PduHdr))->BufferOffset)\r
+\r
+//\r
+// Initiator opcodes.\r
+//\r
+#define ISCSI_OPCODE_NOP_OUT        0x00\r
+#define ISCSI_OPCODE_SCSI_CMD       0x01\r
+#define ISCSI_OPCODE_SCSI_TMF_REQ   0x02\r
+#define ISCSI_OPCODE_LOGIN_REQ      0x03\r
+#define ISCSI_OPCODE_TEXT_REQ       0x04\r
+#define ISCSI_OPCODE_SCSI_DATA_OUT  0x05\r
+#define ISCSI_OPCODE_LOGOUT_REQ     0x06\r
+#define ISCSI_OPCODE_SNACK_REQ      0x10\r
+#define ISCSI_OPCODE_VENDOR_I0      0x1c\r
+#define ISCSI_OPCODE_VENDOR_I1      0x1d\r
+#define ISCSI_OPCODE_VENDOR_I2      0x1e\r
+\r
+//\r
+// Target opcodes.\r
+//\r
+#define ISCSI_OPCODE_NOP_IN       0x20\r
+#define ISCSI_OPCODE_SCSI_RSP     0x21\r
+#define ISCSI_OPCODE_SCSI_TMF_RSP 0x22\r
+#define ISCSI_OPCODE_LOGIN_RSP    0x23\r
+#define ISCSI_OPCODE_TEXT_RSP     0x24\r
+#define ISCSI_OPCODE_SCSI_DATA_IN 0x25\r
+#define ISCSI_OPCODE_LOGOUT_RSP   0x26\r
+#define ISCSI_OPCODE_R2T          0x31\r
+#define ISCSI_OPCODE_ASYNC_MSG    0x32\r
+#define ISCSI_OPCODE_VENDOR_T0    0x3c\r
+#define ISCSI_OPCODE_VENDOR_T1    0x3d\r
+#define ISCSI_OPCODE_VENDOR_T2    0x3e\r
+#define ISCSI_OPCODE_REJECT       0x3f\r
+\r
+#define ISCSI_BHS_FLAG_FINAL      0x80\r
+\r
+//\r
+// Defined AHS types, others are reserved.\r
+//\r
+#define ISCSI_AHS_TYPE_EXT_CDB              0x1\r
+#define ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN 0x2\r
+\r
+#define SCSI_CMD_PDU_FLAG_READ        0x40\r
+#define SCSI_CMD_PDU_FLAG_WRITE       0x20\r
+\r
+#define ISCSI_CMD_PDU_TASK_ATTR_MASK  0x07\r
+\r
+//\r
+// task attributes\r
+//\r
+#define ISCSI_TASK_ATTR_UNTAGGED  0x00\r
+#define ISCSI_TASK_ATTR_SIMPLE    0x01\r
+#define ISCSI_TASK_ATTR_ORDERD    0x02\r
+#define ISCSI_TASK_ATTR_HOQ       0x03\r
+#define ISCSI_TASK_ATTR_ACA       0x04\r
+\r
+//\r
+// Flag bit definitions in SCSI response.\r
+//\r
+#define SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW  0x10\r
+#define SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW 0x08\r
+#define SCSI_RSP_PDU_FLAG_OVERFLOW          0x04\r
+#define SCSI_RSP_PDU_FLAG_UNDERFLOW         0x02\r
+\r
+//\r
+// iSCSI service response codes.\r
+//\r
+#define ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET  0x00\r
+#define ISCSI_SERVICE_RSP_TARGET_FAILURE              0x01\r
+\r
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_COMPLETE           0\r
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_NOT_EXIST              1\r
+#define ISCSI_TMF_RSP_PDU_RSP_LUN_NOT_EXIST               2\r
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_STILL_ALLEGIANT        3\r
+#define ISCSI_TMF_RSP_PDU_RSP_TASK_REASSGIN_NOT_SUPPORTED 4\r
+#define ISCSI_TMF_RSP_PDU_RSP_NOT_SUPPORTED               5\r
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_AHTH_FAILED        6\r
+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_REJECTED           255\r
+\r
+#define SCSI_DATA_IN_PDU_FLAG_ACKKNOWLEDGE  0x40\r
+#define SCSI_DATA_IN_PDU_FLAG_OVERFLOW      SCSI_RSP_PDU_FLAG_OVERFLOW\r
+#define SCSI_DATA_IN_PDU_FLAG_UNDERFLOW     SCSI_RSP_PDU_FLAG_UNDERFLOW\r
+#define SCSI_DATA_IN_PDU_FLAG_STATUS_VALID  0x01\r
+\r
+#define ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT  0x80\r
+#define ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE 0x40\r
+\r
+#define ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT    ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT\r
+#define ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE   ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE\r
+\r
+#define ISCSI_LOGIN_STATUS_SUCCESS          0\r
+#define ISCSI_LOGIN_STATUS_REDIRECTION      1\r
+#define ISCSI_LOGIN_STATUS_INITIATOR_ERROR  2\r
+#define ISCSI_LOGIN_STATUS_TARGET_ERROR     3\r
+\r
+#define ISCSI_LOGOUT_REASON_CLOSE_SESSION                   0\r
+#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION                1\r
+#define ISCSI_LOGOUT_REASON_REMOVE_CONNECTION_FOR_RECOVERY  2\r
+\r
+#define ISCSI_LOGOUT_RESPONSE_SESSION_CLOSED_SUCCESS  0\r
+#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND           1\r
+#define ISCSI_LOGOUT_RESPONSE_RECOVERY_NOT_SUPPORTED  2\r
+#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED          3\r
+\r
+#define ISCSI_SNACK_REQUEST_TYPE_DATA_OR_R2T  0\r
+#define ISCSI_SNACK_REQUEST_TYPE_STATUS       1\r
+#define ISCSI_SNACK_REQUEST_TYPE_DATA_ACK     2\r
+#define ISCSI_SNACK_REQUEST_TYPE_RDATA        3\r
+\r
+#define ISCSI_SECURITY_NEGOTIATION          0\r
+#define ISCSI_LOGIN_OPERATIONAL_NEGOTIATION 1\r
+#define ISCSI_FULL_FEATURE_PHASE            3\r
+\r
+typedef struct _ISCSI_SESSION               ISCSI_SESSION;\r
+typedef struct _ISCSI_CONNECTION            ISCSI_CONNECTION;\r
+\r
+typedef enum {\r
+  DataIn  = 0,\r
+  DataOut = 1,\r
+  DataBi  = 2\r
+} DATA_DIRECTION;\r
+\r
+///\r
+/// iSCSI Basic Header Segment\r
+///\r
+typedef struct _ISCSI_BASIC_HEADER {\r
+  UINT8   OpCode;\r
+  UINT8   Flags;\r
+  UINT16  OpCodeSpecific1;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  OpCodeSpecific2[7];\r
+} ISCSI_BASIC_HEADER;\r
+\r
+typedef struct _ISCSI_ADDTIONAL_HEADER {\r
+  UINT16  Length;\r
+  UINT8   Type;\r
+  UINT8   TypeSpecific[1];\r
+} ISCSI_ADDITIONAL_HEADER;\r
+\r
+typedef struct _ISCSI_BI_EXP_READ_DATA_LEN_AHS {\r
+  UINT16  Length;\r
+  UINT8   Type;\r
+  UINT8   Reserved;\r
+  UINT32  ExpReadDataLength;\r
+} ISCSI_BI_EXP_READ_DATA_LEN_AHS;\r
+\r
+///\r
+/// SCSI Command\r
+///\r
+typedef struct _SCSI_COMMAND {\r
+  UINT8   OpCode;\r
+  UINT8   Flags;\r
+  UINT16  Reserved;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  ExpDataXferLength;\r
+  UINT32  CmdSN;\r
+  UINT32  ExpStatSN;\r
+  UINT8   Cdb[16];\r
+} SCSI_COMMAND;\r
+\r
+///\r
+/// SCSI Response\r
+///\r
+typedef struct _SCSI_RESPONSE {\r
+  UINT8   OpCode;\r
+  UINT8   Flags;\r
+  UINT8   Response;\r
+  UINT8   Status;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Reserved[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  SNACKTag;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT32  ExpDataSN;\r
+  UINT32  BiReadResidualCount;\r
+  UINT32  ResidualCount;\r
+} SCSI_RESPONSE;\r
+\r
+typedef struct _ISCSI_SENSE_DATA {\r
+  UINT16  Length;\r
+  UINT8   Data[2];\r
+} ISCSI_SENSE_DATA;\r
+\r
+///\r
+/// iSCSI Task Managment Function Request.\r
+///\r
+typedef struct _ISCSI_TMF_REQUEST {\r
+  UINT8   OpCode;\r
+  UINT8   Fuction;\r
+  UINT16  Reserved1;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  ReferencedTaskTag;\r
+  UINT32  CmdSN;\r
+  UINT32  ExpStatSN;\r
+  UINT32  RefCmdSN;\r
+  UINT32  ExpDataSN;\r
+  UINT32  Reserved2[2];\r
+} ISCSI_TMF_REQUEST;\r
+\r
+///\r
+/// iSCSI Task Management Function Response.\r
+///\r
+typedef struct _ISCSI_TMF_RESPONSE {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1;\r
+  UINT8   Response;\r
+  UINT8   Reserved2;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT32  Reserver3[2];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  Reserved4;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT32  Reserved[3];\r
+} ISCSI_TMF_RESPONSE;\r
+\r
+///\r
+/// SCSI Data-Out\r
+///\r
+typedef struct _ISCSI_SCSI_DATA_OUT {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1[3];\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  TargetTransferTag;\r
+  UINT32  Reserved2;\r
+  UINT32  ExpStatSN;\r
+  UINT32  Reserved3;\r
+  UINT32  DataSN;\r
+  UINT32  BufferOffset;\r
+  UINT32  Reserved4;\r
+} ISCSI_SCSI_DATA_OUT;\r
+\r
+///\r
+/// SCSI Data-In\r
+///\r
+typedef struct _ISCSI_SCSI_DATA_IN {\r
+  UINT8   OpCode;\r
+  UINT8   Flags;\r
+  UINT8   Reserved1;\r
+  UINT8   Status;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  TargetTransferTag;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT32  DataSN;\r
+  UINT32  BufferOffset;\r
+  UINT32  ResidualCount;\r
+} ISCSI_SCSI_DATA_IN;\r
+\r
+///\r
+/// Ready To Transfer.\r
+///\r
+typedef struct _ISCSI_READY_TO_TRANSFER {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1[3];\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  TargetTransferTag;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT32  R2TSeqNum;\r
+  UINT32  BufferOffset;\r
+  UINT32  DesiredDataTransferLength;\r
+} ISCSI_READY_TO_TRANSFER;\r
+\r
+typedef struct _ISCSI_ASYNC_MESSAGE {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1[8];\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  Reserved2;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT8   AsyncEvent;\r
+  UINT8   AsyncVCode;\r
+  UINT16  Parameter1;\r
+  UINT16  Parameter2;\r
+  UINT16  Parameter3;\r
+  UINT32  Reserved3;\r
+} ISCSI_ASYNC_MESSAGE;\r
+\r
+///\r
+/// Login Request.\r
+///\r
+typedef struct _ISCSI_LOGIN_REQUEST {\r
+  UINT8   OpCode;\r
+  UINT8   Flags;\r
+  UINT8   VersionMax;\r
+  UINT8   VersionMin;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Isid[6];\r
+  UINT16  Tsih;\r
+  UINT32  InitiatorTaskTag;\r
+  UINT16  Cid;\r
+  UINT16  Reserved1;\r
+  UINT32  CmdSN;\r
+  UINT32  ExpStatSN;\r
+  UINT32  Reserved2[4];\r
+} ISCSI_LOGIN_REQUEST;\r
+\r
+///\r
+/// Login Response.\r
+///\r
+typedef struct _ISCSI_LOGIN_RESPONSE {\r
+  UINT8   OpCode;\r
+  UINT8   Flags;\r
+  UINT8   VersionMax;\r
+  UINT8   VersionActive;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Isid[6];\r
+  UINT16  Tsih;\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  Reserved1;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT8   StatusClass;\r
+  UINT8   StatusDetail;\r
+  UINT8   Reserved2[10];\r
+} ISCSI_LOGIN_RESPONSE;\r
+\r
+///\r
+/// Logout Request.\r
+///\r
+typedef struct _ISCSI_LOGOUT_REQUEST {\r
+  UINT8   OpCode;\r
+  UINT8   ReasonCode;\r
+  UINT16  Reserved1;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT32  Reserved2[2];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT16  Cid;\r
+  UINT16  Reserved3;\r
+  UINT32  CmdSN;\r
+  UINT32  ExpStatSN;\r
+  UINT32  Reserved4[4];\r
+} ISCSI_LOGOUT_REQUEST;\r
+\r
+///\r
+/// Logout Response.\r
+///\r
+typedef struct _ISCSI_LOGOUT_RESPONSE {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1;\r
+  UINT8   Response;\r
+  UINT8   Reserved2;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT32  Reserved3[2];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  Reserved4;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT32  Reserved5;\r
+  UINT16  Time2Wait;\r
+  UINT16  Time2Retain;\r
+  UINT32  Reserved6;\r
+} ISCSI_LOGOUT_RESPONSE;\r
+\r
+///\r
+/// SNACK Request.\r
+///\r
+typedef struct _ISCSI_SNACK_REQUEST {\r
+  UINT8   OpCode;\r
+  UINT8   Type;\r
+  UINT16  Reserved1;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  TargetTransferTag;\r
+  UINT32  Reserved2;\r
+  UINT32  ExpStatSN;\r
+  UINT32  Reserved[2];\r
+  UINT32  BegRun;\r
+  UINT32  RunLength;\r
+} ISCSI_SNACK_REQUEST;\r
+\r
+///\r
+/// Reject.\r
+///\r
+typedef struct _ISCSI_REJECT {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1;\r
+  UINT8   Reason;\r
+  UINT8   Reserved2;\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT32  Reserved3[2];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  Reserved4;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT32  DataSN;\r
+  UINT32  Reserved5[2];\r
+} ISCSI_REJECT;\r
+\r
+///\r
+/// NOP-Out.\r
+///\r
+typedef struct _ISCSI_NOP_OUT {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1[3];\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  TargetTransferTag;\r
+  UINT32  CmdSN;\r
+  UINT32  ExpStatSN;\r
+  UINT32  Reserved2[4];\r
+} ISCSI_NOP_OUT;\r
+\r
+///\r
+/// NOP-In.\r
+///\r
+typedef struct _ISCSI_NOP_IN {\r
+  UINT8   OpCode;\r
+  UINT8   Reserved1[3];\r
+  UINT8   TotalAHSLength;\r
+  UINT8   DataSegmentLength[3];\r
+  UINT8   Lun[8];\r
+  UINT32  InitiatorTaskTag;\r
+  UINT32  TargetTransferTag;\r
+  UINT32  StatSN;\r
+  UINT32  ExpCmdSN;\r
+  UINT32  MaxCmdSN;\r
+  UINT32  Reserved2[3];\r
+} ISCSI_NOP_IN;\r
+\r
+typedef enum {\r
+  IScsiDigestNone,\r
+  IScsiDigestCRC32\r
+} ISCSI_DIGEST_TYPE;\r
+\r
+typedef struct _ISCSI_XFER_CONTEXT {\r
+  UINT32  TargetTransferTag;\r
+  UINT32  Offset;\r
+  UINT32  DesiredLength;\r
+  UINT32  ExpDataSN;\r
+} ISCSI_XFER_CONTEXT;\r
+\r
+typedef struct _ISCSI_IN_BUFFER_CONTEXT {\r
+  UINT8   *InData;\r
+  UINT32  InDataLen;\r
+} ISCSI_IN_BUFFER_CONTEXT;\r
+\r
+typedef struct _ISCSI_TCB {\r
+  LIST_ENTRY          Link;\r
+\r
+  BOOLEAN             SoFarInOrder;\r
+  UINT32              ExpDataSN;\r
+  BOOLEAN             FbitReceived;\r
+  BOOLEAN             StatusXferd;\r
+  UINT32              ActiveR2Ts;\r
+  UINT32              Response;\r
+  CHAR8               *Reason;\r
+  UINT32              InitiatorTaskTag;\r
+  UINT32              CmdSN;\r
+  UINT32              SNACKTag;\r
+\r
+  ISCSI_XFER_CONTEXT  XferContext;\r
+\r
+  ISCSI_CONNECTION    *Conn;\r
+} ISCSI_TCB;\r
+\r
+typedef struct _ISCSI_KEY_VALUE_PAIR {\r
+  LIST_ENTRY      List;\r
+\r
+  CHAR8           *Key;\r
+  CHAR8           *Value;\r
+} ISCSI_KEY_VALUE_PAIR;\r
+\r
+/**\r
+  Attach the iSCSI connection to the iSCSI session. \r
+\r
+  @param[in, out]  Session The iSCSI session.\r
+  @param[in, out]  Conn    The iSCSI connection.\r
+\r
+**/\r
+VOID\r
+IScsiAttatchConnection (\r
+  IN OUT ISCSI_SESSION     *Session,\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  );\r
+\r
+/**\r
+  Detach the iSCSI connection from the session it belongs to. \r
+\r
+  @param[in, out]  Conn The iSCSI connection.\r
+\r
+**/\r
+VOID\r
+IScsiDetatchConnection (\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  );\r
+\r
+/**\r
+  This function performs the iSCSI connection login.\r
+\r
+  @param[in, out]  Conn      The iSCSI connection to login.\r
+  @param           Timeout   The timeout value in milliseconds.\r
+\r
+  @retval EFI_SUCCESS        The iSCSI connection is logged into the iSCSI target.\r
+  @retval EFI_TIMEOUT        Timeout occurred during the login procedure.\r
+  @retval Others             Other errors as indicated.  \r
+\r
+**/\r
+EFI_STATUS\r
+IScsiConnLogin (\r
+  IN OUT ISCSI_CONNECTION    *Conn,\r
+  IN     UINT16              Timeout\r
+  );\r
+\r
+/**\r
+  Create a TCP connection for the iSCSI session.\r
+\r
+  @param[in]  Session Points to the iSCSI session.\r
+\r
+  @return The newly created iSCSI connection.\r
+\r
+**/\r
+ISCSI_CONNECTION *\r
+IScsiCreateConnection (\r
+  IN ISCSI_SESSION      *Session\r
+  );\r
+\r
+/**\r
+  Destroy an iSCSI connection.\r
+\r
+  @param[in]  Conn The connection to destroy.\r
+\r
+**/\r
+VOID\r
+IScsiDestroyConnection (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  );\r
+\r
+/**\r
+  Login the iSCSI session.\r
+\r
+  @param[in]  Session           The iSCSI session\r
+\r
+  @retval EFI_SUCCESS           The iSCSI session login procedure finished.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_NO_MEDIA          There was a media error.\r
+  @retval Others                Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSessionLogin (\r
+  IN ISCSI_SESSION  *Session\r
+  );\r
+\r
+/**\r
+  Wait for IPsec negotiation, then try to login the iSCSI session again.\r
+\r
+  @param[in]  Session           The iSCSI session\r
+\r
+  @retval EFI_SUCCESS           The iSCSI session login procedure finished.\r
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR    Some kind of iSCSI protocol error happened.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSessionReLogin (\r
+  IN ISCSI_SESSION  *Session\r
+  );\r
+\r
+/**\r
+  Build and send the iSCSI login request to the iSCSI target according to\r
+  the current login stage.\r
+\r
+  @param[in]  Conn             The connection in the iSCSI login phase.\r
+\r
+  @retval EFI_SUCCESS          The iSCSI login request PDU is built and sent on this\r
+                               connection.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_DEVICE_ERROR     Some kind of device error happened.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSendLoginReq (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  );\r
+\r
+/**\r
+  Receive and process the iSCSI login response.\r
+\r
+  @param[in]  Conn             The connection in the iSCSI login phase.\r
+  \r
+  @retval EFI_SUCCESS          The iSCSI login response PDU is received and processed.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiReceiveLoginRsp (\r
+  IN ISCSI_CONNECTION  *Conn\r
+  );\r
+\r
+/**\r
+  Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.\r
+  The DataSegmentLength and the actual size of the net buffer containing this PDU will be\r
+  updated.\r
+\r
+  @param[in, out]  Pdu         The iSCSI PDU whose data segment the key-value pair will\r
+                               be added to.\r
+  @param[in]       Key         The key name string.\r
+  @param[in]       Value       The value string.\r
+\r
+  @retval EFI_SUCCESS          The key-valu pair is added to the PDU's datasegment and\r
+                               the correspondence length fields are updated.\r
+  @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value\r
+                               pair.\r
+**/\r
+EFI_STATUS\r
+IScsiAddKeyValuePair (\r
+  IN OUT NET_BUF      *Pdu,\r
+  IN CHAR8            *Key,\r
+  IN CHAR8            *Value\r
+  );\r
+\r
+/**\r
+  Prepare the iSCSI login request to be sent according to the current login status.\r
+\r
+  @param[in, out]  Conn The connection in the iSCSI login phase.\r
+\r
+  @return The pointer to the net buffer containing the iSCSI login request built.\r
+  @retval NULL     Other errors as indicated.\r
+\r
+**/\r
+NET_BUF *\r
+IScsiPrepareLoginReq (\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  );\r
+\r
+/**\r
+  Process the iSCSI Login Response.\r
+\r
+  @param[in, out]  Conn The connection on which the iSCSI login response is received.\r
+  @param[in, out]  Pdu  The iSCSI login response PDU.\r
+\r
+  @retval EFI_SUCCESS        The iSCSI login response PDU is processed and all check are passed.\r
+  @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.\r
+  @retval EFI_MEDIA_CHANGED  Target is redirected.\r
+  @retval Others             Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiProcessLoginRsp (\r
+  IN OUT ISCSI_CONNECTION  *Conn,\r
+  IN OUT NET_BUF           *Pdu\r
+  );\r
+\r
+/**\r
+  Updated the target information according the data received in the iSCSI\r
+  login response with an target redirection status.\r
+\r
+  @param[in, out] Session      The iSCSI session.\r
+  @param[in]      Data         The data segment which should contain the\r
+                               TargetAddress key-value list.\r
+  @param[in]      Len          Length of the data.\r
+  \r
+  @retval EFI_SUCCESS          The target address is updated.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_NOT_FOUND        The TargetAddress key is not found.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiUpdateTargetAddress (\r
+  IN OUT ISCSI_SESSION         *Session,\r
+  IN     CHAR8                 *Data,\r
+  IN     UINT32                Len\r
+  );\r
+\r
+/**\r
+  The callback function to free the net buffer list.\r
+\r
+  @param[in]  Arg The opaque parameter.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+IScsiFreeNbufList (\r
+  VOID *Arg\r
+  );\r
+\r
+/**\r
+  Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and\r
+  an optional data segment. The two parts will be put into two blocks of buffers in the\r
+  net buffer. The digest check will be conducted in this function if needed and the digests\r
+  will be trimmed from the PDU buffer.\r
+\r
+  @param[in]  Conn         The iSCSI connection to receive data from.\r
+  @param[out] Pdu          The received iSCSI pdu.\r
+  @param[in]  Context      The context used to describe information on the caller provided\r
+                           buffer to receive data segment of the iSCSI pdu, it's optional.\r
+  @param[in]  HeaderDigest Whether there will be header digest received.\r
+  @param[in]  DataDigest   Whether there will be data digest.\r
+  @param[in]  TimeoutEvent The timeout event, it's optional.\r
+\r
+  @retval EFI_SUCCESS          An iSCSI pdu is received.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol error occurred.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiReceivePdu (\r
+  IN ISCSI_CONNECTION                      *Conn,\r
+  OUT NET_BUF                              **Pdu,\r
+  IN ISCSI_IN_BUFFER_CONTEXT               *Context, OPTIONAL\r
+  IN BOOLEAN                               HeaderDigest,\r
+  IN BOOLEAN                               DataDigest,\r
+  IN EFI_EVENT                             TimeoutEvent OPTIONAL\r
+  );\r
+\r
+/**\r
+  Check and get the result of the prameter negotiation.\r
+\r
+  @param[in, out]  Conn          The connection in iSCSI login.\r
+\r
+  @retval EFI_SUCCESS          The parmeter check is passed and negotiation is finished.\r
+  @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol error occurred.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiCheckOpParams (\r
+  IN OUT ISCSI_CONNECTION  *Conn\r
+  );\r
+\r
+/**\r
+  Fill the oprational prameters.\r
+\r
+  @param[in]       Conn    The connection in iSCSI login.\r
+  @param[in, out]  Pdu     The iSCSI login request PDU to fill the parameters.\r
+\r
+**/\r
+VOID\r
+IScsiFillOpParams (\r
+  IN     ISCSI_CONNECTION  *Conn,\r
+  IN OUT NET_BUF           *Pdu\r
+  );\r
+\r
+/**\r
+  Pad the iSCSI AHS or data segment to an integer number of 4 byte words.\r
+\r
+  @param[in, out]  Pdu         The iSCSI pdu which contains segments to pad.\r
+  @param[in]       Len         The length of the last semgnet in the PDU.\r
+\r
+  @retval EFI_SUCCESS          The segment is padded or no need to pad it.\r
+  @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the\r
+                               padding bytes.\r
+**/\r
+EFI_STATUS\r
+IScsiPadSegment (\r
+  IN OUT NET_BUF      *Pdu,\r
+  IN     UINT32       Len\r
+  );\r
+\r
+/**\r
+  Build a key-value list from the data segment.\r
+\r
+  @param[in]  Data The data segment containing the key-value pairs.\r
+  @param[in]  Len  Length of the data segment.\r
+\r
+  @return The key-value list.\r
+  @retval NULL Other errors as indicated.\r
+\r
+**/\r
+LIST_ENTRY *\r
+IScsiBuildKeyValueList (\r
+  IN CHAR8  *Data,\r
+  IN UINT32 Len\r
+  );\r
+\r
+/**\r
+  Get the value string by the key name from the key-value list. If found,\r
+  the key-value entry will be removed from the list.\r
+\r
+  @param[in, out]  KeyValueList  The key-value list.\r
+  @param[in]       Key           The key name to find.\r
+\r
+  @return The value string.\r
+  @retval NULL The key value pair can not be found.\r
+\r
+**/\r
+CHAR8 *\r
+IScsiGetValueByKeyFromList (\r
+  IN OUT LIST_ENTRY     *KeyValueList,\r
+  IN     CHAR8          *Key\r
+  );\r
+\r
+/**\r
+  Free the key-value list.\r
+\r
+  @param[in]  KeyValueList The key-value list.\r
+\r
+**/\r
+VOID\r
+IScsiFreeKeyValueList (\r
+  IN LIST_ENTRY      *KeyValueList\r
+  );\r
+\r
+/**\r
+  Normalize the iSCSI name according to RFC.\r
+\r
+  @param[in, out]  Name       The iSCSI name.\r
+  @param[in]       Len        length of the iSCSI name.\r
+\r
+  @retval EFI_SUCCESS        The iSCSI name is valid and normalized.\r
+  @retval EFI_PROTOCOL_ERROR The iSCSI name is mal-formatted or not in the IQN format.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiNormalizeName (\r
+  IN OUT CHAR8      *Name,\r
+  IN     UINTN      Len\r
+  );\r
+\r
+/**\r
+  Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.\r
+\r
+  @param[in]       PassThru  The EXT SCSI PASS THRU protocol.\r
+  @param[in]       Target    The target ID.\r
+  @param[in]       Lun       The LUN.\r
+  @param[in, out]  Packet    The request packet containing IO request, SCSI command\r
+                             buffer and buffers to read/write.\r
+                             \r
+  @retval EFI_SUCCES           The SCSI command is executed and the result is updated to \r
+                               the Packet.\r
+  @retval EFI_DEVICE_ERROR     Session state was not as required.\r
+  @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
+  @retval Others               Other errors as indicated.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiExecuteScsiCommand (\r
+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                 *PassThru,\r
+  IN UINT8                                           *Target,\r
+  IN UINT64                                          Lun,\r
+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet\r
+  );\r
+\r
+/**\r
+  Reinstate the session on some error.\r
+\r
+  @param[in]  Session           The iSCSI session\r
+\r
+  @retval EFI_SUCCES            The session is reinstated from some error.\r
+  @retval Other                 Reinstatement failed.\r
+\r
+**/\r
+EFI_STATUS\r
+IScsiSessionReinstatement (\r
+  IN ISCSI_SESSION  *Session\r
+  );\r
+\r
+/**\r
+  Initialize some session parameters before login.\r
+\r
+  @param[in, out]  Session  The iSCSI session.\r
+  @param[in]       Recovery Whether the request is from a fresh new start or recovery.\r
+\r
+**/\r
+VOID\r
+IScsiSessionInit (\r
+  IN OUT ISCSI_SESSION  *Session,\r
+  IN BOOLEAN            Recovery\r
+  );\r
+  \r
+/**\r
+  Abort the iSCSI session, that is, reset all the connection and free the\r
+  resources.\r
+\r
+  @param[in, out]  Session The iSCSI session.\r
+\r
+**/\r
+VOID\r
+IScsiSessionAbort (\r
+  IN OUT ISCSI_SESSION  *Session\r
+  );\r
+\r
+#endif\r
index 2a3a5433411c7bf4d5722700454a1282f58e9859..44e45a59f377373aa6e9bfa0ffc85f3b75ca48e4 100644 (file)
@@ -42,6 +42,7 @@
   NetLib|MdeModulePkg/Library/DxeNetLib/DxeNetLib.inf\r
   IpIoLib|MdeModulePkg/Library/DxeIpIoLib/DxeIpIoLib.inf\r
   UdpIoLib|MdeModulePkg/Library/DxeUdpIoLib/DxeUdpIoLib.inf\r
+  TcpIoLib|MdeModulePkg/Library/DxeTcpIoLib/DxeTcpIoLib.inf\r
   BaseCryptLib|CryptoPkg/Library/BaseCryptLib/BaseCryptLib.inf\r
   OpensslLib|CryptoPkg/Library/OpensslLib/OpensslLib.inf\r
   IntrinsicLib|CryptoPkg/Library/IntrinsicLib/IntrinsicLib.inf\r
@@ -91,8 +92,8 @@
   NetworkPkg/Dhcp6Dxe/Dhcp6Dxe.inf\r
   NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf\r
 \r
-\r
 [Components.IA32, Components.X64, Components.IPF]\r
+  NetworkPkg/IScsiDxe/IScsiDxe.inf\r
   NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf\r
   NetworkPkg/Application/Ping6/Ping6.inf\r
   NetworkPkg/Application/IfConfig6/IfConfig6.inf\r