add iSCSI protocol

git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@4423 6f19259b-4bc3-4df7-8a09-765794883524
diff --git a/MdeModulePkg/Include/Library/NetLib.h b/MdeModulePkg/Include/Library/NetLib.h
index 4a621e1..e621c24 100644
--- a/MdeModulePkg/Include/Library/NetLib.h
+++ b/MdeModulePkg/Include/Library/NetLib.h
@@ -24,6 +24,8 @@
 #define _NET_LIB_H_
 
 #include <PiDxe.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
 #include <Protocol/DriverBinding.h>
 #include <Protocol/ComponentName.h>
 #include <Protocol/DriverConfiguration.h>
@@ -214,6 +216,8 @@
 
 #define NET_IS_DIGIT(Ch)            (('0' <= (Ch)) && ((Ch) <= '9'))
 #define NET_ROUNDUP(size, unit)     (((size) + (unit) - 1) & (~((unit) - 1)))
+#define NET_IS_LOWER_CASE_CHAR(Ch)  (('a' <= (Ch)) && ((Ch) <= 'z'))
+#define NET_IS_UPPER_CASE_CHAR(Ch)  (('A' <= (Ch)) && ((Ch) <= 'Z'))
 
 //
 // Wrap functions to ease the impact of EFI library changes.
diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc
index bda1f8b..5c57569 100644
--- a/MdeModulePkg/MdeModulePkg.dsc
+++ b/MdeModulePkg/MdeModulePkg.dsc
@@ -35,6 +35,7 @@
   PciExpressLib|MdePkg/Library/BasePciExpressLib/BasePciExpressLib.inf

   PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf

   PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf

+  FrameworkIfrSupportLib|IntelFrameworkPkg/Library/FrameworkIfrSupportLib/IfrSupportLib.inf

 

   PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf

   PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf

@@ -257,6 +258,8 @@
   MdeModulePkg/Library/PeiS3LibNull/PeiS3LibNull.inf

   MdeModulePkg/Library/PeiRecoveryLibNull/PeiRecoveryLibNull.inf

 

+  MdeModulePkg/Universal/iScsi/IScsi.inf

+

   MdeModulePkg/Universal/Network/ArpDxe/ArpDxe.inf

   MdeModulePkg/Universal/Network/Dhcp4Dxe/Dhcp4Dxe.inf

   MdeModulePkg/Universal/Network/Ip4ConfigDxe/Ip4ConfigDxe.inf

diff --git a/MdeModulePkg/Universal/iScsi/ComponentName.c b/MdeModulePkg/Universal/iScsi/ComponentName.c
new file mode 100644
index 0000000..146dd3c
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/ComponentName.c
@@ -0,0 +1,146 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  ComponentName.c

+

+Abstract:

+

+  ComponentName protocol for iSCSI.

+

+--*/

+

+#include "IScsiImpl.h"

+

+//

+// EFI Component Name Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL     gIScsiComponentName = {

+  IScsiComponentNameGetDriverName,

+  IScsiComponentNameGetControllerName,

+  "eng"

+};

+

+//

+// EFI Component Name 2 Protocol

+//

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL    gIScsiComponentName2 = {

+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) IScsiComponentNameGetDriverName,

+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) IScsiComponentNameGetControllerName,

+  "en"

+};

+

+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE mIScsiDriverNameTable[] = {

+	{"eng;en", L"iSCSI Driver"}, 

+	{NULL, NULL}

+};

+

+EFI_STATUS

+EFIAPI

+IScsiComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,

+  IN  CHAR8                         *Language,

+  OUT CHAR16                        **DriverName

+  )

+/*++

+

+  Routine Description:

+    Retrieves a Unicode string that is the user readable name of the EFI Driver.

+

+  Arguments:

+    This       - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.

+    Language   - A pointer to a three character ISO 639-2 language identifier.

+                 This is the language of the driver name that that the caller 

+                 is requesting, and it must match one of the languages specified

+                 in SupportedLanguages.  The number of languages supported by a 

+                 driver is up to the driver writer.

+    DriverName - A pointer to the Unicode string to return.  This Unicode string

+                 is the name of the driver specified by This in the language 

+                 specified by Language.

+

+  Returns:

+    EFI_SUCCESS           - The Unicode string for the Driver specified by This

+                            and the language specified by Language was returned 

+                            in DriverName.

+    EFI_INVALID_PARAMETER - Language is NULL.

+    EFI_INVALID_PARAMETER - DriverName is NULL.

+    EFI_UNSUPPORTED       - The driver specified by This does not support the 

+                            language specified by Language.

+

+--*/

+{

+  return LookupUnicodeString2 (

+          Language,

+          This->SupportedLanguages,

+          mIScsiDriverNameTable,

+          DriverName,

+          (BOOLEAN)(This == &gIScsiComponentName)

+          );

+}

+

+EFI_STATUS

+EFIAPI

+IScsiComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,

+  IN  EFI_HANDLE                    ControllerHandle,

+  IN  EFI_HANDLE                    ChildHandle        OPTIONAL,

+  IN  CHAR8                         *Language,

+  OUT CHAR16                        **ControllerName

+  )

+/*++

+

+  Routine Description:

+    Retrieves a Unicode string that is the user readable name of the controller

+    that is being managed by an EFI Driver.

+

+  Arguments:

+    This             - A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.

+    ControllerHandle - The handle of a controller that the driver specified by 

+                       This is managing.  This handle specifies the controller 

+                       whose name is to be returned.

+    ChildHandle      - The handle of the child controller to retrieve the name 

+                       of.  This is an optional parameter that may be NULL.  It 

+                       will be NULL for device drivers.  It will also be NULL 

+                       for a bus drivers that wish to retrieve the name of the 

+                       bus controller.  It will not be NULL for a bus driver 

+                       that wishes to retrieve the name of a child controller.

+    Language         - A pointer to a three character ISO 639-2 language 

+                       identifier.  This is the language of the controller name 

+                       that that the caller is requesting, and it must match one

+                       of the languages specified in SupportedLanguages.  The 

+                       number of languages supported by a driver is up to the 

+                       driver writer.

+    ControllerName   - A pointer to the Unicode string to return.  This Unicode

+                       string is the name of the controller specified by 

+                       ControllerHandle and ChildHandle in the language 

+                       specified by Language from the point of view of the 

+                       driver specified by This. 

+

+  Returns:

+    EFI_SUCCESS           - The Unicode string for the user readable name in the 

+                            language specified by Language for the driver 

+                            specified by This was returned in DriverName.

+    EFI_INVALID_PARAMETER - ControllerHandle is not a valid EFI_HANDLE.

+    EFI_INVALID_PARAMETER - ChildHandle is not NULL and it is not a valid 

+                            EFI_HANDLE.

+    EFI_INVALID_PARAMETER - Language is NULL.

+    EFI_INVALID_PARAMETER - ControllerName is NULL.

+    EFI_UNSUPPORTED       - The driver specified by This is not currently 

+                            managing the controller specified by 

+                            ControllerHandle and ChildHandle.

+    EFI_UNSUPPORTED       - The driver specified by This does not support the 

+                            language specified by Language.

+

+--*/

+{

+  return EFI_UNSUPPORTED;

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsi.inf b/MdeModulePkg/Universal/iScsi/IScsi.inf
new file mode 100644
index 0000000..faa570d
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsi.inf
@@ -0,0 +1,96 @@
+#/** @file

+#  Component description file for iSCSI module.

+#

+#  Copyright (c) 2006 - 2007, Intel Corporation

+#  All rights reserved. This program and the accompanying materials

+#  are licensed and made available under the terms and conditions of the BSD License

+#  which accompanies this distribution. The full text of the license may be found at

+#  http://opensource.org/licenses/bsd-license.php

+#

+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,

+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.

+#

+#

+#**/

+

+[Defines]

+  INF_VERSION                    = 0x00010005

+  BASE_NAME                      = iSCSI

+  FILE_GUID                      = 4579B72D-7EC4-4dd4-8486-083C86B182A7

+  MODULE_TYPE                    = DXE_DRIVER

+  VERSION_STRING                 = 1.0

+  EDK_RELEASE_VERSION            = 0x00020000

+  EFI_SPECIFICATION_VERSION      = 0x00020000

+

+  ENTRY_POINT                    = IScsiDriverEntryPoint

+  UNLOAD_IMAGE                   = EfiIScsiUnload

+

+#

+# The following information is for reference only and not required by the build tools.

+#

+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC

+#

+#  DRIVER_BINDING                =  gIScsiDriverBinding

+#  COMPONENT_NAME                =  gIScsiComponentName

+#

+

+[Sources.common]

+  IScsiTcp4Io.h

+  IScsiProto.h

+  IScsiMisc.h

+  IScsiIbft.h

+  IScsiExtScsiPassThru.h

+  IScsiDriver.h

+  IScsiDhcp.h

+  IScsiCommon.h

+  IScsiCHAP.h

+  IScsiTcp4Io.c

+  IScsiProto.c

+  IScsiMisc.c

+  IScsiInitiatorName.c

+  IScsiIbft.c

+  IScsiExtScsiPassThru.c

+  IScsiDriver.c

+  IScsiDhcp.c

+  IScsiCHAP.c

+  ComponentName.c

+  Md5.c

+  IScsiConfigDxeStrings.uni

+  IScsiConfigDxe.vfr

+  IScsiConfig.c

+  IScsiConfig.h

+

+[Packages]

+  MdePkg/MdePkg.dec

+  MdeModulePkg/MdeModulePkg.dec

+  IntelFrameworkPkg/IntelFrameworkPkg.dec

+  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec

+

+[LibraryClasses]

+  UefiDriverEntryPoint

+  UefiLib

+  BaseLib

+  MemoryAllocationLib

+  BaseMemoryLib

+  UefiBootServicesTableLib

+  UefiRuntimeServicesTableLib

+  DevicePathLib

+  DebugLib

+  PrintLib

+  FrameworkHiiLib

+  FrameworkIfrSupportLib

+  NetLib

+

+[Protocols]

+  gEfiIScsiInitiatorNameProtocolGuid

+  gEfiBlockIoProtocolGuid

+  gEfiTcp4ProtocolGuid

+  gEfiExtScsiPassThruProtocolGuid

+  gEfiDevicePathProtocolGuid

+  gEfiTcp4ServiceBindingProtocolGuid

+  gEfiFormCallbackProtocolGuid

+  gEfiFormBrowserProtocolGuid

+  gEfiPciIoProtocolGuid

+  gEfiAcpiSupportProtocolGuid

+  gEfiDhcp4ProtocolGuid

+  gEfiDhcp4ServiceBindingProtocolGuid
\ No newline at end of file
diff --git a/MdeModulePkg/Universal/iScsi/IScsiCHAP.c b/MdeModulePkg/Universal/iScsi/IScsiCHAP.c
new file mode 100644
index 0000000..e6ea8b5
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiCHAP.c
@@ -0,0 +1,429 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiCHAP.c

+

+Abstract:

+

+--*/

+

+#include "IScsiImpl.h"

+#include "Md5.h"

+

+EFI_GUID  mIScsiCHAPAuthInfoGuid = ISCSI_CHAP_AUTH_INFO_GUID;

+

+EFI_STATUS

+IScsiCHAPCalculateResponse (

+  IN  UINT32  ChapIdentifier,

+  IN  CHAR8   *ChapSecret,

+  IN  UINT32  SecretLength,

+  IN  UINT8   *ChapChallenge,

+  IN  UINT32  ChallengeLength,

+  OUT UINT8   *ChapResponse

+  )

+{

+  MD5_CTX     Md5Ctx;

+  CHAR8       IdByte[1];

+  EFI_STATUS  Status;

+

+  Status = MD5Init (&Md5Ctx);

+

+  //

+  // Hash Identifier - Only calculate 1 byte data (RFC1994)

+  //

+  IdByte[0] = (CHAR8) ChapIdentifier;

+  MD5Update (&Md5Ctx, IdByte, 1);

+

+  //

+  // Hash Secret

+  //

+  if (SecretLength < ISCSI_CHAP_SECRET_MIN_LEN - 1) {

+    return EFI_PROTOCOL_ERROR;

+  }

+

+  MD5Update (&Md5Ctx, ChapSecret, SecretLength);

+

+  //

+  // Hash Challenge received from Target

+  //

+  MD5Update (&Md5Ctx, ChapChallenge, ChallengeLength);

+

+  Status = MD5Final (&Md5Ctx, ChapResponse);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiCHAPAuthTarget (

+  IN  ISCSI_CHAP_AUTH_DATA  *AuthData,

+  IN  UINT8                 *TargetResponse

+  )

+{

+  EFI_STATUS  Status;

+  UINT32      SecretSize;

+  UINT8       VerifyRsp[ISCSI_CHAP_RSP_LEN];

+

+  Status      = EFI_SUCCESS;

+

+  SecretSize  = (UINT32) AsciiStrLen (AuthData->AuthConfig.ReverseCHAPSecret);

+  Status = IScsiCHAPCalculateResponse (

+            AuthData->OutIdentifier,

+            AuthData->AuthConfig.ReverseCHAPSecret,

+            SecretSize,

+            AuthData->OutChallenge,

+            AuthData->OutChallengeLength,

+            VerifyRsp

+            );

+

+  if (NetCompareMem (VerifyRsp, TargetResponse, ISCSI_CHAP_RSP_LEN)) {

+    Status = EFI_SECURITY_VIOLATION;

+  }

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiCHAPOnRspReceived (

+  IN ISCSI_CONNECTION  *Conn,

+  IN BOOLEAN           Transit

+  )

+/*++

+

+Routine Description:

+

+  This function checks the received iSCSI Login Response during the security

+  negotiation stage.

+  

+Arguments:

+

+  Conn    - The iSCSI connection.

+  Transit - The transit flag of the latest iSCSI Login Response.

+

+Returns:

+

+  EFI_SUCCESS          - The Login Response passed the CHAP validation.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+  EFI_PROTOCOL_ERROR   - Some kind of protocol error happend.

+

+--*/

+{

+  EFI_STATUS                Status;

+  ISCSI_SESSION             *Session;

+  ISCSI_SESSION_CONFIG_DATA *ConfigData;

+  ISCSI_CHAP_AUTH_DATA      *AuthData;

+  CHAR8                     *Value;

+  UINT8                     *Data;

+  UINT32                    Len;

+  NET_LIST_ENTRY            *KeyValueList;

+  UINTN                     Algorithm;

+  CHAR8                     *Identifier;

+  CHAR8                     *Challenge;

+  CHAR8                     *Name;

+  CHAR8                     *Response;

+  UINT8                     TargetRsp[ISCSI_CHAP_RSP_LEN];

+  UINT32                    RspLen;

+

+  ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);

+  ASSERT (Conn->RspQue.BufNum != 0);

+

+  Session     = Conn->Session;

+  ConfigData  = &Session->ConfigData;

+  AuthData    = &Session->AuthData;

+

+  Len         = Conn->RspQue.BufSize;

+  Data        = NetAllocatePool (Len);

+  if (Data == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // Copy the data in case the data spans over multiple PDUs.

+  //

+  NetbufQueCopy (&Conn->RspQue, 0, Len, Data);

+

+  //

+  // Build the key-value list from the data segment of the Login Response.

+  //

+  KeyValueList = IScsiBuildKeyValueList (Data, Len);

+  if (KeyValueList == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  Status = EFI_PROTOCOL_ERROR;

+

+  switch (Conn->CHAPStep) {

+  case ISCSI_CHAP_INITIAL:

+    //

+    // The first Login Response.

+    //

+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);

+    if (Value == NULL) {

+      goto ON_EXIT;

+    }

+

+    Session->TargetPortalGroupTag = (UINT16) AsciiStrDecimalToUintn (Value);

+

+    Value                         = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_AUTH_METHOD);

+    if (Value == NULL) {

+      goto ON_EXIT;

+    }

+    //

+    // Initiator mandates CHAP authentication but target replies without "CHAP" or

+    // initiator suggets "None" but target replies with some kind of auth method.

+    //

+    if (AsciiStrCmp (Value, ISCSI_AUTH_METHOD_CHAP) == 0) {

+      if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) {

+        goto ON_EXIT;

+      }

+    } else {

+      if (AuthData->AuthConfig.CHAPType != ISCSI_CHAP_NONE) {

+        goto ON_EXIT;

+      }

+    }

+    //

+    // Transit to CHAP step one.

+    //

+    Conn->CHAPStep  = ISCSI_CHAP_STEP_ONE;

+    Status          = EFI_SUCCESS;

+    break;

+

+  case ISCSI_CHAP_STEP_TWO:

+    //

+    // The Target replies with CHAP_A=<A> CHAP_I=<I> CHAP_C=<C>

+    //

+    Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_ALGORITHM);

+    if (Value == NULL) {

+      goto ON_EXIT;

+    }

+

+    Algorithm = AsciiStrDecimalToUintn (Value);

+    if (Algorithm != ISCSI_CHAP_ALGORITHM_MD5) {

+      //

+      // Unsupported algorithm is chosen by target.

+      //

+      goto ON_EXIT;

+    }

+

+    Identifier = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_IDENTIFIER);

+    if (Identifier == NULL) {

+      goto ON_EXIT;

+    }

+

+    Challenge = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_CHALLENGE);

+    if (Challenge == NULL) {

+      goto ON_EXIT;

+    }

+    //

+    // Process the CHAP identifier and CHAP Challenge from Target

+    // Calculate Response value

+    //

+    AuthData->InIdentifier      = (UINT32) AsciiStrDecimalToUintn (Identifier);

+    AuthData->InChallengeLength = ISCSI_CHAP_AUTH_MAX_LEN;

+    IScsiHexToBin ((UINT8 *) AuthData->InChallenge, &AuthData->InChallengeLength, Challenge);

+    Status = IScsiCHAPCalculateResponse (

+              AuthData->InIdentifier,

+              AuthData->AuthConfig.CHAPSecret,

+              (UINT32) AsciiStrLen (AuthData->AuthConfig.CHAPSecret),

+              AuthData->InChallenge,

+              AuthData->InChallengeLength,

+              AuthData->CHAPResponse

+              );

+

+    //

+    // Transit to next step.

+    //

+    Conn->CHAPStep = ISCSI_CHAP_STEP_THREE;

+    break;

+

+  case ISCSI_CHAP_STEP_THREE:

+    //

+    // one way CHAP authentication and the target would like to

+    // authenticate us.

+    //

+    Status = EFI_SUCCESS;

+    break;

+

+  case ISCSI_CHAP_STEP_FOUR:

+    ASSERT (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL);

+    //

+    // The forth step, CHAP_N=<N> CHAP_R=<R> is received from Target.

+    //

+    Name = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_NAME);

+    if (Name == NULL) {

+      goto ON_EXIT;

+    }

+

+    Response = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_CHAP_RESPONSE);

+    if (Response == NULL) {

+      goto ON_EXIT;

+    }

+

+    RspLen = ISCSI_CHAP_RSP_LEN;

+    IScsiHexToBin (TargetRsp, &RspLen, Response);

+

+    //

+    // Check the CHAP Name and Response replied by Target.

+    //

+    Status = IScsiCHAPAuthTarget (AuthData, TargetRsp);

+    break;

+

+  default:

+    break;

+  }

+

+ON_EXIT:

+

+  IScsiFreeKeyValueList (KeyValueList);

+

+  NetFreePool (Data);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiCHAPToSendReq (

+  IN ISCSI_CONNECTION  *Conn,

+  IN NET_BUF           *Pdu

+  )

+/*++

+

+Routine Description:

+

+  This function fills the CHAP authentication information into the login PDU

+  during the security negotiation stage in the iSCSI connection login.

+  

+Arguments:

+

+  Conn - The iSCSI connection.

+  Pdu  - The PDU to send out.

+

+Returns:

+

+  EFI_SUCCESS          - All check passed and the phase-related CHAP authentication

+                         info is filled into the iSCSI PDU.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+  EFI_PROTOCOL_ERROR   - Some kind of protocol error happend.

+

+--*/

+{

+  EFI_STATUS                Status;

+  ISCSI_SESSION             *Session;

+  ISCSI_LOGIN_REQUEST       *LoginReq;

+  ISCSI_SESSION_CONFIG_DATA *ConfigData;

+  ISCSI_CHAP_AUTH_DATA      *AuthData;

+  CHAR8                     *Value;

+  CHAR8                     ValueStr[256];

+  CHAR8                     *Response;

+  UINT32                    RspLen;

+  CHAR8                     *Challenge;

+  UINT32                    ChallengeLen;

+

+  ASSERT (Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION);

+

+  Session     = Conn->Session;

+  ConfigData  = &Session->ConfigData;

+  AuthData    = &Session->AuthData;

+  LoginReq    = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, 0);

+  Status      = EFI_SUCCESS;

+

+  RspLen      = 2 * ISCSI_CHAP_RSP_LEN + 3;

+  Response    = NetAllocatePool (RspLen);

+  if (Response == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  ChallengeLen  = 2 * ISCSI_CHAP_RSP_LEN + 3;

+  Challenge     = NetAllocatePool (ChallengeLen);

+  if (Challenge == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  switch (Conn->CHAPStep) {

+  case ISCSI_CHAP_INITIAL:

+    //

+    // It's the initial Login Request. Fill in the key=value pairs mandatory

+    // for the initial Login Request.

+    //

+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIATOR_NAME, Session->InitiatorName);

+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_SESSION_TYPE, "Normal");

+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_TARGET_NAME, Session->ConfigData.NvData.TargetName);

+

+    if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_NONE) {

+      Value = ISCSI_KEY_VALUE_NONE;

+      ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);

+    } else {

+      Value = ISCSI_AUTH_METHOD_CHAP;

+    }

+

+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_AUTH_METHOD, Value);

+

+    break;

+

+  case ISCSI_CHAP_STEP_ONE:

+    //

+    // First step, send the Login Request with CHAP_A=<A1,A2...> key-value pair.

+    //

+    AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", ISCSI_CHAP_ALGORITHM_MD5);

+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_ALGORITHM, ValueStr);

+

+    Conn->CHAPStep = ISCSI_CHAP_STEP_TWO;

+    break;

+

+  case ISCSI_CHAP_STEP_THREE:

+    //

+    // Third step, send the Login Request with CHAP_N=<N> CHAP_R=<R> or

+    // CHAP_N=<N> CHAP_R=<R> CHAP_I=<I> CHAP_C=<C> if target ahtentication is

+    // required too.

+    //

+    // CHAP_N=<N>

+    //

+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_NAME, (UINT8 *) &AuthData->AuthConfig.CHAPName);

+    //

+    // CHAP_R=<R>

+    //

+    IScsiBinToHex ((UINT8 *) AuthData->CHAPResponse, ISCSI_CHAP_RSP_LEN, Response, &RspLen);

+    IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_RESPONSE, Response);

+

+    if (AuthData->AuthConfig.CHAPType == ISCSI_CHAP_MUTUAL) {

+      //

+      // CHAP_I=<I>

+      //

+      IScsiGenRandom ((UINT8 *) &AuthData->OutIdentifier, 1);

+      AsciiSPrint (ValueStr, sizeof (ValueStr), "%d", AuthData->OutIdentifier);

+      IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_IDENTIFIER, ValueStr);

+      //

+      // CHAP_C=<C>

+      //

+      IScsiGenRandom ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN);

+      AuthData->OutChallengeLength = ISCSI_CHAP_RSP_LEN;

+      IScsiBinToHex ((UINT8 *) AuthData->OutChallenge, ISCSI_CHAP_RSP_LEN, Challenge, &ChallengeLen);

+      IScsiAddKeyValuePair (Pdu, ISCSI_KEY_CHAP_CHALLENGE, Challenge);

+

+      Conn->CHAPStep = ISCSI_CHAP_STEP_FOUR;

+    }

+    //

+    // set the stage transition flag.

+    //

+    ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);

+    break;

+

+  default:

+    Status = EFI_PROTOCOL_ERROR;

+    break;

+  }

+

+  NetFreePool (Response);

+  NetFreePool (Challenge);

+

+  return Status;

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsiCHAP.h b/MdeModulePkg/Universal/iScsi/IScsiCHAP.h
new file mode 100644
index 0000000..4a15291
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiCHAP.h
@@ -0,0 +1,95 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiCHAP.h

+

+Abstract:

+

+--*/

+

+#ifndef _ISCSI_CHAP_H_

+#define _ISCSI_CHAP_H_

+

+#define ISCSI_CHAP_AUTH_INFO_GUID \

+  { \

+    0x786ec0ac, 0x65ae, 0x4d1b, 0xb1, 0x37, 0xd, 0x11, 0xa, 0x48, 0x37, 0x97 \

+  }

+

+extern EFI_GUID mIScsiCHAPAuthInfoGuid;

+

+#define ISCSI_AUTH_METHOD_CHAP    "CHAP"

+

+#define ISCSI_KEY_CHAP_ALGORITHM  "CHAP_A"

+#define ISCSI_KEY_CHAP_IDENTIFIER "CHAP_I"

+#define ISCSI_KEY_CHAP_CHALLENGE  "CHAP_C"

+#define ISCSI_KEY_CHAP_NAME       "CHAP_N"

+#define ISCSI_KEY_CHAP_RESPONSE   "CHAP_R"

+

+#define ISCSI_CHAP_ALGORITHM_MD5  5

+

+#define ISCSI_CHAP_AUTH_MAX_LEN   1024

+#define ISCSI_CHAP_RSP_LEN        16  // == MD5_HASHSIZE

+typedef enum {

+  ISCSI_CHAP_INITIAL,

+  ISCSI_CHAP_STEP_ONE,

+  ISCSI_CHAP_STEP_TWO,

+  ISCSI_CHAP_STEP_THREE,

+  ISCSI_CHAP_STEP_FOUR

+} ISCSI_CHAP_STEP;

+

+#pragma pack(1)

+

+typedef struct _ISCSI_CHAP_AUTH_CONFIG_NVDATA {

+  UINT8 CHAPType;

+  CHAR8 CHAPName[ISCSI_CHAP_NAME_MAX_LEN];

+  CHAR8 CHAPSecret[ISCSI_CHAP_SECRET_MAX_LEN];

+  CHAR8 ReverseCHAPName[ISCSI_CHAP_NAME_MAX_LEN];

+  CHAR8 ReverseCHAPSecret[ISCSI_CHAP_SECRET_MAX_LEN];

+} ISCSI_CHAP_AUTH_CONFIG_NVDATA;

+

+#pragma pack()

+

+//

+// ISCSI CHAP Authentication Data

+//

+typedef struct _ISCSI_CHAP_AUTH_DATA {

+  ISCSI_CHAP_AUTH_CONFIG_NVDATA AuthConfig;

+  UINT32                        InIdentifier;

+  UINT8                         InChallenge[ISCSI_CHAP_AUTH_MAX_LEN];

+  UINT32                        InChallengeLength;

+  //

+  // Calculated CHAP Response (CHAP_R) value

+  //

+  UINT8                         CHAPResponse[ISCSI_CHAP_RSP_LEN];

+

+  //

+  // Auth-data to be sent out for mutual authentication

+  //

+  UINT32                        OutIdentifier;

+  UINT8                         OutChallenge[ISCSI_CHAP_AUTH_MAX_LEN];

+  UINT32                        OutChallengeLength;

+} ISCSI_CHAP_AUTH_DATA;

+

+EFI_STATUS

+IScsiCHAPOnRspReceived (

+  IN ISCSI_CONNECTION  *Conn,

+  IN BOOLEAN           Transit

+  );

+

+EFI_STATUS

+IScsiCHAPToSendReq (

+  IN ISCSI_CONNECTION  *Conn,

+  IN NET_BUF           *Pdu

+  );

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiCommon.h b/MdeModulePkg/Universal/iScsi/IScsiCommon.h
new file mode 100644
index 0000000..2d0991c
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiCommon.h
@@ -0,0 +1,28 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiCommon.h

+

+Abstract:

+

+--*/

+

+#ifndef _ISCSI_COMMON_H_

+#define _ISCSI_COMMON_H_

+

+typedef struct _ISCSI_SESSION             ISCSI_SESSION;

+typedef struct _ISCSI_CONNECTION          ISCSI_CONNECTION;

+typedef struct _ISCSI_DRIVER_DATA         ISCSI_DRIVER_DATA;

+typedef struct _ISCSI_SESSION_CONFIG_DATA ISCSI_SESSION_CONFIG_DATA;

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiConfig.c b/MdeModulePkg/Universal/iScsi/IScsiConfig.c
new file mode 100644
index 0000000..a8ef2c7
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiConfig.c
@@ -0,0 +1,1074 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiConfig.c

+

+Abstract:

+

+--*/

+

+#include "IScsiImpl.h"

+

+EFI_GUID        mVendorGuid             = ISCSI_CONFIG_GUID;

+BOOLEAN         mIScsiDeviceListUpdated = FALSE;

+UINTN           mNumberOfIScsiDevices   = 0;

+

+NET_LIST_ENTRY  mIScsiConfigFormList = {

+  &mIScsiConfigFormList,

+  &mIScsiConfigFormList

+};

+

+STATIC

+VOID

+IScsiIpToStr (

+  IN  EFI_IPv4_ADDRESS  *Ip,

+  OUT CHAR16            *Str

+  )

+/*++

+

+Routine Description:

+

+  Convert the IPv4 address into a dotted string.

+

+Arguments:

+

+  Ip  - The IPv4 address.

+  Str - The dotted IP string.

+

+Returns:

+

+  None.

+

+--*/

+{

+  UnicodeSPrint ( Str, 2 * IP4_STR_MAX_SIZE, L"%d.%d.%d.%d", Ip->Addr[0], Ip->Addr[1], Ip->Addr[2], Ip->Addr[3]);

+}

+

+VOID

+PopUpInvalidNotify (

+  IN CHAR16 *Warning

+  )

+/*++

+

+Routine Description:

+

+  Pop up an invalid notify which displays the message in Warning.

+

+Arguments:

+

+  Warning - The warning message.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_FORM_BROWSER_PROTOCOL *FormBrowser;

+  EFI_STATUS                Status;

+  EFI_INPUT_KEY             Key;

+  CHAR16                    Buffer[10];

+

+  Status = gBS->LocateProtocol (

+                  &gEfiFormBrowserProtocolGuid,

+                  NULL,

+                  (VOID **)&FormBrowser

+                  );

+  if (EFI_ERROR (Status)) {

+    return ;

+  }

+

+  FormBrowser->CreatePopUp (1, TRUE, 10, Buffer, &Key, Warning);

+}

+

+EFI_STATUS

+IScsiUpdateDeviceList (

+  VOID

+  )

+/*++

+

+Routine Description:

+

+  Update the list of iSCSI devices the iSCSI driver is controlling.

+

+Arguments:

+

+  None.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_STATUS                  Status;

+  ISCSI_DEVICE_LIST           *DeviceList;

+  UINTN                       DataSize;

+  UINTN                       NumHandles;

+  EFI_HANDLE                  *Handles;

+  UINTN                       HandleIndex;

+  UINTN                       Index;

+  UINTN                       LastDeviceIndex;

+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;

+  EFI_SIMPLE_NETWORK_MODE     *Mode;

+  ISCSI_MAC_INFO              *CurMacInfo;

+  ISCSI_MAC_INFO              TempMacInfo;

+  CHAR16                      MacString[65];

+  UINTN                       DeviceListSize;

+

+  //

+  // Dump all the handles the Simple Network Protocol is installed on.

+  //

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &gEfiSimpleNetworkProtocolGuid,

+                  NULL,

+                  &NumHandles,

+                  &Handles

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  DataSize = 0;

+  Status = gRT->GetVariable (

+                  L"iSCSIDeviceList",

+                  &mVendorGuid,

+                  NULL,

+                  &DataSize,

+                  NULL

+                  );

+  if (Status == EFI_BUFFER_TOO_SMALL) {

+    DeviceList = (ISCSI_DEVICE_LIST *) NetAllocatePool (DataSize);

+

+    gRT->GetVariable (

+          L"iSCSIDeviceList",

+          &mVendorGuid,

+          NULL,

+          &DataSize,

+          DeviceList

+          );

+

+    LastDeviceIndex = 0;

+

+    for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) {

+      gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp);

+

+      Mode = Snp->Mode;

+

+      for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {

+        CurMacInfo = &DeviceList->MacInfo[Index];

+        if ((CurMacInfo->Len == Mode->HwAddressSize) &&

+            (NET_MAC_EQUAL (&CurMacInfo->Mac, &Mode->PermanentAddress, Mode->HwAddressSize))

+            ) {

+          //

+          // The previous configured NIC is still here.

+          //

+          if (Index != LastDeviceIndex) {

+            //

+            // Swap the current MAC address entry with the one indexed by

+            // LastDeviceIndex.

+            //

+            NetCopyMem (&TempMacInfo, CurMacInfo, sizeof (ISCSI_MAC_INFO));

+            NetCopyMem (CurMacInfo, &DeviceList->MacInfo[LastDeviceIndex], sizeof (ISCSI_MAC_INFO));

+            NetCopyMem (&DeviceList->MacInfo[LastDeviceIndex], &TempMacInfo, sizeof (ISCSI_MAC_INFO));

+          }

+

+          LastDeviceIndex++;

+        }

+      }

+

+      if (LastDeviceIndex == DeviceList->NumDevice) {

+        break;

+      }

+    }

+

+    for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {

+      //

+      // delete the variables

+      //

+      CurMacInfo = &DeviceList->MacInfo[Index];

+      IScsiMacAddrToStr (&CurMacInfo->Mac, CurMacInfo->Len, MacString);

+      gRT->SetVariable (MacString, &gEfiIScsiInitiatorNameProtocolGuid, 0, 0, NULL);

+      gRT->SetVariable (MacString, &mIScsiCHAPAuthInfoGuid, 0, 0, NULL);

+    }

+

+    NetFreePool (DeviceList);

+  } else if (Status != EFI_NOT_FOUND) {

+    NetFreePool (Handles);

+    return Status;

+  }

+  //

+  // Construct the new iSCSI device list.

+  //

+  DeviceListSize        = sizeof (ISCSI_DEVICE_LIST) + (NumHandles - 1) * sizeof (ISCSI_MAC_INFO);

+  DeviceList            = (ISCSI_DEVICE_LIST *) NetAllocatePool (DeviceListSize);

+  DeviceList->NumDevice = (UINT8) NumHandles;

+

+  for (Index = 0; Index < NumHandles; Index++) {

+    gBS->HandleProtocol (Handles[Index], &gEfiSimpleNetworkProtocolGuid, (VOID **)&Snp);

+    Mode        = Snp->Mode;

+

+    CurMacInfo  = &DeviceList->MacInfo[Index];

+    NetCopyMem (&CurMacInfo->Mac, &Mode->PermanentAddress, Mode->HwAddressSize);

+    CurMacInfo->Len = (UINT8) Mode->HwAddressSize;

+  }

+

+  gRT->SetVariable (

+        L"iSCSIDeviceList",

+        &mVendorGuid,

+        ISCSI_CONFIG_VAR_ATTR,

+        DeviceListSize,

+        DeviceList

+        );

+

+  NetFreePool (DeviceList);

+

+  return Status;

+}

+

+STATIC

+ISCSI_CONFIG_FORM_ENTRY *

+IScsiGetConfigFormEntryByIndex (

+  IN UINT32 Index

+  )

+/*++

+

+Routine Description:

+

+  Get the iSCSI configuration form entry by the index of the goto opcode actived.

+

+Arguments:

+

+  Index - The 0-based index of the goto opcode actived.

+

+Returns:

+

+  The iSCSI configuration form entry found.

+

+--*/

+{

+  UINT32                  CurrentIndex;

+  NET_LIST_ENTRY          *Entry;

+  ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry;

+

+  CurrentIndex    = 0;

+  ConfigFormEntry = NULL;

+

+  NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {

+    if (CurrentIndex == Index) {

+      ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);

+      break;

+    }

+

+    CurrentIndex++;

+  }

+

+  return ConfigFormEntry;

+}

+

+STATIC

+VOID

+IScsiConvertDeviceConfigDataToIfrNvData (

+  IN ISCSI_CONFIG_FORM_ENTRY  *ConfigFormEntry,

+  IN ISCSI_CONFIG_IFR_NVDATA  *IfrNvData

+  )

+/*++

+

+Routine Description:

+

+  Convert the iSCSI configuration data into the IFR data.

+

+Arguments:

+

+  ConfigFormEntry - The iSCSI configuration form entry.

+  IfrNvData       - The IFR nv data.

+

+Returns:

+

+  None.

+

+--*/

+{

+  ISCSI_SESSION_CONFIG_NVDATA   *SessionConfigData;

+  ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;

+

+  //

+  // Normal session configuration parameters.

+  //

+  SessionConfigData                 = &ConfigFormEntry->SessionConfigData;

+  IfrNvData->Enabled                = SessionConfigData->Enabled;

+

+  IfrNvData->InitiatorInfoFromDhcp  = SessionConfigData->InitiatorInfoFromDhcp;

+  IfrNvData->TargetInfoFromDhcp     = SessionConfigData->TargetInfoFromDhcp;

+  IfrNvData->TargetPort             = SessionConfigData->TargetPort;

+

+  IScsiIpToStr (&SessionConfigData->LocalIp, IfrNvData->LocalIp);

+  IScsiIpToStr (&SessionConfigData->SubnetMask, IfrNvData->SubnetMask);

+  IScsiIpToStr (&SessionConfigData->Gateway, IfrNvData->Gateway);

+  IScsiIpToStr (&SessionConfigData->TargetIp, IfrNvData->TargetIp);

+

+  IScsiAsciiStrToUnicodeStr (SessionConfigData->TargetName, IfrNvData->TargetName);

+

+  IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun);

+

+  //

+  // CHAP authentication parameters.

+  //

+  AuthConfigData      = &ConfigFormEntry->AuthConfigData;

+

+  IfrNvData->CHAPType = AuthConfigData->CHAPType;

+

+  IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPName, IfrNvData->CHAPName);

+  IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPSecret, IfrNvData->CHAPSecret);

+  IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPName, IfrNvData->ReverseCHAPName);

+  IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPSecret, IfrNvData->ReverseCHAPSecret);

+}

+

+EFI_STATUS

+EFIAPI

+IScsiFormNvRead (

+  IN     EFI_FORM_CALLBACK_PROTOCOL    * This,

+  IN     CHAR16                        *VariableName,

+  IN     EFI_GUID                      * VendorGuid,

+  OUT    UINT32                        *Attributes OPTIONAL,

+  IN OUT UINTN                         *DataSize,

+  OUT    VOID                          *Buffer

+  )

+/*++

+

+Routine Description:

+

+  NV read function for the iSCSI form callback protocol.

+

+Arguments:

+

+  This         - The EFI form callback protocol instance.

+  VariableName - Name of the variable to read.

+  VendorGuid   - Guid of the variable to read.

+  Attributes   - The storage to get the attributes of the variable.

+  DataSize     - The size of the buffer to store the variable.

+  Buffer       - The buffer to store the variable to read.

+

+Returns:

+

+  EFI_SUCCESS          - The variable is read.

+  EFI_BUFFER_TOO_SMALL - The buffer provided is too small to hold the variable.

+

+--*/

+{

+  EFI_STATUS              Status;

+  CHAR8                   InitiatorName[ISCSI_NAME_IFR_MAX_SIZE];

+  UINTN                   BufferSize;

+  ISCSI_CONFIG_IFR_NVDATA *IfrNvData;

+

+  if (!mIScsiDeviceListUpdated) {

+    //

+    // Update the device list.

+    //

+    IScsiUpdateDeviceList ();

+    mIScsiDeviceListUpdated = TRUE;

+  }

+

+  IfrNvData   = (ISCSI_CONFIG_IFR_NVDATA *) Buffer;

+  BufferSize  = ISCSI_NAME_IFR_MAX_SIZE;

+

+  Status      = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);

+  if (EFI_ERROR (Status)) {

+    IfrNvData->InitiatorName[0] = L'\0';

+  } else {

+    IScsiAsciiStrToUnicodeStr (InitiatorName, IfrNvData->InitiatorName);

+  }

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiFormCallback (

+  IN EFI_FORM_CALLBACK_PROTOCOL       *This,

+  IN UINT16                           KeyValue,

+  IN EFI_IFR_DATA_ARRAY               *Data,

+  OUT EFI_HII_CALLBACK_PACKET         **Packet

+  )

+/*++

+

+Routine Description:

+

+  The form callback function for iSCSI form callback protocol, it processes

+  the events tiggered in the UI and take some operations to update the form,

+  store the data, etc.

+

+Arguments:

+

+  This     - The EFI form callback protocol instance.

+  KeyValue - A unique value which is sent to the original exporting driver so that it

+             can identify the type of data to expect.  The format of the data tends to

+             vary based on the op-code that geerated the callback.

+  Data     - A pointer to the data being sent to the original exporting driver.

+

+Returns:

+

+  EFI_SUCCESS           - The data is valid and the correspondance operation is done.

+  EFI_INVALID_PARAMETER - The data is invalid.

+

+--*/

+{

+  ISCSI_FORM_CALLBACK_INFO  *Private;

+  UINTN                     BufferSize;

+  CHAR8                     IScsiName[ISCSI_NAME_IFR_MAX_SIZE];

+  CHAR16                    PortString[128];

+  CHAR8                     Ip4String[IP4_STR_MAX_SIZE];

+  CHAR8                     LunString[ISCSI_LUN_STR_MAX_LEN];

+  UINT64                    Lun;

+  STRING_REF                DeviceFormTitleToken;

+  ISCSI_CONFIG_IFR_NVDATA   *IfrNvData;

+  ISCSI_CONFIG_FORM_ENTRY   *ConfigFormEntry;

+  EFI_IP_ADDRESS            HostIp;

+  EFI_IP_ADDRESS            SubnetMask;

+  EFI_IP_ADDRESS            Gateway;

+  EFI_STATUS                Status;

+

+  Private   = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);

+  IfrNvData = (ISCSI_CONFIG_IFR_NVDATA *) Data->NvRamMap;

+  Status    = EFI_SUCCESS;

+

+  switch (KeyValue) {

+  case KEY_INITIATOR_NAME:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->InitiatorName, IScsiName);

+    BufferSize  = AsciiStrLen (IScsiName) + 1;

+

+    Status      = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName);

+    if (EFI_ERROR (Status)) {

+      PopUpInvalidNotify (L"Invalid iSCSI Name!");

+    }

+

+    break;

+

+  case KEY_LOCAL_IP:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->LocalIp, Ip4String);

+    Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);

+    if (EFI_ERROR (Status) || !Ip4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {

+      PopUpInvalidNotify (L"Invalid IP address!");

+      Status = EFI_INVALID_PARAMETER;

+    } else {

+      NetCopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));

+    }

+

+    break;

+

+  case KEY_SUBNET_MASK:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->SubnetMask, Ip4String);

+    Status = IScsiAsciiStrToIp (Ip4String, &SubnetMask.v4);

+    if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {

+      PopUpInvalidNotify (L"Invalid Subnet Mask!");

+      Status = EFI_INVALID_PARAMETER;

+    } else {

+      NetCopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));

+    }

+

+    break;

+

+  case KEY_GATE_WAY:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->Gateway, Ip4String);

+    Status = IScsiAsciiStrToIp (Ip4String, &Gateway.v4);

+    if (EFI_ERROR (Status) || ((Gateway.Addr[0] != 0) && !Ip4IsUnicast (NTOHL (Gateway.Addr[0]), 0))) {

+      PopUpInvalidNotify (L"Invalid Gateway!");

+      Status = EFI_INVALID_PARAMETER;

+    } else {

+      NetCopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));

+    }

+

+    break;

+

+  case KEY_TARGET_IP:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->TargetIp, Ip4String);

+    Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);

+    if (EFI_ERROR (Status) || !Ip4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {

+      PopUpInvalidNotify (L"Invalid IP address!");

+      Status = EFI_INVALID_PARAMETER;

+    } else {

+      NetCopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp.v4, sizeof (HostIp.v4));

+    }

+

+    break;

+

+  case KEY_TARGET_NAME:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->TargetName, IScsiName);

+    Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName));

+    if (EFI_ERROR (Status)) {

+      PopUpInvalidNotify (L"Invalid iSCSI Name!");

+    } else {

+      AsciiStrCpy (Private->Current->SessionConfigData.TargetName, IScsiName);

+    }

+

+    break;

+

+  case KEY_DHCP_ENABLE:

+    if (IfrNvData->InitiatorInfoFromDhcp == 0) {

+      IfrNvData->TargetInfoFromDhcp = 0;

+    }

+

+    break;

+

+  case KEY_BOOT_LUN:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->BootLun, LunString);

+    Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);

+    if (EFI_ERROR (Status)) {

+      PopUpInvalidNotify (L"Invalid LUN string!");

+    } else {

+      NetCopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun));

+    }

+

+    break;

+

+  case KEY_CHAP_NAME:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPName, Private->Current->AuthConfigData.CHAPName);

+    break;

+

+  case KEY_CHAP_SECRET:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPSecret, Private->Current->AuthConfigData.CHAPSecret);

+    break;

+

+  case KEY_REVERSE_CHAP_NAME:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPName, Private->Current->AuthConfigData.ReverseCHAPName);

+    break;

+

+  case KEY_REVERSE_CHAP_SECRET:

+    IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPSecret, Private->Current->AuthConfigData.ReverseCHAPSecret);

+    break;

+

+  case KEY_SAVE_CHANGES:

+    //

+    // First, update those fields which don't have INTERACTIVE set.

+    //

+    Private->Current->SessionConfigData.Enabled               = IfrNvData->Enabled;

+    Private->Current->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;

+    Private->Current->SessionConfigData.TargetPort            = IfrNvData->TargetPort;

+    if (Private->Current->SessionConfigData.TargetPort == 0) {

+      Private->Current->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;

+    }

+

+    Private->Current->SessionConfigData.TargetInfoFromDhcp  = IfrNvData->TargetInfoFromDhcp;

+    Private->Current->AuthConfigData.CHAPType               = IfrNvData->CHAPType;

+

+    //

+    // Only do full parameter validation if iSCSI is enabled on this device.

+    //

+    if (Private->Current->SessionConfigData.Enabled) {

+      //

+      // Validate the address configuration of the Initiator if DHCP isn't

+      // deployed.

+      //

+      if (!Private->Current->SessionConfigData.InitiatorInfoFromDhcp) {

+        NetCopyMem (&HostIp.v4, &Private->Current->SessionConfigData.LocalIp, sizeof (HostIp.v4));

+        NetCopyMem (&SubnetMask.v4, &Private->Current->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));

+        NetCopyMem (&Gateway.v4, &Private->Current->SessionConfigData.Gateway, sizeof (Gateway.v4));

+

+        if ((Gateway.Addr[0] != 0)) {

+          if (SubnetMask.Addr[0] == 0) {

+            PopUpInvalidNotify (L"Gateway address is set but subnet mask is zero.");

+            Status = EFI_INVALID_PARAMETER;

+            break;

+          } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) {

+            PopUpInvalidNotify (L"Local IP and Gateway are not in the same subnet.");

+            Status = EFI_INVALID_PARAMETER;

+            break;

+          }

+        }

+      }

+      //

+      // Validate target configuration if DHCP isn't deployed.

+      //

+      if (!Private->Current->SessionConfigData.TargetInfoFromDhcp) {

+        NetCopyMem (&HostIp.v4, &Private->Current->SessionConfigData.TargetIp, sizeof (HostIp.v4));

+        if (!Ip4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {

+          PopUpInvalidNotify (L"Target IP is invalid!");

+          Status = EFI_INVALID_PARAMETER;

+          break;

+        }

+      }

+

+      if (IfrNvData->CHAPType != ISCSI_CHAP_NONE) {

+        if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) {

+          PopUpInvalidNotify (L"CHAP Name or CHAP Secret is invalid!");

+          Status = EFI_INVALID_PARAMETER;

+          break;

+        }

+

+        if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) &&

+            ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0'))

+            ) {

+          PopUpInvalidNotify (L"Reverse CHAP Name or Reverse CHAP Secret is invalid!");

+          Status = EFI_INVALID_PARAMETER;

+          break;

+        }

+      }

+    }

+

+    BufferSize = sizeof (Private->Current->SessionConfigData);

+    gRT->SetVariable (

+          Private->Current->MacString,

+          &gEfiIScsiInitiatorNameProtocolGuid,

+          ISCSI_CONFIG_VAR_ATTR,

+          BufferSize,

+          &Private->Current->SessionConfigData

+          );

+

+    BufferSize = sizeof (Private->Current->AuthConfigData);

+    gRT->SetVariable (

+          Private->Current->MacString,

+          &mIScsiCHAPAuthInfoGuid,

+          ISCSI_CONFIG_VAR_ATTR,

+          BufferSize,

+          &Private->Current->AuthConfigData

+          );

+

+    break;

+

+  default:

+    if ((KeyValue >= KEY_DEVICE_ENTRY_BASE) && (KeyValue < (mNumberOfIScsiDevices + KEY_DEVICE_ENTRY_BASE))) {

+      //

+      // In case goto the device configuration form, update the device form title.

+      //

+      ConfigFormEntry = IScsiGetConfigFormEntryByIndex ((UINT32) (KeyValue - KEY_DEVICE_ENTRY_BASE));

+      ASSERT (ConfigFormEntry != NULL);

+

+      UnicodeSPrint (PortString, 128, L"Port %s", ConfigFormEntry->MacString);

+      DeviceFormTitleToken = (STRING_REF) STR_ISCSI_DEVICE_FORM_TITLE;

+

+      Private->Hii->NewString (

+                      Private->Hii,

+                      NULL,

+                      Private->RegisteredHandle,

+                      &DeviceFormTitleToken,

+                      PortString

+                      );

+

+      IScsiConvertDeviceConfigDataToIfrNvData (ConfigFormEntry, IfrNvData);

+

+      Private->Current = ConfigFormEntry;

+    }

+

+    break;

+  }

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiConfigUpdateForm (

+  IN EFI_HANDLE  DriverBindingHandle,

+  IN EFI_HANDLE  Controller,

+  IN BOOLEAN     AddForm

+  )

+/*++

+

+Routine Description:

+

+  Updates the iSCSI configuration form to add/delete an entry for the iSCSI

+  device specified by the Controller.

+

+Arguments:

+

+  DriverBindingHandle - The driverbinding handle.

+  Controller          - The controller handle of the iSCSI device.

+  AddForm             - Whether to add or delete a form entry.

+

+Returns:

+

+  EFI_SUCCESS          - The iSCSI configuration form is updated.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+

+--*/

+{

+  NET_LIST_ENTRY              *Entry;

+  ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;

+  BOOLEAN                     EntryExisted;

+  EFI_HII_UPDATE_DATA         *UpdateData;

+  EFI_STATUS                  Status;

+  EFI_FORM_CALLBACK_PROTOCOL  *Callback;

+  ISCSI_FORM_CALLBACK_INFO    *CallbackInfo;

+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;

+  CHAR16                      PortString[128];

+  UINT16                      FormIndex;

+  UINTN                       BufferSize;

+

+  //

+  // Get the EFI_FORM_CALLBACK_PROTOCOL.

+  //

+  Status = gBS->HandleProtocol (

+                  DriverBindingHandle,

+                  &gEfiFormCallbackProtocolGuid,

+                  (VOID **)&Callback

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  CallbackInfo    = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (Callback);

+

+  ConfigFormEntry = NULL;

+  EntryExisted    = FALSE;

+

+  NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {

+    ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);

+

+    if (ConfigFormEntry->Controller == Controller) {

+      EntryExisted = TRUE;

+      break;

+    }

+  }

+

+  if (AddForm) {

+    if (EntryExisted) {

+      return EFI_SUCCESS;

+    } else {

+      //

+      // Add a new form.

+      //

+      ConfigFormEntry = (ISCSI_CONFIG_FORM_ENTRY *) NetAllocateZeroPool (sizeof (ISCSI_CONFIG_FORM_ENTRY));

+      if (ConfigFormEntry == NULL) {

+        return EFI_OUT_OF_RESOURCES;

+      }

+

+      NetListInit (&ConfigFormEntry->Link);

+      ConfigFormEntry->Controller = Controller;

+

+      //

+      // Get the simple network protocol and convert the MAC address into

+      // the formatted string.

+      //

+      Status = gBS->HandleProtocol (

+                      Controller,

+                      &gEfiSimpleNetworkProtocolGuid,

+                      (VOID **)&Snp

+                      );

+      ASSERT (Status == EFI_SUCCESS);

+

+      IScsiMacAddrToStr (&Snp->Mode->PermanentAddress, Snp->Mode->HwAddressSize, ConfigFormEntry->MacString);

+

+      //

+      // Get the normal session configuration data.

+      //

+      BufferSize = sizeof (ConfigFormEntry->SessionConfigData);

+      Status = gRT->GetVariable (

+                      ConfigFormEntry->MacString,

+                      &gEfiIScsiInitiatorNameProtocolGuid,

+                      NULL,

+                      &BufferSize,

+                      &ConfigFormEntry->SessionConfigData

+                      );

+      if (EFI_ERROR (Status)) {

+        NetZeroMem (&ConfigFormEntry->SessionConfigData, sizeof (ConfigFormEntry->SessionConfigData));

+      }

+      //

+      // Get the CHAP authentication configuration data.

+      //

+      BufferSize = sizeof (ConfigFormEntry->AuthConfigData);

+      Status = gRT->GetVariable (

+                      ConfigFormEntry->MacString,

+                      &mIScsiCHAPAuthInfoGuid,

+                      NULL,

+                      &BufferSize,

+                      &ConfigFormEntry->AuthConfigData

+                      );

+      if (EFI_ERROR (Status)) {

+        NetZeroMem (&ConfigFormEntry->AuthConfigData, sizeof (ConfigFormEntry->AuthConfigData));

+      }

+      //

+      // Compose the Port string and create a new STRING_REF.

+      //

+      UnicodeSPrint (PortString, 128, L"Port %s", ConfigFormEntry->MacString);

+      CallbackInfo->Hii->NewString (

+                          CallbackInfo->Hii,

+                          NULL,

+                          CallbackInfo->RegisteredHandle,

+                          &ConfigFormEntry->PortTitleToken,

+                          PortString

+                          );

+

+      //

+      // Compose the help string of this port and create a new STRING_REF.

+      //

+      UnicodeSPrint (PortString, 128, L"Set the iSCSI parameters on port %s", ConfigFormEntry->MacString);

+      CallbackInfo->Hii->NewString (

+                          CallbackInfo->Hii,

+                          NULL,

+                          CallbackInfo->RegisteredHandle,

+                          &ConfigFormEntry->PortTitleHelpToken,

+                          PortString

+                          );

+

+      NetListInsertTail (&mIScsiConfigFormList, &ConfigFormEntry->Link);

+      mNumberOfIScsiDevices++;

+    }

+  } else {

+    ASSERT (EntryExisted);

+

+    mNumberOfIScsiDevices--;

+    NetListRemoveEntry (&ConfigFormEntry->Link);

+    NetFreePool (ConfigFormEntry);

+  }

+  //

+  // Allocate space for creation of Buffer

+  //

+  UpdateData = (EFI_HII_UPDATE_DATA *) NetAllocatePool (0x1000);

+  NetZeroMem (UpdateData, 0x1000);

+

+  //

+  // Flag update pending in FormSet

+  //

+  UpdateData->FormSetUpdate = TRUE;

+

+  //

+  // Register CallbackHandle data for FormSet

+  //

+  UpdateData->FormCallbackHandle  = (EFI_PHYSICAL_ADDRESS) (UINTN) CallbackInfo->CallbackHandle;

+  UpdateData->FormUpdate          = FALSE;

+  UpdateData->FormTitle           = 0;

+

+  //

+  // first of all, remove all the forms.

+  //

+  UpdateData->DataCount = 0xFF;

+

+  CallbackInfo->Hii->UpdateForm (

+                      CallbackInfo->Hii,

+                      CallbackInfo->RegisteredHandle,

+                      (EFI_FORM_LABEL) DEVICE_ENTRY_LABEL,

+                      FALSE,

+                      UpdateData

+                      );

+

+  UpdateData->DataCount = 1;

+  FormIndex             = 0;

+

+  NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {

+    ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);

+

+    CreateGotoOpCode (

+      FORMID_DEVICE_FORM,

+      ConfigFormEntry->PortTitleToken,

+      ConfigFormEntry->PortTitleHelpToken,

+      EFI_IFR_FLAG_INTERACTIVE,

+      (UINT16) (KEY_DEVICE_ENTRY_BASE + FormIndex),

+      &UpdateData->Data

+      );

+

+    CallbackInfo->Hii->UpdateForm (

+                        CallbackInfo->Hii,

+                        CallbackInfo->RegisteredHandle,

+                        (EFI_FORM_LABEL) DEVICE_ENTRY_LABEL,

+                        TRUE,

+                        UpdateData

+                        );

+

+    FormIndex++;

+  }

+

+  NetFreePool (UpdateData);

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+IScsiConfigFormInit (

+  IN EFI_HANDLE  DriverBindingHandle

+  )

+/*++

+

+Routine Description:

+

+  Initialize the iSCSI configuration form.

+

+Arguments:

+

+  DriverBindingHandle - The iSCSI driverbinding handle.

+

+Returns:

+

+  EFI_SUCCESS          - The iSCSI configuration form is initialized.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+

+--*/

+{

+  EFI_STATUS                Status;

+  EFI_HII_PROTOCOL          *Hii;

+  EFI_HII_PACKAGES          *PackageList;

+  EFI_HII_HANDLE            HiiHandle;

+  EFI_HII_UPDATE_DATA       *UpdateData;

+  ISCSI_FORM_CALLBACK_INFO  *CallbackInfo;

+  EFI_GUID                  StringPackGuid = ISCSI_CONFIG_GUID;

+

+  Status = gBS->LocateProtocol (&gEfiHiiProtocolGuid, NULL, (VOID **)&Hii);

+  if (EFI_ERROR (Status)) {

+    return Status;;

+  }

+

+  CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) NetAllocatePool (sizeof (ISCSI_FORM_CALLBACK_INFO));

+  if (CallbackInfo == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  CallbackInfo->Signature             = ISCSI_FORM_CALLBACK_INFO_SIGNATURE;

+  CallbackInfo->Hii                   = Hii;

+  CallbackInfo->Current               = NULL;

+

+  CallbackInfo->FormCallback.NvRead   = IScsiFormNvRead;

+  CallbackInfo->FormCallback.NvWrite  = NULL;

+  CallbackInfo->FormCallback.Callback = IScsiFormCallback;

+

+  //

+  // Install protocol interface

+  //

+  Status = gBS->InstallProtocolInterface (

+                  &DriverBindingHandle,

+                  &gEfiFormCallbackProtocolGuid,

+                  EFI_NATIVE_INTERFACE,

+                  &CallbackInfo->FormCallback

+                  );

+

+  ASSERT_EFI_ERROR (Status);

+

+  CallbackInfo->CallbackHandle  = DriverBindingHandle;

+  PackageList                   = PreparePackages (2, &StringPackGuid, iSCSIStrings, IScsiConfigDxeBin);

+  Status                        = Hii->NewPack (Hii, PackageList, &HiiHandle);

+  NetFreePool (PackageList);

+

+  CallbackInfo->RegisteredHandle = HiiHandle;

+

+  //

+  // Allocate space for creation of Buffer

+  //

+  UpdateData = (EFI_HII_UPDATE_DATA *) NetAllocatePool (0x1000);

+  ASSERT (UpdateData != NULL);

+  if (UpdateData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  NetZeroMem (UpdateData, 0x1000);

+

+  //

+  // Flag update pending in FormSet

+  //

+  UpdateData->FormSetUpdate = TRUE;

+

+  //

+  // Register CallbackHandle data for FormSet

+  //

+  UpdateData->FormCallbackHandle  = (EFI_PHYSICAL_ADDRESS) (UINTN) CallbackInfo->CallbackHandle;

+  UpdateData->FormUpdate          = FALSE;

+  UpdateData->FormTitle           = 0;

+  UpdateData->DataCount           = 0x1;

+

+  Hii->UpdateForm (Hii, HiiHandle, (EFI_FORM_LABEL) 0x1000, TRUE, UpdateData);

+

+  NetFreePool (UpdateData);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiConfigFormUnload (

+  IN EFI_HANDLE  DriverBindingHandle

+  )

+/*++

+

+Routine Description:

+

+  Unload the iSCSI configuration form, this includes: delete all the iSCSI

+  device configuration entries, uninstall the form callback protocol and

+  free the resources used.

+

+Arguments:

+

+  DriverBindingHandle - The iSCSI driverbinding handle.

+

+Returns:

+

+  EFI_SUCCESS          - The iSCSI configuration form is unloaded.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+

+--*/

+{

+  ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;

+  EFI_STATUS                  Status;

+  EFI_HII_PROTOCOL            *Hii;

+  EFI_HII_UPDATE_DATA         *UpdateData;

+  EFI_FORM_CALLBACK_PROTOCOL  *FormCallback;

+  ISCSI_FORM_CALLBACK_INFO    *CallbackInfo;

+

+  while (!NetListIsEmpty (&mIScsiConfigFormList)) {

+    //

+    // Uninstall the device forms as the iSCSI driver instance may fail to

+    // control the controller but still install the device configuration form.

+    // In such case, upon driver unloading, the driver instance's driverbinding.

+    // stop () won't be called, so we have to take this chance here to uninstall

+    // the device form.

+    //

+    ConfigFormEntry = NET_LIST_USER_STRUCT (mIScsiConfigFormList.ForwardLink, ISCSI_CONFIG_FORM_ENTRY, Link);

+    IScsiConfigUpdateForm (DriverBindingHandle, ConfigFormEntry->Controller, FALSE);

+  }

+

+  Status = gBS->LocateProtocol (&gEfiHiiProtocolGuid, NULL, (VOID **)&Hii);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->HandleProtocol (DriverBindingHandle, &gEfiFormCallbackProtocolGuid, (VOID **)&FormCallback);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  CallbackInfo = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (FormCallback);

+

+  //

+  // remove the form.

+  //

+  UpdateData = (EFI_HII_UPDATE_DATA *) NetAllocatePool (0x1000);

+  ASSERT (UpdateData != NULL);

+  if (UpdateData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  NetZeroMem (UpdateData, 0x1000);

+

+  UpdateData->FormSetUpdate       = FALSE;

+  UpdateData->FormCallbackHandle  = 0;

+  UpdateData->FormUpdate          = FALSE;

+  UpdateData->FormTitle           = 0;

+  UpdateData->DataCount           = 0xFF;

+

+  Hii->UpdateForm (Hii, CallbackInfo->RegisteredHandle, (EFI_FORM_LABEL) 0x1000, FALSE, UpdateData);

+

+  NetFreePool (UpdateData);

+

+  //

+  // Uninstall the EFI_FORM_CALLBACK_PROTOCOL.

+  //

+  gBS->UninstallProtocolInterface (

+        DriverBindingHandle,

+        &gEfiFormCallbackProtocolGuid,

+        FormCallback

+        );

+

+  //

+  // Remove the package.

+  //

+  Hii->RemovePack (Hii, CallbackInfo->RegisteredHandle);

+

+  NetFreePool (CallbackInfo);

+

+  return EFI_SUCCESS;

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsiConfig.h b/MdeModulePkg/Universal/iScsi/IScsiConfig.h
new file mode 100644
index 0000000..e5c528e
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiConfig.h
@@ -0,0 +1,111 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiConfig.h

+

+Abstract:

+

+

+--*/

+

+#ifndef _ISCSI_CONFIG_H_

+#define _ISCSI_CONFIG_H_

+

+//#include "Tiano.h"

+//#include "EfiDriverLib.h"

+//#include "Base.h"

+#include <Library/FrameworkHiiLib.h>

+#include <Protocol/FrameworkFormBrowser.h>

+#include <Protocol/FrameworkFormCallback.h>

+#include <Library/FrameworkIfrSupportLib.h>

+#include <Library/DebugLib.h>

+#include <Library/BaseLib.h>

+//#include "EfiPrintLib.h"

+//#include EFI_PROTOCOL_DEFINITION (Hii)

+//#include EFI_PROTOCOL_DEFINITION (FormBrowser)

+//#include EFI_PROTOCOL_DEFINITION (FormCallback)

+

+#include <Library/NetLib.h>

+#include "IScsiConfigNVDataStruc.h"

+

+extern UINT8  IScsiConfigDxeBin[];

+extern UINT8  iSCSIStrings[];

+

+#define ISCSI_INITATOR_NAME_VAR_NAME        L"I_NAME"

+

+#define ISCSI_CONFIG_VAR_ATTR               (EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE)

+

+#define ISCSI_FORM_CALLBACK_INFO_SIGNATURE  EFI_SIGNATURE_32 ('I', 'f', 'c', 'i')

+

+#define ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK(Callback) \

+  CR ( \

+  Callback, \

+  ISCSI_FORM_CALLBACK_INFO, \

+  FormCallback, \

+  ISCSI_FORM_CALLBACK_INFO_SIGNATURE \

+  )

+

+#pragma pack(1)

+

+typedef struct _ISCSI_MAC_INFO {

+  EFI_MAC_ADDRESS Mac;

+  UINT8           Len;

+} ISCSI_MAC_INFO;

+

+typedef struct _ISCSI_DEVICE_LIST {

+  UINT8           NumDevice;

+  ISCSI_MAC_INFO  MacInfo[1];

+} ISCSI_DEVICE_LIST;

+

+#pragma pack()

+

+typedef struct _ISCSI_CONFIG_FORM_ENTRY {

+  NET_LIST_ENTRY                Link;

+  EFI_HANDLE                    Controller;

+  CHAR16                        MacString[95];

+  STRING_REF                    PortTitleToken;

+  STRING_REF                    PortTitleHelpToken;

+

+  ISCSI_SESSION_CONFIG_NVDATA   SessionConfigData;

+  ISCSI_CHAP_AUTH_CONFIG_NVDATA AuthConfigData;

+} ISCSI_CONFIG_FORM_ENTRY;

+

+typedef struct _ISCSI_FORM_CALLBACK_INFO {

+  UINTN                       Signature;

+  EFI_HANDLE                  CallbackHandle;

+  EFI_FORM_CALLBACK_PROTOCOL  FormCallback;

+  UINT16                      *KeyList;

+  VOID                        *FormBuffer;

+  EFI_HII_HANDLE              RegisteredHandle;

+  EFI_HII_PROTOCOL            *Hii;

+  ISCSI_CONFIG_FORM_ENTRY     *Current;

+} ISCSI_FORM_CALLBACK_INFO;

+

+EFI_STATUS

+IScsiConfigUpdateForm (

+  IN EFI_HANDLE  DriverBindingHandle,

+  IN EFI_HANDLE  Controller,

+  IN BOOLEAN     AddForm

+  );

+

+EFI_STATUS

+IScsiConfigFormInit (

+  IN EFI_HANDLE  DriverBindingHandle

+  );

+

+EFI_STATUS

+IScsiConfigFormUnload (

+  IN EFI_HANDLE  DriverBindingHandle

+  );

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiConfigDxe.vfr b/MdeModulePkg/Universal/iScsi/IScsiConfigDxe.vfr
new file mode 100644
index 0000000..3df9b18
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiConfigDxe.vfr
@@ -0,0 +1,212 @@
+// *++

+//

+//Copyright (c)  2007 Intel Corporation. All rights reserved

+//This software and associated documentation (if any) is furnished

+//under a license and may only be used or copied in accordance

+//with the terms of the license. Except as permitted by such

+//license, no part of this software or documentation may be

+//reproduced, stored in a retrieval system, or transmitted in any

+//form or by any means without the express written consent of

+//Intel Corporation.

+//

+// Module Name:

+//

+//   IScsiConfigVfr.vfr 

+// 

+// Abstract:

+// 

+// Revision History: 

+// 

+// --*/

+

+#include "IScsiConfigNVDataStruc.h"

+#define EFI_NETWORK_DEVICE_CLASS  0x04

+

+formset 

+  guid     = ISCSI_CONFIG_GUID,

+  title    = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_TITLE),

+  help     = STRING_TOKEN(STR_ISCSI_CONFIG_FORM_HELP),

+  class    = EFI_NETWORK_DEVICE_CLASS,

+  subclass = 0x03,

+

+  form formid = FORMID_MAIN_FORM,

+    title  = STRING_TOKEN(STR_ISCSI_MAIN_FORM_TITLE);  // note formid is a variable (for readability) (UINT16) - also added Form to the line to signify the Op-Code

+

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.InitiatorName, 

+            prompt  = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME),

+            help    = STRING_TOKEN(STR_ISCSI_CONFIG_INIT_NAME_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_INITIATOR_NAME,

+            minsize = 8,

+            maxsize = ISCSI_NAME_IFR_MAX_SIZE,

+    endstring;

+

+    label DEVICE_ENTRY_LABEL;

+

+  endform;

+

+  form formid = FORMID_DEVICE_FORM,

+    title  = STRING_TOKEN(STR_ISCSI_DEVICE_FORM_TITLE);

+

+    checkbox varid = ISCSI_CONFIG_IFR_NVDATA.Enabled,

+            prompt = STRING_TOKEN(STR_ISCSI_DEVICE_ENABLE),

+            help   = STRING_TOKEN(STR_NULL),

+            flags  = 0,

+    endcheckbox;

+

+    checkbox varid = ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp,

+            prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),

+            help   = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP),

+            flags  = INTERACTIVE,

+            key    = KEY_DHCP_ENABLE,

+    endcheckbox;

+

+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x01; 

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.LocalIp, 

+            prompt  = STRING_TOKEN(STR_ISCSI_LOCAL_IP_ADDRESS),

+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_LOCAL_IP,

+            minsize = IP_MIN_SIZE,

+            maxsize = IP_MAX_SIZE,

+    endstring;

+

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.SubnetMask, 

+            prompt  = STRING_TOKEN(STR_ISCSI_LOCAL_MASK),

+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_SUBNET_MASK,

+            minsize = IP_MIN_SIZE,

+            maxsize = IP_MAX_SIZE,

+    endstring;

+

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.Gateway, 

+            prompt  = STRING_TOKEN(STR_ISCSI_LOCAL_GATEWAY),

+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_GATE_WAY,

+            minsize = IP_MIN_SIZE,

+            maxsize = IP_MAX_SIZE,

+    endstring;

+    endif;

+

+    subtitle text = STRING_TOKEN(STR_NULL); 

+

+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.InitiatorInfoFromDhcp == 0x00; 

+    checkbox varid  = ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp,

+             prompt = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),

+             help   = STRING_TOKEN(STR_ISCSI_ENABLE_DHCP_ON_TARGET),

+             flags  = 0,

+    endcheckbox;

+    endif;

+      

+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.TargetInfoFromDhcp == 0x01; 

+

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.TargetName, 

+            prompt  = STRING_TOKEN(STR_ISCSI_TARGET_NAME),

+            help    = STRING_TOKEN(STR_ISCSI_TARGET_NAME),

+            flags   = INTERACTIVE,

+            key     = KEY_TARGET_NAME,

+            minsize = 8,

+            maxsize = ISCSI_NAME_IFR_MAX_SIZE,

+    endstring;

+

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.TargetIp, 

+            prompt  = STRING_TOKEN(STR_ISCSI_TARGET_IP_ADDRESS),

+            help    = STRING_TOKEN(STR_ISCSI_IP_ADDRESS_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_TARGET_IP,

+            minsize = IP_MIN_SIZE,

+            maxsize = IP_MAX_SIZE,

+    endstring;

+    

+    numeric varid   = ISCSI_CONFIG_IFR_NVDATA.TargetPort, 

+            prompt  = STRING_TOKEN(STR_ISCSI_TARGET_PORT),

+            help    = STRING_TOKEN(STR_ISCSI_TARGET_PORT),

+            flags   = 0,

+            minimum = TARGET_PORT_MIN_NUM,

+            maximum = TARGET_PORT_MAX_NUM,     

+            step    = 0,

+    endnumeric;

+    

+    string varid    = ISCSI_CONFIG_IFR_NVDATA.BootLun, 

+            prompt  = STRING_TOKEN(STR_ISCSI_BOOT_LUN),

+            help    = STRING_TOKEN(STR_ISCSI_BOOT_LUN_HELP),

+            flags   = INTERACTIVE,

+            key     = KEY_BOOT_LUN,

+            minsize = LUN_MIN_SIZE,

+            maxsize = LUN_MAX_SIZE,

+    endstring;

+    endif;

+

+    subtitle text = STRING_TOKEN(STR_NULL); 

+

+    oneof varid  = ISCSI_CONFIG_IFR_NVDATA.CHAPType,

+          prompt = STRING_TOKEN(STR_CHAP_TYPE_PROMPT),

+          help   = STRING_TOKEN(STR_CHAP_TYPE_HELP),  

+          option text = STRING_TOKEN(STR_CHAP_TYPE_NONE),   value = ISCSI_CHAP_NONE,   flags = DEFAULT;

+          option text = STRING_TOKEN(STR_CHAP_TYPE_UNI),    value = ISCSI_CHAP_UNI,    flags = 0;

+          option text = STRING_TOKEN(STR_CHAP_TYPE_MUTUAL), value = ISCSI_CHAP_MUTUAL, flags = 0;

+    endoneof;

+

+    suppressif ideqval ISCSI_CONFIG_IFR_NVDATA.CHAPType == ISCSI_CHAP_NONE; 

+

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.CHAPName, 

+            prompt  = STRING_TOKEN(STR_ISCSI_CHAP_NAME),

+            help    = STRING_TOKEN(STR_ISCSI_CHAP_NAME),

+            flags   = INTERACTIVE,

+            key     = KEY_CHAP_NAME,

+            minsize = 0,

+            maxsize = ISCSI_CHAP_NAME_MAX_LEN,

+    endstring;

+

+    string  varid    = ISCSI_CONFIG_IFR_NVDATA.CHAPSecret, 

+            prompt   = STRING_TOKEN(STR_ISCSI_CHAP_SECRET),

+            help     = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),

+            flags    = INTERACTIVE,

+            key      = KEY_CHAP_SECRET,

+            minsize  = ISCSI_CHAP_SECRET_MIN_LEN,

+            maxsize  = ISCSI_CHAP_SECRET_MAX_LEN,

+    endstring;

+

+    endif;

+

+    suppressif NOT ideqval ISCSI_CONFIG_IFR_NVDATA.CHAPType == ISCSI_CHAP_MUTUAL;

+

+    string  varid   = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPName, 

+            prompt  = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),

+            help    = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_NAME),

+            flags   = INTERACTIVE,

+            key     = KEY_REVERSE_CHAP_NAME,

+            minsize = 0,

+            maxsize = ISCSI_CHAP_NAME_MAX_LEN,

+    endstring;

+

+    string  varid    = ISCSI_CONFIG_IFR_NVDATA.ReverseCHAPSecret, 

+            prompt   = STRING_TOKEN(STR_ISCSI_REVERSE_CHAP_SECRET),

+            help     = STRING_TOKEN(STR_ISCSI_CHAP_SECRET_HELP),

+            flags    = INTERACTIVE,

+            key      = KEY_REVERSE_CHAP_SECRET,

+            minsize  = ISCSI_CHAP_SECRET_MIN_LEN,

+            maxsize  = ISCSI_CHAP_SECRET_MAX_LEN,

+    endstring;

+

+    endif;

+

+    subtitle text = STRING_TOKEN(STR_NULL); 

+

+    goto FORMID_DEVICE_FORM,  

+    prompt = STRING_TOKEN (STR_SAVE_CHANGES),

+    help   = STRING_TOKEN (STR_SAVE_CHANGES),

+    flags  = INTERACTIVE,

+    key    = KEY_SAVE_CHANGES;

+

+    goto FORMID_MAIN_FORM, 

+    prompt = STRING_TOKEN (STR_RETURN_MAIN_FORM), 

+    help   = STRING_TOKEN (STR_RETURN_MAIN_FORM),

+    flags  = 0;

+

+  endform;

+

+endformset;

+

diff --git a/MdeModulePkg/Universal/iScsi/IScsiConfigDxeStrings.uni b/MdeModulePkg/Universal/iScsi/IScsiConfigDxeStrings.uni
new file mode 100644
index 0000000..b0f0250
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiConfigDxeStrings.uni
Binary files differ
diff --git a/MdeModulePkg/Universal/iScsi/IScsiConfigNVDataStruc.h b/MdeModulePkg/Universal/iScsi/IScsiConfigNVDataStruc.h
new file mode 100644
index 0000000..444cb69
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiConfigNVDataStruc.h
@@ -0,0 +1,104 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiConfigNVDataStruc.h

+ 

+Abstract:

+ 

+  NVData structure used by the iSCSI configuration component.

+

+--*/

+

+#ifndef _ISCSI_NVDATASTRUC_H_

+#define _ISCSI_NVDATASTRUC_H_

+

+#define ISCSI_CONFIG_GUID \

+  { \

+    0x6456ed61, 0x3579, 0x41c9, { 0x8a, 0x26, 0x0a, 0x0b, 0xd6, 0x2b, 0x78, 0xfc } \

+  }

+

+#define VAR_EQ_TEST_NAME    0x100

+

+#define FORMID_MAIN_FORM    1

+#define FORMID_DEVICE_FORM  2

+

+#define ISCSI_NAME_MAX_SIZE 224

+

+//

+// Vfr has a limit on the size, it's 255 bytes.

+//

+#define ISCSI_NAME_IFR_MAX_SIZE   126

+

+#define IP_MIN_SIZE               7

+#define IP_MAX_SIZE               15

+#define IP4_STR_MAX_SIZE          16

+

+#define LUN_MIN_SIZE              1

+#define LUN_MAX_SIZE              20

+

+#define ISCSI_CHAP_NONE           0

+#define ISCSI_CHAP_UNI            1

+#define ISCSI_CHAP_MUTUAL         2

+

+#define TARGET_PORT_MIN_NUM       0

+#define TARGET_PORT_MAX_NUM       65535

+

+#define DEVICE_ENTRY_LABEL        0x1234

+

+#define KEY_INITIATOR_NAME        0x01

+#define KEY_DHCP_ENABLE           0x02

+#define KEY_LOCAL_IP              0x03

+#define KEY_SUBNET_MASK           0x04

+#define KEY_GATE_WAY              0x05

+#define KEY_TARGET_IP             0x06

+#define KEY_CHAP_NAME             0x07

+#define KEY_CHAP_SECRET           0x08

+#define KEY_REVERSE_CHAP_NAME     0x09

+#define KEY_REVERSE_CHAP_SECRET   0x0a

+#define KEY_SAVE_CHANGES          0x0b

+#define KEY_TARGET_NAME           0x0c

+#define KEY_BOOT_LUN              0x0d

+

+#define KEY_DEVICE_ENTRY_BASE     0x1000

+

+#define ISCSI_LUN_STR_MAX_LEN     21

+#define ISCSI_CHAP_SECRET_MIN_LEN 13

+#define ISCSI_CHAP_SECRET_MAX_LEN 17

+#define ISCSI_CHAP_NAME_MAX_LEN   126

+

+#pragma pack(1)

+typedef struct {

+  CHAR16  InitiatorName[ISCSI_NAME_IFR_MAX_SIZE];

+

+  UINT8   Enabled;

+

+  UINT8   InitiatorInfoFromDhcp;

+  CHAR16  LocalIp[IP4_STR_MAX_SIZE];

+  CHAR16  SubnetMask[IP4_STR_MAX_SIZE];

+  CHAR16  Gateway[IP4_STR_MAX_SIZE];

+

+  CHAR16  TargetName[ISCSI_NAME_IFR_MAX_SIZE];

+  CHAR16  TargetIp[IP4_STR_MAX_SIZE];

+  UINT16  TargetPort;

+  CHAR16  BootLun[ISCSI_LUN_STR_MAX_LEN];

+  UINT8   TargetInfoFromDhcp;

+

+  UINT8   CHAPType;

+  CHAR16  CHAPName[ISCSI_CHAP_NAME_MAX_LEN];

+  CHAR16  CHAPSecret[ISCSI_CHAP_SECRET_MAX_LEN];

+  CHAR16  ReverseCHAPName[ISCSI_CHAP_NAME_MAX_LEN];

+  CHAR16  ReverseCHAPSecret[ISCSI_CHAP_SECRET_MAX_LEN];

+} ISCSI_CONFIG_IFR_NVDATA;

+#pragma pack()

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiDhcp.c b/MdeModulePkg/Universal/iScsi/IScsiDhcp.c
new file mode 100644
index 0000000..b9b8cea
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiDhcp.c
Binary files differ
diff --git a/MdeModulePkg/Universal/iScsi/IScsiDhcp.h b/MdeModulePkg/Universal/iScsi/IScsiDhcp.h
new file mode 100644
index 0000000..9312b54
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiDhcp.h
@@ -0,0 +1,57 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiDhcp.h

+

+Abstract:

+

+--*/

+

+#ifndef _ISCSI_DHCP_H_

+#define _ISCSI_DHCP_H_

+

+//#include "Tiano.h"

+//#include EFI_PROTOCOL_CONSUMER (Dhcp4)

+#include "protocol\Dhcp4.h"

+

+#define DHCP4_TAG_PARA_LIST             55

+#define DHCP4_TAG_NETMASK               1

+#define DHCP4_TAG_ROUTER                3

+#define DHCP4_TAG_DNS                   6

+#define DHCP4_TAG_SERVER_ID             54

+#define DHCP4_TAG_ROOT_PATH             17

+#define ISCSI_ROOT_PATH_ID              "iscsi:"

+#define ISCSI_ROOT_PATH_FIELD_DELIMITER ':'

+

+enum {

+  RP_FIELD_IDX_SERVERNAME = 0,

+  RP_FIELD_IDX_PROTOCOL,

+  RP_FIELD_IDX_PORT,

+  RP_FIELD_IDX_LUN,

+  RP_FIELD_IDX_TARGETNAME,

+  RP_FIELD_IDX_MAX

+};

+

+typedef struct _ISCSI_ROOT_PATH_FIELD {

+  CHAR8 *Str;

+  UINT8 Len;

+} ISCSI_ROOT_PATH_FIELD;

+

+EFI_STATUS

+IScsiDoDhcp (

+  IN EFI_HANDLE                 Image,

+  IN EFI_HANDLE                 Controller,

+  IN ISCSI_SESSION_CONFIG_DATA  *ConfigData

+  );

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiDriver.c b/MdeModulePkg/Universal/iScsi/IScsiDriver.c
new file mode 100644
index 0000000..68c6cb1
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiDriver.c
@@ -0,0 +1,462 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiDriver.c

+

+Abstract:

+

+--*/

+

+#include "IScsiImpl.h"

+

+EFI_DRIVER_BINDING_PROTOCOL gIScsiDriverBinding = {

+  IScsiDriverBindingSupported,

+  IScsiDriverBindingStart,

+  IScsiDriverBindingStop,

+  0xa,

+  NULL,

+  NULL

+};

+

+EFI_GUID                    mIScsiPrivateGuid   = ISCSI_PRIVATE_GUID;

+

+EFI_STATUS

+EFIAPI

+IScsiDriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  * This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     * RemainingDevicePath OPTIONAL

+  )

+/*++

+

+Routine Description:

+

+  Test to see if iSCSI driver supports the given controller. 

+

+Arguments:

+

+  This                - Protocol instance pointer.

+  ControllerHandle    - Handle of controller to test.

+  RemainingDevicePath - Optional parameter use to pick a specific child device to start.

+

+Returns:

+

+  EFI_SUCCES          - This driver supports the controller.

+  EFI_ALREADY_STARTED - This driver is already running on this device.

+  EFI_UNSUPPORTED     - This driver doesn't support the controller.

+

+--*/

+{

+  EFI_STATUS                Status;

+  EFI_DEVICE_PATH_PROTOCOL  *CurrentDevicePath;

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &mIScsiPrivateGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+  if (!EFI_ERROR (Status)) {

+    return EFI_ALREADY_STARTED;

+  }

+

+  Status = gBS->OpenProtocol (

+                  ControllerHandle,

+                  &gEfiTcp4ServiceBindingProtocolGuid,

+                  NULL,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_TEST_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return EFI_UNSUPPORTED;

+  }

+

+  CurrentDevicePath = RemainingDevicePath;

+  if (CurrentDevicePath != NULL) {

+    while (!IsDevicePathEnd (CurrentDevicePath)) {

+      if ((CurrentDevicePath->Type == MESSAGING_DEVICE_PATH) && (CurrentDevicePath->SubType == MSG_ISCSI_DP)) {

+        return EFI_SUCCESS;

+      }

+

+      CurrentDevicePath = NextDevicePathNode (CurrentDevicePath);

+    }

+

+    return EFI_UNSUPPORTED;

+  }

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiDriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  * This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     * RemainingDevicePath OPTIONAL

+  )

+/*++

+

+Routine Description:

+

+  Start to manage the controller. 

+

+Arguments:

+

+  This                - Protocol instance pointer.

+  ControllerHandle    - Handle of the controller.

+  RemainingDevicePath - Optional parameter use to pick a specific child device to start.

+

+Returns:

+

+  EFI_SUCCES          - This driver supports this device.

+  EFI_ALREADY_STARTED - This driver is already running on this device.

+

+--*/

+{

+  EFI_STATUS        Status;

+  ISCSI_DRIVER_DATA *Private;

+

+  //

+  // Try to add a port configuration page for this controller.

+  //

+  IScsiConfigUpdateForm (This->DriverBindingHandle, ControllerHandle, TRUE);

+

+  Private = IScsiCreateDriverData (This->DriverBindingHandle, ControllerHandle);

+  if (Private == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // Get the iSCSI configuration data of this controller.

+  //

+  Status = IScsiGetConfigData (Private);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+  //

+  // Try to login and create an iSCSI session according to the configuration.

+  //

+  Status = IScsiSessionLogin (Private);

+  if (Status == EFI_MEDIA_CHANGED) {

+    //

+    // The specified target is not available and the redirection information is

+    // got, login the session again with the updated target address.

+    //

+    Status = IScsiSessionLogin (Private);

+  }

+

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+  //

+  // Duplicate the Session's tcp connection device path. The source port field

+  // will be set to zero as one iSCSI session is comprised of several iSCSI

+  // connections.

+  //

+  Private->DevicePath = IScsiGetTcpConnDevicePath (Private);

+  if (Private->DevicePath == NULL) {

+    goto ON_ERROR;

+  }

+  //

+  // Install the updated device path onto the ExtScsiPassThruHandle.

+  //

+  Status = gBS->InstallProtocolInterface (

+                  &Private->ExtScsiPassThruHandle,

+                  &gEfiDevicePathProtocolGuid,

+                  EFI_NATIVE_INTERFACE,

+                  Private->DevicePath

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+  //

+  // Install the iSCSI private stuff as a flag to indicate this controller

+  // is already controlled by iSCSI driver.

+  //

+  Status = gBS->InstallProtocolInterface (

+                  &ControllerHandle,

+                  &mIScsiPrivateGuid,

+                  EFI_NATIVE_INTERFACE,

+                  &Private->IScsiIdentifier

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+  //

+  // Update/Publish the iSCSI Boot Firmware Table.

+  //

+  IScsiPublishIbft ();

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  IScsiSessionAbort (&Private->Session);

+  IScsiCleanDriverData (Private);

+

+  return Status;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiDriverBindingStop (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN UINTN                        NumberOfChildren,

+  IN EFI_HANDLE                   *ChildHandleBuffer

+  )

+/*++

+

+Routine Description:

+

+  Release the control of this controller and remove the iSCSI functions.

+

+Arguments:

+

+  This                - Protocol instance pointer.

+  ControllerHandle    - Handle of controller to stop.

+  NumberOfChildren    - Not used.

+  ChildHandleBuffer   - Not used.

+

+Returns:

+

+  EFI_SUCCES          - This driver supports this device.

+

+--*/

+{

+  EFI_HANDLE                      IScsiController;

+  EFI_STATUS                      Status;

+  ISCSI_PRIVATE_PROTOCOL          *IScsiIdentifier;

+  ISCSI_DRIVER_DATA               *Private;

+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru;

+  ISCSI_CONNECTION                *Conn;

+

+  if (NumberOfChildren != 0) {

+    //

+    // We should have only one child.

+    //

+    Status = gBS->OpenProtocol (

+                    ChildHandleBuffer[0],

+                    &gEfiExtScsiPassThruProtocolGuid,

+                    (VOID **) &PassThru,

+                    This->DriverBindingHandle,

+                    ControllerHandle,

+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                    );

+    if (EFI_ERROR (Status)) {

+      return EFI_DEVICE_ERROR;

+    }

+

+    Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);

+    Conn    = NET_LIST_HEAD (&Private->Session.Conns, ISCSI_CONNECTION, Link);

+

+    //

+    // Previously the TCP4 protocol is opened BY_CHILD_CONTROLLER. Just close

+    // the protocol here but not uninstall the device path protocol and

+    // EXT SCSI PASS THRU protocol installed on ExtScsiPassThruHandle.

+    //

+    gBS->CloseProtocol (

+          Conn->Tcp4Io.Handle,

+          &gEfiTcp4ProtocolGuid,

+          Private->Image,

+          Private->ExtScsiPassThruHandle

+          );

+

+    return EFI_SUCCESS;

+  }

+  //

+  // Get the handle of the controller we are controling.

+  //

+  IScsiController = NetLibGetNicHandle (ControllerHandle, &gEfiTcp4ProtocolGuid);

+

+  Status = gBS->OpenProtocol (

+                  IScsiController,

+                  &mIScsiPrivateGuid,

+                  (VOID **)&IScsiIdentifier,

+                  This->DriverBindingHandle,

+                  ControllerHandle,

+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL

+                  );

+  if (EFI_ERROR (Status)) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Private = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);

+

+  //

+  // Uninstall the private protocol.

+  //

+  gBS->UninstallProtocolInterface (

+        IScsiController,

+        &mIScsiPrivateGuid,

+        &Private->IScsiIdentifier

+        );

+

+  //

+  // Update the iSCSI Boot Firware Table.

+  //

+  IScsiPublishIbft ();

+

+  IScsiSessionAbort (&Private->Session);

+  IScsiCleanDriverData (Private);

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+EFIAPI

+EfiIScsiUnload (

+  IN EFI_HANDLE  ImageHandle

+  )

+/*++

+

+Routine Description:

+

+  Unload the iSCSI driver.

+

+Arguments:

+

+  ImageHandle - The handle of the driver image.

+

+Returns:

+

+  EFI_SUCCESS      - The driver is unloaded.

+  EFI_DEVICE_ERROR - Some unexpected error happened.

+

+--*/

+{

+  EFI_STATUS  Status;

+  UINTN       DeviceHandleCount;

+  EFI_HANDLE  *DeviceHandleBuffer;

+  UINTN       Index;

+

+  //

+  // Try to disonnect the driver from the devices it's controlling.

+  //

+  Status = gBS->LocateHandleBuffer (

+                  AllHandles,

+                  NULL,

+                  NULL,

+                  &DeviceHandleCount,

+                  &DeviceHandleBuffer

+                  );

+  if (!EFI_ERROR (Status)) {

+    for (Index = 0; Index < DeviceHandleCount; Index++) {

+      Status = gBS->DisconnectController (

+                      DeviceHandleBuffer[Index],

+                      ImageHandle,

+                      NULL

+                      );

+    }

+

+    if (DeviceHandleBuffer != NULL) {

+      NetFreePool (DeviceHandleBuffer);

+    }

+  }

+  //

+  // Unload the iSCSI configuration form.

+  //

+  IScsiConfigFormUnload (gIScsiDriverBinding.DriverBindingHandle);

+

+  //

+  // Uninstall the protocols installed by iSCSI driver.

+  //

+  Status = gBS->UninstallMultipleProtocolInterfaces (

+                  ImageHandle,

+                  &gEfiDriverBindingProtocolGuid,

+                  &gIScsiDriverBinding,

+                  &gEfiComponentName2ProtocolGuid,

+                  &gIScsiComponentName2,

+                  &gEfiComponentNameProtocolGuid,

+                  &gIScsiComponentName,

+                  &gEfiIScsiInitiatorNameProtocolGuid,

+                  &gIScsiInitiatorName,

+                  NULL

+                  );

+

+  return Status;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiDriverEntryPoint (

+  IN EFI_HANDLE         ImageHandle,

+  IN EFI_SYSTEM_TABLE   *SystemTable

+  )

+/*++

+

+Routine Description:

+

+  Initialize the global variables publish the driver binding protocol.

+

+Arguments:

+

+  ImageHandle - The handle of the driver image.

+  SystemTable - The EFI system table.

+

+Returns:

+

+  EFI_SUCCESS      - The protocols are installed.

+  EFI_DEVICE_ERROR - Some unexpected error happened.

+

+--*/

+{

+  EFI_STATUS  Status;

+  //EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;

+

+  //

+  // Initialize the EFI Driver Library

+  //

+  Status = EfiLibInstallDriverBindingComponentName2 (

+             ImageHandle,

+             SystemTable,

+             &gIScsiDriverBinding,

+             ImageHandle,

+             &gIScsiComponentName,

+             &gIScsiComponentName2

+           );

+  

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (!EFI_ERROR (Status)) {

+    Status = gBS->InstallProtocolInterface (

+                    &ImageHandle,

+                    &gEfiIScsiInitiatorNameProtocolGuid,

+                    EFI_NATIVE_INTERFACE,

+                    &gIScsiInitiatorName

+                    );

+    if (EFI_ERROR (Status)) {

+      gBS->UninstallMultipleProtocolInterfaces (

+            ImageHandle,

+            &gEfiDriverBindingProtocolGuid,

+            &gIScsiDriverBinding,

+            &gEfiComponentName2ProtocolGuid,

+            &gIScsiComponentName2,

+            &gEfiComponentNameProtocolGuid,

+            &gIScsiComponentName,

+            NULL

+            );

+    }

+  }

+  //

+  // Initialize the configuration form of iSCSI.

+  //

+  IScsiConfigFormInit (gIScsiDriverBinding.DriverBindingHandle);

+

+  return Status;

+}

+

diff --git a/MdeModulePkg/Universal/iScsi/IScsiDriver.h b/MdeModulePkg/Universal/iScsi/IScsiDriver.h
new file mode 100644
index 0000000..5334e34
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiDriver.h
@@ -0,0 +1,123 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiDriver.h

+

+Abstract:

+

+--*/

+

+#ifndef _ISCSI_DRIVER_H_

+#define _ISCSI_DRIVER_H_

+

+#include <PiDxe.h>

+#include <Protocol/DevicePath.h>

+#include <Protocol/LoadedImage.h>

+#include <Library/UefiDriverEntryPoint.h>

+#include <Library/UefiBootServicesTableLib.h>

+#include <Library/UefiLib.h>

+#include <Library/DevicePathLib.h>

+#include <protocol/DriverBinding.h>

+#include <protocol/ScsiPassThruExt.h>

+#include <protocol/IScsiInitiatorName.h>

+#include <protocol/Ip4Config.h>

+#include <protocol/ComponentName.h>

+#include <protocol/ComponentName2.h>

+

+#define ISCSI_PRIVATE_GUID \

+  { \

+    0xfa3cde4c, 0x87c2, 0x427d, 0xae, 0xde, 0x7d, 0xd0, 0x96, 0xc8, 0x8c, 0x58 \

+  }

+

+#define ISCSI_INITIATOR_NAME_VAR_NAME L"I_NAME"

+

+extern EFI_COMPONENT_NAME2_PROTOCOL       gIScsiComponentName2;

+extern EFI_COMPONENT_NAME_PROTOCOL        gIScsiComponentName;

+

+extern EFI_ISCSI_INITIATOR_NAME_PROTOCOL  gIScsiInitiatorName;

+

+extern EFI_GUID                           mIScsiPrivateGuid;

+

+typedef struct _ISCSI_PRIVATE_PROTOCOL {

+  UINT32  Reserved;

+} ISCSI_PRIVATE_PROTOCOL;

+

+//

+// EFI Driver Binding Protocol for iSCSI driver.

+//

+EFI_STATUS

+EFIAPI

+IScsiDriverBindingSupported (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+EFI_STATUS

+EFIAPI

+IScsiDriverBindingStart (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL

+  );

+

+EFI_STATUS

+EFIAPI

+IScsiDriverBindingStop (

+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,

+  IN EFI_HANDLE                   ControllerHandle,

+  IN UINTN                        NumberOfChildren,

+  IN EFI_HANDLE                   *ChildHandleBuffer

+  );

+

+//

+// EFI Component Name Protocol for iSCSI driver.

+//

+EFI_STATUS

+EFIAPI

+IScsiComponentNameGetDriverName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,

+  IN  CHAR8                         *Language,

+  OUT CHAR16                        **DriverName

+  );

+

+EFI_STATUS

+EFIAPI

+IScsiComponentNameGetControllerName (

+  IN  EFI_COMPONENT_NAME_PROTOCOL   *This,

+  IN  EFI_HANDLE                    ControllerHandle,

+  IN  EFI_HANDLE                    ChildHandle        OPTIONAL,

+  IN  CHAR8                         *Language,

+  OUT CHAR16                        **ControllerName

+  );

+

+//

+// EFI iSCSI Initiator Name Protocol for iSCSI driver.

+//

+EFI_STATUS

+EFIAPI

+IScsiGetInitiatorName (

+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,

+  IN OUT UINTN                              *BufferSize,

+  OUT    VOID                               *Buffer

+  );

+

+EFI_STATUS

+EFIAPI

+IScsiSetInitiatorName (

+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,

+  IN OUT UINTN                              *BufferSize,

+  OUT    VOID                               *Buffer

+  );

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiExtScsiPassThru.c b/MdeModulePkg/Universal/iScsi/IScsiExtScsiPassThru.c
new file mode 100644
index 0000000..42b29ef
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiExtScsiPassThru.c
@@ -0,0 +1,401 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiExtScsiPassThru.c

+

+Abstract:

+

+--*/

+

+#include "IScsiImpl.h"

+

+EFI_STATUS

+EFIAPI

+IScsiExtScsiPassThruFunction (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                          *This,

+  IN UINT8                                                    *Target,

+  IN UINT64                                                   Lun,

+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET           *Packet,

+  IN EFI_EVENT                                                Event     OPTIONAL

+  )

+/*++

+

+Routine Description:

+

+  This function sends out the SCSI command via iSCSI transport layer and returned

+  back the data received from the iSCSI target. 

+

+Arguments:

+

+  This   - The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance.

+  Target - The Target ID of device to send the SCSI Request Packet. 

+  Lun    - The LUN of the device to send the SCSI Request Packet.

+  Packet - The SCSI Request Packet to send to the device.

+  Event  - The event used in non-blocking mode, it should be always NULL.

+

+Returns:

+

+  EFI_STATUS

+

+--*/

+{

+  if (Target[0] != 0) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if ((Packet == NULL) || (Packet->Cdb == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  return IScsiExecuteScsiCommand (This, Target, Lun, Packet);

+}

+

+EFI_STATUS

+EFIAPI

+IScsiExtScsiPassThruGetNextTargetLun (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,

+  IN OUT UINT8                        **Target,

+  IN OUT UINT64                       *Lun

+  )

+/*++

+

+Routine Description:

+

+  Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel.

+

+Arguments:

+

+  This   - The EFI_EXT_SCSI_PASS_THRU_PROTOCOL instance..

+  Target - On input, a pointer to the Target ID of a SCSI device present on the

+           SCSI channel.  On output, a pointer to the Target ID of the next SCSI

+           device present on a SCSI channel.  An input value of 0xFFFFFFFF

+           retrieves the Target ID of the first SCSI device present on a SCSI channel.

+  Lun    - On input, a pointer to the LUN of a SCSI device present on the SCSI

+           channel. On output, a pointer to the LUN of the next SCSI device present on 

+           a SCSI channel.

+

+Returns:

+

+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device 

+                          on the SCSI channel was returned in Target and Lun.

+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.

+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not

+                           returned on a previous call to GetNextDevice().

+

+--*/

+{

+  ISCSI_DRIVER_DATA           *Private;

+  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;

+  UINT8                       TargetId[TARGET_MAX_BYTES];

+

+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);

+  ConfigNvData  = &Private->Session.ConfigData.NvData;

+

+  if ((*Target)[0] == 0 && (NetCompareMem (Lun, ConfigNvData->BootLun, sizeof (UINT64)) == 0)) {

+    //

+    // Only one <Target, Lun> pair per iSCSI Driver instance.

+    //

+    return EFI_NOT_FOUND;

+  }

+

+  NetSetMem (TargetId, TARGET_MAX_BYTES, 0xFF);

+  if (NetCompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {

+    (*Target)[0] = 0;

+    NetCopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));

+

+    return EFI_SUCCESS;

+  }

+

+  return EFI_INVALID_PARAMETER;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiExtScsiPassThruBuildDevicePath (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,

+  IN UINT8                            *Target,

+  IN UINT64                           Lun,

+  IN OUT EFI_DEVICE_PATH_PROTOCOL     **DevicePath

+  )

+/*++

+

+Routine Description:

+

+  Allocate and build a device path node for a SCSI device on a SCSI channel.

+

+Arguments:

+

+  This                  - Protocol instance pointer.

+  Target                - The Target ID of the SCSI device for which

+                          a device path node is to be allocated and built.

+  Lun                   - The LUN of the SCSI device for which a device 

+                          path node is to be allocated and built.

+  DevicePath            - A pointer to a single device path node that 

+                          describes the SCSI device specified by 

+                          Target and Lun. This function is responsible 

+                          for allocating the buffer DevicePath with the boot

+                          service AllocatePool().  It is the caller's 

+                          responsibility to free DevicePath when the caller

+                          is finished with DevicePath.    

+

+Returns:

+

+  EFI_SUCCESS           - The device path node that describes the SCSI device

+                          specified by Target and Lun was allocated and 

+                          returned in DevicePath.

+  EFI_NOT_FOUND         - The SCSI devices specified by Target and Lun does

+                          not exist on the SCSI channel.

+  EFI_INVALID_PARAMETER - DevicePath is NULL.

+  EFI_OUT_OF_RESOURCES  - There are not enough resources to allocate 

+                          DevicePath.

+

+--*/

+{

+  ISCSI_DRIVER_DATA             *Private;

+  ISCSI_SESSION                 *Session;

+  ISCSI_SESSION_CONFIG_NVDATA   *ConfigNvData;

+  ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfig;

+  EFI_DEV_PATH                  *Node;

+  UINTN                         DevPathNodeLen;

+

+  if ((DevicePath == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (Target[0] != 0) {

+    return EFI_NOT_FOUND;

+  }

+

+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);

+  Session       = &Private->Session;

+  ConfigNvData  = &Session->ConfigData.NvData;

+  AuthConfig    = &Session->AuthData.AuthConfig;

+

+  if (NetCompareMem (&Lun, ConfigNvData->BootLun, sizeof (UINT64)) != 0) {

+    return EFI_NOT_FOUND;

+  }

+

+  DevPathNodeLen  = sizeof (ISCSI_DEVICE_PATH) + AsciiStrLen (ConfigNvData->TargetName) + 1;

+  Node            = NetAllocatePool (DevPathNodeLen);

+  if (Node == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;

+  Node->DevPath.SubType = MSG_ISCSI_DP;

+  SetDevicePathNodeLength (&Node->DevPath, DevPathNodeLen);

+

+  //

+  // 0 for TCP, others are reserved.

+  //

+  Node->Iscsi.NetworkProtocol = 0;

+

+  Node->Iscsi.LoginOption     = 0;

+  switch (AuthConfig->CHAPType) {

+  case ISCSI_CHAP_NONE:

+    Node->Iscsi.LoginOption |= 0x0800;

+    break;

+

+  case ISCSI_CHAP_UNI:

+    Node->Iscsi.LoginOption |= 0x1000;

+    break;

+

+  default:

+    break;

+  }

+

+  NetCopyMem (&Node->Iscsi.Lun, ConfigNvData->BootLun, sizeof (UINT64));

+  Node->Iscsi.TargetPortalGroupTag = Session->TargetPortalGroupTag;

+  AsciiStrCpy ((CHAR8 *) Node + sizeof (ISCSI_DEVICE_PATH), ConfigNvData->TargetName);

+

+  *DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Node;

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiExtScsiPassThruGetTargetLun (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,

+  IN EFI_DEVICE_PATH_PROTOCOL         *DevicePath,

+  OUT UINT8                           **Target,

+  OUT UINT64                          *Lun

+  )

+/*++

+

+Routine Description:

+

+  Translate a device path node to a Target ID and LUN.

+

+Arguments:

+

+  This                  - Protocol instance pointer.

+  DevicePath            - A pointer to the device path node that 

+                          describes a SCSI device on the SCSI channel.

+  Target                - A pointer to the Target ID of a SCSI device 

+                          on the SCSI channel. 

+  Lun                   - A pointer to the LUN of a SCSI device on 

+                          the SCSI channel.    

+Returns:

+

+  EFI_SUCCESS           - DevicePath was successfully translated to a 

+                          Target ID and LUN, and they were returned 

+                          in Target and Lun.

+  EFI_INVALID_PARAMETER - DevicePath/Target/Lun is NULL.

+  EFI_UNSUPPORTED       - This driver does not support the device path 

+                          node type in DevicePath.

+  EFI_NOT_FOUND         - A valid translation from DevicePath to a 

+                          Target ID and LUN does not exist.

+

+--*/

+{

+  ISCSI_DRIVER_DATA           *Private;

+  ISCSI_SESSION_CONFIG_NVDATA *ConfigNvData;

+

+  if ((DevicePath == NULL) || (Target == NULL) || (Lun == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if ((DevicePath->Type != MESSAGING_DEVICE_PATH) ||

+      (DevicePath->SubType != MSG_ISCSI_DP) ||

+      (DevicePathNodeLength (DevicePath) <= sizeof (ISCSI_DEVICE_PATH))

+      ) {

+    return EFI_UNSUPPORTED;

+  }

+

+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (This);

+  ConfigNvData  = &Private->Session.ConfigData.NvData;

+

+  NetZeroMem (*Target, TARGET_MAX_BYTES);

+

+  if (AsciiStrCmp (ConfigNvData->TargetName, (CHAR8 *) DevicePath + sizeof (ISCSI_DEVICE_PATH)) != 0) {

+    return EFI_UNSUPPORTED;

+  }

+

+  NetCopyMem (Lun, ConfigNvData->BootLun, sizeof (UINT64));

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiExtScsiPassThruResetChannel (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This

+  )

+/*++

+

+Routine Description:

+

+  Resets a SCSI channel.This operation resets all the SCSI devices connected to

+  the SCSI channel.

+

+Arguments:

+

+  This            - Protocol instance pointer.

+

+Returns:

+

+  EFI_UNSUPPORTED - It's not supported.

+

+--*/

+{

+  return EFI_UNSUPPORTED;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiExtScsiPassThruResetTargetLun (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,

+  IN UINT8                            *Target,

+  IN UINT64                           Lun

+  )

+/*++

+

+Routine Description:

+

+  Resets a SCSI device that is connected to a SCSI channel.

+

+Arguments:

+

+  This            - Protocol instance pointer.

+  Target          - The Target ID of the SCSI device to reset. 

+  Lun             - The LUN of the SCSI device to reset.

+    

+Returns:

+

+  EFI_UNSUPPORTED - It's not supported.

+

+--*/

+{

+  return EFI_UNSUPPORTED;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiExtScsiPassThruGetNextTarget (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL  *This,

+  IN OUT UINT8                        **Target

+  )

+/*++

+

+Routine Description:

+

+  Retrieve the list of legal Target IDs for SCSI devices on a SCSI channel.

+

+Arguments:

+  This                  - Protocol instance pointer.

+  Target                - On input, a pointer to the Target ID of a SCSI 

+                          device present on the SCSI channel.  On output, 

+                          a pointer to the Target ID of the next SCSI device

+                           present on a SCSI channel.  An input value of 

+                           0xFFFFFFFF retrieves the Target ID of the first 

+                           SCSI device present on a SCSI channel.

+  Lun                   - On input, a pointer to the LUN of a SCSI device

+                          present on the SCSI channel. On output, a pointer

+                          to the LUN of the next SCSI device present on 

+                          a SCSI channel.

+    

+Returns:

+

+  EFI_SUCCESS           - The Target ID and Lun of the next SCSI device 

+                          on the SCSI channel was returned in Target and Lun.

+  EFI_NOT_FOUND         - There are no more SCSI devices on this SCSI channel.

+  EFI_INVALID_PARAMETER - Target is not 0xFFFFFFFF,and Target and Lun were not

+                          returned on a previous call to GetNextDevice().

+

+--*/

+{

+  UINT8 TargetId[TARGET_MAX_BYTES];

+

+  NetSetMem (TargetId, TARGET_MAX_BYTES, 0xFF);

+

+  if (NetCompareMem (*Target, TargetId, TARGET_MAX_BYTES) == 0) {

+    (*Target)[0] = 0;

+    return EFI_SUCCESS;

+  } else if ((*Target)[0] == 0) {

+    return EFI_NOT_FOUND;

+  } else {

+    return EFI_INVALID_PARAMETER;

+  }

+}

+

+EFI_EXT_SCSI_PASS_THRU_PROTOCOL gIScsiExtScsiPassThruProtocolTemplate = {

+  NULL,

+  IScsiExtScsiPassThruFunction,

+  IScsiExtScsiPassThruGetNextTargetLun,

+  IScsiExtScsiPassThruBuildDevicePath,

+  IScsiExtScsiPassThruGetTargetLun,

+  IScsiExtScsiPassThruResetChannel,

+  IScsiExtScsiPassThruResetTargetLun,

+  IScsiExtScsiPassThruGetNextTarget

+};

diff --git a/MdeModulePkg/Universal/iScsi/IScsiExtScsiPassThru.h b/MdeModulePkg/Universal/iScsi/IScsiExtScsiPassThru.h
new file mode 100644
index 0000000..7cb9be2
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiExtScsiPassThru.h
@@ -0,0 +1,27 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiExtScsiPassThru.h

+

+Abstract:

+

+--*/

+

+#ifndef _ISCSI_EXT_SCSI_PASS_THRU_H_

+#define _ISCSI_EXT_SCSI_PASS_THRU_H_

+

+#include <protocol/ScsiPassThruExt.h>

+

+extern EFI_EXT_SCSI_PASS_THRU_PROTOCOL  gIScsiExtScsiPassThruProtocolTemplate;

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiIbft.c b/MdeModulePkg/Universal/iScsi/IScsiIbft.c
new file mode 100644
index 0000000..e5d6324
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiIbft.c
@@ -0,0 +1,645 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiIbft.c

+

+Abstract:

+

+  Implementation for iSCSI Boot Firmware Table publication.

+

+--*/

+

+#include "IScsiImpl.h"

+

+STATIC

+VOID

+IScsiInitIbfTableHeader (

+  IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER  *Header

+  )

+/*++

+

+Routine Description:

+

+  Initialize the header of the iSCSI Boot Firmware Table.

+  

+Arguments:

+

+  Header - The header of the iSCSI Boot Firmware Table.

+

+Returns:

+

+  None.

+

+--*/

+{

+  NetZeroMem (Header, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER));

+

+  Header->Signature = EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE;

+  Header->Length    = IBFT_HEAP_OFFSET;

+  Header->Revision  = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_REVISION;

+  Header->Checksum  = 0;

+

+  Header->OemId[0]  = 'I';

+  Header->OemId[1]  = 'N';

+  Header->OemId[2]  = 'T';

+  Header->OemId[3]  = 'E';

+  Header->OemId[4]  = 'L';

+}

+

+STATIC

+VOID

+IScsiInitControlSection (

+  IN EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER  *Table,

+  IN UINTN                                      HandleCount

+  )

+/*++

+

+Routine Description:

+

+  Initialize the control section of the iSCSI Boot Firmware Table.

+  

+Arguments:

+

+  Table       - The ACPI table.

+  HandleCount - The number of the handles associated with iSCSI sessions, it's

+                equal to the number of iSCSI sessions.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE  *Control;

+  UINTN                                                 NumOffset;

+

+  Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);

+

+  NetZeroMem (Control, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE));

+

+  Control->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_ID;

+  Control->Header.Version     = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE_VERSION;

+  Control->Header.Length      = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE);

+

+  //

+  // Each session occupies two offsets, one for the NIC section,

+  // the other for the Target section.

+  //

+  NumOffset = 2 * HandleCount;

+  if (NumOffset > 4) {

+    //

+    // Need expand the control section if more than 2 NIC/Target sections

+    // exist.

+    //

+    Control->Header.Length += (UINT16) (NumOffset - 4) * sizeof (UINT16);

+  }

+}

+

+STATIC

+VOID

+IScsiAddHeapItem (

+  IN OUT UINT8  **Heap,

+  IN     VOID   *Data,

+  IN     UINTN  Len

+  )

+/*++

+

+Routine Description:

+

+  Add one item into the heap.

+  

+Arguments:

+

+  Heap - On input, the current address of the heap; On output, the address of

+         the heap after the item is added.

+  Data - The data to add into the heap.

+  Len  - Length of the Data in byte.

+

+Returns:

+

+  None.

+

+--*/

+{

+  //

+  // Add one byte for the NULL delimiter.

+  //

+  *Heap -= Len + 1;

+

+  NetCopyMem (*Heap, Data, Len);

+  *(*Heap + Len) = 0;

+}

+

+STATIC

+VOID

+IScsiFillInitiatorSection (

+  IN     EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER  *Table,

+  IN OUT UINT8                                      **Heap,

+  IN     EFI_HANDLE                                 Handle

+  )

+/*++

+

+Routine Description:

+

+  Fill the Initiator section of the iSCSI Boot Firmware Table.

+  

+Arguments:

+

+  Table  - The ACPI table.

+  Heap   - The heap.

+  Handle - The handle associated with the iSCSI session.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE    *Control;

+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE  *Initiator;

+  ISCSI_DRIVER_DATA                                       *DriverData;

+  ISCSI_SESSION                                           *Session;

+  ISCSI_PRIVATE_PROTOCOL                                  *IScsiIdentifier;

+  EFI_STATUS                                              Status;

+

+  Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);

+

+  //

+  // Initiator section immediately follows the control section.

+  //

+  Initiator = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE *) ((UINT8 *) Control + IBFT_ROUNDUP (Control->Header.Length));

+

+  Control->InitiatorOffset = (UINT16) ((UINTN) Initiator - (UINTN) Table);

+

+  NetZeroMem (Initiator, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE));

+

+  Initiator->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_ID;

+  Initiator->Header.Version     = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_VERSION;

+  Initiator->Header.Length      = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE);

+  Initiator->Header.Flags       = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE_FLAG_BOOT_SELECTED;

+

+  //

+  // Get the identifier from the handle.

+  //

+  Status = gBS->HandleProtocol (Handle, &mIScsiPrivateGuid, &IScsiIdentifier);

+  if (EFI_ERROR (Status)) {

+    ASSERT (FALSE);

+    return ;

+  }

+

+  DriverData  = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);

+  Session     = &DriverData->Session;

+

+  //

+  // Fill the iSCSI Initiator Name into the heap.

+  //

+  IScsiAddHeapItem (Heap, Session->InitiatorName, Session->InitiatorNameLength - 1);

+

+  Initiator->IScsiNameLength  = (UINT16) (Session->InitiatorNameLength - 1);

+  Initiator->IScsiNameOffset  = (UINT16) ((UINTN) *Heap - (UINTN) Table);

+}

+

+STATIC

+VOID

+IScsiMapV4ToV6Addr (

+  IN  EFI_IPv4_ADDRESS *V4,

+  OUT EFI_IPv6_ADDRESS *V6

+  )

+/*++

+

+Routine Description:

+

+  Map the v4 IP address into v6 IP address.

+  

+Arguments:

+

+  V4 - The v4 IP address.

+  V6 - The v6 IP address.

+

+Returns:

+

+  None.

+

+--*/

+{

+  UINTN Index;

+

+  NetZeroMem (V6, sizeof (EFI_IPv6_ADDRESS));

+

+  V6->Addr[10]  = 0xff;

+  V6->Addr[11]  = 0xff;

+

+  for (Index = 0; Index < 4; Index++) {

+    V6->Addr[12 + Index] = V4->Addr[Index];

+  }

+}

+

+STATIC

+UINT16

+IScsiGetNICPciLocation (

+  IN EFI_HANDLE  Controller

+  )

+/*++

+

+Routine Description:

+

+  Get the NIC's PCI location and return it accroding to the composited

+  format defined in iSCSI Boot Firmware Table.

+  

+Arguments:

+

+  Controller - The handle of the controller.

+

+Returns:

+

+  UINT16 - The composited representation of the NIC PCI location.

+

+--*/

+{

+  EFI_STATUS                Status;

+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;

+  EFI_HANDLE                PciIoHandle;

+  EFI_PCI_IO_PROTOCOL       *PciIo;

+  UINTN                     Segment;

+  UINTN                     Bus;

+  UINTN                     Device;

+  UINTN                     Function;

+

+  Status = gBS->HandleProtocol (

+                  Controller,

+                  &gEfiDevicePathProtocolGuid,

+                  &DevicePath

+                  );

+  if (EFI_ERROR (Status)) {

+    return 0;

+  }

+

+  Status = gBS->LocateDevicePath (

+                  &gEfiPciIoProtocolGuid,

+                  &DevicePath,

+                  &PciIoHandle

+                  );

+  if (EFI_ERROR (Status)) {

+    return 0;

+  }

+

+  Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, &PciIo);

+  if (EFI_ERROR (Status)) {

+    return 0;

+  }

+

+  Status = PciIo->GetLocation (PciIo, &Segment, &Bus, &Device, &Function);

+  if (EFI_ERROR (Status)) {

+    return 0;

+  }

+

+  return (UINT16) ((Bus << 8) | (Device << 3) | Function);

+}

+

+STATIC

+EFI_MAC_ADDRESS *

+IScsiGetMacAddress (

+  IN EFI_HANDLE  Controller

+  )

+/*++

+

+Routine Description:

+

+  Get the MAC address of the controller.

+  

+Arguments:

+

+  Controller - The handle of the controller.

+

+Returns:

+

+  EFI_MAC_ADDRESS * - The mac address.

+

+--*/

+{

+  EFI_STATUS                  Status;

+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;

+

+  Status = gBS->HandleProtocol (

+                  Controller,

+                  &gEfiSimpleNetworkProtocolGuid,

+                  &Snp

+                  );

+  ASSERT_EFI_ERROR (Status);

+

+  return &Snp->Mode->PermanentAddress;

+}

+

+STATIC

+VOID

+IScsiFillNICAndTargetSections (

+  IN     EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER  *Table,

+  IN OUT UINT8                                      **Heap,

+  IN     UINTN                                      HandleCount,

+  IN     EFI_HANDLE                                 *Handles

+  )

+/*++

+

+Routine Description:

+

+  Fill the NIC and target sections in iSCSI Boot Firmware Table.

+

+Arguments:

+

+  Table       - The buffer of the ACPI table.

+  Heap        - The heap buffer used to store the variable length parameters such as iSCSI name.

+  HandleCount - The number of handles having iSCSI private protocol installed.

+  Handles     - The handle buffer.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE  *Control;

+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE      *Nic;

+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE   *Target;

+  ISCSI_DRIVER_DATA                                     *DriverData;

+  ISCSI_SESSION_CONFIG_DATA                             *SessionConfigData;

+  ISCSI_CHAP_AUTH_CONFIG_NVDATA                         *AuthConfig;

+  UINT16                                                *SectionOffset;

+  UINTN                                                 Index;

+  UINT16                                                Length;

+  EFI_MAC_ADDRESS                                       *Mac;

+  ISCSI_PRIVATE_PROTOCOL                                *IScsiIdentifier;

+  EFI_STATUS                                            Status;

+

+  //

+  // Get the offset of the first Nic and Target section.

+  //

+  Control = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_CONTROL_STRUCTURE *) (Table + 1);

+  Nic     = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Table +

+          Control->InitiatorOffset + IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_INITIATOR_STRUCTURE)));

+  Target  = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +

+          IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));

+

+  SectionOffset = &Control->NIC0Offset;

+

+  for (Index = 0; Index < HandleCount; Index++) {

+    Status = gBS->HandleProtocol (Handles[Index], &mIScsiPrivateGuid, &IScsiIdentifier);

+    if (EFI_ERROR (Status)) {

+      ASSERT (FALSE);

+      return ;

+    }

+

+    DriverData        = ISCSI_DRIVER_DATA_FROM_IDENTIFIER (IScsiIdentifier);

+    SessionConfigData = &DriverData->Session.ConfigData;

+    AuthConfig        = &DriverData->Session.AuthData.AuthConfig;

+

+    //

+    // Fill the Nic section.

+    //

+    NetZeroMem (Nic, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE));

+

+    Nic->Header.StructureId = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_ID;

+    Nic->Header.Version     = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_VERSION;

+    Nic->Header.Length      = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE);

+    Nic->Header.Index       = (UINT8) Index;

+    Nic->Header.Flags       = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BLOCK_VALID |

+                            EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_BOOT_SELECTED |

+                            EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE_FLAG_GLOBAL;

+

+    //

+    // Get the subnet mask prefix length.

+    //

+    Nic->SubnetMaskPrefixLength = IScsiGetSubnetMaskPrefixLength (&SessionConfigData->NvData.SubnetMask);

+

+    if (SessionConfigData->NvData.InitiatorInfoFromDhcp) {

+      Nic->Origin = IpPrefixOriginDhcp;

+    } else {

+      Nic->Origin = IpPrefixOriginManual;

+    }

+    //

+    // Map the various v4 addresses into v6 addresses.

+    //

+    IScsiMapV4ToV6Addr (&SessionConfigData->NvData.LocalIp, &Nic->Ip);

+    IScsiMapV4ToV6Addr (&SessionConfigData->NvData.Gateway, &Nic->Gateway);

+    IScsiMapV4ToV6Addr (&SessionConfigData->PrimaryDns, &Nic->PrimaryDns);

+    IScsiMapV4ToV6Addr (&SessionConfigData->SecondaryDns, &Nic->SecondaryDns);

+    IScsiMapV4ToV6Addr (&SessionConfigData->DhcpServer, &Nic->DhcpServer);

+

+    Mac = IScsiGetMacAddress (DriverData->Controller);

+    NetCopyMem (Nic->Mac, Mac, sizeof (Nic->Mac));

+

+    //

+    // Get the PCI location of the Nic.

+    //

+    Nic->PciLocation  = IScsiGetNICPciLocation (DriverData->Controller);

+

+    *SectionOffset    = (UINT16) ((UINTN) Nic - (UINTN) Table);

+    SectionOffset++;

+

+    //

+    // Fill the Target section.

+    //

+    NetZeroMem (Target, sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE));

+

+    Target->Header.StructureId  = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_ID;

+    Target->Header.Version      = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_VERSION;

+    Target->Header.Length       = sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE);

+    Target->Header.Index        = (UINT8) Index;

+    Target->Header.Flags        = EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BLOCK_VALID | EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE_FLAG_BOOT_SELECTED;

+    Target->Port                = SessionConfigData->NvData.TargetPort;

+    Target->CHAPType            = AuthConfig->CHAPType;

+    Target->NicIndex            = (UINT8) Index;

+

+    IScsiMapV4ToV6Addr (&SessionConfigData->NvData.TargetIp, &Target->Ip);

+    NetCopyMem (Target->BootLun, SessionConfigData->NvData.BootLun, sizeof (Target->BootLun));

+

+    //

+    // Target iSCSI Name, CHAP name/secret, reverse CHAP name/secret.

+    //

+    Length = (UINT16) AsciiStrLen (SessionConfigData->NvData.TargetName);

+    IScsiAddHeapItem (Heap, SessionConfigData->NvData.TargetName, Length);

+

+    Target->IScsiNameLength = Length;

+    Target->IScsiNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);

+

+    if (Target->CHAPType != ISCSI_CHAP_NONE) {

+      //

+      // CHAP Name

+      //

+      Length = (UINT16) AsciiStrLen (AuthConfig->CHAPName);

+      IScsiAddHeapItem (Heap, AuthConfig->CHAPName, Length);

+      Target->CHAPNameLength  = Length;

+      Target->CHAPNameOffset  = (UINT16) ((UINTN) *Heap - (UINTN) Table);

+

+      //

+      // CHAP Secret

+      //

+      Length = (UINT16) AsciiStrLen (AuthConfig->CHAPSecret);

+      IScsiAddHeapItem (Heap, AuthConfig->CHAPSecret, Length);

+      Target->CHAPSecretLength  = Length;

+      Target->CHAPSecretOffset  = (UINT16) ((UINTN) *Heap - (UINTN) Table);

+

+      if (Target->CHAPType == ISCSI_CHAP_MUTUAL) {

+        //

+        // Reverse CHAP Name

+        //

+        Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPName);

+        IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPName, Length);

+        Target->ReverseCHAPNameLength = Length;

+        Target->ReverseCHAPNameOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);

+

+        //

+        // Reverse CHAP Secret

+        //

+        Length = (UINT16) AsciiStrLen (AuthConfig->ReverseCHAPSecret);

+        IScsiAddHeapItem (Heap, AuthConfig->ReverseCHAPSecret, Length);

+        Target->ReverseCHAPSecretLength = Length;

+        Target->ReverseCHAPSecretOffset = (UINT16) ((UINTN) *Heap - (UINTN) Table);

+      }

+    }

+

+    *SectionOffset = (UINT16) ((UINTN) Target - (UINTN) Table);

+    SectionOffset++;

+

+    //

+    // Advance to the next NIC/Target pair

+    //

+    Nic    = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE *) ((UINTN) Target +

+           IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE)));

+    Target = (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_TARGET_STRUCTURE *) ((UINTN) Nic +

+           IBFT_ROUNDUP (sizeof (EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_NIC_STRUCTURE)));

+  }

+}

+

+VOID

+IScsiPublishIbft (

+  IN VOID

+  )

+/*++

+

+Routine Description:

+

+  Publish and remove the iSCSI Boot Firmware Table according to the iSCSI

+  session status.

+

+Arguments:

+

+  None.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_STATUS                                Status;

+  UINTN                                     TableHandle;

+  EFI_ACPI_SUPPORT_PROTOCOL                 *AcpiSupport;

+  EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_HEADER *Table;

+  UINTN                                     HandleCount;

+  EFI_HANDLE                                *HandleBuffer;

+  UINT8                                     *Heap;

+  INTN                                      Index;

+  EFI_ACPI_TABLE_VERSION                    Version;

+  UINT32                                    Signature;

+

+  Status = gBS->LocateProtocol (&gEfiAcpiSupportProtocolGuid, NULL, &AcpiSupport);

+  if (EFI_ERROR (Status)) {

+    return ;

+  }

+  //

+  // Try to remove the old iSCSI Boot Firmware Table.

+  //

+  for (Index = 0;; Index++) {

+    Status = AcpiSupport->GetAcpiTable (

+                            AcpiSupport,

+                            Index,

+                            &Table,

+                            &Version,

+                            &TableHandle

+                            );

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+

+    Signature = Table->Signature;

+    NetFreePool (Table);

+

+    if (Signature == EFI_ACPI_3_0_ISCSI_BOOT_FIRMWARE_TABLE_SIGNATURE) {

+      //

+      // Remove the table.

+      //

+      Status = AcpiSupport->SetAcpiTable (

+                              AcpiSupport,

+                              NULL,

+                              FALSE,

+                              Version,

+                              &TableHandle

+                              );

+      if (EFI_ERROR (Status)) {

+        return ;

+      }

+

+      break;

+    }

+  }

+  //

+  // Get all iSCSI private protocols.

+  //

+  Status = gBS->LocateHandleBuffer (

+                  ByProtocol,

+                  &mIScsiPrivateGuid,

+                  NULL,

+                  &HandleCount,

+                  &HandleBuffer

+                  );

+  if (EFI_ERROR (Status)) {

+    return ;

+  }

+  //

+  // Allocate 4k bytes to hold the ACPI table.

+  //

+  Table = NetAllocatePool (IBFT_MAX_SIZE);

+  if (Table == NULL) {

+    return ;

+  }

+

+  Heap = (CHAR8 *) Table + IBFT_HEAP_OFFSET;

+

+  //

+  // Fill in the various section of the iSCSI Boot Firmware Table.

+  //

+  IScsiInitIbfTableHeader (Table);

+  IScsiInitControlSection (Table, HandleCount);

+  IScsiFillInitiatorSection (Table, &Heap, HandleBuffer[0]);

+  IScsiFillNICAndTargetSections (Table, &Heap, HandleCount, HandleBuffer);

+

+  NetFreePool (HandleBuffer);

+

+  TableHandle = 0;

+

+  //

+  // Install or update the iBFT table.

+  //

+  Status = AcpiSupport->SetAcpiTable (

+                          AcpiSupport,

+                          Table,

+                          TRUE,

+                          EFI_ACPI_TABLE_VERSION_3_0,

+                          &TableHandle

+                          );

+  if (!EFI_ERROR (Status)) {

+    AcpiSupport->PublishTables (AcpiSupport, EFI_ACPI_TABLE_VERSION_3_0);

+  }

+

+  NetFreePool (Table);

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsiIbft.h b/MdeModulePkg/Universal/iScsi/IScsiIbft.h
new file mode 100644
index 0000000..b925b14
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiIbft.h
@@ -0,0 +1,40 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiIbft.h

+

+Abstract:

+

+  Some extra definitions for iBFT.

+

+--*/

+

+#ifndef _ISCSI_IBFT_H_

+#define _ISCSI_IBFT_H_

+

+#include <industrystandard/IScsiBootFirmwareTable.h>

+#include <protocol/AcpiSupport.h>

+#include <protocol/PciIo.h>

+

+#define IBFT_TABLE_VAR_NAME L"iBFT"

+#define IBFT_MAX_SIZE       4096

+#define IBFT_HEAP_OFFSET    2048

+

+#define IBFT_ROUNDUP(size)  NET_ROUNDUP ((size), EFI_ACPI_ISCSI_BOOT_FIRMWARE_TABLE_STRUCTURE_ALIGNMENT)

+

+VOID

+IScsiPublishIbft (

+  IN VOID

+  );

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiImpl.h b/MdeModulePkg/Universal/iScsi/IScsiImpl.h
new file mode 100644
index 0000000..715f814
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiImpl.h
@@ -0,0 +1,161 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiImpl.h

+

+Abstract:

+

+--*/

+

+#ifndef _ISCSI_IMPL_H_

+#define _ISCSI_IMPL_H_

+

+#include <Library/NetLib.h>

+#include <Library/PrintLib.h>

+#include <Library/UefiRuntimeServicesTableLib.h>

+#include "IScsiCommon.h"

+#include "IScsiDriver.h"

+#include "IScsiConfigNVDataStruc.h"

+#include "IScsiExtScsiPassThru.h"

+#include "IScsiProto.h"

+#include "IScsiCHAP.h"

+#include "IScsiDhcp.h"

+#include "IScsiTcp4Io.h"

+#include "IScsiIbft.h"

+#include "IScsiMisc.h"

+#include "IScsiConfig.h"

+

+#define ISCSI_SESSION_SIGNATURE EFI_SIGNATURE_32 ('I', 'S', 'S', 'N')

+

+typedef struct _ISCSI_SESSION {

+  UINT32                    Signature;

+

+  ISCSI_SESSION_CONFIG_DATA ConfigData;

+  ISCSI_CHAP_AUTH_DATA      AuthData;

+

+  CHAR8                     InitiatorName[ISCSI_NAME_MAX_SIZE];

+  UINTN                     InitiatorNameLength;

+  UINT8                     State;

+

+  UINT8                     ISID[6];

+  UINT16                    TSIH;

+

+  UINT32                    CmdSN;

+  UINT32                    ExpCmdSN;

+  UINT32                    MaxCmdSN;

+

+  UINT32                    InitiatorTaskTag;

+  UINT16                    NextCID;

+

+  NET_LIST_ENTRY            Conns;

+  UINT32                    NumConns;

+

+  NET_LIST_ENTRY            TcbList;

+

+  //

+  // session-wide parameters

+  //

+  UINT16                    TargetPortalGroupTag;

+  UINT32                    MaxConnections;

+  BOOLEAN                   InitialR2T;

+  BOOLEAN                   ImmediateData;

+  UINT32                    MaxBurstLength;

+  UINT32                    FirstBurstLength;

+  UINT32                    DefaultTime2Wait;

+  UINT32                    DefaultTime2Retain;

+  UINT16                    MaxOutstandingR2T;

+  BOOLEAN                   DataPDUInOrder;

+  BOOLEAN                   DataSequenceInOrder;

+  UINT8                     ErrorRecoveryLevel;

+} ISCSI_SESSION;

+

+#define ISCSI_CONNECTION_SIGNATURE  EFI_SIGNATURE_32 ('I', 'S', 'C', 'N')

+

+typedef struct _ISCSI_CONNECTION {

+  UINT32            Signature;

+  NET_LIST_ENTRY    Link;

+

+  EFI_EVENT         TimeoutEvent;

+

+  ISCSI_SESSION     *Session;

+

+  UINT8             State;

+  UINT8             CurrentStage;

+  UINT8             NextStage;

+

+  UINT8             CHAPStep;

+

+  BOOLEAN           PartialReqSent;

+  BOOLEAN           PartialRspRcvd;

+

+  BOOLEAN           TransitInitiated;

+

+  UINT16            CID;

+  UINT32            ExpStatSN;

+

+  //

+  // queues...

+  //

+  NET_BUF_QUEUE     RspQue;

+

+  TCP4_IO           Tcp4Io;

+

+  //

+  // connection-only parameters

+  //

+  UINT32            MaxRecvDataSegmentLength;

+  ISCSI_DIGEST_TYPE HeaderDigest;

+  ISCSI_DIGEST_TYPE DataDigest;

+} ISCSI_CONNECTION;

+

+#define ISCSI_DRIVER_DATA_SIGNATURE EFI_SIGNATURE_32 ('I', 'S', 'D', 'A')

+

+#define ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU(PassThru) \

+  CR ( \

+  PassThru, \

+  ISCSI_DRIVER_DATA, \

+  IScsiExtScsiPassThru, \

+  ISCSI_DRIVER_DATA_SIGNATURE \

+  )

+#define ISCSI_DRIVER_DATA_FROM_IDENTIFIER(Identifier) \

+  CR ( \

+  Identifier, \

+  ISCSI_DRIVER_DATA, \

+  IScsiIdentifier, \

+  ISCSI_DRIVER_DATA_SIGNATURE \

+  )

+#define ISCSI_DRIVER_DATA_FROM_SESSION(s) \

+  CR ( \

+  s, \

+  ISCSI_DRIVER_DATA, \

+  Session, \

+  ISCSI_DRIVER_DATA_SIGNATURE \

+  )

+

+typedef struct _ISCSI_DRIVER_DATA {

+  UINT32                          Signature;

+  EFI_HANDLE                      Image;

+  EFI_HANDLE                      Controller;

+  ISCSI_PRIVATE_PROTOCOL          IScsiIdentifier;

+

+  EFI_EVENT                       ExitBootServiceEvent;

+

+  EFI_EXT_SCSI_PASS_THRU_PROTOCOL IScsiExtScsiPassThru;

+  EFI_EXT_SCSI_PASS_THRU_MODE     ExtScsiPassThruMode;

+  EFI_HANDLE                      ExtScsiPassThruHandle;

+  EFI_DEVICE_PATH_PROTOCOL        *DevicePath;

+

+  ISCSI_SESSION                   Session;

+} ISCSI_DRIVER_DATA;

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiInitiatorName.c b/MdeModulePkg/Universal/iScsi/IScsiInitiatorName.c
new file mode 100644
index 0000000..f026174
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiInitiatorName.c
@@ -0,0 +1,145 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiInitiatorName.c

+

+Abstract:

+

+  Implementation for EFI iSCSI Initiator Name Protocol.

+

+--*/

+

+#include "IScsiImpl.h"

+

+EFI_ISCSI_INITIATOR_NAME_PROTOCOL gIScsiInitiatorName = {

+  IScsiGetInitiatorName,

+  IScsiSetInitiatorName

+};

+

+EFI_STATUS

+EFIAPI

+IScsiGetInitiatorName (

+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,

+  IN OUT UINTN                              *BufferSize,

+  OUT    VOID                               *Buffer

+  )

+/*++

+

+Routine Description:

+

+  Retrieves the current set value of iSCSI Initiator Name. 

+

+Arguments:

+

+  This       - Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.

+  BufferSize - Size of the buffer in bytes pointed to by Buffer / Actual size of

+               the variable data buffer.

+  Buffer     - Pointer to the buffer for data to be read.

+

+Returns:

+

+  EFI_SUCCESS           - Data was successfully retrieved into the provided 

+                          buffer and the BufferSize was sufficient to handle the

+                          iSCSI initiator name.

+  EFI_BUFFER_TOO_SMALL  - BufferSize is too small for the result. BufferSize will

+                          be updated with the size required to complete the request.

+                          Buffer will not be affected.

+  EFI_INVALID_PARAMETER - BufferSize is NULL. BufferSize and Buffer will not be

+                          affected.

+  EFI_INVALID_PARAMETER - Buffer is NULL. BufferSize and Buffer will not be

+                          affected.

+  EFI_DEVICE_ERROR      - The iSCSI initiator name could not be retrieved due to

+                          a hardware error.

+

+--*/

+{

+  EFI_STATUS  Status;

+

+  if ((BufferSize == NULL) || (Buffer == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  Status = gRT->GetVariable (

+                  ISCSI_INITIATOR_NAME_VAR_NAME,

+                  &gEfiIScsiInitiatorNameProtocolGuid,

+                  NULL,

+                  BufferSize,

+                  Buffer

+                  );

+

+  return Status;

+}

+

+EFI_STATUS

+EFIAPI

+IScsiSetInitiatorName (

+  IN     EFI_ISCSI_INITIATOR_NAME_PROTOCOL  *This,

+  IN OUT UINTN                              *BufferSize,

+  OUT    VOID                               *Buffer

+  )

+/*++

+

+Routine Description:

+

+  Sets the iSSI Initiator Name. 

+

+Arguments:

+

+  This       - Pointer to the EFI_ISCSI_INITIATOR_NAME_PROTOCOL instance.

+  BufferSize - Size of the buffer in bytes pointed to by Buffer.

+  Buffer     - Pointer to the buffer for data to be written.

+

+Returns:

+

+  EFI_SUCCESS           - Data was successfully stored by the protocol.

+  EFI_UNSUPPORTED       - Platform policies do not allow for data to be written.

+  EFI_INVALID_PARAMETER - BufferSize exceeds the maximum allowed limit.

+                          BufferSize will be updated with the maximum size

+                          required to complete the request.

+  EFI_INVALID_PARAMETER - Buffersize is NULL. BufferSize and Buffer will not be

+                          affected.

+  EFI_INVALID_PARAMETER - Buffer is NULL. BufferSize and Buffer will not be affected.

+  EFI_DEVICE_ERROR      - The data could not be stored due to a hardware error.

+  EFI_OUT_OF_RESOURCES  - Not enough storage is available to hold the data

+  EFI_PROTOCOL_ERROR    - Input iSCSI initiator name does not adhere to RFC 3720

+

+--*/

+{

+  EFI_STATUS  Status;

+

+  if ((BufferSize == NULL) || (Buffer == NULL)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (*BufferSize > ISCSI_NAME_MAX_SIZE) {

+    *BufferSize = ISCSI_NAME_MAX_SIZE;

+    return EFI_INVALID_PARAMETER;

+  }

+  //

+  // only support iqn iSCSI names.

+  //

+  Status = IScsiNormalizeName ((CHAR8 *) Buffer, *BufferSize - 1);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gRT->SetVariable (

+                  ISCSI_INITIATOR_NAME_VAR_NAME,

+                  &gEfiIScsiInitiatorNameProtocolGuid,

+                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,

+                  *BufferSize,

+                  Buffer

+                  );

+

+  return Status;

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsiMisc.c b/MdeModulePkg/Universal/iScsi/IScsiMisc.c
new file mode 100644
index 0000000..0347d32
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiMisc.c
@@ -0,0 +1,965 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiMisc.c

+

+Abstract:

+

+  Miscellaneous routines for iSCSI driver.

+

+--*/

+

+#include "IScsiImpl.h"

+

+STATIC CONST CHAR8  IScsiHexString[] = "0123456789ABCDEFabcdef";

+

+static

+BOOLEAN

+IsHexDigit (

+  OUT UINT8      *Digit,

+  IN  CHAR16      Char

+  )

+/*++

+

+  Routine Description:

+    Determines if a Unicode character is a hexadecimal digit.

+    The test is case insensitive.

+

+  Arguments:

+    Digit - Pointer to byte that receives the value of the hex character.

+    Char  - Unicode character to test.

+

+  Returns:

+    TRUE  - If the character is a hexadecimal digit.

+    FALSE - Otherwise.

+

+--*/

+{

+  if ((Char >= L'0') && (Char <= L'9')) {

+    *Digit = (UINT8) (Char - L'0');

+    return TRUE;

+  }

+

+  if ((Char >= L'A') && (Char <= L'F')) {

+    *Digit = (UINT8) (Char - L'A' + 0x0A);

+    return TRUE;

+  }

+

+  if ((Char >= L'a') && (Char <= L'f')) {

+    *Digit = (UINT8) (Char - L'a' + 0x0A);

+    return TRUE;

+  }

+

+  return FALSE;

+}

+

+static

+VOID

+StrTrim (

+  IN OUT CHAR16   *str,

+  IN     CHAR16   CharC

+  )

+/*++

+

+Routine Description:

+  

+  Removes (trims) specified leading and trailing characters from a string.

+  

+Arguments: 

+  

+  str     - Pointer to the null-terminated string to be trimmed. On return, 

+            str will hold the trimmed string. 

+  CharC       - Character will be trimmed from str.

+  

+Returns:

+

+--*/

+{

+  CHAR16  *p1;

+  CHAR16  *p2;

+  

+  if (*str == 0) {

+    return;

+  }

+  

+  //

+  // Trim off the leading and trailing characters c

+  //

+  for (p1 = str; *p1 && *p1 == CharC; p1++) {

+    ;

+  }

+  

+  p2 = str;

+  if (p2 == p1) {

+    while (*p1) {

+      p2++;

+      p1++;

+    }

+  } else {

+    while (*p1) {    

+    *p2 = *p1;    

+    p1++;

+    p2++;

+    }

+    *p2 = 0;

+  }

+  

+  

+  for (p1 = str + StrLen(str) - 1; p1 >= str && *p1 == CharC; p1--) {

+    ;

+  }

+  if  (p1 !=  str + StrLen(str) - 1) { 

+    *(p1 + 1) = 0;

+  }

+}

+

+UINT8

+IScsiGetSubnetMaskPrefixLength (

+  IN EFI_IPv4_ADDRESS  *SubnetMask

+  )

+/*++

+

+Routine Description:

+

+  Calculate the prefix length of the IPv4 subnet mask.

+

+Arguments:

+

+  SubnetMask - The IPv4 subnet mask.

+

+Returns:

+

+  The prefix length of the subnet mask.

+

+--*/

+{

+  UINT8   Len;

+  UINT32  ReverseMask;

+

+  //

+  // The SubnetMask is in network byte order.

+  //

+  ReverseMask = (SubnetMask->Addr[0] << 24) | (SubnetMask->Addr[1] << 16) | (SubnetMask->Addr[2] << 8) | (SubnetMask->Addr[3]);

+

+  //

+  // Reverse it.

+  //

+  ReverseMask = ~ReverseMask;

+

+  if (ReverseMask & (ReverseMask + 1)) {

+    return 0;

+  }

+

+  Len = 0;

+

+  while (ReverseMask != 0) {

+    ReverseMask = ReverseMask >> 1;

+    Len++;

+  }

+

+  return 32 - Len;

+}

+

+EFI_STATUS

+IScsiAsciiStrToLun (

+  IN  CHAR8  *Str,

+  OUT UINT8  *Lun

+  )

+/*++

+

+Routine Description:

+

+  Convert the hexadecimal encoded LUN string into the 64-bit LUN. 

+

+Arguments:

+

+  Str - The hexadecimal encoded LUN string.

+  Lun - Storage to return the 64-bit LUN.

+

+Returns:

+

+  EFI_SUCCESS           - The 64-bit LUN is stored in Lun.

+  EFI_INVALID_PARAMETER - The string is malformatted.

+

+--*/

+{

+  UINT32  Index;

+  CHAR8   *LunUnitStr[4];

+  CHAR8   Digit;

+

+  NetZeroMem (Lun, 8);

+  NetZeroMem (LunUnitStr, sizeof (LunUnitStr));

+

+  Index         = 0;

+  LunUnitStr[0] = Str;

+

+  if (!IsHexDigit (&Digit, *Str)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  while (*Str != '\0') {

+    //

+    // Legal representations of LUN:

+    //   4752-3A4F-6b7e-2F99,

+    //   6734-9-156f-127,

+    //   4186-9

+    //

+    if (*Str == '-') {

+      *Str = '\0';

+      Index++;

+

+      if (*(Str + 1) != '\0') {

+        if (!IsHexDigit (&Digit, *(Str + 1))) {

+          return EFI_INVALID_PARAMETER;

+        }

+

+        LunUnitStr[Index] = Str + 1;

+      }

+    } else if (!IsHexDigit (&Digit, *Str)) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    Str++;

+  }

+

+  for (Index = 0; (Index < 4) && (LunUnitStr[Index] != NULL); Index++) {

+    if (AsciiStrLen (LunUnitStr[Index]) > 4) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    *((UINT16 *) &Lun[Index * 2]) = HTONS (AsciiStrHexToUintn (LunUnitStr[Index]));

+  }

+

+  return EFI_SUCCESS;

+}

+

+VOID

+IScsiLunToUnicodeStr (

+  IN UINT8    *Lun,

+  OUT CHAR16  *Str

+  )

+/*++

+

+Routine Description:

+

+  Convert the 64-bit LUN into the hexadecimal encoded LUN string.

+

+Arguments:

+

+  Lun - The 64-bit LUN.

+  Str - The storage to return the hexadecimal encoded LUN string.

+

+Returns:

+

+  None.

+

+--*/

+{

+  UINTN   Index;

+  CHAR16  *TempStr;

+

+  TempStr = Str;

+

+  for (Index = 0; Index < 4; Index++) {

+

+    if ((Lun[2 * Index] | Lun[2 * Index + 1]) == 0) {

+      StrCpy (TempStr, L"0-");

+    } else {

+      TempStr[0]  = (CHAR16) IScsiHexString[Lun[2 * Index] >> 4];

+      TempStr[1]  = (CHAR16) IScsiHexString[Lun[2 * Index] & 0xf];

+      TempStr[2]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] >> 4];

+      TempStr[3]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] & 0xf];

+      TempStr[4]  = L'-';

+      TempStr[5]  = 0;

+

+      StrTrim (TempStr, L'0');

+    }

+

+    TempStr += StrLen (TempStr);

+  }

+

+  Str[StrLen (Str) - 1] = 0;

+

+  for (Index = StrLen (Str) - 1; Index > 1; Index = Index - 2) {

+    if ((Str[Index] == L'0') && (Str[Index - 1] == L'-')) {

+      Str[Index - 1] = 0;

+    } else {

+      break;

+    }

+  }

+}

+

+CHAR16 *

+IScsiAsciiStrToUnicodeStr (

+  IN  CHAR8   *Source,

+  OUT CHAR16  *Destination

+  )

+/*++

+

+Routine Description:

+

+  Convert the ASCII string into a UNICODE string.

+

+Arguments:

+

+  Source      - The ASCII string.

+  Destination - The storage to return the UNICODE string.

+

+Returns:

+

+  Pointer to the UNICODE string.

+

+--*/

+{

+  ASSERT (Destination != NULL);

+  ASSERT (Source != NULL);

+

+  while (*Source != '\0') {

+    *(Destination++) = (CHAR16) *(Source++);

+  }

+

+  *Destination = '\0';

+

+  return Destination;

+}

+

+CHAR8 *

+IScsiUnicodeStrToAsciiStr (

+  IN  CHAR16  *Source,

+  OUT CHAR8   *Destination

+  )

+/*++

+

+Routine Description:

+

+  Convert the UNICODE string into an ASCII string.

+

+Arguments:

+

+  Source      - The UNICODE string.

+  Destination - The storage to return the ASCII string.

+

+Returns:

+

+  Pointer to the ASCII string.

+

+--*/

+{

+  ASSERT (Destination != NULL);

+  ASSERT (Source != NULL);

+

+  while (*Source != '\0') {

+    //

+    // If any Unicode characters in Source contain

+    // non-zero value in the upper 8 bits, then ASSERT().

+    //

+    ASSERT (*Source < 0x100);

+    *(Destination++) = (CHAR8) *(Source++);

+  }

+

+  *Destination = '\0';

+

+  return Destination;

+}

+

+EFI_STATUS

+IScsiAsciiStrToIp (

+  IN  CHAR8             *Str,

+  OUT EFI_IPv4_ADDRESS  *Ip

+  )

+/*++

+

+Routine Description:

+

+  Convert the decimal dotted IPv4 address into the binary IPv4 address.

+

+Arguments:

+

+  Str - The UNICODE string.

+  Ip  - The storage to return the ASCII string.

+

+Returns:

+

+  EFI_SUCCESS           - The binary IP address is returned in Ip.

+  EFI_INVALID_PARAMETER - The IP string is malformatted.

+

+--*/

+{

+  UINTN Index;

+  UINTN Number;

+

+  Index = 0;

+

+  while (*Str) {

+

+    if (Index > 3) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    Number = 0;

+    while (NET_IS_DIGIT (*Str)) {

+      Number = Number * 10 + (*Str - '0');

+      Str++;

+    }

+

+    if (Number > 0xFF) {

+      return EFI_INVALID_PARAMETER;

+    }

+

+    Ip->Addr[Index] = (UINT8) Number;

+

+    if ((*Str != '\0') && (*Str != '.')) {

+      //

+      // The current character should be either the NULL terminator or

+      // the dot delimiter.

+      //

+      return EFI_INVALID_PARAMETER;

+    }

+

+    if (*Str == '.') {

+      //

+      // Skip the delimiter.

+      //

+      Str++;

+    }

+

+    Index++;

+  }

+

+  if (Index != 4) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  return EFI_SUCCESS;

+}

+

+VOID

+IScsiMacAddrToStr (

+  IN  EFI_MAC_ADDRESS  *Mac,

+  IN  UINT32           Len,

+  OUT CHAR16           *Str

+  )

+/*++

+

+Routine Description:

+

+  Convert the mac address into a hexadecimal encoded "-" seperated string.

+

+Arguments:

+

+  Mac - The mac address.

+  Len - Length in bytes of the mac address.

+  Str - The storage to return the mac string.

+

+Returns:

+

+  None.

+

+--*/

+{

+  UINT32  Index;

+

+  for (Index = 0; Index < Len; Index++) {

+    Str[3 * Index]      = NibbleToHexChar (Mac->Addr[Index] >> 4);

+    Str[3 * Index + 1]  = NibbleToHexChar (Mac->Addr[Index]);

+    Str[3 * Index + 2]  = L'-';

+  }

+

+  Str[3 * Index - 1] = L'\0';

+}

+

+EFI_STATUS

+IScsiBinToHex (

+  IN     UINT8  *BinBuffer,

+  IN     UINT32 BinLength,

+  IN OUT CHAR8  *HexStr,

+  IN OUT UINT32 *HexLength

+  )

+/*++

+

+Routine Description:

+

+  Convert the binary encoded buffer into a hexadecimal encoded string.

+

+Arguments:

+

+  BinBuffer - The buffer containing the binary data.

+  BinLength - Length of the binary buffer.

+  HexStr    - Pointer to the string.

+  HexLength - The length of the string.

+

+Returns:

+

+  EFI_SUCCESS          - The binary data is converted to the hexadecimal string 

+                         and the length of the string is updated.

+  EFI_BUFFER_TOO_SMALL - The string is too small.

+

+--*/

+{

+  UINTN Index;

+

+  if ((HexStr == NULL) || (BinBuffer == NULL) || (BinLength == 0)) {

+    return EFI_INVALID_PARAMETER;

+  }

+

+  if (((*HexLength) - 3) < BinLength * 2) {

+    *HexLength = BinLength * 2 + 3;

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  *HexLength = BinLength * 2 + 3;

+  //

+  // Prefix for Hex String

+  //

+  HexStr[0] = '0';

+  HexStr[1] = 'x';

+

+  for (Index = 0; Index < BinLength; Index++) {

+    HexStr[Index * 2 + 2] = IScsiHexString[BinBuffer[Index] >> 4];

+    HexStr[Index * 2 + 3] = IScsiHexString[BinBuffer[Index] & 0xf];

+  }

+

+  HexStr[Index * 2 + 2] = '\0';

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+IScsiHexToBin (

+  IN OUT UINT8  *BinBuffer,

+  IN OUT UINT32 *BinLength,

+  IN     CHAR8  *HexStr

+  )

+/*++

+

+Routine Description:

+

+  Convert the hexadecimal string into a binary encoded buffer.

+

+Arguments:

+

+  BinBuffer - The binary buffer.

+  BinLength - Length of the binary buffer.

+  HexStr    - The hexadecimal string.

+

+Returns:

+

+  EFI_SUCCESS          - The hexadecimal string is converted into a binary 

+                         encoded buffer.

+  EFI_BUFFER_TOO_SMALL - The binary buffer is too small to hold the converted data.s

+

+--*/

+{

+  UINTN   Index;

+  UINT32  HexCount;

+  CHAR8   *HexBuf;

+  UINT8   Digit;

+  UINT8   Byte;

+

+  //

+  // Find out how many hex characters the string has.

+  //

+  HexBuf = HexStr;

+  if ((HexBuf[0] == '0') && ((HexBuf[1] == 'x') || (HexBuf[1] == 'X'))) {

+    HexBuf += 2;

+  }

+

+  for (Index = 0, HexCount = 0; IsHexDigit (&Digit, HexBuf[Index]); Index++, HexCount++)

+    ;

+

+  if (HexCount == 0) {

+    *BinLength = 0;

+    return EFI_SUCCESS;

+  }

+  //

+  // Test if buffer is passed enough.

+  //

+  if (((HexCount + 1) / 2) > *BinLength) {

+    *BinLength = (HexCount + 1) / 2;

+    return EFI_BUFFER_TOO_SMALL;

+  }

+

+  *BinLength = (HexCount + 1) / 2;

+

+  for (Index = 0; Index < HexCount; Index++) {

+

+    IsHexDigit (&Digit, HexBuf[HexCount - 1 - Index]);

+

+    if ((Index & 1) == 0) {

+      Byte = Digit;

+    } else {

+      Byte = BinBuffer[*BinLength - 1 - Index / 2];

+      Byte &= 0x0F;

+      Byte |= Digit << 4;

+    }

+

+    BinBuffer[*BinLength - 1 - Index / 2] = Byte;

+  }

+

+  return EFI_SUCCESS;

+}

+

+VOID

+IScsiGenRandom (

+  IN OUT UINT8  *Rand,

+  IN     UINTN  RandLength

+  )

+/*++

+

+Routine Description:

+

+  Generate random numbers.

+

+Arguments:

+

+  Rand       - The buffer to contain random numbers.

+  RandLength - The length of the Rand buffer.

+

+Returns:

+

+  None.

+

+--*/

+{

+  UINT32  Random;

+

+  while (RandLength > 0) {

+    Random  = NET_RANDOM (NetRandomInitSeed ());

+    *Rand++ = (UINT8) (Random);

+    RandLength--;

+  }

+}

+

+ISCSI_DRIVER_DATA *

+IScsiCreateDriverData (

+  IN EFI_HANDLE  Image,

+  IN EFI_HANDLE  Controller

+  )

+/*++

+

+Routine Description:

+

+  Create the iSCSI driver data..

+

+Arguments:

+

+  Image      - The handle of the driver image.

+  Controller - The handle of the controller.

+

+Returns:

+

+  The iSCSI driver data created.

+

+--*/

+{

+  ISCSI_DRIVER_DATA *Private;

+  EFI_STATUS        Status;

+

+  Private = NetAllocateZeroPool (sizeof (ISCSI_DRIVER_DATA));

+  if (Private == NULL) {

+    return NULL;

+  }

+

+  Private->Signature  = ISCSI_DRIVER_DATA_SIGNATURE;

+  Private->Image      = Image;

+  Private->Controller = Controller;

+

+  //

+  // Create an event to be signal when the BS to RT transition is triggerd so

+  // as to abort the iSCSI session.

+  //

+  Status = gBS->CreateEvent (

+                  EFI_EVENT_SIGNAL_EXIT_BOOT_SERVICES,

+                  TPL_CALLBACK,

+                  IScsiOnExitBootService,

+                  Private,

+                  &Private->ExitBootServiceEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    NetFreePool (Private);

+    return NULL;

+  }

+

+  NetCopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL));

+

+  //

+  // 0 is designated to the TargetId, so use another value for the AdapterId.

+  //

+  Private->ExtScsiPassThruMode.AdapterId = 2;

+  Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;

+  Private->ExtScsiPassThruMode.IoAlign  = 4;

+  Private->IScsiExtScsiPassThru.Mode    = &Private->ExtScsiPassThruMode;

+

+  //

+  // Install the Ext SCSI PASS THRU protocol.

+  //

+  Status = gBS->InstallProtocolInterface (

+                  &Private->ExtScsiPassThruHandle,

+                  &gEfiExtScsiPassThruProtocolGuid,

+                  EFI_NATIVE_INTERFACE,

+                  &Private->IScsiExtScsiPassThru

+                  );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseEvent (Private->ExitBootServiceEvent);

+    NetFreePool (Private);

+

+    return NULL;

+  }

+

+  IScsiSessionInit (&Private->Session, FALSE);

+

+  return Private;

+}

+

+VOID

+IScsiCleanDriverData (

+  IN ISCSI_DRIVER_DATA  *Private

+  )

+/*++

+

+Routine Description:

+

+  Clean the iSCSI driver data.

+

+Arguments:

+

+  Private - The iSCSI driver data.

+

+Returns:

+

+ None.

+

+--*/

+{

+  if (Private->DevicePath != NULL) {

+    gBS->UninstallProtocolInterface (

+          Private->ExtScsiPassThruHandle,

+          &gEfiDevicePathProtocolGuid,

+          Private->DevicePath

+          );

+

+    NetFreePool (Private->DevicePath);

+  }

+

+  if (Private->ExtScsiPassThruHandle != NULL) {

+    gBS->UninstallProtocolInterface (

+          Private->ExtScsiPassThruHandle,

+          &gEfiExtScsiPassThruProtocolGuid,

+          &Private->IScsiExtScsiPassThru

+          );

+  }

+

+  gBS->CloseEvent (Private->ExitBootServiceEvent);

+

+  NetFreePool (Private);

+}

+

+EFI_STATUS

+IScsiGetConfigData (

+  IN ISCSI_DRIVER_DATA  *Private

+  )

+/*++

+

+Routine Description:

+

+  Get the various configuration data of this iSCSI instance.

+

+Arguments:

+

+  Private - The iSCSI driver data.

+

+Returns:

+

+  EFI_SUCCESS   - The configuration of this instance is got.

+  EFI_NOT_FOUND - This iSCSI instance is not configured yet.

+

+--*/

+{

+  EFI_STATUS                  Status;

+  ISCSI_SESSION               *Session;

+  UINTN                       BufferSize;

+  EFI_SIMPLE_NETWORK_PROTOCOL *Snp;

+  EFI_SIMPLE_NETWORK_MODE     *Mode;

+  CHAR16                      MacString[65];

+

+  //

+  // get the iSCSI Initiator Name

+  //

+  Session                       = &Private->Session;

+  Session->InitiatorNameLength  = ISCSI_NAME_MAX_SIZE;

+  Status = gIScsiInitiatorName.Get (

+                                &gIScsiInitiatorName,

+                                &Session->InitiatorNameLength,

+                                Session->InitiatorName

+                                );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->HandleProtocol (

+                  Private->Controller,

+                  &gEfiSimpleNetworkProtocolGuid,

+                  &Snp

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Mode = Snp->Mode;

+

+  //

+  // Get the mac string, it's the name of various variable

+  //

+  IScsiMacAddrToStr (&Mode->PermanentAddress, Mode->HwAddressSize, MacString);

+

+  //

+  // Get the normal configuration.

+  //

+  BufferSize = sizeof (Session->ConfigData.NvData);

+  Status = gRT->GetVariable (

+                  MacString,

+                  &gEfiIScsiInitiatorNameProtocolGuid,

+                  NULL,

+                  &BufferSize,

+                  &Session->ConfigData.NvData

+                  );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (!Session->ConfigData.NvData.Enabled) {

+    return EFI_ABORTED;

+  }

+  //

+  // Get the CHAP Auth information.

+  //

+  BufferSize = sizeof (Session->AuthData.AuthConfig);

+  Status = gRT->GetVariable (

+                  MacString,

+                  &mIScsiCHAPAuthInfoGuid,

+                  NULL,

+                  &BufferSize,

+                  &Session->AuthData.AuthConfig

+                  );

+

+  if (!EFI_ERROR (Status) && Session->ConfigData.NvData.InitiatorInfoFromDhcp) {

+    //

+    // Start dhcp.

+    //

+    Status = IScsiDoDhcp (Private->Image, Private->Controller, &Session->ConfigData);

+  }

+

+  return Status;

+}

+

+EFI_DEVICE_PATH_PROTOCOL *

+IScsiGetTcpConnDevicePath (

+  IN ISCSI_DRIVER_DATA  *Private

+  )

+/*++

+

+Routine Description:

+

+  Get the device path of the iSCSI tcp connection and update it.

+

+Arguments:

+

+  Private - The iSCSI driver data.

+

+Returns:

+

+  The updated device path.

+

+--*/

+{

+  ISCSI_SESSION             *Session;

+  ISCSI_CONNECTION          *Conn;

+  TCP4_IO                   *Tcp4Io;

+  EFI_DEVICE_PATH_PROTOCOL  *DevicePath;

+  EFI_STATUS                Status;

+  EFI_DEV_PATH              *DPathNode;

+

+  Session = &Private->Session;

+  if (Session->State != SESSION_STATE_LOGGED_IN) {

+    return NULL;

+  }

+

+  Conn = NET_LIST_USER_STRUCT_S (

+          Session->Conns.ForwardLink,

+          ISCSI_CONNECTION,

+          Link,

+          ISCSI_CONNECTION_SIGNATURE

+          );

+  Tcp4Io = &Conn->Tcp4Io;

+

+  Status = gBS->HandleProtocol (

+                  Tcp4Io->Handle,

+                  &gEfiDevicePathProtocolGuid,

+                  &DevicePath

+                  );

+  if (EFI_ERROR (Status)) {

+    return NULL;

+  }

+  //

+  // Duplicate it.

+  //

+  DevicePath  = DuplicateDevicePath (DevicePath);

+

+  DPathNode   = (EFI_DEV_PATH *) DevicePath;

+

+  while (!IsDevicePathEnd (&DPathNode->DevPath)) {

+    if ((DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) &&

+        (DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP)

+        ) {

+

+      DPathNode->Ipv4.LocalPort       = 0;

+      DPathNode->Ipv4.StaticIpAddress = !Session->ConfigData.NvData.InitiatorInfoFromDhcp;

+      break;

+    }

+

+    DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath);

+  }

+

+  return DevicePath;

+}

+

+VOID

+EFIAPI

+IScsiOnExitBootService (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+/*++

+

+Routine Description:

+

+  Abort the session when the transition from BS to RT is initiated.

+

+Arguments:

+

+  Event   - The event signaled.

+  Context - The iSCSI driver data.

+

+Returns:

+

+  None.

+

+--*/

+{

+  ISCSI_DRIVER_DATA *Private;

+

+  Private = (ISCSI_DRIVER_DATA *) Context;

+  gBS->CloseEvent (Private->ExitBootServiceEvent);

+

+  IScsiSessionAbort (&Private->Session);

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsiMisc.h b/MdeModulePkg/Universal/iScsi/IScsiMisc.h
new file mode 100644
index 0000000..33c5184
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiMisc.h
@@ -0,0 +1,143 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiMisc.h

+

+Abstract:

+

+  Miscellaneous definitions for iSCSI driver.

+

+--*/

+

+#ifndef _ISCSI_MISC_H_

+#define _ISCSI_MISC_H_

+

+#pragma pack(1)

+typedef struct _ISCSI_SESSION_CONFIG_NVDATA {

+  BOOLEAN           Enabled;

+

+  BOOLEAN           InitiatorInfoFromDhcp;

+  EFI_IPv4_ADDRESS  LocalIp;

+  EFI_IPv4_ADDRESS  SubnetMask;

+  EFI_IPv4_ADDRESS  Gateway;

+

+  BOOLEAN           TargetInfoFromDhcp;

+  CHAR8             TargetName[ISCSI_NAME_MAX_SIZE];

+  EFI_IPv4_ADDRESS  TargetIp;

+  UINT16            TargetPort;

+  UINT8             BootLun[8];

+} ISCSI_SESSION_CONFIG_NVDATA;

+#pragma pack()

+

+typedef struct _ISCSI_SESSION_CONFIG_DATA {

+  ISCSI_SESSION_CONFIG_NVDATA NvData;

+

+  EFI_IPv4_ADDRESS            PrimaryDns;

+  EFI_IPv4_ADDRESS            SecondaryDns;

+  EFI_IPv4_ADDRESS            DhcpServer;

+} ISCSI_SESSION_CONFIG_DATA;

+

+UINT8

+IScsiGetSubnetMaskPrefixLength (

+  IN EFI_IPv4_ADDRESS  *SubnetMask

+  );

+

+EFI_STATUS

+IScsiAsciiStrToLun (

+  IN  CHAR8  *Str,

+  OUT UINT8  *Lun

+  );

+

+VOID

+IScsiLunToUnicodeStr (

+  IN UINT8    *Lun,

+  OUT CHAR16  *String

+  );

+

+CHAR16                    *

+IScsiAsciiStrToUnicodeStr (

+  IN  CHAR8   *Source,

+  OUT CHAR16  *Destination

+  );

+

+CHAR8                     *

+IScsiUnicodeStrToAsciiStr (

+  IN  CHAR16  *Source,

+  OUT CHAR8   *Destination

+  );

+

+VOID

+IScsiMacAddrToStr (

+  IN  EFI_MAC_ADDRESS  *Mac,

+  IN  UINT32           Len,

+  OUT CHAR16           *Str

+  );

+

+EFI_STATUS

+IScsiAsciiStrToIp (

+  IN  CHAR8             *Str,

+  OUT EFI_IPv4_ADDRESS  *Ip

+  );

+

+EFI_STATUS

+IScsiBinToHex (

+  IN     UINT8  *BinBuffer,

+  IN     UINT32 BinLength,

+  IN OUT CHAR8  *HexStr,

+  IN OUT UINT32 *HexLength

+  );

+

+EFI_STATUS

+IScsiHexToBin (

+  IN OUT UINT8  *BinBuffer,

+  IN OUT UINT32 *BinLength,

+  IN     CHAR8  *HexStr

+  );

+

+VOID

+IScsiGenRandom (

+  IN OUT UINT8  *Rand,

+  IN     UINTN  RandLength

+  );

+

+ISCSI_DRIVER_DATA         *

+IScsiCreateDriverData (

+  IN EFI_HANDLE  Image,

+  IN EFI_HANDLE  Controller

+  );

+

+VOID

+IScsiCleanDriverData (

+  IN ISCSI_DRIVER_DATA  *Private

+  );

+

+EFI_STATUS

+IScsiGetConfigData (

+  IN ISCSI_DRIVER_DATA  *Private

+  );

+

+EFI_DEVICE_PATH_PROTOCOL  *

+IScsiGetTcpConnDevicePath (

+  IN ISCSI_DRIVER_DATA  *Private

+  );

+

+VOID

+EFIAPI

+IScsiOnExitBootService (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  );

+

+extern CHAR16 NibbleToHexChar(UINT8 Nibble);

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiProto.c b/MdeModulePkg/Universal/iScsi/IScsiProto.c
new file mode 100644
index 0000000..4cef3dc
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiProto.c
@@ -0,0 +1,3080 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiProto.c

+

+Abstract:

+

+--*/

+

+#include "IScsiImpl.h"

+

+static UINT32 mDataSegPad = 0;

+

+VOID

+IScsiAttatchConnection (

+  IN ISCSI_SESSION     *Session,

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  Attach the iSCSI connection to the iSCSI session. 

+

+Arguments:

+

+  Session - The iSCSI session.

+  Conn    - The iSCSI connection.

+

+Returns:

+

+  None.

+

+--*/

+{

+  NetListInsertTail (&Session->Conns, &Conn->Link);

+  Conn->Session = Session;

+  Session->NumConns++;

+}

+

+VOID

+IScsiDetatchConnection (

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  Detach the iSCSI connection from the session it belongs to. 

+

+Arguments:

+

+  Conn - The iSCSI connection.

+

+Returns:

+

+  None.

+

+--*/

+{

+  NetListRemoveEntry (&Conn->Link);

+  Conn->Session->NumConns--;

+  Conn->Session = NULL;

+}

+

+EFI_STATUS

+IScsiCheckSN (

+  IN UINT32  *ExpSN,

+  IN UINT32  NewSN

+  )

+/*++

+

+Routine Description:

+

+  Check the sequence number according to RFC3720. 

+

+Arguments:

+

+  ExpSN - The currently expected sequence number.

+  NewSN - The sequence number to check.

+

+Returns:

+

+  EFI_SUCCESS - The check passed and the ExpSN is increased.

+

+--*/

+{

+  if (!ISCSI_SEQ_EQ (NewSN, *ExpSN)) {

+    if (ISCSI_SEQ_LT (NewSN, *ExpSN)) {

+      //

+      // Duplicate

+      //

+      return EFI_NOT_READY;

+    } else {

+      return EFI_PROTOCOL_ERROR;

+    }

+  } else {

+    //

+    // Advance the ExpSN

+    //

+    (*ExpSN)++;

+    return EFI_SUCCESS;

+  }

+}

+

+VOID

+IScsiUpdateCmdSN (

+  IN ISCSI_SESSION  *Session,

+  IN UINT32         MaxCmdSN,

+  IN UINT32         ExpCmdSN

+  )

+/*++

+

+Routine Description:

+

+  Update the sequence numbers for the iSCSI command.

+

+Arguments:

+

+  Session  - The iSCSI session.

+  MaxCmdSN - Maximum CmdSN from the target.

+  ExpCmdSN - Next expected CmdSN from the target.

+

+Returns:

+

+  None.

+

+--*/

+{

+  if (ISCSI_SEQ_LT (MaxCmdSN, ExpCmdSN - 1)) {

+    return ;

+  }

+

+  if (ISCSI_SEQ_GT (MaxCmdSN, Session->MaxCmdSN)) {

+    Session->MaxCmdSN = MaxCmdSN;

+  }

+

+  if (ISCSI_SEQ_GT (ExpCmdSN, Session->ExpCmdSN)) {

+    Session->ExpCmdSN = ExpCmdSN;

+  }

+}

+

+EFI_STATUS

+IScsiConnLogin (

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  This function does the iSCSI connection login.

+

+Arguments:

+

+  Conn - The iSCSI connection to login.

+

+Returns:

+

+  EFI_SUCCESS        - The iSCSI connection is logged into the iSCSI target.

+  EFI_TIMEOUT        - Timeout happened during the login procedure.

+  EFI_PROTOCOL_ERROR - Some kind of iSCSI protocol error happened.

+

+--*/

+{

+  EFI_STATUS  Status;

+

+  //

+  // Start the timer, wait 16 seconds to establish the TCP connection.

+  //

+  Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, 16 * TICKS_PER_SECOND);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+  //

+  // try to establish the tcp connection

+  //

+  Status = Tcp4IoConnect (&Conn->Tcp4Io, Conn->TimeoutEvent);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  gBS->SetTimer (Conn->TimeoutEvent, TimerCancel, 0);

+  Conn->State = CONN_STATE_IN_LOGIN;

+

+  //

+  // connection is established, start the iSCSI Login

+  //

+  do {

+    Status = IScsiSendLoginReq (Conn);

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+

+    Status = IScsiReceiveLoginRsp (Conn);

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+  } while (Conn->CurrentStage != ISCSI_FULL_FEATURE_PHASE);

+

+  return Status;

+}

+

+VOID

+IScsiConnReset (

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  Reset the iSCSI connection.

+

+Arguments:

+

+  Conn - The iSCSI connection to reset.

+

+Returns:

+

+  None.

+

+--*/

+{

+  Tcp4IoReset (&Conn->Tcp4Io);

+}

+

+ISCSI_CONNECTION *

+IScsiCreateConnection (

+  IN ISCSI_DRIVER_DATA  *Private,

+  IN ISCSI_SESSION      *Session

+  )

+/*++

+

+Routine Description:

+

+  Create a TCP connection for the iSCSI session.

+

+Arguments:

+

+  Private - The iSCSI driver data.

+  Session - Maximum CmdSN from the target.

+

+Returns:

+

+  The newly created iSCSI connection.

+

+--*/

+{

+  ISCSI_CONNECTION    *Conn;

+  TCP4_IO_CONFIG_DATA Tcp4IoConfig;

+  EFI_STATUS          Status;

+

+  Conn = NetAllocatePool (sizeof (ISCSI_CONNECTION));

+  if (Conn == NULL) {

+    return NULL;

+  }

+

+  Conn->Signature       = ISCSI_CONNECTION_SIGNATURE;

+  Conn->State           = CONN_STATE_FREE;

+  Conn->CurrentStage    = ISCSI_SECURITY_NEGOTIATION;

+  Conn->NextStage       = ISCSI_LOGIN_OPERATIONAL_NEGOTIATION;

+  Conn->CHAPStep        = ISCSI_CHAP_INITIAL;

+  Conn->ExpStatSN       = 0;

+  Conn->PartialReqSent  = FALSE;

+  Conn->PartialRspRcvd  = FALSE;

+  Conn->CID             = Session->NextCID++;

+

+  Status = gBS->CreateEvent (

+                  EFI_EVENT_TIMER,

+                  NET_TPL_TIMER,

+                  NULL,

+                  NULL,

+                  &Conn->TimeoutEvent

+                  );

+  if (EFI_ERROR (Status)) {

+    NetFreePool (Conn);

+    return NULL;

+  }

+

+  NetbufQueInit (&Conn->RspQue);

+

+  //

+  // set the default connection-only parameters

+  //

+  Conn->MaxRecvDataSegmentLength  = MAX_RECV_DATA_SEG_LEN_IN_FFP;

+  Conn->HeaderDigest              = ISCSI_DIGEST_NONE;

+  Conn->DataDigest                = ISCSI_DIGEST_NONE;

+

+  NetCopyMem (&Tcp4IoConfig.LocalIp, &Session->ConfigData.NvData.LocalIp, sizeof (EFI_IPv4_ADDRESS));

+  NetCopyMem (&Tcp4IoConfig.SubnetMask, &Session->ConfigData.NvData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+  NetCopyMem (&Tcp4IoConfig.Gateway, &Session->ConfigData.NvData.Gateway, sizeof (EFI_IPv4_ADDRESS));

+  NetCopyMem (&Tcp4IoConfig.RemoteIp, &Session->ConfigData.NvData.TargetIp, sizeof (EFI_IPv4_ADDRESS));

+

+  Tcp4IoConfig.RemotePort = Session->ConfigData.NvData.TargetPort;

+

+  //

+  // Create the tcp4 IO for this connection

+  //

+  Status = Tcp4IoCreateSocket (

+            Private->Image,

+            Private->Controller,

+            &Tcp4IoConfig,

+            &Conn->Tcp4Io

+            );

+  if (EFI_ERROR (Status)) {

+    gBS->CloseEvent (Conn->TimeoutEvent);

+    NetFreePool (Conn);

+    Conn = NULL;

+  }

+

+  return Conn;

+}

+

+VOID

+IScsiDestroyConnection (

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  Destroy an iSCSI connection.

+

+Arguments:

+

+  Conn - The connection to destroy.

+

+Returns:

+

+  None.

+

+--*/

+{

+  Tcp4IoDestroySocket (&Conn->Tcp4Io);

+  NetbufQueFlush (&Conn->RspQue);

+  gBS->CloseEvent (Conn->TimeoutEvent);

+  NetFreePool (Conn);

+}

+

+EFI_STATUS

+IScsiSessionLogin (

+  IN ISCSI_DRIVER_DATA  *Private

+  )

+/*++

+

+Routine Description:

+

+  Login the iSCSI session.

+

+Arguments:

+

+  Private - The iSCSI driver data.

+

+Returns:

+

+  EFI_SUCCESS          - The iSCSI session login procedure finished.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+  EFI_PROTOCOL_ERROR   - Some kind of iSCSI protocol error happened.

+

+--*/

+{

+  EFI_STATUS        Status;

+  ISCSI_SESSION     *Session;

+  ISCSI_CONNECTION  *Conn;

+  EFI_TCP4_PROTOCOL *Tcp4;

+

+  Session = &Private->Session;

+

+  //

+  // Create a connection for the session.

+  //

+  Conn = IScsiCreateConnection (Private, Session);

+  if (Conn == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  IScsiAttatchConnection (Session, Conn);

+

+  //

+  // Login througth the newly created connection.

+  //

+  Status = IScsiConnLogin (Conn);

+  if (EFI_ERROR (Status)) {

+    IScsiConnReset (Conn);

+    IScsiDetatchConnection (Conn);

+    IScsiDestroyConnection (Conn);

+  } else {

+    Session->State = SESSION_STATE_LOGGED_IN;

+

+    gBS->OpenProtocol (

+          Conn->Tcp4Io.Handle,

+          &gEfiTcp4ProtocolGuid,

+          (VOID **)&Tcp4,

+          Private->Image,

+          Private->ExtScsiPassThruHandle,

+          EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER

+          );

+  }

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiSendLoginReq (

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  Build and send the iSCSI login request to the iSCSI target according to

+  the current login stage.

+

+Arguments:

+

+  Conn - The connection in the iSCSI login phase.

+

+Returns:

+

+  EFI_SUCCESS          - The iSCSI login request PDU is built and sent on this

+                         connection.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+  EFI_PROTOCOL_ERROR   - Some kind of iSCSI protocol error happened.

+

+--*/

+{

+  NET_BUF     *Pdu;

+  EFI_STATUS  Status;

+

+  //

+  // build the Login Request PDU

+  //

+  Pdu = IScsiPrepareLoginReq (Conn);

+  if (Pdu == NULL) {

+    return EFI_DEVICE_ERROR;

+  }

+  //

+  // Send it to the iSCSI target.

+  //

+  Status = Tcp4IoTransmit (&Conn->Tcp4Io, Pdu);

+

+  NetbufFree (Pdu);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiReceiveLoginRsp (

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  Receive and process the iSCSI login response.

+

+Arguments:

+

+  Conn - The connection in the iSCSI login phase.

+

+Returns:

+

+  EFI_SUCCESS          - The iSCSI login response PDU is received and processed.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+  EFI_PROTOCOL_ERROR   - Some kind of iSCSI protocol error happened.

+

+--*/

+{

+  EFI_STATUS  Status;

+  NET_BUF     *Pdu;

+

+  //

+  // Receive the iSCSI login response.

+  //

+  Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+  //

+  // A Login Response is received, process it.

+  //

+  Status = IScsiProcessLoginRsp (Conn, Pdu);

+

+  NetbufFree (Pdu);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiAddKeyValuePair (

+  IN NET_BUF          *Pdu,

+  IN CHAR8            *Key,

+  IN CHAR8            *Value

+  )

+/*++

+

+Routine Description:

+

+  Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.

+  The DataSegmentLength and the actual size of the net buffer containing this PDU will be

+  updated.

+

+Arguments:

+

+  Pdu   - The iSCSI PDU whose data segment the key-value pair will be added to.

+  Key   - The key name string.

+  Value - The value string.

+

+Returns:

+

+  EFI_SUCCESS          - The key-valu pair is added to the PDU's datasegment and

+                         the correspondence length fields are updated.

+  EFI_OUT_OF_RESOURCES - There is not enough space in the PDU to add the key-value

+                         pair.

+

+--*/

+{

+  UINT32              DataSegLen;

+  UINT32              KeyLen;

+  UINT32              ValueLen;

+  UINT32              TotalLen;

+  ISCSI_LOGIN_REQUEST *LoginReq;

+  CHAR8               *Data;

+

+  LoginReq    = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL);

+  DataSegLen  = NTOH24 (LoginReq->DataSegmentLength);

+

+  KeyLen      = (UINT32) AsciiStrLen (Key);

+  ValueLen    = (UINT32) AsciiStrLen (Value);

+

+  //

+  // 1 byte for the key value separator '=' and 1 byte for the null

+  // delimiter after the value.

+  //

+  TotalLen = KeyLen + 1 + ValueLen + 1;

+

+  //

+  // Allocate the space for the key-value pair.

+  //

+  Data = NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL);

+  if (Data == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // Add the key.

+  //

+  NetCopyMem (Data, Key, KeyLen);

+  Data += KeyLen;

+

+  *Data = '=';

+  Data++;

+

+  //

+  // Add the value.

+  //

+  NetCopyMem (Data, Value, ValueLen);

+  Data += ValueLen;

+

+  *Data = '\0';

+

+  //

+  // update the DataSegmentLength

+  //

+  ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen);

+

+  return EFI_SUCCESS;

+}

+

+NET_BUF *

+IScsiPrepareLoginReq (

+  IN ISCSI_CONNECTION  *Conn

+  )

+/*++

+

+Routine Description:

+

+  Prepare the iSCSI login request to be sent according to the current login status.

+

+Arguments:

+

+  Conn - The connection in the iSCSI login phase.

+

+Returns:

+

+  The pointer to the net buffer containing the iSCSI login request built.

+

+--*/

+{

+  ISCSI_SESSION       *Session;

+  NET_BUF             *Nbuf;

+  ISCSI_LOGIN_REQUEST *LoginReq;

+  EFI_STATUS          Status;

+

+  Session = Conn->Session;

+

+  Nbuf    = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN);

+  if (Nbuf == NULL) {

+    return NULL;

+  }

+

+  LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufAllocSpace (Nbuf, sizeof (ISCSI_LOGIN_REQUEST), NET_BUF_TAIL);

+  NetZeroMem (LoginReq, sizeof (ISCSI_LOGIN_REQUEST));

+

+  //

+  // Init the login request pdu

+  //

+  ISCSI_SET_OPCODE (LoginReq, ISCSI_OPCODE_LOGIN_REQ, ISCSI_REQ_IMMEDIATE);

+  ISCSI_SET_STAGES (LoginReq, Conn->CurrentStage, Conn->NextStage);

+  LoginReq->VersionMax        = ISCSI_VERSION_MAX;

+  LoginReq->VersionMin        = ISCSI_VERSION_MIN;

+  LoginReq->TSIH              = HTONS (Session->TSIH);

+  LoginReq->InitiatorTaskTag  = HTONL (Session->InitiatorTaskTag);

+  LoginReq->CID               = HTONS (Conn->CID);

+  LoginReq->CmdSN             = HTONL (Session->CmdSN);

+

+  //

+  // For the first Login Request on a coonection this is ExpStatSN for the

+  // old connection and this field is only valid if the Login Request restarts

+  // a connection.

+  // For subsequent Login Requests it is used to acknowledge the Login Responses

+  // with their increasing StatSN values.

+  //

+  LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN);

+  NetCopyMem (LoginReq->ISID, Session->ISID, sizeof (LoginReq->ISID));

+

+  if (Conn->PartialRspRcvd) {

+    //

+    // A partial response, initiator must send an empty Login Request.

+    //

+    return Nbuf;

+  }

+

+  switch (Conn->CurrentStage) {

+  case ISCSI_SECURITY_NEGOTIATION:

+    Status = IScsiCHAPToSendReq (Conn, Nbuf);

+    break;

+

+  case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:

+    Status = IScsiFillOpParams (Conn, Nbuf);

+    ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);

+    break;

+

+  default:

+    //

+    // something error happens...

+    //

+    Status = EFI_DEVICE_ERROR;

+    break;

+  }

+

+  if (EFI_ERROR (Status)) {

+    NetbufFree (Nbuf);

+    Nbuf = NULL;

+  } else {

+    //

+    // Pad the data segment if needed.

+    //

+    IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq));

+    //

+    // Check whether we will issue the stage transition signal?

+    //

+    Conn->TransitInitiated = ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);

+  }

+

+  return Nbuf;

+}

+

+EFI_STATUS

+IScsiProcessLoginRsp (

+  IN ISCSI_CONNECTION  *Conn,

+  IN NET_BUF           *Pdu

+  )

+/*++

+

+Routine Description:

+

+  Process the iSCSI Login Response.

+

+Arguments:

+

+  Conn - The connection on which the iSCSI login response is received.

+  Pdu  - The iSCSI login response PDU.

+

+Returns:

+

+  EFI_SUCCESS        - The iSCSI login response PDU is processed and all check are passed.

+  EFI_PROTOCOL_ERROR - Some kind of iSCSI protocol error happened.

+

+--*/

+{

+  EFI_STATUS            Status;

+  ISCSI_SESSION         *Session;

+  ISCSI_LOGIN_RESPONSE  *LoginRsp;

+  BOOLEAN               Transit;

+  BOOLEAN               Continue;

+  UINT8                 CurrentStage;

+  UINT8                 NextStage;

+  UINT8                 *DataSeg;

+  UINT32                DataSegLen;

+

+  Session   = Conn->Session;

+

+  LoginRsp  = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);

+  if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) {

+    //

+    // It's not a Login Response

+    //

+    return EFI_PROTOCOL_ERROR;

+  }

+  //

+  // Get the data segment if any.

+  //

+  DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp);

+  if (DataSegLen != 0) {

+    DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL);

+  } else {

+    DataSeg = NULL;

+  }

+  //

+  // Check the status class in the login response PDU.

+  //

+  switch (LoginRsp->StatusClass) {

+  case ISCSI_LOGIN_STATUS_SUCCESS:

+    //

+    // Just break here, the response and the data segment will be processed later.

+    //

+    break;

+

+  case ISCSI_LOGIN_STATUS_REDIRECTION:

+    //

+    // The target may be moved to a different address

+    //

+    if (DataSeg == NULL) {

+      return EFI_PROTOCOL_ERROR;

+    }

+    //

+    // Process the TargetAddress key-value strings in the data segment to update the

+    // target address info.

+    //

+    Status = IScsiUpdateTargetAddress (Session, DataSeg, DataSegLen);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+    //

+    // Session will be restarted on this error status because the Target is

+    // redirected by this Login Response.

+    //

+    return EFI_MEDIA_CHANGED;

+

+  default:

+    //

+    // Initiator Error, Target Error, or any other undefined error code.

+    //

+    return EFI_PROTOCOL_ERROR;

+  }

+  //

+  // The status is sucess, extract the wanted fields from the header segment.

+  //

+  Transit                     = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT);

+  Continue                    = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE);

+

+  CurrentStage                = ISCSI_GET_CURRENT_STAGE (LoginRsp);

+  NextStage                   = ISCSI_GET_NEXT_STAGE (LoginRsp);

+

+  LoginRsp->InitiatorTaskTag  = NTOHL (LoginRsp->InitiatorTaskTag);

+

+  if ((Transit && Continue) ||

+      (CurrentStage != Conn->CurrentStage) ||

+      (!Conn->TransitInitiated && Transit) ||

+      (Transit && (NextStage != Conn->NextStage)) ||

+      (NetCompareMem (Session->ISID, LoginRsp->ISID, sizeof (LoginRsp->ISID)) != 0) ||

+      (LoginRsp->InitiatorTaskTag != Session->InitiatorTaskTag)

+      ) {

+    //

+    // A Login Response with the C bit set to 1 MUST have the T bit set to 0;

+    // The CSG in the Login Response MUST be the same with the I-end of this connection;

+    // The T bit can't be 1 if the last Login Response sent by the initiator doesn't

+    // initiate the transistion;

+    // The NSG MUST be the same with the I-end of this connection if Transit is required.

+    // The ISID in the Login Response MUST be the same with this session.

+    //

+    return EFI_PROTOCOL_ERROR;

+  }

+

+  LoginRsp->StatSN    = NTOHL (LoginRsp->StatSN);

+  LoginRsp->ExpCmdSN  = NTOHL (LoginRsp->ExpCmdSN);

+  LoginRsp->MaxCmdSN  = NTOHL (LoginRsp->MaxCmdSN);

+

+  if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->CHAPStep == ISCSI_CHAP_INITIAL)) {

+    //

+    // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN

+    // and ExpCmdSN.

+    //

+    Conn->ExpStatSN   = LoginRsp->StatSN + 1;

+    Session->MaxCmdSN = LoginRsp->MaxCmdSN;

+    Session->ExpCmdSN = LoginRsp->ExpCmdSN;

+  } else {

+    //

+    // Check the StatSN of this PDU

+    //

+    Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN);

+    if (!EFI_ERROR (Status)) {

+      //

+      // Update the MaxCmdSN and ExpCmdSN

+      //

+      IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN);

+    } else {

+      return Status;

+    }

+  }

+  //

+  // Trim off the header segment.

+  //

+  NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD);

+

+  //

+  // Queue this login response first in case it's a partial response so that

+  // later when the full response list is received we can combine these scattered

+  // responses' data segment and then process it.

+  //

+  NET_GET_REF (Pdu);

+  NetbufQueAppend (&Conn->RspQue, Pdu);

+

+  Conn->PartialRspRcvd = Continue;

+  if (Continue) {

+    //

+    // It's a partial response, have to wait for another or more Request/Response

+    // conversations to get the full response.

+    //

+    return EFI_SUCCESS;

+  }

+

+  switch (CurrentStage) {

+  case ISCSI_SECURITY_NEGOTIATION:

+    //

+    // In security negotiation stage, let CHAP module handle it.

+    //

+    Status = IScsiCHAPOnRspReceived (Conn, Transit);

+    break;

+

+  case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:

+    //

+    // Response received with negotiation resonse on iSCSI parameters, check them.

+    //

+    Status = IScsiCheckOpParams (Conn, Transit);

+    break;

+

+  default:

+    //

+    // Should never get here.

+    //

+    Status = EFI_PROTOCOL_ERROR;

+    break;

+  }

+

+  if (Transit && (Status == EFI_SUCCESS)) {

+    //

+    // Do the state transition.

+    //

+    Conn->CurrentStage = Conn->NextStage;

+

+    if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) {

+      Conn->NextStage = ISCSI_FULL_FEATURE_PHASE;

+    } else {

+      //

+      // CurrentStage is iSCSI Full Feature, it's the Login-Final Response,

+      // get the TSIH from the Login Response.

+      //

+      Session->TSIH = NTOHS (LoginRsp->TSIH);

+    }

+  }

+  //

+  // Flush the response(s) received.

+  //

+  NetbufQueFlush (&Conn->RspQue);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiUpdateTargetAddress (

+  IN ISCSI_SESSION  *Session,

+  IN CHAR8          *Data,

+  IN UINT32         Len

+  )

+/*++

+

+Routine Description:

+

+  Updated the target information according the data received in the iSCSI

+  login response with an target redirection status.

+

+Arguments:

+

+  Session - The iSCSI session.

+  Data    - The data segment which should contain the TargetAddress key-value list.

+  Len     - Length of the data.

+

+Returns:

+

+  EFI_SUCCESS          - The target address is updated.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+  EFI_NOT_FOUND        - The TargetAddress key is not found.

+

+--*/

+{

+  NET_LIST_ENTRY  *KeyValueList;

+  CHAR8           *TargetAddress;

+  CHAR8           *IpStr;

+  EFI_STATUS      Status;

+  UINTN           Number;

+

+  KeyValueList = IScsiBuildKeyValueList (Data, Len);

+  if (KeyValueList == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = EFI_NOT_FOUND;

+

+  while (TRUE) {

+    TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);

+    if (TargetAddress == NULL) {

+      break;

+    }

+

+    if (!NET_IS_DIGIT (TargetAddress[0])) {

+      //

+      // The domainname of the target may be presented in three formats: a DNS host name,

+      // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted

+      // IPv4 address.

+      //

+      continue;

+    }

+

+    IpStr = TargetAddress;

+

+    while (*TargetAddress && (*TargetAddress != ':') && (*TargetAddress != ',')) {

+      //

+      // NULL, ':' or ',' ends the IPv4 string.

+      //

+      TargetAddress++;

+    }

+

+    if (*TargetAddress == ',') {

+      //

+      // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent

+      // as the result of a redirection.

+      //

+      continue;

+    } else if (*TargetAddress == ':') {

+      *TargetAddress = '\0';

+

+      TargetAddress++;

+

+      Number = AsciiStrDecimalToUintn (TargetAddress);

+      if (Number > 0xFFFF) {

+        continue;

+      } else {

+        Session->ConfigData.NvData.TargetPort = (UINT16) Number;

+      }

+    } else {

+      //

+      // The string only contains the IPv4 address. Use the well known port.

+      //

+      Session->ConfigData.NvData.TargetPort = ISCSI_WELL_KNOWN_PORT;

+    }

+    //

+    // Update the target IP address.

+    //

+    Status = IScsiAsciiStrToIp (IpStr, &Session->ConfigData.NvData.TargetIp);

+    if (EFI_ERROR (Status)) {

+      continue;

+    } else {

+      break;

+    }

+  }

+

+  IScsiFreeKeyValueList (KeyValueList);

+

+  return Status;

+}

+

+VOID

+IScsiFreeNbufList (

+  VOID *Arg

+  )

+/*++

+

+Routine Description:

+

+  The callback function to free the net buffer list.

+

+Arguments:

+

+  Arg - The opaque parameter.

+

+Returns:

+

+  None.

+

+--*/

+{

+  ASSERT (Arg != NULL);

+

+  NetbufFreeList ((NET_LIST_ENTRY *) Arg);

+  NetFreePool (Arg);

+}

+

+VOID

+IScsiNbufExtFree (

+  VOID *Arg

+  )

+/*++

+

+Routine Description:

+

+  The callback function called in NetBufFree, it does nothing.

+

+Arguments:

+

+  Arg - The opaque parameter.

+

+Returns:

+

+  None.

+

+--*/

+{

+}

+

+EFI_STATUS

+IScsiReceivePdu (

+  IN ISCSI_CONNECTION                      *Conn,

+  OUT NET_BUF                              **Pdu,

+  IN ISCSI_IN_BUFFER_CONTEXT               *Context, OPTIONAL

+  IN BOOLEAN                               HeaderDigest,

+  IN BOOLEAN                               DataDigest,

+  IN EFI_EVENT                             TimeoutEvent OPTIONAL

+  )

+/*++

+

+Routine Description:

+

+  Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and

+  an optional data segment. The two parts will be put into two blocks of buffers in the

+  net buffer. The digest check will be conducted in this function if needed and the digests

+  will be trimmed from the PDU buffer.

+

+Arguments:

+

+  Conn         - The iSCSI connection to receive data from.

+  Pdu          - The received iSCSI pdu.

+  Context      - The context used to describe information on the caller provided

+                 buffer to receive data segment of the iSCSI pdu, it's optional.

+  HeaderDigest - Whether there will be header digest received.

+  DataDigest   - Whether there will be data digest.

+  TimeoutEvent - The timeout event, it's optional.

+

+Returns:

+

+  EFI_SUCCESS - An iSCSI pdu is received.

+  EFI_TIMEOUT - Timeout happenend.

+

+--*/

+{

+  NET_LIST_ENTRY  *NbufList;

+  UINT32          Len;

+  NET_BUF         *PduHdr;

+  UINT8           *Header;

+  EFI_STATUS      Status;

+  UINT32          PadLen;

+  UINT32          InDataOffset;

+  NET_FRAGMENT    Fragment[2];

+  UINT32          FragmentCount;

+  NET_BUF         *DataSeg;

+  UINT32          PadAndCRC32[2];

+

+  NbufList = NetAllocatePool (sizeof (NET_LIST_ENTRY));

+  if (NbufList == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  NetListInit (NbufList);

+

+  //

+  // The header digest will be received together with the PDU header if exists.

+  //

+  Len     = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0);

+  PduHdr  = NetbufAlloc (Len);

+  if (PduHdr == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);

+  NetListInsertTail (NbufList, &PduHdr->List);

+

+  //

+  // First step, receive the BHS of the PDU.

+  //

+  Status = Tcp4IoReceive (&Conn->Tcp4Io, PduHdr, FALSE, TimeoutEvent);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  if (HeaderDigest) {

+    //

+    // TODO: check the header-digest.

+    //

+    //

+    // Trim off the digest.

+    //

+    NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL);

+  }

+

+  Len = ISCSI_GET_DATASEG_LEN (Header);

+  if (Len == 0) {

+    //

+    // No data segment.

+    //

+    goto FORM_PDU;

+  }

+  //

+  // Get the length of the padding bytes of the data segment.

+  //

+  PadLen = ISCSI_GET_PAD_LEN (Len);

+

+  switch (ISCSI_GET_OPCODE (Header)) {

+  case ISCSI_OPCODE_SCSI_DATA_IN:

+    //

+    // Try to use the buffer described by Context if the PDU is an

+    // iSCSI SCSI data in pdu so as to reduce memory copy overhead.

+    //

+    InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header);

+    if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) {

+      Status = EFI_PROTOCOL_ERROR;

+      goto ON_EXIT;

+    }

+

+    Fragment[0].Len   = Len;

+    Fragment[0].Bulk  = Context->InData + InDataOffset;

+

+    if (DataDigest || (PadLen != 0)) {

+      //

+      // The data segment is padded, use two fragments to receive it.

+      // The first to receive the useful data. The second to receive the padding.

+      //

+      Fragment[1].Len   = PadLen + (DataDigest ? sizeof (UINT32) : 0);

+      Fragment[1].Bulk  = (UINT8 *) ((UINTN) &PadAndCRC32[1] - PadLen);

+

+      FragmentCount     = 2;

+    } else {

+      FragmentCount = 1;

+    }

+

+    DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);

+    if (DataSeg == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    break;

+

+  case ISCSI_OPCODE_SCSI_RSP:

+  case ISCSI_OPCODE_NOP_IN:

+  case ISCSI_OPCODE_LOGIN_RSP:

+  case ISCSI_OPCODE_TEXT_RSP:

+  case ISCSI_OPCODE_ASYNC_MSG:

+  case ISCSI_OPCODE_REJECT:

+  case ISCSI_OPCODE_VENDOR_T0:

+  case ISCSI_OPCODE_VENDOR_T1:

+  case ISCSI_OPCODE_VENDOR_T2:

+    //

+    // Allocate buffer to receive the data segment.

+    //

+    Len += PadLen + (DataDigest ? sizeof (UINT32) : 0);

+    DataSeg = NetbufAlloc (Len);

+    if (DataSeg == NULL) {

+      Status = EFI_OUT_OF_RESOURCES;

+      goto ON_EXIT;

+    }

+

+    NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);

+    break;

+

+  default:

+    Status = EFI_PROTOCOL_ERROR;

+    goto ON_EXIT;

+  }

+

+  NetListInsertTail (NbufList, &DataSeg->List);

+

+  //

+  // Receive the data segment with the data digest if any.

+  //

+  Status = Tcp4IoReceive (&Conn->Tcp4Io, DataSeg, FALSE, TimeoutEvent);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  if (DataDigest) {

+    //

+    // TODO: Check the data digest.

+    //

+    NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL);

+  }

+

+  if (PadLen != 0) {

+    //

+    // Trim off the padding bytes in the data segment.

+    //

+    NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL);

+  }

+

+FORM_PDU:

+  //

+  // Form the pdu from a list of pdu segments.

+  //

+  *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);

+  if (*Pdu == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+  }

+

+ON_EXIT:

+

+  if (EFI_ERROR (Status)) {

+    //

+    // Free the Nbufs in this NbufList and the NbufList itself.

+    //

+    IScsiFreeNbufList (NbufList);

+  }

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiCheckOpParams (

+  IN ISCSI_CONNECTION  *Conn,

+  IN BOOLEAN           Transit

+  )

+/*++

+

+Routine Description:

+

+  Check and get the result of the prameter negotiation.

+

+Arguments:

+

+  Conn - The connection in iSCSI login.

+  Pdu  - The iSCSI response PDU containing the parameter list.

+

+Returns:

+

+  EFI_SUCCESS        - The parmeter check is passed and negotiation is finished.

+  EFI_PROTOCOL_ERROR - Some kind of iSCSI protocol error happened.

+

+--*/

+{

+  EFI_STATUS      Status;

+  NET_LIST_ENTRY  *KeyValueList;

+  CHAR8           *Data;

+  UINT32          Len;

+  ISCSI_SESSION   *Session;

+  CHAR8           *Value;

+  UINTN           NumericValue;

+

+  ASSERT (Conn->RspQue.BufNum != 0);

+

+  Session = Conn->Session;

+

+  Len     = Conn->RspQue.BufSize;

+  Data    = NetAllocatePool (Len);

+  if (Data == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  NetbufQueCopy (&Conn->RspQue, 0, Len, Data);

+

+  Status = EFI_PROTOCOL_ERROR;

+

+  //

+  // Extract the Key-Value pairs into a list.

+  //

+  KeyValueList = IScsiBuildKeyValueList (Data, Len);

+  if (KeyValueList == NULL) {

+    NetFreePool (Data);

+    return Status;

+  }

+  //

+  // HeaderDigest

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  if (AsciiStrCmp (Value, "CRC32") == 0) {

+    if (Conn->HeaderDigest != ISCSI_DIGEST_CRC32) {

+      goto ON_ERROR;

+    }

+  } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {

+    Conn->HeaderDigest = ISCSI_DIGEST_NONE;

+  } else {

+    goto ON_ERROR;

+  }

+  //

+  // DataDigest

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  if (AsciiStrCmp (Value, "CRC32") == 0) {

+    if (Conn->DataDigest != ISCSI_DIGEST_CRC32) {

+      goto ON_ERROR;

+    }

+  } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {

+    Conn->DataDigest = ISCSI_DIGEST_NONE;

+  } else {

+    goto ON_ERROR;

+  }

+  //

+  // ErrorRecoveryLevel, result fuction is Minimum.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  NumericValue = AsciiStrDecimalToUintn (Value);

+  if (NumericValue > 2) {

+    goto ON_ERROR;

+  }

+

+  Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue);

+

+  //

+  // InitialR2T, result function is OR.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  Session->InitialR2T = Session->InitialR2T || (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);

+

+  //

+  // ImmediateData, result function is AND.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  Session->ImmediateData = Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);

+

+  //

+  // MaxRecvDataSegmentLength, result function is Mininum.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH);

+  if (Value != NULL) {

+    //

+    // MaxRecvDataSegmentLength is declarative.

+    //

+    NumericValue                    = AsciiStrDecimalToUintn (Value);

+

+    Conn->MaxRecvDataSegmentLength  = (UINT32) MIN (Conn->MaxRecvDataSegmentLength, NumericValue);

+  }

+  //

+  // MaxBurstLength, result funtion is Mininum.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  NumericValue            = AsciiStrDecimalToUintn (Value);

+  Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue);

+

+  //

+  // FirstBurstLength, result function is Minimum. Irrelevant when InitialR2T=Yes and

+  // ImmediateData=No.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);

+  if ((Value == NULL) && !(Session->InitialR2T && !Session->ImmediateData)) {

+    goto ON_ERROR;

+  }

+

+  NumericValue              = AsciiStrDecimalToUintn (Value);

+  Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue);

+

+  //

+  // MaxConnections, result function is Minimum.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  NumericValue = AsciiStrDecimalToUintn (Value);

+  if ((NumericValue == 0) || (NumericValue > 65535)) {

+    goto ON_ERROR;

+  }

+

+  Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue);

+

+  //

+  // DataPDUInOrder, result function is OR.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  Session->DataPDUInOrder = Session->DataPDUInOrder || (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);

+

+  //

+  // DataSequenceInorder, result function is OR.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  Session->DataSequenceInOrder = Session->DataSequenceInOrder || (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);

+

+  //

+  // DefaultTime2Wait, result function is Maximum.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  NumericValue = AsciiStrDecimalToUintn (Value);

+  if (NumericValue == 0) {

+    Session->DefaultTime2Wait = 0;

+  } else if (NumericValue > 3600) {

+    goto ON_ERROR;

+  } else {

+    Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue);

+  }

+  //

+  // DefaultTime2Retain, result function is Minimum.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  NumericValue = AsciiStrDecimalToUintn (Value);

+  if (NumericValue == 0) {

+    Session->DefaultTime2Retain = 0;

+  } else if (NumericValue > 3600) {

+    goto ON_ERROR;

+  } else {

+    Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue);

+  }

+  //

+  // MaxOutstandingR2T, result function is Minimum.

+  //

+  Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T);

+  if (Value == NULL) {

+    goto ON_ERROR;

+  }

+

+  NumericValue = AsciiStrDecimalToUintn (Value);

+  if ((NumericValue == 0) || (NumericValue > 65535)) {

+    goto ON_ERROR;

+  }

+

+  Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue);

+

+  //

+  // Remove declarative key-value paris if any.

+  //

+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE);

+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS);

+  IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);

+

+  if (NetListIsEmpty (KeyValueList)) {

+    //

+    // Succeed if no more keys in the list.

+    //

+    Status = EFI_SUCCESS;

+  }

+

+ON_ERROR:

+

+  IScsiFreeKeyValueList (KeyValueList);

+

+  NetFreePool (Data);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiFillOpParams (

+  IN ISCSI_CONNECTION  *Conn,

+  IN NET_BUF           *Pdu

+  )

+/*++

+

+Routine Description:

+

+  Fill the oprational prameters.

+

+Arguments:

+

+  Conn - The connection in iSCSI login.

+  Pdu  - The iSCSI login request PDU to fill the parameters.

+

+Returns:

+

+  EFI_SUCCESS          - The parmeters are filled into the iSCSI login request PDU.

+  EFI_OUT_OF_RESOURCES - There is not enough space in the PDU to hold the parameters.

+

+--*/

+{

+  ISCSI_SESSION *Session;

+  CHAR8         Value[256];

+

+  Session = Conn->Session;

+

+  AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == ISCSI_DIGEST_CRC32) ? "None,CRC32" : "None");

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == ISCSI_DIGEST_CRC32) ? "None,CRC32" : "None");

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Conn->MaxRecvDataSegmentLength);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);

+

+  AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);

+  IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+IScsiPadSegment (

+  IN NET_BUF  *Pdu,

+  IN UINT32   Len

+  )

+/*++

+

+Routine Description:

+

+  Pad the iSCSI AHS or data segment to an integer number of 4 byte words.

+

+Arguments:

+

+  Pdu - The iSCSI pdu which contains segments to pad.

+  Len - The length of the last semgnet in the PDU.

+

+Returns:

+

+  EFI_SUCCESS          - The segment is padded or no need to pad it.

+  EFI_OUT_OF_RESOURCES - There is not enough remaining free space to add the

+                         padding bytes.

+

+--*/

+{

+  UINT32  PadLen;

+  UINT8   *Data;

+

+  PadLen = ISCSI_GET_PAD_LEN (Len);

+

+  if (PadLen != 0) {

+    Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);

+    if (Data == NULL) {

+      return EFI_OUT_OF_RESOURCES;

+    }

+

+    NetZeroMem (Data, PadLen);

+  }

+

+  return EFI_SUCCESS;

+}

+

+NET_LIST_ENTRY *

+IScsiBuildKeyValueList (

+  IN CHAR8  *Data,

+  IN UINT32 Len

+  )

+/*++

+

+Routine Description:

+

+  Build a key-value list from the data segment.

+

+Arguments:

+

+  Data - The data segment containing the key-value pairs.

+  Len  - Length of the data segment.

+

+Returns:

+

+  The key-value list.

+

+--*/

+{

+  NET_LIST_ENTRY        *ListHead;

+  ISCSI_KEY_VALUE_PAIR  *KeyValuePair;

+

+  ListHead = NetAllocatePool (sizeof (NET_LIST_ENTRY));

+  if (ListHead == NULL) {

+    return NULL;

+  }

+

+  NetListInit (ListHead);

+

+  while (Len > 0) {

+    KeyValuePair = NetAllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));

+    if (KeyValuePair == NULL) {

+      goto ON_ERROR;

+    }

+

+    NetListInit (&KeyValuePair->List);

+

+    KeyValuePair->Key = Data;

+

+    while ((Len > 0) && (*Data != '=')) {

+      Len--;

+      Data++;

+    }

+

+    if (*Data == '=') {

+      *Data = '\0';

+

+      Data++;

+      Len--;

+    } else {

+      NetFreePool (KeyValuePair);

+      goto ON_ERROR;

+    }

+

+    KeyValuePair->Value = Data;

+

+    NetListInsertTail (ListHead, &KeyValuePair->List);;

+

+    Data += AsciiStrLen (KeyValuePair->Value) + 1;

+    Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;

+  }

+

+  return ListHead;

+

+ON_ERROR:

+

+  IScsiFreeKeyValueList (ListHead);

+

+  return NULL;

+}

+

+CHAR8 *

+IScsiGetValueByKeyFromList (

+  IN NET_LIST_ENTRY  *KeyValueList,

+  IN CHAR8           *Key

+  )

+/*++

+

+Routine Description:

+

+  Get the value string by the key name from the key-value list. If found,

+  the key-value entry will be removed from the list.

+

+Arguments:

+

+  KeyValueList - The key-value list.

+  Key          - The key name to find.

+

+Returns:

+

+  The value string.

+

+--*/

+{

+  NET_LIST_ENTRY        *Entry;

+  ISCSI_KEY_VALUE_PAIR  *KeyValuePair;

+  CHAR8                 *Value;

+

+  Value = NULL;

+

+  NET_LIST_FOR_EACH (Entry, KeyValueList) {

+    KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);

+

+    if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {

+      Value = KeyValuePair->Value;

+

+      NetListRemoveEntry (&KeyValuePair->List);

+      NetFreePool (KeyValuePair);

+      break;

+    }

+  }

+

+  return Value;

+}

+

+VOID

+IScsiFreeKeyValueList (

+  IN NET_LIST_ENTRY  *KeyValueList

+  )

+/*++

+

+Routine Description:

+

+  Free the key-value list.

+

+Arguments:

+

+  KeyValueList - The key-value list.

+

+Returns:

+

+  None.

+

+--*/

+{

+  NET_LIST_ENTRY        *Entry;

+  ISCSI_KEY_VALUE_PAIR  *KeyValuePair;

+

+  while (!NetListIsEmpty (KeyValueList)) {

+    Entry         = NetListRemoveHead (KeyValueList);

+    KeyValuePair  = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);

+

+    NetFreePool (KeyValuePair);

+  }

+

+  NetFreePool (KeyValueList);

+}

+

+EFI_STATUS

+IScsiNormalizeName (

+  IN CHAR8  *Name,

+  IN UINTN  Len

+  )

+/*++

+

+Routine Description:

+

+  Normalize the iSCSI name according to RFC.

+

+Arguments:

+

+  Name - The iSCSI name.

+  Len  - length of the iSCSI name.

+

+Returns:

+

+  EFI_SUCCESS        - The iSCSI name is valid and normalized.

+  EFI_PROTOCOL_ERROR - The iSCSI name is mal-formatted or not in the IQN format.

+

+--*/

+{

+  UINTN Index;

+

+  for (Index = 0; Index < Len; Index++) {

+    if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {

+      //

+      // Convert the upper-case characters to lower-case ones

+      //

+      Name[Index] = Name[Index] - 'A' + 'a';

+    }

+

+    if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&

+        !NET_IS_DIGIT (Name[Index]) &&

+        (Name[Index] != '-') &&

+        (Name[Index] != '.') &&

+        (Name[Index] != ':')

+        ) {

+      //

+      // ASCII dash, dot, colon lower-case characters and digit characters

+      // are allowed.

+      //

+      return EFI_PROTOCOL_ERROR;

+    }

+  }

+

+  if ((Len < 4) || (NetCompareMem (Name, "iqn.", 4) != 0)) {

+    //

+    // Only IQN format is accepted now.

+    //

+    return EFI_PROTOCOL_ERROR;

+  }

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+IScsiNewTcb (

+  IN  ISCSI_CONNECTION  *Conn,

+  OUT ISCSI_TCB         **Tcb

+  )

+/*++

+

+Routine Description:

+

+  Create an iSCSI task control block.

+

+Arguments:

+

+  Conn - The connection on which the task control block will be created.

+  Tcb  - The newly created task control block.

+

+Returns:

+

+  EFI_SUCCESS          - The task control block is created.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+

+--*/

+{

+  ISCSI_SESSION *Session;

+  ISCSI_TCB     *NewTcb;

+

+  ASSERT (Tcb != NULL);

+

+  Session = Conn->Session;

+

+  if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {

+    return EFI_NOT_READY;

+  }

+

+  NewTcb = NetAllocateZeroPool (sizeof (ISCSI_TCB));

+  if (NewTcb == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  NetListInit (&NewTcb->Link);

+

+  NewTcb->SoFarInOrder      = TRUE;

+  NewTcb->InitiatorTaskTag  = Session->InitiatorTaskTag;

+  NewTcb->CmdSN             = Session->CmdSN;

+  NewTcb->Conn              = Conn;

+

+  NetListInsertTail (&Session->TcbList, &NewTcb->Link);

+

+  //

+  // Advance the initiator task tag.

+  //

+  Session->InitiatorTaskTag++;

+  Session->CmdSN++;

+

+  *Tcb = NewTcb;

+

+  return EFI_SUCCESS;

+}

+

+VOID

+IScsiDelTcb (

+  IN ISCSI_TCB  *Tcb

+  )

+/*++

+

+Routine Description:

+

+  Delete the tcb from the connection and destroy it.

+

+Arguments:

+

+  Tcb - The tcb to delete.

+

+Returns:

+

+  None.

+

+--*/

+{

+  NetListRemoveEntry (&Tcb->Link);

+

+  NetFreePool (Tcb);

+}

+

+ISCSI_TCB *

+IScsiFindTcbByITT (

+  IN NET_LIST_ENTRY  *TcbList,

+  IN UINT32          InitiatorTaskTag

+  )

+/*++

+

+Routine Description:

+

+  Find the task control block by the initator task tag.

+

+Arguments:

+

+  TcbList          - The tcb list.

+  InitiatorTaskTag - The initiator task tag.

+

+Returns:

+

+  The task control block found.

+

+--*/

+{

+  ISCSI_TCB       *Tcb;

+  NET_LIST_ENTRY  *Entry;

+

+  Tcb = NULL;

+

+  NET_LIST_FOR_EACH (Entry, TcbList) {

+    Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);

+

+    if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {

+      break;

+    }

+

+    Tcb = NULL;

+  }

+

+  return Tcb;

+}

+

+NET_BUF *

+IScsiNewDataSegment (

+  IN UINT8    *Data,

+  IN UINT32   Len,

+  IN BOOLEAN  DataDigest

+  )

+/*++

+

+Routine Description:

+

+  Create a data segment, pad it and calculate the CRC if needed.

+

+Arguments:

+

+  Data       - The data to fill into the data segment.

+  Len        - Length of the data.

+  DataDigest - Whether to calculate CRC for this data segment.

+

+Returns:

+

+  The net buffer wrapping the data segment.

+

+--*/

+{

+  NET_FRAGMENT  Fragment[2];

+  UINT32        FragmentCount;

+  UINT32        PadLen;

+  NET_BUF       *DataSeg;

+

+  Fragment[0].Len   = Len;

+  Fragment[0].Bulk  = Data;

+

+  PadLen            = ISCSI_GET_PAD_LEN (Len);

+  if (PadLen != 0) {

+    Fragment[1].Len   = PadLen;

+    Fragment[1].Bulk  = (UINT8 *) &mDataSegPad;

+

+    FragmentCount     = 2;

+  } else {

+    FragmentCount = 1;

+  }

+

+  DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);

+

+  return DataSeg;

+}

+

+NET_BUF *

+IScsiNewScsiCmdPdu (

+  IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,

+  IN UINT64                                     Lun,

+  IN ISCSI_TCB                                  *Tcb

+  )

+/*++

+

+Routine Description:

+

+  Create a iSCSI SCSI command PDU to encapsulate the command issued

+  by SCSI through the EXT SCSI PASS THRU Protocol.

+

+Arguments:

+

+  Packet - The EXT SCSI PASS THRU request packet containing the SCSI command.

+  Lun    - The LUN.

+  Tcb    - The tcb assocated with this SCSI command.

+

+Returns:

+

+  The created iSCSI SCSI command PDU.

+

+--*/

+{

+  NET_LIST_ENTRY                  *NbufList;

+  NET_BUF                         *Pdu;

+  NET_BUF                         *PduHeader;

+  NET_BUF                         *DataSeg;

+  SCSI_COMMAND                    *ScsiCmd;

+  UINT8                           AHSLength;

+  UINT32                          Length;

+  ISCSI_ADDITIONAL_HEADER         *Header;

+  ISCSI_BI_EXP_READ_DATA_LEN_AHS  *BiExpReadDataLenAHS;

+  ISCSI_SESSION                   *Session;

+  UINT32                          ImmediateDataLen;

+

+  AHSLength = 0;

+

+  if (Packet->DataDirection == DataBi) {

+    //

+    // Bi directional Read/Write command, the bidirectional expected

+    // read data length AHS is required.

+    //

+    AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);

+  }

+

+  if (Packet->CdbLength > 16) {

+    //

+    // The CDB exceeds 16 bytes, an extended CDB AHS is required.

+    //

+    AHSLength += ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER);

+  }

+

+  Length    = sizeof (SCSI_COMMAND) + AHSLength;

+  PduHeader = NetbufAlloc (Length);

+  if (PduHeader == NULL) {

+    return NULL;

+  }

+

+  ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);

+  Header  = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);

+

+  NetZeroMem (ScsiCmd, Length);

+

+  ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);

+  ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);

+

+  //

+  // Set the READ/WRITE flags according to the IO type of this request.

+  //

+  switch (Packet->DataDirection) {

+  case DataIn:

+    ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);

+    ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);

+    break;

+

+  case DataOut:

+    ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);

+    ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);

+    break;

+

+  case DataBi:

+    ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);

+    ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);

+

+    //

+    // Fill the bidirectional expected read data length AHS.

+    //

+    BiExpReadDataLenAHS                     = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;

+    Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);

+

+    BiExpReadDataLenAHS->Length = NTOHS (5);

+    BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;

+    BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);

+

+    break;

+  }

+

+  ScsiCmd->TotalAHSLength = AHSLength;

+  NetCopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));

+  ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);

+  ScsiCmd->CmdSN            = NTOHL (Tcb->CmdSN);

+  ScsiCmd->ExpStatSN        = NTOHL (Tcb->Conn->ExpStatSN);

+

+  NetCopyMem (ScsiCmd->CDB, Packet->Cdb, sizeof (ScsiCmd->CDB));

+

+  if (Packet->CdbLength > 16) {

+    Header->Length  = NTOHS (Packet->CdbLength - 15);

+    Header->Type    = ISCSI_AHS_TYPE_EXT_CDB;

+

+    NetCopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);

+  }

+

+  Pdu               = PduHeader;

+  Session           = Tcb->Conn->Session;

+  ImmediateDataLen  = 0;

+

+  if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {

+    //

+    // Send immediate data in this SCSI Command PDU. The length of the immeidate

+    // data is the minimum of FirstBurstLength, the data length to be xfered and

+    // the MaxRecvdataSegmentLength on this connection.

+    //

+    ImmediateDataLen  = MIN (Session->FirstBurstLength, Packet->OutTransferLength);

+    ImmediateDataLen  = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);

+

+    //

+    // Update the data segment length in the PDU header.

+    //

+    ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);

+

+    //

+    // Create the data segment.

+    //

+    DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);

+    if (DataSeg == NULL) {

+      NetbufFree (PduHeader);

+      Pdu = NULL;

+      goto ON_EXIT;

+    }

+

+    NbufList = NetAllocatePool (sizeof (NET_LIST_ENTRY));

+    if (NbufList == NULL) {

+      NetbufFree (PduHeader);

+      NetbufFree (DataSeg);

+

+      Pdu = NULL;

+      goto ON_EXIT;

+    }

+

+    NetListInit (NbufList);

+    NetListInsertTail (NbufList, &PduHeader->List);

+    NetListInsertTail (NbufList, &DataSeg->List);

+

+    Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);

+    if (Pdu == NULL) {

+      IScsiFreeNbufList (NbufList);

+    }

+  }

+

+  if (Session->InitialR2T ||

+      (ImmediateDataLen == Session->FirstBurstLength) ||

+      (ImmediateDataLen == Packet->OutTransferLength)

+      ) {

+    //

+    // Unsolicited data out sequence is not allowed,

+    // or FirstBustLength data is already sent out by immediate data

+    // or all the OUT data accompany this SCSI packet is sent as

+    // immediate data, the final flag should be set on this SCSI Command

+    // PDU.

+    //

+    ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);

+  }

+

+ON_EXIT:

+

+  return Pdu;

+}

+

+NET_BUF *

+IScsiNewDataOutPdu (

+  IN UINT8      *Data,

+  IN UINT32     Len,

+  IN UINT32     DataSN,

+  IN ISCSI_TCB  *Tcb,

+  IN UINT64     Lun

+  )

+/*++

+

+Routine Description:

+

+  Create a new iSCSI SCSI Data Out PDU.

+

+Arguments:

+

+  Data   - The data to put into the Data Out PDU.

+  Len    - Length of the data.

+  DataSN - The DataSN of the Data Out PDU.

+  Tcb    - The task control block of this Data Out PDU.

+  Lun    - The LUN.

+

+Returns:

+

+  The net buffer wrapping the Data Out PDU.

+

+--*/

+{

+  NET_LIST_ENTRY      *NbufList;

+  NET_BUF             *PduHdr;

+  NET_BUF             *DataSeg;

+  NET_BUF             *Pdu;

+  ISCSI_SCSI_DATA_OUT *DataOutHdr;

+  ISCSI_XFER_CONTEXT  *XferContext;

+

+  NbufList = NetAllocatePool (sizeof (NET_LIST_ENTRY));

+  if (NbufList == NULL) {

+    return NULL;

+  }

+

+  NetListInit (NbufList);

+

+  //

+  // Allocate memory for the BHS.

+  //

+  PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));

+  if (PduHdr == NULL) {

+    NetFreePool (NbufList);

+    return NULL;

+  }

+  //

+  // Insert the BHS into the buffer list.

+  //

+  NetListInsertTail (NbufList, &PduHdr->List);

+

+  DataOutHdr  = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);

+  XferContext = &Tcb->XferContext;

+

+  NetZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));

+

+  //

+  // Set the flags and fields of the Data Out PDU BHS.

+  //

+  ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);

+  ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);

+

+  DataOutHdr->InitiatorTaskTag  = HTONL (Tcb->InitiatorTaskTag);

+  DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);

+  DataOutHdr->ExpStatSN         = HTONL (Tcb->Conn->ExpStatSN);

+  DataOutHdr->DataSN            = HTONL (DataSN);

+  DataOutHdr->BufferOffset      = HTONL (XferContext->Offset);

+

+  if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {

+    NetCopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));

+  }

+  //

+  // Build the data segment for this Data Out PDU.

+  //

+  DataSeg = IScsiNewDataSegment (Data, Len, FALSE);

+  if (DataSeg == NULL) {

+    IScsiFreeNbufList (NbufList);

+    return NULL;

+  }

+  //

+  // Put the data segment into the buffer list and combine it with the BHS

+  // into a full Data Out PDU.

+  //

+  NetListInsertTail (NbufList, &DataSeg->List);

+  Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);

+  if (Pdu == NULL) {

+    IScsiFreeNbufList (NbufList);

+  }

+

+  return Pdu;

+}

+

+NET_LIST_ENTRY *

+IScsiGenerateDataOutPduSequence (

+  IN UINT8      *Data,

+  IN ISCSI_TCB  *Tcb,

+  IN UINT64     Lun

+  )

+/*++

+

+Routine Description:

+

+  Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.

+

+Arguments:

+

+  Data - The data  which will be carried by the sequence of iSCSI SCSI Data Out PDUs.

+  Tcb  - The task control block of the data to send out.

+  Lun  - The LUN the data will be sent to.

+

+Returns:

+

+  A list of net buffers with each of them wraps an iSCSI SCSI Data Out PDU.

+

+--*/

+{

+  NET_LIST_ENTRY      *PduList;

+  UINT32              DataSN;

+  UINT32              DataLen;

+  NET_BUF             *DataOutPdu;

+  ISCSI_CONNECTION    *Conn;

+  ISCSI_XFER_CONTEXT  *XferContext;

+

+  PduList = NetAllocatePool (sizeof (NET_LIST_ENTRY));

+  if (PduList == NULL) {

+    return NULL;

+  }

+

+  NetListInit (PduList);

+

+  DataSN      = 0;

+  Conn        = Tcb->Conn;

+  DataOutPdu  = NULL;

+  XferContext = &Tcb->XferContext;

+

+  while (XferContext->DesiredLength > 0) {

+    //

+    // Determine the length of data this Data Out PDU can carry.

+    //

+    DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);

+    Data += DataLen;

+

+    //

+    // Create a Data Out PDU.

+    //

+    DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);

+    if (DataOutPdu == NULL) {

+      IScsiFreeNbufList (PduList);

+      PduList = NULL;

+

+      goto ON_EXIT;

+    }

+

+    NetListInsertTail (PduList, &DataOutPdu->List);

+

+    //

+    // Update the context and DataSN.

+    //

+    XferContext->Offset += DataLen;

+    XferContext->DesiredLength -= DataLen;

+    DataSN++;

+  }

+  //

+  // Set the F bit for the last data out PDU in this sequence.

+  //

+  ISCSI_SET_FLAG (NetbufGetByte (DataOutPdu, 0, NULL), ISCSI_BHS_FLAG_FINAL);

+

+ON_EXIT:

+

+  return PduList;

+}

+

+EFI_STATUS

+IScsiSendDataOutPduSequence (

+  IN UINT8      *Data,

+  IN UINT64     Lun,

+  IN ISCSI_TCB  *Tcb

+  )

+/*++

+

+Routine Description:

+

+  Send the Data in a sequence of Data Out PDUs one by one.

+

+Arguments:

+

+  Data - The data to carry by Data Out PDUs.

+  Lun  - The LUN the data will be sent to.

+  Tcb  - The task control block.

+

+Returns:

+

+  EFI_SUCCES           - The data is sent out to the LUN.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+

+--*/

+{

+  NET_LIST_ENTRY  *DataOutPduList;

+  NET_LIST_ENTRY  *Entry;

+  NET_BUF         *Pdu;

+  EFI_STATUS      Status;

+

+  //

+  // Generate the Data Out PDU sequence.

+  //

+  DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);

+  if (DataOutPduList == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  Status = EFI_SUCCESS;

+

+  //

+  // Send the Data Out PDU's one by one.

+  //

+  NET_LIST_FOR_EACH (Entry, DataOutPduList) {

+    Pdu     = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);

+

+    Status  = Tcp4IoTransmit (&Tcb->Conn->Tcp4Io, Pdu);

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+  }

+

+  IScsiFreeNbufList (DataOutPduList);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiOnDataInRcvd (

+  IN NET_BUF                                         *Pdu,

+  IN ISCSI_TCB                                       *Tcb,

+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet

+  )

+/*++

+

+Routine Description:

+

+  Process the received iSCSI SCSI Data In PDU.

+

+Arguments:

+

+  Pdu    - The Data In PDU received.

+  Tcb    - The task control block.

+  Packet - The EXT SCSI PASS THRU request packet.

+

+Returns:

+

+  EFI_SUCCES          - The check on the Data IN PDU is passed and some update

+                        actions are taken.

+  EFI_PROTOCOL_ERROR  - Some kind of iSCSI protocol errror happened.

+

+--*/

+{

+  ISCSI_SCSI_DATA_IN  *DataInHdr;

+  EFI_STATUS          Status;

+

+  DataInHdr                   = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);

+

+  DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);

+  DataInHdr->ExpCmdSN         = NTOHL (DataInHdr->ExpCmdSN);

+  DataInHdr->MaxCmdSN         = NTOHL (DataInHdr->MaxCmdSN);

+  DataInHdr->DataSN           = NTOHL (DataInHdr->DataSN);

+

+  //

+  // Check the DataSN.

+  //

+  Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {

+    return EFI_PROTOCOL_ERROR;

+  }

+  //

+  // Update the command related sequence numbers.

+  //

+  IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);

+

+  if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {

+    if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {

+      //

+      // The S bit is on but the F bit is off.

+      //

+      return EFI_PROTOCOL_ERROR;

+    }

+

+    Tcb->StatusXferd = TRUE;

+

+    if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {

+      //

+      // Underflow and Overflow are mutual flags.

+      //

+      return EFI_PROTOCOL_ERROR;

+    }

+    //

+    // S bit is on, the StatSN is valid.

+    //

+    Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+

+    Packet->HostAdapterStatus = 0;

+    Packet->TargetStatus      = DataInHdr->Status;

+

+    if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {

+      Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);

+      Status = EFI_BAD_BUFFER_SIZE;

+    }

+

+    if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {

+      Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);

+    }

+  }

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiOnR2TRcvd (

+  IN NET_BUF                                         *Pdu,

+  IN ISCSI_TCB                                       *Tcb,

+  IN UINT64                                          Lun,

+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet

+  )

+/*++

+

+Routine Description:

+

+  Process the received iSCSI R2T PDU.

+

+Arguments:

+

+  Pdu    - The R2T PDU received.

+  Tcb    - The task control block.

+  Lun    - The Lun.

+  Packet - The EXT SCSI PASS THRU request packet.

+

+Returns:

+

+  EFI_SUCCES          - The R2T PDU is valid and the solicited data is sent out.

+  EFI_PROTOCOL_ERROR  - Some kind of iSCSI protocol errror happened.

+

+--*/

+{

+  ISCSI_READY_TO_TRANSFER *R2THdr;

+  EFI_STATUS              Status;

+  ISCSI_XFER_CONTEXT      *XferContext;

+  UINT8                   *Data;

+

+  R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);

+

+  R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);

+  R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);

+  R2THdr->StatSN = NTOHL (R2THdr->StatSN);

+  R2THdr->R2TSN = NTOHL (R2THdr->R2TSN);

+  R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);

+  R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);

+

+  if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {

+    return EFI_PROTOCOL_ERROR;;

+  }

+  //

+  // Check the sequence number.

+  //

+  Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSN);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  XferContext                     = &Tcb->XferContext;

+  XferContext->TargetTransferTag  = R2THdr->TargetTransferTag;

+  XferContext->Offset             = R2THdr->BufferOffset;

+  XferContext->DesiredLength      = R2THdr->DesiredDataTransferLength;

+

+  if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||

+      (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)

+      ) {

+    return EFI_PROTOCOL_ERROR;

+  }

+  //

+  // Send the data solicited by this R2T.

+  //

+  Data    = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;

+  Status  = IScsiSendDataOutPduSequence (Data, Lun, Tcb);

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiOnScsiRspRcvd (

+  IN NET_BUF                                         *Pdu,

+  IN ISCSI_TCB                                       *Tcb,

+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet

+  )

+/*++

+

+Routine Description:

+

+  Process the received iSCSI SCSI Response PDU.

+

+Arguments:

+

+  Pdu    - The Response PDU received.

+  Tcb    - The task control block.

+  Packet - The EXT SCSI PASS THRU request packet.

+

+Returns:

+

+  EFI_SUCCES         - The Response PDU is processed.

+  EFI_PROTOCOL_ERROR - Some kind of iSCSI protocol errror happened.

+

+--*/

+{

+  SCSI_RESPONSE     *ScsiRspHdr;

+  ISCSI_SENSE_DATA  *SenseData;

+  EFI_STATUS        Status;

+  UINT32            DataSegLen;

+

+  ScsiRspHdr                    = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);

+

+  ScsiRspHdr->InitiatorTaskTag  = NTOHL (ScsiRspHdr->InitiatorTaskTag);

+  if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {

+    return EFI_PROTOCOL_ERROR;

+  }

+

+  ScsiRspHdr->StatSN  = NTOHL (ScsiRspHdr->StatSN);

+

+  Status              = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  ScsiRspHdr->MaxCmdSN  = NTOHL (ScsiRspHdr->MaxCmdSN);

+  ScsiRspHdr->ExpCmdSN  = NTOHL (ScsiRspHdr->ExpCmdSN);

+  IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);

+

+  Tcb->StatusXferd          = TRUE;

+

+  Packet->HostAdapterStatus = ScsiRspHdr->Response;

+  if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {

+    return EFI_SUCCESS;

+  }

+

+  Packet->TargetStatus = ScsiRspHdr->Status;

+

+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||

+      ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)

+        ) {

+    return EFI_PROTOCOL_ERROR;

+  }

+

+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {

+    Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);

+    Status = EFI_BAD_BUFFER_SIZE;

+  }

+

+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {

+    Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);

+  }

+

+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {

+    if (Packet->DataDirection == DataIn) {

+      Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);

+    } else {

+      Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);

+    }

+

+    Status = EFI_BAD_BUFFER_SIZE;

+  }

+

+  if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {

+    if (Packet->DataDirection == DataIn) {

+      Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);

+    } else {

+      Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);

+    }

+  }

+

+  DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);

+  if (DataSegLen != 0) {

+    SenseData               = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);

+

+    SenseData->Length       = NTOHS (SenseData->Length);

+

+    Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);

+    if (Packet->SenseDataLength != 0) {

+      NetCopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);

+    }

+  } else {

+    Packet->SenseDataLength = 0;

+  }

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiOnNopInRcvd (

+  IN NET_BUF    *Pdu,

+  IN ISCSI_TCB  *Tcb

+  )

+/*++

+

+Routine Description:

+

+  Process the received NOP In PDU.

+

+Arguments:

+

+  Pdu    - The NOP In PDU received.

+  Tcb    - The task control block.

+

+Returns:

+

+  EFI_SUCCES          - The NOP In PDU is processed and the related sequence

+                        numbers are updated.

+  EFI_PROTOCOL_ERROR  - Some kind of iSCSI protocol errror happened.

+

+--*/

+{

+  ISCSI_NOP_IN  *NopInHdr;

+  EFI_STATUS    Status;

+

+  NopInHdr            = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);

+

+  NopInHdr->StatSN    = NTOHL (NopInHdr->StatSN);

+  NopInHdr->ExpCmdSN  = NTOHL (NopInHdr->ExpCmdSN);

+  NopInHdr->MaxCmdSN  = NTOHL (NopInHdr->MaxCmdSN);

+

+  if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {

+    if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {

+      return EFI_PROTOCOL_ERROR;

+    }

+  } else {

+    Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);

+    if (EFI_ERROR (Status)) {

+      return Status;

+    }

+  }

+

+  IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+IScsiExecuteScsiCommand (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                 *PassThru,

+  IN UINT8                                           *Target,

+  IN UINT64                                          Lun,

+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet

+  )

+/*++

+

+Routine Description:

+

+  Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.

+

+Arguments:

+

+  PassThru - The EXT SCSI PASS THRU protocol.

+  Target   - The target ID.

+  Lun      - The LUN.

+  Packet   - The request packet containing IO request, SCSI command buffer and

+             buffers to read/write.

+

+Returns:

+

+  EFI_SUCCES       - The SCSI command is executed and the result is updated to 

+                     the Packet.

+  EFI_DEVICE_ERROR - Some unexpected error happened.

+

+--*/

+{

+  EFI_STATUS              Status;

+  ISCSI_DRIVER_DATA       *Private;

+  ISCSI_SESSION           *Session;

+  EFI_EVENT               TimeoutEvent;

+  ISCSI_CONNECTION        *Conn;

+  ISCSI_TCB               *Tcb;

+  NET_BUF                 *Pdu;

+  ISCSI_XFER_CONTEXT      *XferContext;

+  UINT8                   *Data;

+  ISCSI_IN_BUFFER_CONTEXT InBufferContext;

+  UINT64                  Timeout;

+

+  Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);

+  Session       = &Private->Session;

+  Status        = EFI_SUCCESS;

+  Tcb           = NULL;

+  TimeoutEvent  = NULL;

+

+  if (Session->State != SESSION_STATE_LOGGED_IN) {

+    return EFI_DEVICE_ERROR;

+  }

+

+  Conn = NET_LIST_USER_STRUCT_S (

+          Session->Conns.ForwardLink,

+          ISCSI_CONNECTION,

+          Link,

+          ISCSI_CONNECTION_SIGNATURE

+          );

+

+  if (Packet->Timeout != 0) {

+    Timeout = MultU64x32 (Packet->Timeout, 2);

+    //

+    // Start the timeout timer.

+    //

+    Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+    TimeoutEvent = Conn->TimeoutEvent;

+  }

+

+  Status = IScsiNewTcb (Conn, &Tcb);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+  //

+  // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.

+  //

+  Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);

+  if (Pdu == NULL) {

+    Status = EFI_OUT_OF_RESOURCES;

+    goto ON_EXIT;

+  }

+

+  XferContext         = &Tcb->XferContext;

+  XferContext->Offset = ISCSI_GET_DATASEG_LEN (NetbufGetByte (Pdu, 0, NULL));

+

+  //

+  // Transmit the SCSI Command PDU.

+  //

+  Status = Tcp4IoTransmit (&Conn->Tcp4Io, Pdu);

+

+  NetbufFree (Pdu);

+

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  if (!Session->InitialR2T &&

+      (XferContext->Offset < Session->FirstBurstLength) &&

+      (XferContext->Offset < Packet->OutTransferLength)

+      ) {

+    //

+    // Unsolicited Data-Out sequence is allowed, there is remaining SCSI

+    // OUT data and the limit of FirstBurstLength is not reached.

+    //

+    XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;

+    XferContext->DesiredLength = MIN (

+                                  Session->FirstBurstLength,

+                                  Packet->OutTransferLength - XferContext->Offset

+                                  );

+

+    Data    = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;

+    Status  = IScsiSendDataOutPduSequence (Data, Lun, Tcb);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+  }

+

+  InBufferContext.InData    = (UINT8 *) Packet->InDataBuffer;

+  InBufferContext.InDataLen = Packet->InTransferLength;

+

+  while (!Tcb->StatusXferd) {

+    //

+    // try to receive PDU from target.

+    //

+    Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+    switch (ISCSI_GET_OPCODE (NetbufGetByte (Pdu, 0, NULL))) {

+    case ISCSI_OPCODE_SCSI_DATA_IN:

+      Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);

+      break;

+

+    case ISCSI_OPCODE_R2T:

+      Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);

+      break;

+

+    case ISCSI_OPCODE_SCSI_RSP:

+      Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);

+      break;

+

+    case ISCSI_OPCODE_NOP_IN:

+      Status = IScsiOnNopInRcvd (Pdu, Tcb);

+      break;

+

+    case ISCSI_OPCODE_VENDOR_T0:

+    case ISCSI_OPCODE_VENDOR_T1:

+    case ISCSI_OPCODE_VENDOR_T2:

+      //

+      // These messages are vendor specific, skip them.

+      //

+      break;

+

+    default:

+      Status = EFI_PROTOCOL_ERROR;

+      break;

+    }

+

+    NetbufFree (Pdu);

+

+    if (EFI_ERROR (Status)) {

+      break;

+    }

+  }

+

+ON_EXIT:

+

+  if (TimeoutEvent != NULL) {

+    gBS->SetTimer (TimeoutEvent, TimerCancel, 0);

+  }

+

+  if (Tcb != NULL) {

+    IScsiDelTcb (Tcb);

+  }

+

+  if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {

+    //

+    // Reinstate the session.

+    //

+    if (EFI_ERROR (IScsiSessionReinstatement (Private))) {

+      Status = EFI_DEVICE_ERROR;

+    }

+  }

+

+  return Status;

+}

+

+EFI_STATUS

+IScsiSessionReinstatement (

+  IN ISCSI_DRIVER_DATA  *Private

+  )

+/*++

+

+Routine Description:

+

+  Reinstate the session on some error.

+

+Arguments:

+

+  Private - The iSCSI driver data.

+

+Returns:

+

+  EFI_SUCCES - The session is reinstated from some error.

+  other      - Reinstatement failed.

+

+--*/

+{

+  ISCSI_SESSION *Session;

+  EFI_STATUS    Status;

+

+  Session = &Private->Session;

+  ASSERT (Session->State == SESSION_STATE_LOGGED_IN);

+

+  //

+  // Abort the session and re-init it.

+  //

+  IScsiSessionAbort (Session);

+  IScsiSessionInit (Session, TRUE);

+

+  //

+  // Login again.

+  //

+  Status = IScsiSessionLogin (Private);

+

+  return Status;

+}

+

+VOID

+IScsiSessionInit (

+  IN ISCSI_SESSION  *Session,

+  IN BOOLEAN        Recovery

+  )

+/*++

+

+Routine Description:

+

+  Initialize some session parameters before login.

+

+Arguments:

+

+  Session  - The iSCSI session.

+  Recovery - Whether the request is from a fresh new start or recovery.

+

+Returns:

+

+  None.

+

+--*/

+{

+  UINT32  Random;

+

+  if (!Recovery) {

+    Session->Signature  = ISCSI_SESSION_SIGNATURE;

+    Session->State      = SESSION_STATE_FREE;

+

+    Random              = NET_RANDOM (NetRandomInitSeed ());

+

+    Session->ISID[0]    = ISID_BYTE_0;

+    Session->ISID[1]    = ISID_BYTE_1;

+    Session->ISID[2]    = ISID_BYTE_2;

+    Session->ISID[3]    = ISID_BYTE_3;

+    Session->ISID[4]    = (UINT8) Random;

+    Session->ISID[5]    = (UINT8) (Random >> 8);

+

+    NetListInit (&Session->Conns);

+    NetListInit (&Session->TcbList);

+  }

+

+  Session->TSIH                 = 0;

+

+  Session->CmdSN                = 1;

+  Session->InitiatorTaskTag     = 1;

+  Session->NextCID              = 1;

+

+  Session->TargetPortalGroupTag = 0;

+  Session->MaxConnections       = ISCSI_MAX_CONNS_PER_SESSION;

+  Session->InitialR2T           = FALSE;

+  Session->ImmediateData        = TRUE;

+  Session->MaxBurstLength       = 262144;

+  Session->FirstBurstLength     = MAX_RECV_DATA_SEG_LEN_IN_FFP;

+  Session->DefaultTime2Wait     = 2;

+  Session->DefaultTime2Retain   = 20;

+  Session->MaxOutstandingR2T    = DEFAULT_MAX_OUTSTANDING_R2T;

+  Session->DataPDUInOrder       = TRUE;

+  Session->DataSequenceInOrder  = TRUE;

+  Session->ErrorRecoveryLevel   = 0;

+}

+

+EFI_STATUS

+IScsiSessionAbort (

+  IN ISCSI_SESSION  *Session

+  )

+/*++

+

+Routine Description:

+

+  Abort the iSCSI session, that is, reset all the connection and free the

+  resources.

+

+Arguments:

+

+  Session - The iSCSI session.

+

+Returns:

+

+  EFI_SUCCES - The session is aborted.

+

+--*/

+{

+  ISCSI_DRIVER_DATA *Private;

+  ISCSI_CONNECTION  *Conn;

+

+  if (Session->State != SESSION_STATE_LOGGED_IN) {

+    return EFI_SUCCESS;

+  }

+

+  ASSERT (!NetListIsEmpty (&Session->Conns));

+

+  Private = ISCSI_DRIVER_DATA_FROM_SESSION (Session);

+

+  while (!NetListIsEmpty (&Session->Conns)) {

+    Conn = NET_LIST_USER_STRUCT_S (

+            Session->Conns.ForwardLink,

+            ISCSI_CONNECTION,

+            Link,

+            ISCSI_CONNECTION_SIGNATURE

+            );

+

+    gBS->CloseProtocol (

+          Conn->Tcp4Io.Handle,

+          &gEfiTcp4ProtocolGuid,

+          Private->Image,

+          Private->ExtScsiPassThruHandle

+          );

+

+    IScsiConnReset (Conn);

+

+    IScsiDetatchConnection (Conn);

+    IScsiDestroyConnection (Conn);

+  }

+

+  Session->State = SESSION_STATE_FAILED;

+

+  return EFI_SUCCESS;

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsiProto.h b/MdeModulePkg/Universal/iScsi/IScsiProto.h
new file mode 100644
index 0000000..59608fc
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiProto.h
@@ -0,0 +1,786 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiProto.h

+

+Abstract:

+

+  Protocol definitions for iSCSI driver, mainly from RFC3720.

+

+--*/

+

+#ifndef _ISCSI_PROTO_H_

+#define _ISCSI_PROTO_H_

+

+#include <protocol/ScsiPassThruExt.h>

+

+//

+// RFC 1982 Serial Number Arithmetic, SERIAL_BITS = 32

+//

+#define ISCSI_SEQ_EQ(s1, s2)  ((s1) == (s2))

+#define ISCSI_SEQ_LT(s1, s2) \

+    ( \

+      (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) < (1 << 31)) || \

+      (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) > (1 << 31)) \

+    )

+#define ISCSI_SEQ_GT(s1, s2) \

+    ( \

+      (((INT32) (s1) < (INT32) (s2)) && (s2 - s1) > (1 << 31)) || \

+      (((INT32) (s1) > (INT32) (s2)) && (s1 - s2) < (1 << 31)) \

+    )

+

+#define ISCSI_WELL_KNOWN_PORT                   3260

+#define ISCSI_MAX_CONNS_PER_SESSION             1

+

+#define DEFAULT_MAX_RECV_DATA_SEG_LEN           8192

+#define MAX_RECV_DATA_SEG_LEN_IN_FFP            65536

+#define DEFAULT_MAX_OUTSTANDING_R2T             1

+

+#define ISCSI_VERSION_MAX                       0x00

+#define ISCSI_VERSION_MIN                       0x00

+

+#define ISID_BYTE_0                             0 // OUI format

+#define ISID_BYTE_1                             0

+#define ISID_BYTE_2                             0xaa

+#define ISID_BYTE_3                             0x1

+

+#define ISCSI_KEY_AUTH_METHOD                   "AuthMethod"

+#define ISCSI_KEY_HEADER_DIGEST                 "HeaderDigest"

+#define ISCSI_KEY_DATA_DIGEST                   "DataDigest"

+#define ISCSI_KEY_MAX_CONNECTIONS               "MaxConnections"

+#define ISCSI_KEY_TARGET_NAME                   "TargetName"

+#define ISCSI_KEY_INITIATOR_NAME                "InitiatorName"

+#define ISCSI_KEY_TARGET_ALIAS                  "TargetAlias"

+#define ISCSI_KEY_INITIATOR_ALIAS               "InitiatorAlias"

+#define ISCSI_KEY_TARGET_ADDRESS                "TargetAddress"

+#define ISCSI_KEY_INITIAL_R2T                   "InitialR2T"

+#define ISCSI_KEY_IMMEDIATE_DATA                "ImmediateData"

+#define ISCSI_KEY_TARGET_PORTAL_GROUP_TAG       "TargetPortalGroupTag"

+#define ISCSI_KEY_MAX_BURST_LENGTH              "MaxBurstLength"

+#define ISCSI_KEY_FIRST_BURST_LENGTH            "FirstBurstLength"

+#define ISCSI_KEY_DEFAULT_TIME2WAIT             "DefaultTime2Wait"

+#define ISCSI_KEY_DEFAULT_TIME2RETAIN           "DefaultTime2Retain"

+#define ISCSI_KEY_MAX_OUTSTANDING_R2T           "MaxOutstandingR2T"

+#define ISCSI_KEY_DATA_PDU_IN_ORDER             "DataPDUInOrder"

+#define ISCSI_KEY_DATA_SEQUENCE_IN_ORDER        "DataSequenceInOrder"

+#define ISCSI_KEY_ERROR_RECOVERY_LEVEL          "ErrorRecoveryLevel"

+#define ISCSI_KEY_SESSION_TYPE                  "SessionType"

+#define ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH  "MaxRecvDataSegmentLength"

+

+#define ISCSI_KEY_VALUE_NONE                    "None"

+

+//

+// connection state for initiator

+//

+typedef enum {

+  CONN_STATE_FREE,

+  CONN_STATE_XPT_WAIT,

+  CONN_STATE_IN_LOGIN,

+  CONN_STATE_LOGGED_IN,

+  CONN_STATE_IN_LOGOUT,

+  CONN_STATE_LOGOUT_REQUESTED,

+  CONN_STATE_CLEANUP_WAIT,

+  CONN_STATE_IN_CLEANUP

+} CONNECTION_STATE;

+

+//

+// session state for initiator

+//

+typedef enum {

+  SESSION_STATE_FREE,

+  SESSION_STATE_LOGGED_IN,

+  SESSION_STATE_FAILED

+} SESSION_STATE;

+

+typedef enum {

+  DataIn  = 0,

+  DataOut = 1,

+  DataBi  = 2

+} DATA_DIRECTION;

+

+#define ISCSI_RESERVED_TAG                  0xffffffff

+

+#define ISCSI_REQ_IMMEDIATE                 0x40

+#define ISCSI_OPCODE_MASK                   0x3F

+

+#define ISCSI_SET_OPCODE(PduHdr, Op, Flgs)  ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) = ((Op) | (Flgs)))

+#define ISCSI_GET_OPCODE(PduHdr)            ((((ISCSI_BASIC_HEADER *) (PduHdr))->OpCode) & ISCSI_OPCODE_MASK)

+#define ISCSI_CHECK_OPCODE(PduHdr, Op)      ((((PduHdr)->OpCode) & ISCSI_OPCODE_MASK) == (Op))

+#define ISCSI_IMMEDIATE_ON(PduHdr)          ((PduHdr)->OpCode & ISCSI_REQ_IMMEDIATE)

+#define ISCSI_SET_FLAG(PduHdr, Flag)        (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags |= (Flag))

+#define ISCSI_CLEAR_FLAG(PduHdr, Flag)      (((ISCSI_BASIC_HEADER *) (PduHdr))->Flags &= ~(Flag))

+#define ISCSI_FLAG_ON(PduHdr, Flag)         ((((ISCSI_BASIC_HEADER *) (PduHdr))->Flags & (Flag)) == (Flag))

+#define ISCSI_SET_STAGES(PduHdr, Cur, Nxt)  ((PduHdr)->Flags |= ((Cur) << 2 | (Nxt)))

+#define ISCSI_GET_CURRENT_STAGE(PduHdr)     (((PduHdr)->Flags >> 2) & 0x3)

+#define ISCSI_GET_NEXT_STAGE(PduHdr)        (((PduHdr)->Flags) & 0x3)

+

+#define ISCSI_GET_PAD_LEN(DataLen)          ((~(DataLen) + 1) & 0x3)

+#define ISCSI_ROUNDUP(DataLen)              (((DataLen) + 3) &~(0x3))

+

+#define HTON24(Dst, Src) \

+  do { \

+    (Dst)[0]  = (UINT8) ((Src) >> 16) & 0xFF; \

+    (Dst)[1]  = (UINT8) ((Src) >> 8) & 0xFF; \

+    (Dst)[2]  = (UINT8) (Src) & 0xFF; \

+  } while (0);

+

+#define NTOH24(src)                         (((src)[0] << 16) | ((src)[1] << 8) | ((src)[2]))

+

+#define ISCSI_GET_DATASEG_LEN(PduHdr)       NTOH24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength)

+#define ISCSI_SET_DATASEG_LEN(PduHdr, Len)  HTON24 (((ISCSI_BASIC_HEADER *) (PduHdr))->DataSegmentLength, (Len))

+

+//

+// initiator opcodes

+//

+#define ISCSI_OPCODE_NOP_OUT        0x00

+#define ISCSI_OPCODE_SCSI_CMD       0x01

+#define ISCSI_OPCODE_SCSI_TMF_REQ   0x02

+#define ISCSI_OPCODE_LOGIN_REQ      0x03

+#define ISCSI_OPCODE_TEXT_REQ       0x04

+#define ISCSI_OPCODE_SCSI_DATA_OUT  0x05

+#define ISCSI_OPCODE_LOGOUT_REQ     0x06

+#define ISCSI_OPCODE_SNACK_REQ      0x10

+#define ISCSI_OPCODE_VENDOR_I0      0x1c

+#define ISCSI_OPCODE_VENDOR_I1      0x1d

+#define ISCSI_OPCODE_VENDOR_I2      0x1e

+

+//

+// target opcodes

+//

+#define ISCSI_OPCODE_NOP_IN       0x20

+#define ISCSI_OPCODE_SCSI_RSP     0x21

+#define ISCSI_OPCODE_SCSI_TMF_RSP 0x22

+#define ISCSI_OPCODE_LOGIN_RSP    0x23

+#define ISCSI_OPCODE_TEXT_RSP     0x24

+#define ISCSI_OPCODE_SCSI_DATA_IN 0x25

+#define ISCSI_OPCODE_LOGOUT_RSP   0x26

+#define ISCSI_OPCODE_R2T          0x31

+#define ISCSI_OPCODE_ASYNC_MSG    0x32

+#define ISCSI_OPCODE_VENDOR_T0    0x3c

+#define ISCSI_OPCODE_VENDOR_T1    0x3d

+#define ISCSI_OPCODE_VENDOR_T2    0x3e

+#define ISCSI_OPCODE_REJECT       0x3f

+

+#define ISCSI_BHS_FLAG_FINAL      0x80

+

+//

+// iSCSI Basic Header Segment

+//

+typedef struct _ISCSI_BASIC_HEADER {

+  UINT8   OpCode;

+  UINT8   Flags;

+  UINT16  OpCodeSpecific1;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  OpCodeSpecific2[7];

+} ISCSI_BASIC_HEADER;

+

+//

+// Defined AHS types, others are reserved.

+//

+#define ISCSI_AHS_TYPE_EXT_CDB              0x1

+#define ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN 0x2

+

+typedef struct _ISCSI_ADDTIONAL_HEADER {

+  UINT16  Length;

+  UINT8   Type;

+  UINT8   TypeSpecific[1];

+} ISCSI_ADDITIONAL_HEADER;

+

+typedef struct _ISCSI_BI_EXP_READ_DATA_LEN_AHS {

+  UINT16  Length;

+  UINT8   Type;

+  UINT8   Reserved;

+  UINT32  ExpReadDataLength;

+} ISCSI_BI_EXP_READ_DATA_LEN_AHS;

+

+#define SCSI_CMD_PDU_FLAG_READ        0x40

+#define SCSI_CMD_PDU_FLAG_WRITE       0x20

+

+#define ISCSI_CMD_PDU_TASK_ATTR_MASK  0x07

+

+//

+// task attributes

+//

+#define ISCSI_TASK_ATTR_UNTAGGED  0x00

+#define ISCSI_TASK_ATTR_SIMPLE    0x01

+#define ISCSI_TASK_ATTR_ORDERD    0x02

+#define ISCSI_TASK_ATTR_HOQ       0x03

+#define ISCSI_TASK_ATTR_ACA       0x04

+

+//

+// SCSI Command

+//

+typedef struct _SCSI_COMMAND {

+  UINT8   OpCode;

+  UINT8   Flags;

+  UINT16  Reserved;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  ExpDataXferLength;

+  UINT32  CmdSN;

+  UINT32  ExpStatSN;

+  UINT8   CDB[16];

+} SCSI_COMMAND;

+

+//

+// flag bit definitions in SCSI response

+//

+#define SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW  0x10

+#define SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW 0x08

+#define SCSI_RSP_PDU_FLAG_OVERFLOW          0x04

+#define SCSI_RSP_PDU_FLAG_UNDERFLOW         0x02

+

+//

+// iSCSI service response codes

+//

+#define ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET  0x00

+#define ISCSI_SERVICE_RSP_TARGET_FAILURE              0x01

+

+//

+// SCSI Response

+//

+typedef struct _SCSI_RESPONSE {

+  UINT8   OpCode;

+  UINT8   Flags;

+  UINT8   Response;

+  UINT8   Status;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Reserved[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  SNACKTag;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT32  ExpDataSN;

+  UINT32  BiReadResidualCount;

+  UINT32  ResidualCount;

+} SCSI_RESPONSE;

+

+typedef struct _ISCSI_SENSE_DATA {

+  UINT16  Length;

+  UINT8   Data[2];

+} ISCSI_SENSE_DATA;

+

+//

+// iSCSI Task Managment Function Request

+//

+typedef struct _ISCSI_TMF_REQUEST {

+  UINT8   OpCode;

+  UINT8   Fuction;

+  UINT16  Reserved1;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  ReferencedTaskTag;

+  UINT32  CmdSN;

+  UINT32  ExpStatSN;

+  UINT32  RefCmdSN;

+  UINT32  ExpDataSN;

+  UINT32  Reserved2[2];

+} ISCSI_TMF_REQUEST;

+

+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_COMPLETE           0

+#define ISCSI_TMF_RSP_PDU_RSP_TASK_NOT_EXIST              1

+#define ISCSI_TMF_RSP_PDU_RSP_LUN_NOT_EXIST               2

+#define ISCSI_TMF_RSP_PDU_RSP_TASK_STILL_ALLEGIANT        3

+#define ISCSI_TMF_RSP_PDU_RSP_TASK_REASSGIN_NOT_SUPPORTED 4

+#define ISCSI_TMF_RSP_PDU_RSP_NOT_SUPPORTED               5

+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_AHTH_FAILED        6

+#define ISCSI_TMF_RSP_PDU_RSP_FUNCTION_REJECTED           255

+

+//

+// iSCSI Task Management Function Response

+//

+typedef struct _ISCSI_TMF_RESPONSE {

+  UINT8   OpCode;

+  UINT8   Reserved1;

+  UINT8   Response;

+  UINT8   Reserved2;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT32  Reserver3[2];

+  UINT32  InitiatorTaskTag;

+  UINT32  Reserved4;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT32  Reserved[3];

+} ISCSI_TMF_RESPONSE;

+

+//

+// SCSI Data-Out

+//

+typedef struct _ISCSI_SCSI_DATA_OUT {

+  UINT8   OpCode;

+  UINT8   Reserved1[3];

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  TargetTransferTag;

+  UINT32  Reserved2;

+  UINT32  ExpStatSN;

+  UINT32  Reserved3;

+  UINT32  DataSN;

+  UINT32  BufferOffset;

+  UINT32  Reserved4;

+} ISCSI_SCSI_DATA_OUT;

+

+#define SCSI_DATA_IN_PDU_FLAG_ACKKNOWLEDGE  0x40

+#define SCSI_DATA_IN_PDU_FLAG_OVERFLOW      SCSI_RSP_PDU_FLAG_OVERFLOW

+#define SCSI_DATA_IN_PDU_FLAG_UNDERFLOW     SCSI_RSP_PDU_FLAG_UNDERFLOW

+#define SCSI_DATA_IN_PDU_FLAG_STATUS_VALID  0x01

+//

+// SCSI Data-In

+//

+typedef struct _ISCSI_SCSI_DATA_IN {

+  UINT8   OpCode;

+  UINT8   Flags;

+  UINT8   Reserved1;

+  UINT8   Status;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  TargetTransferTag;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT32  DataSN;

+  UINT32  BufferOffset;

+  UINT32  ResidualCount;

+} ISCSI_SCSI_DATA_IN;

+

+#define ISCSI_GET_BUFFER_OFFSET(PduHdr) NTOHL (((ISCSI_SCSI_DATA_IN *) (PduHdr))->BufferOffset)

+//

+// Ready To Transfer

+//

+typedef struct _ISCSI_READY_TO_TRANSFER {

+  UINT8   OpCode;

+  UINT8   Reserved1[3];

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  TargetTransferTag;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT32  R2TSN;

+  UINT32  BufferOffset;

+  UINT32  DesiredDataTransferLength;

+} ISCSI_READY_TO_TRANSFER;

+

+typedef struct _ISCSI_ASYNC_MESSAGE {

+  UINT8   OpCode;

+  UINT8   Reserved1[8];

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  Reserved2;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT8   AsyncEvent;

+  UINT8   AsyncVCode;

+  UINT16  Parameter1;

+  UINT16  Parameter2;

+  UINT16  Parameter3;

+  UINT32  Reserved3;

+} ISCSI_ASYNC_MESSAGE;

+

+#define ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT  0x80

+#define ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE 0x40

+

+//

+// Login Request

+//

+typedef struct _ISCSI_LOGIN_REQUEST {

+  UINT8   OpCode;

+  UINT8   Flags;

+  UINT8   VersionMax;

+  UINT8   VersionMin;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   ISID[6];

+  UINT16  TSIH;

+  UINT32  InitiatorTaskTag;

+  UINT16  CID;

+  UINT16  Reserved1;

+  UINT32  CmdSN;

+  UINT32  ExpStatSN;

+  UINT32  Reserved2[4];

+} ISCSI_LOGIN_REQUEST;

+

+#define ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT    ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT

+#define ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE   ISCSI_LOGIN_REQ_PDU_FLAG_CONTINUE

+

+#define ISCSI_LOGIN_STATUS_SUCCESS          0

+#define ISCSI_LOGIN_STATUS_REDIRECTION      1

+#define ISCSI_LOGIN_STATUS_INITIATOR_ERROR  2

+#define ISCSI_LOGIN_STATUS_TARGET_ERROR     3

+

+//

+// Login Response

+//

+typedef struct _ISCSI_LOGIN_RESPONSE {

+  UINT8   OpCode;

+  UINT8   Flags;

+  UINT8   VersionMax;

+  UINT8   VersionActive;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   ISID[6];

+  UINT16  TSIH;

+  UINT32  InitiatorTaskTag;

+  UINT32  Reserved1;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT8   StatusClass;

+  UINT8   StatusDetail;

+  UINT8   Reserved2[10];

+} ISCSI_LOGIN_RESPONSE;

+

+#define ISCSI_LOGOUT_REASON_CLOSE_SESSION                   0

+#define ISCSI_LOGOUT_REASON_CLOSE_CONNECTION                1

+#define ISCSI_LOGOUT_REASON_REMOVE_CONNECTION_FOR_RECOVERY  2

+

+//

+// Logout Request

+//

+typedef struct _ISCSI_LOGOUT_REQUEST {

+  UINT8   OpCode;

+  UINT8   ReasonCode;

+  UINT16  Reserved1;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT32  Reserved2[2];

+  UINT32  InitiatorTaskTag;

+  UINT16  CID;

+  UINT16  Reserved3;

+  UINT32  CmdSN;

+  UINT32  ExpStatSN;

+  UINT32  Reserved4[4];

+} ISCSI_LOGOUT_REQUEST;

+

+#define ISCSI_LOGOUT_RESPONSE_SESSION_CLOSED_SUCCESS  0

+#define ISCSI_LOGOUT_RESPONSE_CID_NOT_FOUND           1

+#define ISCSI_LOGOUT_RESPONSE_RECOVERY_NOT_SUPPORTED  2

+#define ISCSI_LOGOUT_RESPONSE_CLEANUP_FAILED          3

+

+//

+// Logout Response

+//

+typedef struct _ISCSI_LOGOUT_RESPONSE {

+  UINT8   OpCode;

+  UINT8   Reserved1;

+  UINT8   Response;

+  UINT8   Reserved2;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT32  Reserved3[2];

+  UINT32  InitiatorTaskTag;

+  UINT32  Reserved4;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT32  Reserved5;

+  UINT16  Time2Wait;

+  UINT16  Time2Retain;

+  UINT32  Reserved6;

+} ISCSI_LOGOUT_RESPONSE;

+

+#define ISCSI_SNACK_REQUEST_TYPE_DATA_OR_R2T  0

+#define ISCSI_SNACK_REQUEST_TYPE_STATUS       1

+#define ISCSI_SNACK_REQUEST_TYPE_DATA_ACK     2

+#define ISCSI_SNACK_REQUEST_TYPE_RDATA        3

+

+//

+// SNACK Request

+//

+typedef struct _ISCSI_SNACK_REQUEST {

+  UINT8   OpCode;

+  UINT8   Type;

+  UINT16  Reserved1;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  TargetTransferTag;

+  UINT32  Reserved2;

+  UINT32  ExpStatSN;

+  UINT32  Reserved[2];

+  UINT32  BegRun;

+  UINT32  RunLength;

+} ISCSI_SNACK_REQUEST;

+

+//

+// Reject

+//

+typedef struct _ISCSI_REJECT {

+  UINT8   OpCode;

+  UINT8   Reserved1;

+  UINT8   Reason;

+  UINT8   Reserved2;

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT32  Reserved3[2];

+  UINT32  InitiatorTaskTag;

+  UINT32  Reserved4;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT32  DataSN;

+  UINT32  Reserved5[2];

+} ISCSI_REJECT;

+

+//

+// NOP-Out

+//

+typedef struct _ISCSI_NOP_OUT {

+  UINT8   OpCode;

+  UINT8   Reserved1[3];

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  TargetTransferTag;

+  UINT32  CmdSN;

+  UINT32  ExpStatSN;

+  UINT32  Reserved2[4];

+} ISCSI_NOP_OUT;

+

+//

+// NOP-In

+//

+typedef struct _ISCSI_NOP_IN {

+  UINT8   OpCode;

+  UINT8   Reserved1[3];

+  UINT8   TotalAHSLength;

+  UINT8   DataSegmentLength[3];

+  UINT8   Lun[8];

+  UINT32  InitiatorTaskTag;

+  UINT32  TargetTransferTag;

+  UINT32  StatSN;

+  UINT32  ExpCmdSN;

+  UINT32  MaxCmdSN;

+  UINT32  Reserved2[3];

+} ISCSI_NOP_IN;

+

+#define ISCSI_SECURITY_NEGOTIATION          0

+#define ISCSI_LOGIN_OPERATIONAL_NEGOTIATION 1

+#define ISCSI_FULL_FEATURE_PHASE            3

+

+typedef enum {

+  ISCSI_DIGEST_NONE,

+  ISCSI_DIGEST_CRC32

+} ISCSI_DIGEST_TYPE;

+

+typedef struct _ISCSI_XFER_CONTEXT {

+  UINT32  TargetTransferTag;

+  UINT32  Offset;

+  UINT32  DesiredLength;

+  UINT32  ExpDataSN;

+} ISCSI_XFER_CONTEXT;

+

+typedef struct _ISCSI_IN_BUFFER_CONTEXT {

+  UINT8   *InData;

+  UINT32  InDataLen;

+} ISCSI_IN_BUFFER_CONTEXT;

+

+typedef struct _ISCSI_TCB {

+  NET_LIST_ENTRY      Link;

+

+  BOOLEAN             SoFarInOrder;

+  UINT32              ExpDataSN;

+  BOOLEAN             FbitReceived;

+  BOOLEAN             StatusXferd;

+  UINT32              ActiveR2Ts;

+  UINT32              Response;

+  CHAR8               *Reason;

+  UINT32              InitiatorTaskTag;

+  UINT32              CmdSN;

+  UINT32              SNACKTag;

+

+  ISCSI_XFER_CONTEXT  XferContext;

+

+  ISCSI_CONNECTION    *Conn;

+} ISCSI_TCB;

+

+typedef struct _ISCSI_KEY_VALUE_PAIR {

+  NET_LIST_ENTRY  List;

+

+  CHAR8           *Key;

+  CHAR8           *Value;

+} ISCSI_KEY_VALUE_PAIR;

+

+//

+// function prototypes.

+//

+VOID

+IScsiAttatchConnection (

+  IN ISCSI_SESSION     *Session,

+  IN ISCSI_CONNECTION  *Conn

+  );

+

+VOID

+IScsiDetatchConnection (

+  IN ISCSI_CONNECTION  *Conn

+  );

+

+EFI_STATUS

+IScsiConnLogin (

+  IN ISCSI_CONNECTION  *Conn

+  );

+

+ISCSI_CONNECTION  *

+IScsiCreateConnection (

+  IN ISCSI_DRIVER_DATA  *Private,

+  IN ISCSI_SESSION      *Session

+  );

+

+VOID

+IScsiDestroyConnection (

+  IN ISCSI_CONNECTION  *Conn

+  );

+

+EFI_STATUS

+IScsiSessionLogin (

+  IN ISCSI_DRIVER_DATA  *Private

+  );

+

+EFI_STATUS

+IScsiSendLoginReq (

+  IN ISCSI_CONNECTION  *Conn

+  );

+

+EFI_STATUS

+IScsiReceiveLoginRsp (

+  IN ISCSI_CONNECTION  *Conn

+  );

+

+EFI_STATUS

+IScsiAddKeyValuePair (

+  IN NET_BUF          *Pdu,

+  IN CHAR8            *Key,

+  IN CHAR8            *Value

+  );

+

+NET_BUF           *

+IScsiPrepareLoginReq (

+  IN ISCSI_CONNECTION  *Conn

+  );

+

+EFI_STATUS

+IScsiProcessLoginRsp (

+  IN ISCSI_CONNECTION  *Conn,

+  IN NET_BUF           *Pdu

+  );

+

+EFI_STATUS

+IScsiUpdateTargetAddress (

+  IN ISCSI_SESSION  *Session,

+  IN CHAR8          *Data,

+  IN UINT32         Len

+  );

+

+VOID

+IScsiFreeNbufList (

+  VOID *Arg

+  );

+

+EFI_STATUS

+IScsiReceivePdu (

+  IN ISCSI_CONNECTION                      *Conn,

+  OUT NET_BUF                              **Pdu,

+  IN ISCSI_IN_BUFFER_CONTEXT               *Context, OPTIONAL

+  IN BOOLEAN                               HeaderDigest,

+  IN BOOLEAN                               DataDigest,

+  IN EFI_EVENT                             TimeoutEvent OPTIONAL

+  );

+

+EFI_STATUS

+IScsiCheckOpParams (

+  IN ISCSI_CONNECTION  *Conn,

+  IN BOOLEAN           Transit

+  );

+

+EFI_STATUS

+IScsiFillOpParams (

+  IN ISCSI_CONNECTION  *Conn,

+  IN NET_BUF           *Pdu

+  );

+

+EFI_STATUS

+IScsiPadSegment (

+  IN NET_BUF  *Pdu,

+  IN UINT32   Len

+  );

+

+NET_LIST_ENTRY    *

+IScsiBuildKeyValueList (

+  IN CHAR8  *Data,

+  IN UINT32 Len

+  );

+

+CHAR8             *

+IScsiGetValueByKeyFromList (

+  IN NET_LIST_ENTRY  *KeyValueList,

+  IN CHAR8           *Key

+  );

+

+VOID

+IScsiFreeKeyValueList (

+  IN NET_LIST_ENTRY  *KeyValueList

+  );

+

+EFI_STATUS

+IScsiNormalizeName (

+  IN CHAR8  *Name,

+  IN UINTN  Len

+  );

+

+EFI_STATUS

+IScsiExecuteScsiCommand (

+  IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                 *PassThru,

+  IN UINT8                                           *Target,

+  IN UINT64                                          Lun,

+  IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet

+  );

+

+EFI_STATUS

+IScsiSessionReinstatement (

+  IN ISCSI_DRIVER_DATA  *Private

+  );

+

+VOID

+IScsiSessionInit (

+  IN ISCSI_SESSION  *Session,

+  IN BOOLEAN        Recovery

+  );

+

+EFI_STATUS

+IScsiSessionAbort (

+  IN ISCSI_SESSION  *Session

+  );

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/IScsiTcp4Io.c b/MdeModulePkg/Universal/iScsi/IScsiTcp4Io.c
new file mode 100644
index 0000000..b5b85a7
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiTcp4Io.c
@@ -0,0 +1,550 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiTcp4Io.c

+

+Abstract:

+

+--*/

+

+#include "IScsiImpl.h"

+

+VOID

+EFIAPI

+Tcp4IoCommonNotify (

+  IN EFI_EVENT  Event,

+  IN VOID       *Context

+  )

+/*++

+

+Routine Description:

+

+  The common notify function associated with various Tcp4Io events. 

+

+Arguments:

+

+  Event   - The event signaled.

+  Contect - The context.

+

+Returns:

+

+  None.

+

+--*/

+{

+  *((BOOLEAN *) Context) = TRUE;

+}

+

+EFI_STATUS

+Tcp4IoCreateSocket (

+  IN EFI_HANDLE           Image,

+  IN EFI_HANDLE           Controller,

+  IN TCP4_IO_CONFIG_DATA  *ConfigData,

+  IN TCP4_IO              *Tcp4Io

+  )

+/*++

+

+Routine Description:

+

+  Create a TCP socket with the specified configuration data. 

+

+Arguments:

+

+  Image      - The handle of the driver image.

+  Controller - The handle of the controller.

+  ConfigData - The Tcp4 configuration data.

+  Tcp4Io     - The Tcp4Io.

+

+Returns:

+

+  EFI_SUCCESS - The TCP socket is created and configured.

+  other       - Failed to create the TCP socket or configure it.

+

+--*/

+{

+  EFI_STATUS            Status;

+  EFI_TCP4_PROTOCOL     *Tcp4;

+  EFI_TCP4_CONFIG_DATA  Tcp4ConfigData;

+  EFI_TCP4_OPTION       ControlOption;

+  EFI_TCP4_ACCESS_POINT *AccessPoint;

+

+  Tcp4Io->Handle = NULL;

+  Tcp4Io->ConnToken.CompletionToken.Event = NULL;

+  Tcp4Io->TxToken.CompletionToken.Event = NULL;

+  Tcp4Io->RxToken.CompletionToken.Event = NULL;

+  Tcp4Io->CloseToken.CompletionToken.Event = NULL;

+  Tcp4 = NULL;

+

+  //

+  // Create the TCP4 child instance and get the TCP4 protocol.

+  //

+  Status = NetLibCreateServiceChild (

+            Controller,

+            Image,

+            &gEfiTcp4ServiceBindingProtocolGuid,

+            &Tcp4Io->Handle

+            );

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  Status = gBS->OpenProtocol (

+                  Tcp4Io->Handle,

+                  &gEfiTcp4ProtocolGuid,

+                  (VOID **)&Tcp4Io->Tcp4,

+                  Image,

+                  Controller,

+                  EFI_OPEN_PROTOCOL_BY_DRIVER

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Tcp4Io->Image       = Image;

+  Tcp4Io->Controller  = Controller;

+  Tcp4                = Tcp4Io->Tcp4;

+

+  //

+  // Set the configuration parameters.

+  //

+  ControlOption.ReceiveBufferSize       = 0x200000;

+  ControlOption.SendBufferSize          = 0x200000;

+  ControlOption.MaxSynBackLog           = 0;

+  ControlOption.ConnectionTimeout       = 0;

+  ControlOption.DataRetries             = 6;

+  ControlOption.FinTimeout              = 0;

+  ControlOption.TimeWaitTimeout         = 0;

+  ControlOption.KeepAliveProbes         = 4;

+  ControlOption.KeepAliveTime           = 0;

+  ControlOption.KeepAliveInterval       = 0;

+  ControlOption.EnableNagle             = FALSE;

+  ControlOption.EnableTimeStamp         = FALSE;

+  ControlOption.EnableWindowScaling     = TRUE;

+  ControlOption.EnableSelectiveAck      = FALSE;

+  ControlOption.EnablePathMtuDiscovery  = FALSE;

+

+  Tcp4ConfigData.TypeOfService          = 8;

+  Tcp4ConfigData.TimeToLive             = 255;

+  Tcp4ConfigData.ControlOption          = &ControlOption;

+

+  AccessPoint = &Tcp4ConfigData.AccessPoint;

+

+  AccessPoint->UseDefaultAddress = FALSE;

+  AccessPoint->StationPort = 0;

+  AccessPoint->RemotePort = ConfigData->RemotePort;

+  AccessPoint->ActiveFlag = TRUE;

+

+  NetCopyMem (&AccessPoint->StationAddress, &ConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS));

+  NetCopyMem (&AccessPoint->SubnetMask, &ConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));

+  NetCopyMem (&AccessPoint->RemoteAddress, &ConfigData->RemoteIp, sizeof (EFI_IPv4_ADDRESS));

+

+  //

+  // Configure the TCP4 protocol.

+  //

+  Status = Tcp4->Configure (Tcp4, &Tcp4ConfigData);

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  if (!EFI_IP4_EQUAL (&ConfigData->Gateway, &mZeroIp4Addr)) {

+    //

+    // the gateway is not zero, add the default route by hand

+    //

+    Status = Tcp4->Routes (Tcp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, &ConfigData->Gateway);

+    if (EFI_ERROR (Status)) {

+      goto ON_ERROR;

+    }

+  }

+  //

+  // Create events for variuos asynchronous operations.

+  //

+  Status = gBS->CreateEvent (

+                  EFI_EVENT_NOTIFY_SIGNAL,

+                  NET_TPL_EVENT,

+                  Tcp4IoCommonNotify,

+                  &Tcp4Io->IsConnDone,

+                  &Tcp4Io->ConnToken.CompletionToken.Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->CreateEvent (

+                  EFI_EVENT_NOTIFY_SIGNAL,

+                  NET_TPL_EVENT,

+                  Tcp4IoCommonNotify,

+                  &Tcp4Io->IsTxDone,

+                  &Tcp4Io->TxToken.CompletionToken.Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->CreateEvent (

+                  EFI_EVENT_NOTIFY_SIGNAL,

+                  NET_TPL_EVENT,

+                  Tcp4IoCommonNotify,

+                  &Tcp4Io->IsRxDone,

+                  &Tcp4Io->RxToken.CompletionToken.Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Status = gBS->CreateEvent (

+                  EFI_EVENT_NOTIFY_SIGNAL,

+                  NET_TPL_EVENT,

+                  Tcp4IoCommonNotify,

+                  &Tcp4Io->IsCloseDone,

+                  &Tcp4Io->CloseToken.CompletionToken.Event

+                  );

+  if (EFI_ERROR (Status)) {

+    goto ON_ERROR;

+  }

+

+  Tcp4Io->IsTxDone  = FALSE;

+  Tcp4Io->IsRxDone  = FALSE;

+

+  return EFI_SUCCESS;

+

+ON_ERROR:

+

+  if (Tcp4Io->RxToken.CompletionToken.Event != NULL) {

+    gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event);

+  }

+

+  if (Tcp4Io->TxToken.CompletionToken.Event != NULL) {

+    gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event);

+  }

+

+  if (Tcp4Io->ConnToken.CompletionToken.Event != NULL) {

+    gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event);

+  }

+

+  if (Tcp4 != NULL) {

+    Tcp4->Configure (Tcp4, NULL);

+

+    gBS->CloseProtocol (

+          Tcp4Io->Handle,

+          &gEfiTcp4ProtocolGuid,

+          Image,

+          Controller

+          );

+  }

+

+  NetLibDestroyServiceChild (

+    Controller,

+    Image,

+    &gEfiTcp4ServiceBindingProtocolGuid,

+    Tcp4Io->Handle

+    );

+

+  return Status;

+}

+

+VOID

+Tcp4IoDestroySocket (

+  IN TCP4_IO  *Tcp4Io

+  )

+/*++

+

+Routine Description:

+

+  Destroy the socket. 

+

+Arguments:

+

+  Tcp4Io - The Tcp4Io which wraps the socket to be destroyeds.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_TCP4_PROTOCOL *Tcp4;

+

+  Tcp4 = Tcp4Io->Tcp4;

+

+  Tcp4->Configure (Tcp4, NULL);

+

+  gBS->CloseEvent (Tcp4Io->TxToken.CompletionToken.Event);

+  gBS->CloseEvent (Tcp4Io->RxToken.CompletionToken.Event);

+  gBS->CloseEvent (Tcp4Io->ConnToken.CompletionToken.Event);

+

+  gBS->CloseProtocol (

+        Tcp4Io->Handle,

+        &gEfiTcp4ProtocolGuid,

+        Tcp4Io->Image,

+        Tcp4Io->Controller

+        );

+

+  NetLibDestroyServiceChild (

+    Tcp4Io->Controller,

+    Tcp4Io->Image,

+    &gEfiTcp4ServiceBindingProtocolGuid,

+    Tcp4Io->Handle

+    );

+}

+

+EFI_STATUS

+Tcp4IoConnect (

+  IN TCP4_IO    *Tcp4Io,

+  IN EFI_EVENT  Timeout

+  )

+/*++

+

+Routine Description:

+

+  Connect to the other endpoint of the TCP socket.

+

+Arguments:

+

+  Tcp4Io  - The Tcp4Io wrapping the TCP socket.

+  Timeout - The time to wait for connection done.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_TCP4_PROTOCOL *Tcp4;

+  EFI_STATUS        Status;

+

+  Tcp4Io->IsConnDone  = FALSE;

+  Tcp4                = Tcp4Io->Tcp4;

+  Status              = Tcp4->Connect (Tcp4, &Tcp4Io->ConnToken);

+  if (EFI_ERROR (Status)) {

+    return Status;

+  }

+

+  while (!Tcp4Io->IsConnDone && EFI_ERROR (gBS->CheckEvent (Timeout))) {

+    Tcp4->Poll (Tcp4);

+  }

+

+  if (!Tcp4Io->IsConnDone) {

+    Status = EFI_TIMEOUT;

+  } else {

+    Status = Tcp4Io->ConnToken.CompletionToken.Status;

+  }

+

+  return Status;

+}

+

+VOID

+Tcp4IoReset (

+  IN TCP4_IO  *Tcp4Io

+  )

+/*++

+

+Routine Description:

+

+  Reset the socket.

+

+Arguments:

+

+  Tcp4Io - The Tcp4Io wrapping the TCP socket.

+

+Returns:

+

+  None.

+

+--*/

+{

+  EFI_STATUS        Status;

+  EFI_TCP4_PROTOCOL *Tcp4;

+

+  Tcp4Io->CloseToken.AbortOnClose = TRUE;

+  Tcp4Io->IsCloseDone             = FALSE;

+

+  Tcp4 = Tcp4Io->Tcp4;

+  Status = Tcp4->Close (Tcp4, &Tcp4Io->CloseToken);

+  if (EFI_ERROR (Status)) {

+    return ;

+  }

+

+  while (!Tcp4Io->IsCloseDone) {

+    Tcp4->Poll (Tcp4);

+  }

+}

+

+EFI_STATUS

+Tcp4IoTransmit (

+  IN TCP4_IO  *Tcp4Io,

+  IN NET_BUF  *Packet

+  )

+/*++

+

+Routine Description:

+

+  Transmit the Packet to the other endpoint of the socket.

+

+Arguments:

+

+  Tcp4Io - The Tcp4Io wrapping the TCP socket.

+  Packet - The packet to transmit

+

+Returns:

+

+  EFI_SUCCESS          - The packet is trasmitted.

+  EFI_OUT_OF_RESOURCES - Failed to allocate memory.

+

+--*/

+{

+  EFI_TCP4_TRANSMIT_DATA  *TxData;

+  EFI_TCP4_PROTOCOL       *Tcp4;

+  EFI_STATUS              Status;

+

+  TxData = NetAllocatePool (sizeof (EFI_TCP4_TRANSMIT_DATA) + (Packet->BlockOpNum - 1) * sizeof (EFI_TCP4_FRAGMENT_DATA));

+  if (TxData == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+

+  TxData->Push        = TRUE;

+  TxData->Urgent      = FALSE;

+  TxData->DataLength  = Packet->TotalSize;

+

+  //

+  // Build the fragment table.

+  //

+  TxData->FragmentCount = Packet->BlockOpNum;

+  NetbufBuildExt (Packet, (NET_FRAGMENT *) &TxData->FragmentTable[0], &TxData->FragmentCount);

+

+  Tcp4Io->TxToken.Packet.TxData = TxData;

+

+  //

+  // Trasnmit the packet.

+  //

+  Tcp4    = Tcp4Io->Tcp4;

+  Status  = Tcp4->Transmit (Tcp4, &Tcp4Io->TxToken);

+  if (EFI_ERROR (Status)) {

+    goto ON_EXIT;

+  }

+

+  while (!Tcp4Io->IsTxDone) {

+    Tcp4->Poll (Tcp4);

+  }

+

+  Tcp4Io->IsTxDone  = FALSE;

+

+  Status            = Tcp4Io->TxToken.CompletionToken.Status;

+

+ON_EXIT:

+

+  NetFreePool (TxData);

+

+  return Status;

+}

+

+EFI_STATUS

+Tcp4IoReceive (

+  IN TCP4_IO    *Tcp4Io,

+  IN NET_BUF    *Packet,

+  IN BOOLEAN    AsyncMode,

+  IN EFI_EVENT  Timeout

+  )

+/*++

+

+Routine Description:

+

+  Receive data from the socket.

+

+Arguments:

+

+  Tcp4Io    - The Tcp4Io which wraps the socket to be destroyeds.

+  Packet    - The buffer to hold the data copy from the soket rx buffer.

+  AsyncMode - Is this receive asyncronous or not.

+  Timeout   - The time to wait for receiving the amount of data the Packet

+              can hold.

+

+Returns:

+

+  EFI_SUCCESS          - The required amount of data is received from the socket.

+  EFI_OUT_OF_RESOURCES - Failed to allocate momery.

+  EFI_TIMEOUT          - Failed to receive the required amount of data in the

+                         specified time period.

+

+--*/

+{

+  EFI_TCP4_PROTOCOL     *Tcp4;

+  EFI_TCP4_RECEIVE_DATA RxData;

+  EFI_STATUS            Status;

+  NET_FRAGMENT          *Fragment;

+  UINT32                FragmentCount;

+  UINT32                CurrentFragment;

+

+  FragmentCount = Packet->BlockOpNum;

+  Fragment      = NetAllocatePool (FragmentCount * sizeof (NET_FRAGMENT));

+  if (Fragment == NULL) {

+    return EFI_OUT_OF_RESOURCES;

+  }

+  //

+  // Build the fragment table.

+  //

+  NetbufBuildExt (Packet, Fragment, &FragmentCount);

+

+  RxData.FragmentCount          = 1;

+  Tcp4Io->RxToken.Packet.RxData = &RxData;

+  CurrentFragment               = 0;

+  Tcp4                          = Tcp4Io->Tcp4;

+  Status                        = EFI_SUCCESS;

+

+  while (CurrentFragment < FragmentCount) {

+    RxData.DataLength                       = Fragment[CurrentFragment].Len;

+    RxData.FragmentTable[0].FragmentLength  = Fragment[CurrentFragment].Len;

+    RxData.FragmentTable[0].FragmentBuffer  = Fragment[CurrentFragment].Bulk;

+

+    Status = Tcp4->Receive (Tcp4, &Tcp4Io->RxToken);

+    if (EFI_ERROR (Status)) {

+      goto ON_EXIT;

+    }

+

+    while (!Tcp4Io->IsRxDone && ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) {

+      //

+      // Poll until some data is received or something error happens.

+      //

+      Tcp4->Poll (Tcp4);

+    }

+

+    if (!Tcp4Io->IsRxDone) {

+      //

+      // Timeout occurs, cancel the receive request.

+      //

+      Tcp4->Cancel (Tcp4, &Tcp4Io->RxToken.CompletionToken);

+

+      Status = EFI_TIMEOUT;

+      goto ON_EXIT;

+    } else {

+      Tcp4Io->IsRxDone = FALSE;

+    }

+

+    if (EFI_ERROR (Tcp4Io->RxToken.CompletionToken.Status)) {

+      Status = Tcp4Io->RxToken.CompletionToken.Status;

+      goto ON_EXIT;

+    }

+

+    Fragment[CurrentFragment].Len -= RxData.FragmentTable[0].FragmentLength;

+    if (Fragment[CurrentFragment].Len == 0) {

+      CurrentFragment++;

+    } else {

+      Fragment[CurrentFragment].Bulk += RxData.FragmentTable[0].FragmentLength;

+    }

+  }

+

+ON_EXIT:

+

+  NetFreePool (Fragment);

+

+  return Status;

+}

diff --git a/MdeModulePkg/Universal/iScsi/IScsiTcp4Io.h b/MdeModulePkg/Universal/iScsi/IScsiTcp4Io.h
new file mode 100644
index 0000000..c983e05
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/IScsiTcp4Io.h
@@ -0,0 +1,93 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  IScsiTcp4Io.h

+

+Abstract:

+

+  iSCSI Tcp4 IO related definitions.

+

+--*/

+

+#ifndef _ISCSI_TCP4_IO_H_

+#define _ISCSI_TCP4_IO_H_

+

+#include <Library/NetLib.h>

+#include <protocol/Tcp4.h>

+

+typedef struct _TCP4_IO_CONFIG_DATA {

+  EFI_IPv4_ADDRESS  LocalIp;

+  EFI_IPv4_ADDRESS  SubnetMask;

+  EFI_IPv4_ADDRESS  Gateway;

+

+  EFI_IPv4_ADDRESS  RemoteIp;

+  UINT16            RemotePort;

+} TCP4_IO_CONFIG_DATA;

+

+typedef struct _TCP4_IO {

+  EFI_HANDLE                Image;

+  EFI_HANDLE                Controller;

+

+  EFI_HANDLE                Handle;

+  EFI_TCP4_PROTOCOL         *Tcp4;

+

+  EFI_TCP4_CONNECTION_TOKEN ConnToken;

+  EFI_TCP4_IO_TOKEN         TxToken;

+  EFI_TCP4_IO_TOKEN         RxToken;

+  EFI_TCP4_CLOSE_TOKEN      CloseToken;

+

+  BOOLEAN                   IsConnDone;

+  BOOLEAN                   IsTxDone;

+  BOOLEAN                   IsRxDone;

+  BOOLEAN                   IsCloseDone;

+} TCP4_IO;

+

+EFI_STATUS

+Tcp4IoCreateSocket (

+  IN EFI_HANDLE           Image,

+  IN EFI_HANDLE           Controller,

+  IN TCP4_IO_CONFIG_DATA  *ConfigData,

+  IN TCP4_IO              *Tcp4Io

+  );

+

+VOID

+Tcp4IoDestroySocket (

+  IN TCP4_IO  *Tcp4Io

+  );

+

+EFI_STATUS

+Tcp4IoConnect (

+  IN TCP4_IO    *Tcp4Io,

+  IN EFI_EVENT  Timeout

+  );

+

+VOID

+Tcp4IoReset (

+  IN TCP4_IO  *Tcp4Io

+  );

+

+EFI_STATUS

+Tcp4IoTransmit (

+  IN TCP4_IO  *Tcp4Io,

+  IN NET_BUF  *Packet

+  );

+

+EFI_STATUS

+Tcp4IoReceive (

+  IN TCP4_IO    *Tcp4Io,

+  IN NET_BUF    *Packet,

+  IN BOOLEAN    AsyncMode,

+  IN EFI_EVENT  Timeout

+  );

+

+#endif

diff --git a/MdeModulePkg/Universal/iScsi/Md5.c b/MdeModulePkg/Universal/iScsi/Md5.c
new file mode 100644
index 0000000..d283c62
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/Md5.c
@@ -0,0 +1,335 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  Md5.c

+

+Abstract:

+

+  Implementation of MD5 algorithm

+

+--*/

+

+#include "Md5.h"

+

+STATIC CONST UINT32  MD5_K[][2] = {

+  { 0, 1 },

+  { 1, 5 },

+  { 5, 3 },

+  { 0, 7 }

+};

+

+STATIC CONST UINT32  MD5_S[][4] = {

+  { 7, 22, 17, 12 },

+  { 5, 20, 14, 9 },

+  { 4, 23, 16 ,11 },

+  { 6, 21, 15, 10 },

+};

+

+STATIC CONST UINT32  MD5_T[] = {

+  0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE,

+  0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501,

+  0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE,

+  0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821,

+  0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA,

+  0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8,

+  0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED,

+  0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A,

+  0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C,

+  0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70,

+  0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05,

+  0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665,

+  0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039,

+  0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1,

+  0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1,

+  0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391

+};

+

+STATIC CONST UINT8 Md5HashPadding[] =

+{

+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 

+};

+

+//

+// ROTATE_LEFT rotates x left n bits.

+//

+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))

+

+#define SA            S[j & 3]

+#define SB            S[(j + 1) & 3]

+#define SC            S[(j + 2) & 3]

+#define SD            S[(j + 3) & 3]

+

+//

+// TF1, TF2, TF3, TF4 are basic MD5 transform functions

+//

+UINT32 TF1 (UINT32 A, UINT32 B, UINT32 C)

+{

+  return (A & B) | (~A & C);

+}

+

+UINT32 TF2 (UINT32 A, UINT32 B, UINT32 C)

+{

+  return (A & C) | (B & ~C);

+}

+

+UINT32 TF3 (UINT32 A, UINT32 B, UINT32 C)

+{

+  return A ^ B ^ C;

+}

+

+UINT32 TF4 (UINT32 A, UINT32 B, UINT32 C)

+{

+  return B ^ (A | ~C);

+}

+

+typedef

+UINT32

+(*MD5_TRANSFORM_FUNC) (

+  IN UINT32  A,

+  IN UINT32  B,

+  IN UINT32  C

+  );

+

+STATIC CONST MD5_TRANSFORM_FUNC MD5_F[] = {

+  TF1,

+  TF2,

+  TF3,

+  TF4

+};

+

+STATIC

+VOID

+MD5Transform (

+  IN MD5_CTX  *Md5Ctx

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+

+Returns:

+

+  GC_TODO: add return values

+

+--*/

+{

+  UINT32  i;

+  UINT32  j;

+  UINT32  S[MD5_HASHSIZE >> 2];

+  UINT32  *X;

+  UINT32  k;

+  UINT32  t;

+

+  X = (UINT32 *) Md5Ctx->M;

+

+  //

+  // Copy MD5 states to S

+  //

+  NetCopyMem (S, Md5Ctx->States, MD5_HASHSIZE);

+

+  t = 0;

+  for (i = 0; i < 4; i++) {

+    k = MD5_K[i][0];

+    for (j = 16; j > 0; j--) {

+      SA += (*MD5_F[i]) (SB, SC, SD) + X[k] + MD5_T[t];

+      SA  = ROTATE_LEFT (SA, MD5_S[i][j & 3]);

+      SA += SB;

+

+      k += MD5_K[i][1];

+      k &= 15;

+

+      t++;

+    }

+  }

+

+  for (i = 0; i < 4; i++) {

+    Md5Ctx->States[i] += S[i];

+  }

+}

+

+STATIC

+VOID

+MD5UpdateBlock (

+  IN MD5_CTX      *Md5Ctx,

+  IN CONST UINT8  *Data,

+  IN       UINTN  DataLen

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+  Data    - GC_TODO: add argument description

+  DataLen - GC_TODO: add argument description

+

+Returns:

+

+  GC_TODO: add return values

+

+--*/

+{

+  UINTN Limit;

+

+  for (Limit = 64 - Md5Ctx->Count; DataLen >= 64 - Md5Ctx->Count; Limit = 64) {

+    NetCopyMem (Md5Ctx->M + Md5Ctx->Count, (VOID *)Data, Limit);

+    MD5Transform (Md5Ctx);

+    

+    Md5Ctx->Count = 0;

+    Data         += Limit;

+    DataLen      -= Limit;

+  }

+

+  NetCopyMem (Md5Ctx->M + Md5Ctx->Count, (VOID *)Data, DataLen);

+  Md5Ctx->Count += DataLen;

+}

+

+EFI_STATUS

+MD5Init (

+  IN MD5_CTX  *Md5Ctx

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+

+Returns:

+

+  EFI_SUCCESS - GC_TODO: Add description for return value

+

+--*/

+{

+  NetZeroMem (Md5Ctx, sizeof (*Md5Ctx));

+

+  //

+  // Set magic initialization constants.

+  //

+  Md5Ctx->States[0] = 0x67452301;

+  Md5Ctx->States[1] = 0xefcdab89;

+  Md5Ctx->States[2] = 0x98badcfe;

+  Md5Ctx->States[3] = 0x10325476;  

+

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+MD5Update (

+  IN  MD5_CTX  *Md5Ctx,

+  IN  VOID     *Data,

+  IN  UINTN    DataLen

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+  Data    - GC_TODO: add argument description

+  DataLen - GC_TODO: add argument description

+

+Returns:

+

+  EFI_SUCCESS - GC_TODO: Add description for return value

+

+--*/

+{

+  if (EFI_ERROR (Md5Ctx->Status)) {

+    return Md5Ctx->Status;

+  }

+

+  MD5UpdateBlock (Md5Ctx, (CONST UINT8 *) Data, DataLen);

+  Md5Ctx->Length += DataLen;

+  return EFI_SUCCESS;

+}

+

+EFI_STATUS

+MD5Final (

+  IN  MD5_CTX  *Md5Ctx,

+  OUT UINT8    *HashVal

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+  HashVal - GC_TODO: add argument description

+

+Returns:

+

+  EFI_SUCCESS - GC_TODO: Add description for return value

+

+--*/

+{

+  UINTN PadLength;

+

+  if (Md5Ctx->Status == EFI_ALREADY_STARTED) {

+    //

+    // Store Hashed value & Zeroize sensitive context information.

+    //

+    NetCopyMem (HashVal, (UINT8 *) Md5Ctx->States, MD5_HASHSIZE);

+    NetZeroMem ((UINT8 *)Md5Ctx, sizeof (*Md5Ctx));

+    

+    return EFI_SUCCESS;

+  }

+

+  if (EFI_ERROR (Md5Ctx->Status)) {

+    return Md5Ctx->Status;

+  }

+

+  PadLength  = Md5Ctx->Count >= 56 ? 120 : 56;

+  PadLength -= Md5Ctx->Count;

+  MD5UpdateBlock (Md5Ctx, Md5HashPadding, PadLength);

+  Md5Ctx->Length = LShiftU64 (Md5Ctx->Length, 3);

+  MD5UpdateBlock (Md5Ctx, (CONST UINT8 *) &Md5Ctx->Length, 8);

+

+  NetZeroMem (Md5Ctx->M, sizeof (Md5Ctx->M));

+  Md5Ctx->Length  = 0;

+  Md5Ctx->Status  = EFI_ALREADY_STARTED;

+  return MD5Final (Md5Ctx, HashVal);

+}

+

diff --git a/MdeModulePkg/Universal/iScsi/Md5.h b/MdeModulePkg/Universal/iScsi/Md5.h
new file mode 100644
index 0000000..ee06fd7
--- /dev/null
+++ b/MdeModulePkg/Universal/iScsi/Md5.h
@@ -0,0 +1,108 @@
+/*++

+

+Copyright (c)  2007 Intel Corporation. All rights reserved

+This software and associated documentation (if any) is furnished

+under a license and may only be used or copied in accordance

+with the terms of the license. Except as permitted by such

+license, no part of this software or documentation may be

+reproduced, stored in a retrieval system, or transmitted in any

+form or by any means without the express written consent of

+Intel Corporation.

+

+Module Name:

+

+  Md5.h

+

+Abstract:

+

+  Header file for Md5

+

+--*/

+

+#ifndef _MD5_H_

+#define _MD5_H_

+

+#include <uefi/UefiBaseType.h>

+#include <Library/BaseLib.h>

+#include <Library/NetLib.h>

+

+#define MD5_HASHSIZE  16

+

+typedef struct _MD5_CTX {

+  EFI_STATUS  Status;

+  UINT64      Length;

+  UINT32      States[MD5_HASHSIZE / sizeof (UINT32)];

+  UINT8       M[64];

+  UINTN       Count;

+} MD5_CTX;

+

+EFI_STATUS

+MD5Init (

+  IN MD5_CTX  *Md5Ctx

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+

+Returns:

+

+  EFI_SUCCESS - GC_TODO: Add description for return value

+

+--*/

+;

+

+EFI_STATUS

+MD5Update (

+  IN MD5_CTX  *Md5Ctx,

+  IN VOID     *Data,

+  IN UINTN    DataLen

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+  Data    - GC_TODO: add argument description

+  DataLen - GC_TODO: add argument description

+

+Returns:

+

+  EFI_SUCCESS - GC_TODO: Add description for return value

+

+--*/

+;

+

+EFI_STATUS

+MD5Final (

+  IN  MD5_CTX  *Md5Ctx,

+  OUT UINT8    *HashVal

+  )

+/*++

+

+Routine Description:

+

+  GC_TODO: Add function description

+

+Arguments:

+

+  Md5Ctx  - GC_TODO: add argument description

+  HashVal - GC_TODO: add argument description

+

+Returns:

+

+  EFI_SUCCESS - GC_TODO: Add description for return value

+

+--*/

+;

+

+#endif // _MD5_H