Home | History | Annotate | Download | only in IScsiDxe
      1 /** @file
      2   The implementation of iSCSI protocol based on RFC3720.
      3 
      4 Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "IScsiImpl.h"
     16 
     17 UINT32 mDataSegPad = 0;
     18 
     19 /**
     20   Attach the iSCSI connection to the iSCSI session.
     21 
     22   @param[in, out]  Session The iSCSI session.
     23   @param[in, out]  Conn    The iSCSI connection.
     24 
     25 **/
     26 VOID
     27 IScsiAttatchConnection (
     28   IN OUT ISCSI_SESSION     *Session,
     29   IN OUT ISCSI_CONNECTION  *Conn
     30   )
     31 {
     32   InsertTailList (&Session->Conns, &Conn->Link);
     33   Conn->Session = Session;
     34   Session->NumConns++;
     35 }
     36 
     37 /**
     38   Detach the iSCSI connection from the session it belongs to.
     39 
     40   @param[in, out]  Conn The iSCSI connection.
     41 
     42 **/
     43 VOID
     44 IScsiDetatchConnection (
     45   IN OUT ISCSI_CONNECTION  *Conn
     46   )
     47 {
     48   RemoveEntryList (&Conn->Link);
     49   Conn->Session->NumConns--;
     50   Conn->Session = NULL;
     51 }
     52 
     53 
     54 /**
     55   Check the sequence number according to RFC3720.
     56 
     57   @param[in, out]  ExpSN   The currently expected sequence number.
     58   @param[in]       NewSN   The sequence number to check.
     59 
     60   @retval EFI_SUCCESS         The check passed and the ExpSN is increased.
     61   @retval EFI_NOT_READY       Response was sent due to a retransmission request.
     62   @retval EFI_PROTOCOL_ERROR  Some kind of iSCSI protocol error occurred.
     63 
     64 **/
     65 EFI_STATUS
     66 IScsiCheckSN (
     67   IN OUT UINT32  *ExpSN,
     68   IN UINT32      NewSN
     69   )
     70 {
     71   if (!ISCSI_SEQ_EQ (NewSN, *ExpSN)) {
     72     if (ISCSI_SEQ_LT (NewSN, *ExpSN)) {
     73       //
     74       // Duplicate
     75       //
     76       return EFI_NOT_READY;
     77     } else {
     78       return EFI_PROTOCOL_ERROR;
     79     }
     80   } else {
     81     //
     82     // Advance the ExpSN
     83     //
     84     (*ExpSN)++;
     85     return EFI_SUCCESS;
     86   }
     87 }
     88 
     89 
     90 /**
     91   Update the sequence numbers for the iSCSI command.
     92 
     93   @param[in, out]  Session  The iSCSI session.
     94   @param[in]       MaxCmdSN Maximum CmdSN from the target.
     95   @param[in]       ExpCmdSN Next expected CmdSN from the target.
     96 
     97 **/
     98 VOID
     99 IScsiUpdateCmdSN (
    100   IN OUT ISCSI_SESSION  *Session,
    101   IN UINT32             MaxCmdSN,
    102   IN UINT32             ExpCmdSN
    103   )
    104 {
    105   if (ISCSI_SEQ_LT (MaxCmdSN, ExpCmdSN - 1)) {
    106     return ;
    107   }
    108 
    109   if (ISCSI_SEQ_GT (MaxCmdSN, Session->MaxCmdSN)) {
    110     Session->MaxCmdSN = MaxCmdSN;
    111   }
    112 
    113   if (ISCSI_SEQ_GT (ExpCmdSN, Session->ExpCmdSN)) {
    114     Session->ExpCmdSN = ExpCmdSN;
    115   }
    116 }
    117 
    118 
    119 /**
    120   This function does the iSCSI connection login.
    121 
    122   @param[in, out]  Conn      The iSCSI connection to login.
    123   @param           Timeout   The timeout value in millisecond.
    124 
    125   @retval EFI_SUCCESS        The iSCSI connection is logged into the iSCSI target.
    126   @retval EFI_TIMEOUT        Timeout occurred during the login procedure.
    127   @retval Others             Other errors as indicated.
    128 
    129 **/
    130 EFI_STATUS
    131 IScsiConnLogin (
    132   IN OUT ISCSI_CONNECTION    *Conn,
    133   IN     UINT16              Timeout
    134   )
    135 {
    136   EFI_STATUS  Status;
    137 
    138   //
    139   // Start the timer, and wait Timeout seconds to establish the TCP connection.
    140   //
    141   Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout * TICKS_PER_MS);
    142   if (EFI_ERROR (Status)) {
    143     return Status;
    144   }
    145 
    146   //
    147   // Try to establish the tcp connection.
    148   //
    149   Status = TcpIoConnect (&Conn->TcpIo, Conn->TimeoutEvent);
    150   gBS->SetTimer (Conn->TimeoutEvent, TimerCancel, 0);
    151 
    152   if (EFI_ERROR (Status)) {
    153     return Status;
    154   }
    155 
    156   Conn->State = CONN_STATE_IN_LOGIN;
    157 
    158   //
    159   // Connection is established, start the iSCSI Login.
    160   //
    161   do {
    162     Status = IScsiSendLoginReq (Conn);
    163     if (EFI_ERROR (Status)) {
    164       break;
    165     }
    166 
    167     Status = IScsiReceiveLoginRsp (Conn);
    168     if (EFI_ERROR (Status)) {
    169       break;
    170     }
    171   } while (Conn->CurrentStage != ISCSI_FULL_FEATURE_PHASE);
    172 
    173   return Status;
    174 }
    175 
    176 
    177 /**
    178   Reset the iSCSI connection.
    179 
    180   @param[in, out]  Conn The iSCSI connection to reset.
    181 
    182 **/
    183 VOID
    184 IScsiConnReset (
    185   IN OUT ISCSI_CONNECTION  *Conn
    186   )
    187 {
    188   TcpIoReset (&Conn->TcpIo);
    189 }
    190 
    191 
    192 /**
    193   Create a TCP connection for the iSCSI session.
    194 
    195   @param[in]  Session Points to the iSCSI session.
    196 
    197   @return The newly created iSCSI connection.
    198 
    199 **/
    200 ISCSI_CONNECTION *
    201 IScsiCreateConnection (
    202   IN ISCSI_SESSION      *Session
    203   )
    204 {
    205   ISCSI_DRIVER_DATA            *Private;
    206   ISCSI_SESSION_CONFIG_NVDATA  *NvData;
    207   ISCSI_CONNECTION             *Conn;
    208   TCP_IO_CONFIG_DATA           TcpIoConfig;
    209   TCP4_IO_CONFIG_DATA          *Tcp4IoConfig;
    210   TCP6_IO_CONFIG_DATA          *Tcp6IoConfig;
    211   EFI_STATUS                   Status;
    212 
    213   Private = Session->Private;
    214   NvData  = &Session->ConfigData->SessionConfigData;
    215 
    216   Conn = AllocateZeroPool (sizeof (ISCSI_CONNECTION));
    217   if (Conn == NULL) {
    218     return NULL;
    219   }
    220 
    221   Conn->Signature       = ISCSI_CONNECTION_SIGNATURE;
    222   Conn->State           = CONN_STATE_FREE;
    223   Conn->CurrentStage    = ISCSI_SECURITY_NEGOTIATION;
    224   Conn->NextStage       = ISCSI_LOGIN_OPERATIONAL_NEGOTIATION;
    225   Conn->AuthStep        = ISCSI_AUTH_INITIAL;
    226   Conn->ExpStatSN       = 0;
    227   Conn->PartialReqSent  = FALSE;
    228   Conn->PartialRspRcvd  = FALSE;
    229   Conn->ParamNegotiated = FALSE;
    230   Conn->Cid             = Session->NextCid++;
    231   Conn->Ipv6Flag        = NvData->IpMode == IP_MODE_IP6 || Session->ConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6;
    232 
    233   Status = gBS->CreateEvent (
    234                   EVT_TIMER,
    235                   TPL_CALLBACK,
    236                   NULL,
    237                   NULL,
    238                   &Conn->TimeoutEvent
    239                   );
    240   if (EFI_ERROR (Status)) {
    241     FreePool (Conn);
    242     return NULL;
    243   }
    244 
    245   NetbufQueInit (&Conn->RspQue);
    246 
    247   //
    248   // Set the default connection-only parameters.
    249   //
    250   Conn->MaxRecvDataSegmentLength  = DEFAULT_MAX_RECV_DATA_SEG_LEN;
    251   Conn->HeaderDigest              = IScsiDigestNone;
    252   Conn->DataDigest                = IScsiDigestNone;
    253 
    254   if (!Conn->Ipv6Flag) {
    255     Tcp4IoConfig = &TcpIoConfig.Tcp4IoConfigData;
    256 
    257     CopyMem (&Tcp4IoConfig->LocalIp, &NvData->LocalIp, sizeof (EFI_IPv4_ADDRESS));
    258     CopyMem (&Tcp4IoConfig->SubnetMask, &NvData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
    259     CopyMem (&Tcp4IoConfig->Gateway, &NvData->Gateway, sizeof (EFI_IPv4_ADDRESS));
    260     CopyMem (&Tcp4IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv4_ADDRESS));
    261 
    262     Tcp4IoConfig->RemotePort  = NvData->TargetPort;
    263     Tcp4IoConfig->ActiveFlag  = TRUE;
    264     Tcp4IoConfig->StationPort = 0;
    265   } else {
    266     Tcp6IoConfig = &TcpIoConfig.Tcp6IoConfigData;
    267 
    268     CopyMem (&Tcp6IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS));
    269     Tcp6IoConfig->RemotePort  = NvData->TargetPort;
    270     Tcp6IoConfig->ActiveFlag  = TRUE;
    271     Tcp6IoConfig->StationPort = 0;
    272   }
    273 
    274   //
    275   // Create the TCP IO for this connection.
    276   //
    277   Status = TcpIoCreateSocket (
    278              Private->Image,
    279              Private->Controller,
    280              (UINT8) (!Conn->Ipv6Flag ? TCP_VERSION_4: TCP_VERSION_6),
    281              &TcpIoConfig,
    282              &Conn->TcpIo
    283              );
    284   if (EFI_ERROR (Status)) {
    285     gBS->CloseEvent (Conn->TimeoutEvent);
    286     FreePool (Conn);
    287     Conn = NULL;
    288   }
    289 
    290   return Conn;
    291 }
    292 
    293 
    294 /**
    295   Destroy an iSCSI connection.
    296 
    297   @param[in]  Conn The connection to destroy.
    298 
    299 **/
    300 VOID
    301 IScsiDestroyConnection (
    302   IN ISCSI_CONNECTION  *Conn
    303   )
    304 {
    305   TcpIoDestroySocket (&Conn->TcpIo);
    306 
    307   NetbufQueFlush (&Conn->RspQue);
    308   gBS->CloseEvent (Conn->TimeoutEvent);
    309   FreePool (Conn);
    310 }
    311 
    312 /**
    313   Retrieve the IPv6 Address/Prefix/Gateway from the established TCP connection, these informations
    314   will be filled in the iSCSI Boot Firmware Table.
    315 
    316   @param[in]  Conn             The connection used in the iSCSI login phase.
    317 
    318   @retval     EFI_SUCCESS      Get the NIC information successfully.
    319   @retval     Others           Other errors as indicated.
    320 
    321 **/
    322 EFI_STATUS
    323 IScsiGetIp6NicInfo (
    324   IN ISCSI_CONNECTION  *Conn
    325   )
    326 {
    327   ISCSI_SESSION_CONFIG_NVDATA  *NvData;
    328   EFI_TCP6_PROTOCOL            *Tcp6;
    329   EFI_IP6_MODE_DATA            Ip6ModeData;
    330   EFI_STATUS                   Status;
    331   EFI_IPv6_ADDRESS             *TargetIp;
    332   UINTN                        Index;
    333   UINT8                        SubnetPrefixLength;
    334   UINTN                        RouteEntry;
    335 
    336   NvData   = &Conn->Session->ConfigData->SessionConfigData;
    337   TargetIp = &NvData->TargetIp.v6;
    338   Tcp6     = Conn->TcpIo.Tcp.Tcp6;
    339 
    340   ZeroMem (&Ip6ModeData, sizeof (EFI_IP6_MODE_DATA));
    341   Status = Tcp6->GetModeData (
    342                    Tcp6,
    343                    NULL,
    344                    NULL,
    345                    &Ip6ModeData,
    346                    NULL,
    347                    NULL
    348                    );
    349   if (EFI_ERROR (Status)) {
    350     return Status;
    351   }
    352 
    353   if (!Ip6ModeData.IsConfigured) {
    354     Status = EFI_ABORTED;
    355     goto ON_EXIT;
    356   }
    357 
    358   IP6_COPY_ADDRESS (&NvData->LocalIp, &Ip6ModeData.ConfigData.StationAddress);
    359 
    360   NvData->PrefixLength = 0;
    361   for (Index = 0; Index < Ip6ModeData.AddressCount; Index++) {
    362     if (EFI_IP6_EQUAL (&NvData->LocalIp.v6, &Ip6ModeData.AddressList[Index].Address)) {
    363       NvData->PrefixLength = Ip6ModeData.AddressList[Index].PrefixLength;
    364       break;
    365     }
    366   }
    367 
    368   SubnetPrefixLength = 0;
    369   RouteEntry = Ip6ModeData.RouteCount;
    370   for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {
    371     if (NetIp6IsNetEqual (TargetIp, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {
    372       if (SubnetPrefixLength < Ip6ModeData.RouteTable[Index].PrefixLength) {
    373         SubnetPrefixLength = Ip6ModeData.RouteTable[Index].PrefixLength;
    374         RouteEntry = Index;
    375       }
    376     }
    377   }
    378   if (RouteEntry != Ip6ModeData.RouteCount) {
    379     IP6_COPY_ADDRESS (&NvData->Gateway, &Ip6ModeData.RouteTable[RouteEntry].Gateway);
    380   }
    381 
    382 ON_EXIT:
    383   if (Ip6ModeData.AddressList != NULL) {
    384     FreePool (Ip6ModeData.AddressList);
    385   }
    386   if (Ip6ModeData.GroupTable!= NULL) {
    387     FreePool (Ip6ModeData.GroupTable);
    388   }
    389   if (Ip6ModeData.RouteTable!= NULL) {
    390     FreePool (Ip6ModeData.RouteTable);
    391   }
    392   if (Ip6ModeData.NeighborCache!= NULL) {
    393     FreePool (Ip6ModeData.NeighborCache);
    394   }
    395   if (Ip6ModeData.PrefixTable!= NULL) {
    396     FreePool (Ip6ModeData.PrefixTable);
    397   }
    398   if (Ip6ModeData.IcmpTypeList!= NULL) {
    399     FreePool (Ip6ModeData.IcmpTypeList);
    400   }
    401 
    402   return Status;
    403 }
    404 
    405 /**
    406   Login the iSCSI session.
    407 
    408   @param[in]  Session           The iSCSI session.
    409 
    410   @retval EFI_SUCCESS           The iSCSI session login procedure finished.
    411   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
    412   @retval EFI_NO_MEDIA          There was a media error.
    413   @retval Others                Other errors as indicated.
    414 
    415 **/
    416 EFI_STATUS
    417 IScsiSessionLogin (
    418   IN ISCSI_SESSION  *Session
    419   )
    420 {
    421   EFI_STATUS        Status;
    422   ISCSI_CONNECTION  *Conn;
    423   VOID              *Tcp;
    424   EFI_GUID          *ProtocolGuid;
    425   UINT8             RetryCount;
    426   BOOLEAN           MediaPresent;
    427 
    428   //
    429   // Check media status before session login.
    430   //
    431   MediaPresent = TRUE;
    432   NetLibDetectMedia (Session->Private->Controller, &MediaPresent);
    433   if (!MediaPresent) {
    434     return EFI_NO_MEDIA;
    435   }
    436 
    437   //
    438   // Set session identifier
    439   //
    440   CopyMem (Session->Isid, Session->ConfigData->SessionConfigData.IsId, 6);
    441 
    442   RetryCount = 0;
    443 
    444   do {
    445     //
    446     // Create a connection for the session.
    447     //
    448     Conn = IScsiCreateConnection (Session);
    449     if (Conn == NULL) {
    450       return EFI_OUT_OF_RESOURCES;
    451     }
    452 
    453     IScsiAttatchConnection (Session, Conn);
    454 
    455     //
    456     // Login througth the newly created connection.
    457     //
    458     Status = IScsiConnLogin (Conn, Session->ConfigData->SessionConfigData.ConnectTimeout);
    459     if (EFI_ERROR (Status)) {
    460       IScsiConnReset (Conn);
    461       IScsiDetatchConnection (Conn);
    462       IScsiDestroyConnection (Conn);
    463     }
    464 
    465     if (Status != EFI_TIMEOUT) {
    466       break;
    467     }
    468 
    469     RetryCount++;
    470   } while (RetryCount <= Session->ConfigData->SessionConfigData.ConnectRetryCount);
    471 
    472   if (!EFI_ERROR (Status)) {
    473     Session->State = SESSION_STATE_LOGGED_IN;
    474 
    475     if (!Conn->Ipv6Flag) {
    476       ProtocolGuid = &gEfiTcp4ProtocolGuid;
    477     } else {
    478       ProtocolGuid = &gEfiTcp6ProtocolGuid;
    479     }
    480 
    481     Status = gBS->OpenProtocol (
    482                     Conn->TcpIo.Handle,
    483                     ProtocolGuid,
    484                     (VOID **) &Tcp,
    485                     Session->Private->Image,
    486                     Session->Private->ExtScsiPassThruHandle,
    487                     EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
    488                     );
    489 
    490     ASSERT_EFI_ERROR (Status);
    491 
    492     if (Conn->Ipv6Flag) {
    493       Status = IScsiGetIp6NicInfo (Conn);
    494     }
    495   }
    496 
    497   return Status;
    498 }
    499 
    500 
    501 /**
    502   Wait for IPsec negotiation, then try to login the iSCSI session again.
    503 
    504   @param[in]  Session           The iSCSI session.
    505 
    506   @retval EFI_SUCCESS           The iSCSI session login procedure finished.
    507   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
    508   @retval EFI_PROTOCOL_ERROR    Some kind of iSCSI protocol error occurred.
    509 
    510 **/
    511 EFI_STATUS
    512 IScsiSessionReLogin (
    513   IN ISCSI_SESSION  *Session
    514   )
    515 {
    516 
    517   EFI_STATUS                Status;
    518   EFI_STATUS                TimerStatus;
    519   EFI_EVENT                 Timer;
    520 
    521   Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
    522   if (EFI_ERROR (Status)) {
    523     return Status;
    524   }
    525 
    526   Status = gBS->SetTimer (
    527                   Timer,
    528                   TimerRelative,
    529                   ISCSI_WAIT_IPSEC_TIMEOUT
    530                   );
    531 
    532   if (EFI_ERROR (Status)) {
    533     gBS->CloseEvent (Timer);
    534     return Status;
    535   }
    536 
    537   do {
    538 
    539     TimerStatus = gBS->CheckEvent (Timer);
    540 
    541     if (!EFI_ERROR (TimerStatus)) {
    542       Status = IScsiSessionLogin (Session);
    543     }
    544 
    545   } while (TimerStatus == EFI_NOT_READY);
    546 
    547   gBS->CloseEvent (Timer);
    548   return Status;
    549 }
    550 
    551 
    552 /**
    553   Build and send the iSCSI login request to the iSCSI target according to
    554   the current login stage.
    555 
    556   @param[in]  Conn             The connection in the iSCSI login phase.
    557 
    558   @retval EFI_SUCCESS          The iSCSI login request PDU is built and sent on this
    559                                connection.
    560   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
    561   @retval EFI_DEVICE_ERROR     Some kind of device error occurred.
    562 
    563 **/
    564 EFI_STATUS
    565 IScsiSendLoginReq (
    566   IN ISCSI_CONNECTION  *Conn
    567   )
    568 {
    569   NET_BUF     *Pdu;
    570   EFI_STATUS  Status;
    571 
    572   //
    573   // Build the Login Request PDU.
    574   //
    575   Pdu = IScsiPrepareLoginReq (Conn);
    576   if (Pdu == NULL) {
    577     return EFI_DEVICE_ERROR;
    578   }
    579   //
    580   // Send it to the iSCSI target.
    581   //
    582   Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
    583 
    584   NetbufFree (Pdu);
    585 
    586   return Status;
    587 }
    588 
    589 
    590 /**
    591   Receive and process the iSCSI login response.
    592 
    593   @param[in]  Conn             The connection in the iSCSI login phase.
    594 
    595   @retval EFI_SUCCESS          The iSCSI login response PDU is received and processed.
    596   @retval Others               Other errors as indicated.
    597 
    598 **/
    599 EFI_STATUS
    600 IScsiReceiveLoginRsp (
    601   IN ISCSI_CONNECTION  *Conn
    602   )
    603 {
    604   EFI_STATUS  Status;
    605   NET_BUF     *Pdu;
    606 
    607   Pdu = NULL;
    608 
    609   //
    610   // Receive the iSCSI login response.
    611   //
    612   Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL);
    613   if (EFI_ERROR (Status)) {
    614     return Status;
    615   }
    616   ASSERT (Pdu != NULL);
    617 
    618   //
    619   // A Login Response is received; process it.
    620   //
    621   Status = IScsiProcessLoginRsp (Conn, Pdu);
    622 
    623   NetbufFree (Pdu);
    624 
    625   return Status;
    626 }
    627 
    628 
    629 /**
    630   Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
    631   The DataSegmentLength and the actual size of the net buffer containing this PDU will be
    632   updated.
    633 
    634   @param[in, out]  Pdu         The iSCSI PDU whose data segment the key-value pair will
    635                                be added to.
    636   @param[in]       Key         The key name string.
    637   @param[in]       Value       The value string.
    638 
    639   @retval EFI_SUCCESS          The key-value pair is added to the PDU's data segment and
    640                                the correspondence length fields are updated.
    641   @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
    642                                pair.
    643   @retval EFI_PROTOCOL_ERROR   There is no such data in the net buffer.
    644 **/
    645 EFI_STATUS
    646 IScsiAddKeyValuePair (
    647   IN OUT NET_BUF      *Pdu,
    648   IN CHAR8            *Key,
    649   IN CHAR8            *Value
    650   )
    651 {
    652   UINT32              DataSegLen;
    653   UINT32              KeyLen;
    654   UINT32              ValueLen;
    655   UINT32              TotalLen;
    656   ISCSI_LOGIN_REQUEST *LoginReq;
    657   CHAR8               *Data;
    658 
    659   LoginReq    = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL);
    660   if (LoginReq == NULL) {
    661     return EFI_PROTOCOL_ERROR;
    662   }
    663   DataSegLen  = NTOH24 (LoginReq->DataSegmentLength);
    664 
    665   KeyLen      = (UINT32) AsciiStrLen (Key);
    666   ValueLen    = (UINT32) AsciiStrLen (Value);
    667 
    668   //
    669   // 1 byte for the key value separator '=' and 1 byte for the null
    670   // delimiter after the value.
    671   //
    672   TotalLen = KeyLen + 1 + ValueLen + 1;
    673 
    674   //
    675   // Allocate the space for the key-value pair.
    676   //
    677   Data = (CHAR8 *) NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL);
    678   if (Data == NULL) {
    679     return EFI_OUT_OF_RESOURCES;
    680   }
    681   //
    682   // Add the key.
    683   //
    684   CopyMem (Data, Key, KeyLen);
    685   Data += KeyLen;
    686 
    687   *Data = '=';
    688   Data++;
    689 
    690   //
    691   // Add the value.
    692   //
    693   CopyMem (Data, Value, ValueLen);
    694   Data += ValueLen;
    695 
    696   *Data = '\0';
    697 
    698   //
    699   // Update the DataSegmentLength
    700   //
    701   ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen);
    702 
    703   return EFI_SUCCESS;
    704 }
    705 
    706 
    707 /**
    708   Prepare the iSCSI login request to be sent according to the current login status.
    709 
    710   @param[in, out]  Conn The connection in the iSCSI login phase.
    711 
    712   @return The pointer to the net buffer containing the iSCSI login request built.
    713   @retval NULL     Other errors as indicated.
    714 
    715 **/
    716 NET_BUF *
    717 IScsiPrepareLoginReq (
    718   IN OUT ISCSI_CONNECTION  *Conn
    719   )
    720 {
    721   ISCSI_SESSION       *Session;
    722   NET_BUF             *Nbuf;
    723   ISCSI_LOGIN_REQUEST *LoginReq;
    724   EFI_STATUS          Status;
    725 
    726   Session = Conn->Session;
    727 
    728   Nbuf    = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN);
    729   if (Nbuf == NULL) {
    730     return NULL;
    731   }
    732 
    733   LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufAllocSpace (Nbuf, sizeof (ISCSI_LOGIN_REQUEST), NET_BUF_TAIL);
    734   ASSERT (LoginReq != NULL);
    735   ZeroMem (LoginReq, sizeof (ISCSI_LOGIN_REQUEST));
    736 
    737   //
    738   // Init the login request pdu
    739   //
    740   ISCSI_SET_OPCODE (LoginReq, ISCSI_OPCODE_LOGIN_REQ, ISCSI_REQ_IMMEDIATE);
    741   ISCSI_SET_STAGES (LoginReq, Conn->CurrentStage, Conn->NextStage);
    742   LoginReq->VersionMax        = ISCSI_VERSION_MAX;
    743   LoginReq->VersionMin        = ISCSI_VERSION_MIN;
    744   LoginReq->Tsih              = HTONS (Session->Tsih);
    745   LoginReq->InitiatorTaskTag  = HTONL (Session->InitiatorTaskTag);
    746   LoginReq->Cid               = HTONS (Conn->Cid);
    747   LoginReq->CmdSN             = HTONL (Session->CmdSN);
    748 
    749   //
    750   // For the first Login Request on a coonection this is ExpStatSN for the
    751   // old connection, and this field is only valid if the Login Request restarts
    752   // a connection.
    753   // For subsequent Login Requests it is used to acknowledge the Login Responses
    754   // with their increasing StatSN values.
    755   //
    756   LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN);
    757   CopyMem (LoginReq->Isid, Session->Isid, sizeof (LoginReq->Isid));
    758 
    759   if (Conn->PartialRspRcvd) {
    760     //
    761     // A partial response. The initiator must send an empty Login Request.
    762     //
    763     return Nbuf;
    764   }
    765 
    766   Status = EFI_SUCCESS;
    767 
    768   switch (Conn->CurrentStage) {
    769   case ISCSI_SECURITY_NEGOTIATION:
    770     //
    771     // Both none authentication and CHAP authentication share the CHAP path.
    772     //
    773     //
    774     if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
    775       Status = IScsiCHAPToSendReq (Conn, Nbuf);
    776     }
    777 
    778     break;
    779 
    780   case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
    781     //
    782     // Only negotiate the paramter once.
    783     //
    784     if (!Conn->ParamNegotiated) {
    785       IScsiFillOpParams (Conn, Nbuf);
    786     }
    787 
    788     ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
    789     break;
    790 
    791   default:
    792     //
    793     // An error occurs...
    794     //
    795     Status = EFI_DEVICE_ERROR;
    796     break;
    797   }
    798 
    799   if (EFI_ERROR (Status)) {
    800     NetbufFree (Nbuf);
    801     Nbuf = NULL;
    802   } else {
    803     //
    804     // Pad the data segment if needed.
    805     //
    806     IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq));
    807     //
    808     // Check whether we will issue the stage transition signal?
    809     //
    810     Conn->TransitInitiated = ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
    811   }
    812 
    813   return Nbuf;
    814 }
    815 
    816 
    817 /**
    818   Process the iSCSI Login Response.
    819 
    820   @param[in, out]  Conn The connection on which the iSCSI login response is received.
    821   @param[in, out]  Pdu  The iSCSI login response PDU.
    822 
    823   @retval EFI_SUCCESS        The iSCSI login response PDU is processed, and all checks are passed.
    824   @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
    825   @retval EFI_MEDIA_CHANGED  Target is redirected.
    826   @retval Others             Other errors as indicated.
    827 
    828 **/
    829 EFI_STATUS
    830 IScsiProcessLoginRsp (
    831   IN OUT ISCSI_CONNECTION  *Conn,
    832   IN OUT NET_BUF           *Pdu
    833   )
    834 {
    835   EFI_STATUS            Status;
    836   ISCSI_SESSION         *Session;
    837   ISCSI_LOGIN_RESPONSE  *LoginRsp;
    838   BOOLEAN               Transit;
    839   BOOLEAN               Continue;
    840   UINT8                 CurrentStage;
    841   UINT8                 NextStage;
    842   UINT8                 *DataSeg;
    843   UINT32                DataSegLen;
    844 
    845   Status   = EFI_SUCCESS;
    846   Session   = Conn->Session;
    847 
    848   LoginRsp  = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
    849   if (LoginRsp == NULL) {
    850     return EFI_PROTOCOL_ERROR;
    851   }
    852   if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) {
    853     //
    854     // It is not a Login Response.
    855     //
    856     return EFI_PROTOCOL_ERROR;
    857   }
    858   //
    859   // Get the data segment, if any.
    860   //
    861   DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp);
    862   if (DataSegLen != 0) {
    863     DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL);
    864   } else {
    865     DataSeg = NULL;
    866   }
    867   //
    868   // Check the status class in the login response PDU.
    869   //
    870   switch (LoginRsp->StatusClass) {
    871   case ISCSI_LOGIN_STATUS_SUCCESS:
    872     //
    873     // Just break here; the response and the data segment will be processed later.
    874     //
    875     break;
    876 
    877   case ISCSI_LOGIN_STATUS_REDIRECTION:
    878     //
    879     // The target may be moved to a different address.
    880     //
    881     if (DataSeg == NULL) {
    882       return EFI_PROTOCOL_ERROR;
    883     }
    884     //
    885     // Process the TargetAddress key-value strings in the data segment to update the
    886     // target address info.
    887     //
    888     Status = IScsiUpdateTargetAddress (Session, (CHAR8 *) DataSeg, DataSegLen);
    889     if (EFI_ERROR (Status)) {
    890       return Status;
    891     }
    892     //
    893     // Session will be restarted on this error status because the Target is
    894     // redirected by this Login Response.
    895     //
    896     return EFI_MEDIA_CHANGED;
    897 
    898   default:
    899     //
    900     // Initiator Error, Target Error, or any other undefined error code.
    901     //
    902     return EFI_PROTOCOL_ERROR;
    903   }
    904   //
    905   // The status is success; extract the wanted fields from the header segment.
    906   //
    907   Transit                     = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT);
    908   Continue                    = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE);
    909 
    910   CurrentStage                = ISCSI_GET_CURRENT_STAGE (LoginRsp);
    911   NextStage                   = ISCSI_GET_NEXT_STAGE (LoginRsp);
    912 
    913   LoginRsp->InitiatorTaskTag  = NTOHL (LoginRsp->InitiatorTaskTag);
    914 
    915   if ((Transit && Continue) ||
    916       (CurrentStage != Conn->CurrentStage) ||
    917       (!Conn->TransitInitiated && Transit) ||
    918       (Transit && (NextStage != Conn->NextStage)) ||
    919       (CompareMem (Session->Isid, LoginRsp->Isid, sizeof (LoginRsp->Isid)) != 0) ||
    920       (LoginRsp->InitiatorTaskTag != Session->InitiatorTaskTag)
    921       ) {
    922     //
    923     // A Login Response with the C bit set to 1 MUST have the T bit set to 0.
    924     // The CSG in the Login Response MUST be the same with the I-end of this connection.
    925     // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
    926     // initiate the transistion.
    927     // The NSG MUST be the same with the I-end of this connection if Transit is required.
    928     // The ISID in the Login Response MUST be the same with this session.
    929     //
    930     return EFI_PROTOCOL_ERROR;
    931   }
    932 
    933   LoginRsp->StatSN    = NTOHL (LoginRsp->StatSN);
    934   LoginRsp->ExpCmdSN  = NTOHL (LoginRsp->ExpCmdSN);
    935   LoginRsp->MaxCmdSN  = NTOHL (LoginRsp->MaxCmdSN);
    936 
    937   if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->AuthStep == ISCSI_AUTH_INITIAL)) {
    938     //
    939     // If the Login Request is a leading Login Request, the target MUST use
    940     // the value presented in CmdSN as the target value for ExpCmdSN.
    941     //
    942     if ((Session->State == SESSION_STATE_FREE) && (Session->CmdSN != LoginRsp->ExpCmdSN)) {
    943       return EFI_PROTOCOL_ERROR;
    944     }
    945 
    946     //
    947     // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
    948     // and ExpCmdSN.
    949     //
    950     Conn->ExpStatSN   = LoginRsp->StatSN + 1;
    951     Session->MaxCmdSN = LoginRsp->MaxCmdSN;
    952     Session->ExpCmdSN = LoginRsp->ExpCmdSN;
    953   } else {
    954     //
    955     // Check the StatSN of this PDU.
    956     //
    957     Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN);
    958     if (!EFI_ERROR (Status)) {
    959       //
    960       // Update the MaxCmdSN and ExpCmdSN.
    961       //
    962       IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN);
    963     } else {
    964       return Status;
    965     }
    966   }
    967   //
    968   // Trim off the header segment.
    969   //
    970   NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD);
    971 
    972   //
    973   // Queue this login response first in case it's a partial response so that
    974   // later when the full response list is received we can combine these scattered
    975   // responses' data segment and then process it.
    976   //
    977   NET_GET_REF (Pdu);
    978   NetbufQueAppend (&Conn->RspQue, Pdu);
    979 
    980   Conn->PartialRspRcvd = Continue;
    981   if (Continue) {
    982     //
    983     // It is a partial response; must wait for another or more Request/Response
    984     // conversations to get the full response.
    985     //
    986     return EFI_SUCCESS;
    987   }
    988 
    989   switch (CurrentStage) {
    990   case ISCSI_SECURITY_NEGOTIATION:
    991     //
    992     // In security negotiation stage, let CHAP module handle it.
    993     //
    994     if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
    995       Status = IScsiCHAPOnRspReceived (Conn);
    996     }
    997     break;
    998 
    999   case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
   1000     //
   1001     // Response received with negotiation response on iSCSI parameters: check them.
   1002     //
   1003     Status = IScsiCheckOpParams (Conn);
   1004     if (!EFI_ERROR (Status)) {
   1005       Conn->ParamNegotiated = TRUE;
   1006     }
   1007 
   1008     break;
   1009 
   1010   default:
   1011     //
   1012     // Should never get here.
   1013     //
   1014     Status = EFI_PROTOCOL_ERROR;
   1015     break;
   1016   }
   1017 
   1018   if (Transit && (Status == EFI_SUCCESS)) {
   1019     //
   1020     // Do the state transition.
   1021     //
   1022     Conn->CurrentStage = Conn->NextStage;
   1023 
   1024     if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) {
   1025       Conn->NextStage = ISCSI_FULL_FEATURE_PHASE;
   1026     } else {
   1027       //
   1028       // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
   1029       // get the TSIH from the Login Response.
   1030       //
   1031       Session->Tsih = NTOHS (LoginRsp->Tsih);
   1032     }
   1033   }
   1034   //
   1035   // Flush the response(s) received.
   1036   //
   1037   NetbufQueFlush (&Conn->RspQue);
   1038 
   1039   return Status;
   1040 }
   1041 
   1042 
   1043 /**
   1044   Updated the target information according the data received in the iSCSI
   1045   login response with an target redirection status.
   1046 
   1047   @param[in, out] Session      The iSCSI session.
   1048   @param[in]      Data         The data segment that should contain the
   1049                                TargetAddress key-value list.
   1050   @param[in]      Len          Length of the data.
   1051 
   1052   @retval EFI_SUCCESS          The target address is updated.
   1053   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
   1054   @retval EFI_NOT_FOUND        The TargetAddress key is not found.
   1055   @retval Others               Other errors as indicated.
   1056 
   1057 **/
   1058 EFI_STATUS
   1059 IScsiUpdateTargetAddress (
   1060   IN OUT ISCSI_SESSION         *Session,
   1061   IN     CHAR8                 *Data,
   1062   IN     UINT32                Len
   1063   )
   1064 {
   1065   LIST_ENTRY      *KeyValueList;
   1066   CHAR8           *TargetAddress;
   1067   CHAR8           *IpStr;
   1068   EFI_STATUS      Status;
   1069   UINTN           Number;
   1070   UINT8           IpMode;
   1071 
   1072   KeyValueList = IScsiBuildKeyValueList (Data, Len);
   1073   if (KeyValueList == NULL) {
   1074     return EFI_OUT_OF_RESOURCES;
   1075   }
   1076 
   1077   Status = EFI_NOT_FOUND;
   1078 
   1079   while (TRUE) {
   1080     TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);
   1081     if (TargetAddress == NULL) {
   1082       break;
   1083     }
   1084 
   1085     if (!NET_IS_DIGIT (TargetAddress[0])) {
   1086       //
   1087       // The domainname of the target may be presented in three formats: a DNS host name,
   1088       // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted
   1089       // IPv4 address.
   1090       //
   1091       continue;
   1092     }
   1093 
   1094     IpStr = TargetAddress;
   1095 
   1096     while ((*TargetAddress != 0) && (*TargetAddress != ':') && (*TargetAddress != ',')) {
   1097       //
   1098       // NULL, ':', or ',' ends the IPv4 string.
   1099       //
   1100       TargetAddress++;
   1101     }
   1102 
   1103     if (*TargetAddress == ',') {
   1104       //
   1105       // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
   1106       // as the result of a redirection.
   1107       //
   1108       continue;
   1109     } else if (*TargetAddress == ':') {
   1110       *TargetAddress = '\0';
   1111 
   1112       TargetAddress++;
   1113 
   1114       Number = AsciiStrDecimalToUintn (TargetAddress);
   1115       if (Number > 0xFFFF) {
   1116         continue;
   1117       } else {
   1118         Session->ConfigData->SessionConfigData.TargetPort = (UINT16) Number;
   1119       }
   1120     } else {
   1121       //
   1122       // The string only contains the IPv4 address. Use the well-known port.
   1123       //
   1124       Session->ConfigData->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
   1125     }
   1126     //
   1127     // Update the target IP address.
   1128     //
   1129     if (Session->ConfigData->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {
   1130       IpMode = Session->ConfigData->SessionConfigData.IpMode;
   1131     } else {
   1132       IpMode = Session->ConfigData->AutoConfigureMode;
   1133     }
   1134 
   1135     Status = IScsiAsciiStrToIp (
   1136                IpStr,
   1137                IpMode,
   1138                &Session->ConfigData->SessionConfigData.TargetIp
   1139                );
   1140 
   1141     if (EFI_ERROR (Status)) {
   1142       continue;
   1143     } else {
   1144       break;
   1145     }
   1146   }
   1147 
   1148   IScsiFreeKeyValueList (KeyValueList);
   1149 
   1150   return Status;
   1151 }
   1152 
   1153 
   1154 /**
   1155   The callback function to free the net buffer list.
   1156 
   1157   @param[in]  Arg The opaque parameter.
   1158 
   1159 **/
   1160 VOID
   1161 EFIAPI
   1162 IScsiFreeNbufList (
   1163   VOID *Arg
   1164   )
   1165 {
   1166   ASSERT (Arg != NULL);
   1167 
   1168   NetbufFreeList ((LIST_ENTRY *) Arg);
   1169   FreePool (Arg);
   1170 }
   1171 
   1172 
   1173 /**
   1174   The callback function called in NetBufFree; it does nothing.
   1175 
   1176   @param[in]   Arg  The opaque parameter.
   1177 
   1178 **/
   1179 VOID
   1180 EFIAPI
   1181 IScsiNbufExtFree (
   1182   VOID *Arg
   1183   )
   1184 {
   1185 }
   1186 
   1187 
   1188 /**
   1189   Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
   1190   an optional data segment. The two parts will be put into two blocks of buffers in the
   1191   net buffer. The digest check will be conducted in this function if needed and the digests
   1192   will be trimmed from the PDU buffer.
   1193 
   1194   @param[in]  Conn         The iSCSI connection to receive data from.
   1195   @param[out] Pdu          The received iSCSI pdu.
   1196   @param[in]  Context      The context used to describe information on the caller provided
   1197                            buffer to receive data segment of the iSCSI pdu. It is optional.
   1198   @param[in]  HeaderDigest Whether there will be header digest received.
   1199   @param[in]  DataDigest   Whether there will be data digest.
   1200   @param[in]  TimeoutEvent The timeout event. It is optional.
   1201 
   1202   @retval EFI_SUCCESS          An iSCSI pdu is received.
   1203   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
   1204   @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol error occurred.
   1205   @retval Others               Other errors as indicated.
   1206 
   1207 **/
   1208 EFI_STATUS
   1209 IScsiReceivePdu (
   1210   IN ISCSI_CONNECTION                      *Conn,
   1211   OUT NET_BUF                              **Pdu,
   1212   IN ISCSI_IN_BUFFER_CONTEXT               *Context, OPTIONAL
   1213   IN BOOLEAN                               HeaderDigest,
   1214   IN BOOLEAN                               DataDigest,
   1215   IN EFI_EVENT                             TimeoutEvent OPTIONAL
   1216   )
   1217 {
   1218   LIST_ENTRY      *NbufList;
   1219   UINT32          Len;
   1220   NET_BUF         *PduHdr;
   1221   UINT8           *Header;
   1222   EFI_STATUS      Status;
   1223   UINT32          PadLen;
   1224   UINT32          InDataOffset;
   1225   NET_FRAGMENT    Fragment[2];
   1226   UINT32          FragmentCount;
   1227   NET_BUF         *DataSeg;
   1228   UINT32          PadAndCRC32[2];
   1229 
   1230   NbufList = AllocatePool (sizeof (LIST_ENTRY));
   1231   if (NbufList == NULL) {
   1232     return EFI_OUT_OF_RESOURCES;
   1233   }
   1234 
   1235   InitializeListHead (NbufList);
   1236 
   1237   //
   1238   // The header digest will be received together with the PDU header, if exists.
   1239   //
   1240   Len     = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0);
   1241   PduHdr  = NetbufAlloc (Len);
   1242   if (PduHdr == NULL) {
   1243     Status = EFI_OUT_OF_RESOURCES;
   1244     goto ON_EXIT;
   1245   }
   1246 
   1247   Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);
   1248   ASSERT (Header != NULL);
   1249   InsertTailList (NbufList, &PduHdr->List);
   1250 
   1251   //
   1252   // First step, receive the BHS of the PDU.
   1253   //
   1254   Status = TcpIoReceive (&Conn->TcpIo, PduHdr, FALSE, TimeoutEvent);
   1255 
   1256   if (EFI_ERROR (Status)) {
   1257     goto ON_EXIT;
   1258   }
   1259 
   1260   if (HeaderDigest) {
   1261     //
   1262     // TODO: check the header-digest.
   1263     //
   1264     //
   1265     // Trim off the digest.
   1266     //
   1267     NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL);
   1268   }
   1269 
   1270   Len = ISCSI_GET_DATASEG_LEN (Header);
   1271   if (Len == 0) {
   1272     //
   1273     // No data segment.
   1274     //
   1275     goto FORM_PDU;
   1276   }
   1277   //
   1278   // Get the length of the padding bytes of the data segment.
   1279   //
   1280   PadLen = ISCSI_GET_PAD_LEN (Len);
   1281 
   1282   switch (ISCSI_GET_OPCODE (Header)) {
   1283   case ISCSI_OPCODE_SCSI_DATA_IN:
   1284     //
   1285     // To reduce memory copy overhead, try to use the buffer described by Context
   1286     // if the PDU is an iSCSI SCSI data.
   1287     //
   1288     InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header);
   1289     if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) {
   1290       Status = EFI_PROTOCOL_ERROR;
   1291       goto ON_EXIT;
   1292     }
   1293 
   1294     Fragment[0].Len   = Len;
   1295     Fragment[0].Bulk  = Context->InData + InDataOffset;
   1296 
   1297     if (DataDigest || (PadLen != 0)) {
   1298       //
   1299       // The data segment is padded. Use two fragments to receive it:
   1300       // the first to receive the useful data; the second to receive the padding.
   1301       //
   1302       Fragment[1].Len   = PadLen + (DataDigest ? sizeof (UINT32) : 0);
   1303       Fragment[1].Bulk  = (UINT8 *)PadAndCRC32 + (4 - PadLen);
   1304 
   1305       FragmentCount     = 2;
   1306     } else {
   1307       FragmentCount = 1;
   1308     }
   1309 
   1310     DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
   1311     if (DataSeg == NULL) {
   1312       Status = EFI_OUT_OF_RESOURCES;
   1313       goto ON_EXIT;
   1314     }
   1315 
   1316     break;
   1317 
   1318   case ISCSI_OPCODE_SCSI_RSP:
   1319   case ISCSI_OPCODE_NOP_IN:
   1320   case ISCSI_OPCODE_LOGIN_RSP:
   1321   case ISCSI_OPCODE_TEXT_RSP:
   1322   case ISCSI_OPCODE_ASYNC_MSG:
   1323   case ISCSI_OPCODE_REJECT:
   1324   case ISCSI_OPCODE_VENDOR_T0:
   1325   case ISCSI_OPCODE_VENDOR_T1:
   1326   case ISCSI_OPCODE_VENDOR_T2:
   1327     //
   1328     // Allocate buffer to receive the data segment.
   1329     //
   1330     Len += PadLen + (DataDigest ? sizeof (UINT32) : 0);
   1331     DataSeg = NetbufAlloc (Len);
   1332     if (DataSeg == NULL) {
   1333       Status = EFI_OUT_OF_RESOURCES;
   1334       goto ON_EXIT;
   1335     }
   1336 
   1337     NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);
   1338     break;
   1339 
   1340   default:
   1341     Status = EFI_PROTOCOL_ERROR;
   1342     goto ON_EXIT;
   1343   }
   1344 
   1345   InsertTailList (NbufList, &DataSeg->List);
   1346 
   1347   //
   1348   // Receive the data segment with the data digest, if any.
   1349   //
   1350   Status = TcpIoReceive (&Conn->TcpIo, DataSeg, FALSE, TimeoutEvent);
   1351 
   1352   if (EFI_ERROR (Status)) {
   1353     goto ON_EXIT;
   1354   }
   1355 
   1356   if (DataDigest) {
   1357     //
   1358     // TODO: Check the data digest.
   1359     //
   1360     NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL);
   1361   }
   1362 
   1363   if (PadLen != 0) {
   1364     //
   1365     // Trim off the padding bytes in the data segment.
   1366     //
   1367     NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL);
   1368   }
   1369 
   1370 FORM_PDU:
   1371   //
   1372   // Form the pdu from a list of pdu segments.
   1373   //
   1374   *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
   1375   if (*Pdu == NULL) {
   1376     Status = EFI_OUT_OF_RESOURCES;
   1377   }
   1378 
   1379 ON_EXIT:
   1380 
   1381   if (EFI_ERROR (Status)) {
   1382     //
   1383     // Free the Nbufs in this NbufList and the NbufList itself.
   1384     //
   1385     IScsiFreeNbufList (NbufList);
   1386   }
   1387 
   1388   return Status;
   1389 }
   1390 
   1391 
   1392 /**
   1393   Check and get the result of the parameter negotiation.
   1394 
   1395   @param[in, out]  Conn          The connection in iSCSI login.
   1396 
   1397   @retval EFI_SUCCESS          The parmeter check is passed and negotiation is finished.
   1398   @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol error occurred.
   1399   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
   1400 
   1401 **/
   1402 EFI_STATUS
   1403 IScsiCheckOpParams (
   1404   IN OUT ISCSI_CONNECTION  *Conn
   1405   )
   1406 {
   1407   EFI_STATUS      Status;
   1408   LIST_ENTRY      *KeyValueList;
   1409   CHAR8           *Data;
   1410   UINT32          Len;
   1411   ISCSI_SESSION   *Session;
   1412   CHAR8           *Value;
   1413   UINTN           NumericValue;
   1414 
   1415   ASSERT (Conn->RspQue.BufNum != 0);
   1416 
   1417   Session = Conn->Session;
   1418 
   1419   Len     = Conn->RspQue.BufSize;
   1420   Data    = AllocatePool (Len);
   1421   if (Data == NULL) {
   1422     return EFI_OUT_OF_RESOURCES;
   1423   }
   1424 
   1425   NetbufQueCopy (&Conn->RspQue, 0, Len, (UINT8 *) Data);
   1426 
   1427   Status = EFI_PROTOCOL_ERROR;
   1428 
   1429   //
   1430   // Extract the Key-Value pairs into a list.
   1431   //
   1432   KeyValueList = IScsiBuildKeyValueList (Data, Len);
   1433   if (KeyValueList == NULL) {
   1434     FreePool (Data);
   1435     return Status;
   1436   }
   1437   //
   1438   // HeaderDigest
   1439   //
   1440   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST);
   1441   if (Value == NULL) {
   1442     goto ON_ERROR;
   1443   }
   1444 
   1445   if (AsciiStrCmp (Value, "CRC32") == 0) {
   1446     if (Conn->HeaderDigest != IScsiDigestCRC32) {
   1447       goto ON_ERROR;
   1448     }
   1449   } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
   1450     Conn->HeaderDigest = IScsiDigestNone;
   1451   } else {
   1452     goto ON_ERROR;
   1453   }
   1454   //
   1455   // DataDigest
   1456   //
   1457   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST);
   1458   if (Value == NULL) {
   1459     goto ON_ERROR;
   1460   }
   1461 
   1462   if (AsciiStrCmp (Value, "CRC32") == 0) {
   1463     if (Conn->DataDigest != IScsiDigestCRC32) {
   1464       goto ON_ERROR;
   1465     }
   1466   } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
   1467     Conn->DataDigest = IScsiDigestNone;
   1468   } else {
   1469     goto ON_ERROR;
   1470   }
   1471   //
   1472   // ErrorRecoveryLevel: result fuction is Minimum.
   1473   //
   1474   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL);
   1475   if (Value == NULL) {
   1476     goto ON_ERROR;
   1477   }
   1478 
   1479   NumericValue = IScsiNetNtoi (Value);
   1480   if (NumericValue > 2) {
   1481     goto ON_ERROR;
   1482   }
   1483 
   1484   Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue);
   1485 
   1486   //
   1487   // InitialR2T: result function is OR.
   1488   //
   1489   if (!Session->InitialR2T) {
   1490     Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
   1491     if (Value == NULL) {
   1492       goto ON_ERROR;
   1493     }
   1494 
   1495     Session->InitialR2T = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
   1496   }
   1497 
   1498   //
   1499   // ImmediateData: result function is AND.
   1500   //
   1501   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA);
   1502   if (Value == NULL) {
   1503     goto ON_ERROR;
   1504   }
   1505 
   1506   Session->ImmediateData = (BOOLEAN) (Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0));
   1507 
   1508   //
   1509   // MaxRecvDataSegmentLength is declarative.
   1510   //
   1511   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH);
   1512   if (Value != NULL) {
   1513     Conn->MaxRecvDataSegmentLength = (UINT32) IScsiNetNtoi (Value);
   1514   }
   1515   //
   1516   // MaxBurstLength: result funtion is Mininum.
   1517   //
   1518   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH);
   1519   if (Value == NULL) {
   1520     goto ON_ERROR;
   1521   }
   1522 
   1523   NumericValue            = IScsiNetNtoi (Value);
   1524   Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue);
   1525 
   1526   //
   1527   // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
   1528   // ImmediateData=No.
   1529   //
   1530   if (!(Session->InitialR2T && !Session->ImmediateData)) {
   1531     Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
   1532     if (Value == NULL) {
   1533       goto ON_ERROR;
   1534     }
   1535 
   1536     NumericValue              = IScsiNetNtoi (Value);
   1537     Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue);
   1538   }
   1539 
   1540   //
   1541   // MaxConnections: result function is Minimum.
   1542   //
   1543   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS);
   1544   if (Value == NULL) {
   1545     goto ON_ERROR;
   1546   }
   1547 
   1548   NumericValue = IScsiNetNtoi (Value);
   1549   if ((NumericValue == 0) || (NumericValue > 65535)) {
   1550     goto ON_ERROR;
   1551   }
   1552 
   1553   Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue);
   1554 
   1555   //
   1556   // DataPDUInOrder: result function is OR.
   1557   //
   1558   if (!Session->DataPDUInOrder) {
   1559     Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
   1560     if (Value == NULL) {
   1561       goto ON_ERROR;
   1562     }
   1563 
   1564     Session->DataPDUInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
   1565   }
   1566 
   1567   //
   1568   // DataSequenceInorder: result function is OR.
   1569   //
   1570   if (!Session->DataSequenceInOrder) {
   1571     Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
   1572     if (Value == NULL) {
   1573       goto ON_ERROR;
   1574     }
   1575 
   1576     Session->DataSequenceInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
   1577   }
   1578 
   1579   //
   1580   // DefaultTime2Wait: result function is Maximum.
   1581   //
   1582   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT);
   1583   if (Value == NULL) {
   1584     goto ON_ERROR;
   1585   }
   1586 
   1587   NumericValue = IScsiNetNtoi (Value);
   1588   if (NumericValue == 0) {
   1589     Session->DefaultTime2Wait = 0;
   1590   } else if (NumericValue > 3600) {
   1591     goto ON_ERROR;
   1592   } else {
   1593     Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue);
   1594   }
   1595   //
   1596   // DefaultTime2Retain: result function is Minimum.
   1597   //
   1598   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN);
   1599   if (Value == NULL) {
   1600     goto ON_ERROR;
   1601   }
   1602 
   1603   NumericValue = IScsiNetNtoi (Value);
   1604   if (NumericValue == 0) {
   1605     Session->DefaultTime2Retain = 0;
   1606   } else if (NumericValue > 3600) {
   1607     goto ON_ERROR;
   1608   } else {
   1609     Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue);
   1610   }
   1611   //
   1612   // MaxOutstandingR2T: result function is Minimum.
   1613   //
   1614   Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T);
   1615   if (Value == NULL) {
   1616     goto ON_ERROR;
   1617   }
   1618 
   1619   NumericValue = IScsiNetNtoi (Value);
   1620   if ((NumericValue == 0) || (NumericValue > 65535)) {
   1621     goto ON_ERROR;
   1622   }
   1623 
   1624   Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue);
   1625 
   1626   //
   1627   // Remove declarative key-value pairs, if any.
   1628   //
   1629   IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE);
   1630   IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS);
   1631   IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
   1632 
   1633 
   1634   //
   1635   // Remove the key-value that may not needed for result function is OR.
   1636   //
   1637   IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
   1638   IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
   1639   IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
   1640 
   1641   //
   1642   // Remove irrelevant parameter, if any.
   1643   //
   1644   if (Session->InitialR2T && !Session->ImmediateData) {
   1645     IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
   1646   }
   1647 
   1648   if (IsListEmpty (KeyValueList)) {
   1649     //
   1650     // Succeed if no more keys in the list.
   1651     //
   1652     Status = EFI_SUCCESS;
   1653   }
   1654 
   1655 ON_ERROR:
   1656 
   1657   IScsiFreeKeyValueList (KeyValueList);
   1658 
   1659   FreePool (Data);
   1660 
   1661   return Status;
   1662 }
   1663 
   1664 
   1665 /**
   1666   Fill the operational parameters.
   1667 
   1668   @param[in]       Conn    The connection in iSCSI login.
   1669   @param[in, out]  Pdu     The iSCSI login request PDU to fill the parameters.
   1670 
   1671 **/
   1672 VOID
   1673 IScsiFillOpParams (
   1674   IN     ISCSI_CONNECTION  *Conn,
   1675   IN OUT NET_BUF           *Pdu
   1676   )
   1677 {
   1678   ISCSI_SESSION *Session;
   1679   CHAR8         Value[256];
   1680 
   1681   Session = Conn->Session;
   1682 
   1683   AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
   1684   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);
   1685 
   1686   AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
   1687   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);
   1688 
   1689   AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);
   1690   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);
   1691 
   1692   AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");
   1693   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);
   1694 
   1695   AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");
   1696   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);
   1697 
   1698   AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);
   1699   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);
   1700 
   1701   AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);
   1702   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);
   1703 
   1704   AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);
   1705   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);
   1706 
   1707   AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);
   1708   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);
   1709 
   1710   AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");
   1711   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);
   1712 
   1713   AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");
   1714   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);
   1715 
   1716   AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);
   1717   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);
   1718 
   1719   AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);
   1720   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);
   1721 
   1722   AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);
   1723   IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);
   1724 }
   1725 
   1726 
   1727 /**
   1728   Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
   1729 
   1730   @param[in, out]  Pdu         The iSCSI pdu which contains segments to pad.
   1731   @param[in]       Len         The length of the last segment in the PDU.
   1732 
   1733   @retval EFI_SUCCESS          The segment is padded or there is no need to pad it.
   1734   @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
   1735                                padding bytes.
   1736 **/
   1737 EFI_STATUS
   1738 IScsiPadSegment (
   1739   IN OUT NET_BUF      *Pdu,
   1740   IN     UINT32       Len
   1741   )
   1742 {
   1743   UINT32  PadLen;
   1744   UINT8   *Data;
   1745 
   1746   PadLen = ISCSI_GET_PAD_LEN (Len);
   1747 
   1748   if (PadLen != 0) {
   1749     Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);
   1750     if (Data == NULL) {
   1751       return EFI_OUT_OF_RESOURCES;
   1752     }
   1753 
   1754     ZeroMem (Data, PadLen);
   1755   }
   1756 
   1757   return EFI_SUCCESS;
   1758 }
   1759 
   1760 
   1761 /**
   1762   Build a key-value list from the data segment.
   1763 
   1764   @param[in]  Data The data segment containing the key-value pairs.
   1765   @param[in]  Len  Length of the data segment.
   1766 
   1767   @return The key-value list.
   1768   @retval NULL Other errors as indicated.
   1769 
   1770 **/
   1771 LIST_ENTRY *
   1772 IScsiBuildKeyValueList (
   1773   IN CHAR8  *Data,
   1774   IN UINT32 Len
   1775   )
   1776 {
   1777   LIST_ENTRY            *ListHead;
   1778   ISCSI_KEY_VALUE_PAIR  *KeyValuePair;
   1779 
   1780   ListHead = AllocatePool (sizeof (LIST_ENTRY));
   1781   if (ListHead == NULL) {
   1782     return NULL;
   1783   }
   1784 
   1785   InitializeListHead (ListHead);
   1786 
   1787   while (Len > 0) {
   1788     KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));
   1789     if (KeyValuePair == NULL) {
   1790       goto ON_ERROR;
   1791     }
   1792 
   1793     InitializeListHead (&KeyValuePair->List);
   1794 
   1795     KeyValuePair->Key = Data;
   1796 
   1797     while ((Len > 0) && (*Data != '=')) {
   1798       Len--;
   1799       Data++;
   1800     }
   1801 
   1802     if (*Data == '=') {
   1803       *Data = '\0';
   1804 
   1805       Data++;
   1806       Len--;
   1807     } else {
   1808       FreePool (KeyValuePair);
   1809       goto ON_ERROR;
   1810     }
   1811 
   1812     KeyValuePair->Value = Data;
   1813 
   1814     InsertTailList (ListHead, &KeyValuePair->List);;
   1815 
   1816     Data += AsciiStrLen (KeyValuePair->Value) + 1;
   1817     Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;
   1818   }
   1819 
   1820   return ListHead;
   1821 
   1822 ON_ERROR:
   1823 
   1824   IScsiFreeKeyValueList (ListHead);
   1825 
   1826   return NULL;
   1827 }
   1828 
   1829 
   1830 /**
   1831   Get the value string by the key name from the key-value list. If found,
   1832   the key-value entry will be removed from the list.
   1833 
   1834   @param[in, out]  KeyValueList  The key-value list.
   1835   @param[in]       Key           The key name to find.
   1836 
   1837   @return The value string.
   1838   @retval NULL The key value pair cannot be found.
   1839 
   1840 **/
   1841 CHAR8 *
   1842 IScsiGetValueByKeyFromList (
   1843   IN OUT LIST_ENTRY     *KeyValueList,
   1844   IN     CHAR8          *Key
   1845   )
   1846 {
   1847   LIST_ENTRY            *Entry;
   1848   ISCSI_KEY_VALUE_PAIR  *KeyValuePair;
   1849   CHAR8                 *Value;
   1850 
   1851   Value = NULL;
   1852 
   1853   NET_LIST_FOR_EACH (Entry, KeyValueList) {
   1854     KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
   1855 
   1856     if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {
   1857       Value = KeyValuePair->Value;
   1858 
   1859       RemoveEntryList (&KeyValuePair->List);
   1860       FreePool (KeyValuePair);
   1861       break;
   1862     }
   1863   }
   1864 
   1865   return Value;
   1866 }
   1867 
   1868 
   1869 /**
   1870   Free the key-value list.
   1871 
   1872   @param[in]  KeyValueList The key-value list.
   1873 
   1874 **/
   1875 VOID
   1876 IScsiFreeKeyValueList (
   1877   IN LIST_ENTRY      *KeyValueList
   1878   )
   1879 {
   1880   LIST_ENTRY            *Entry;
   1881   ISCSI_KEY_VALUE_PAIR  *KeyValuePair;
   1882 
   1883   while (!IsListEmpty (KeyValueList)) {
   1884     Entry         = NetListRemoveHead (KeyValueList);
   1885     KeyValuePair  = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
   1886 
   1887     FreePool (KeyValuePair);
   1888   }
   1889 
   1890   FreePool (KeyValueList);
   1891 }
   1892 
   1893 
   1894 /**
   1895   Normalize the iSCSI name according to RFC.
   1896 
   1897   @param[in, out]  Name       The iSCSI name.
   1898   @param[in]       Len        Length of the iSCSI name.
   1899 
   1900   @retval EFI_SUCCESS        The iSCSI name is valid and normalized.
   1901   @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.
   1902 
   1903 **/
   1904 EFI_STATUS
   1905 IScsiNormalizeName (
   1906   IN OUT CHAR8      *Name,
   1907   IN     UINTN      Len
   1908   )
   1909 {
   1910   UINTN Index;
   1911 
   1912   for (Index = 0; Index < Len; Index++) {
   1913     if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {
   1914       //
   1915       // Convert the upper-case characters to lower-case ones.
   1916       //
   1917       Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');
   1918     }
   1919 
   1920     if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&
   1921         !NET_IS_DIGIT (Name[Index]) &&
   1922         (Name[Index] != '-') &&
   1923         (Name[Index] != '.') &&
   1924         (Name[Index] != ':')
   1925         ) {
   1926       //
   1927       // ASCII dash, dot, colon lower-case characters and digit characters
   1928       // are allowed.
   1929       //
   1930       return EFI_PROTOCOL_ERROR;
   1931     }
   1932   }
   1933 
   1934   if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {
   1935     //
   1936     // Only IQN format is accepted now.
   1937     //
   1938     return EFI_PROTOCOL_ERROR;
   1939   }
   1940 
   1941   return EFI_SUCCESS;
   1942 }
   1943 
   1944 
   1945 /**
   1946   Create an iSCSI task control block.
   1947 
   1948   @param[in]   Conn           The connection on which the task control block will be created.
   1949   @param[out]  Tcb            The newly created task control block.
   1950 
   1951   @retval EFI_SUCCESS          The task control block is created.
   1952   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
   1953   @retval EFI_NOT_READY        The target cannot accept new commands.
   1954 
   1955 **/
   1956 EFI_STATUS
   1957 IScsiNewTcb (
   1958   IN  ISCSI_CONNECTION  *Conn,
   1959   OUT ISCSI_TCB         **Tcb
   1960   )
   1961 {
   1962   ISCSI_SESSION *Session;
   1963   ISCSI_TCB     *NewTcb;
   1964 
   1965   ASSERT (Tcb != NULL);
   1966 
   1967   Session = Conn->Session;
   1968 
   1969   if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {
   1970     return EFI_NOT_READY;
   1971   }
   1972 
   1973   NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));
   1974   if (NewTcb == NULL) {
   1975     return EFI_OUT_OF_RESOURCES;
   1976   }
   1977 
   1978   InitializeListHead (&NewTcb->Link);
   1979 
   1980   NewTcb->SoFarInOrder      = TRUE;
   1981   NewTcb->InitiatorTaskTag  = Session->InitiatorTaskTag;
   1982   NewTcb->CmdSN             = Session->CmdSN;
   1983   NewTcb->Conn              = Conn;
   1984 
   1985   InsertTailList (&Session->TcbList, &NewTcb->Link);
   1986 
   1987   //
   1988   // Advance the initiator task tag.
   1989   //
   1990   Session->InitiatorTaskTag++;
   1991   Session->CmdSN++;
   1992 
   1993   *Tcb = NewTcb;
   1994 
   1995   return EFI_SUCCESS;
   1996 }
   1997 
   1998 
   1999 /**
   2000   Delete the tcb from the connection and destroy it.
   2001 
   2002   @param[in]  Tcb The tcb to delete.
   2003 
   2004 **/
   2005 VOID
   2006 IScsiDelTcb (
   2007   IN ISCSI_TCB  *Tcb
   2008   )
   2009 {
   2010   RemoveEntryList (&Tcb->Link);
   2011 
   2012   FreePool (Tcb);
   2013 }
   2014 
   2015 
   2016 /**
   2017   Find the task control block by the initator task tag.
   2018 
   2019   @param[in]  TcbList         The tcb list.
   2020   @param[in]  InitiatorTaskTag The initiator task tag.
   2021 
   2022   @return The task control block found.
   2023   @retval NULL The task control block cannot be found.
   2024 
   2025 **/
   2026 ISCSI_TCB *
   2027 IScsiFindTcbByITT (
   2028   IN LIST_ENTRY      *TcbList,
   2029   IN UINT32          InitiatorTaskTag
   2030   )
   2031 {
   2032   ISCSI_TCB       *Tcb;
   2033   LIST_ENTRY      *Entry;
   2034 
   2035   Tcb = NULL;
   2036 
   2037   NET_LIST_FOR_EACH (Entry, TcbList) {
   2038     Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);
   2039 
   2040     if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {
   2041       break;
   2042     }
   2043   }
   2044 
   2045   return Tcb;
   2046 }
   2047 
   2048 
   2049 /**
   2050   Create a data segment, pad it, and calculate the CRC if needed.
   2051 
   2052   @param[in]  Data       The data to fill into the data segment.
   2053   @param[in]  Len        Length of the data.
   2054   @param[in]  DataDigest Whether to calculate CRC for this data segment.
   2055 
   2056   @return The net buffer wrapping the data segment.
   2057 
   2058 **/
   2059 NET_BUF *
   2060 IScsiNewDataSegment (
   2061   IN UINT8    *Data,
   2062   IN UINT32   Len,
   2063   IN BOOLEAN  DataDigest
   2064   )
   2065 {
   2066   NET_FRAGMENT  Fragment[2];
   2067   UINT32        FragmentCount;
   2068   UINT32        PadLen;
   2069   NET_BUF       *DataSeg;
   2070 
   2071   Fragment[0].Len   = Len;
   2072   Fragment[0].Bulk  = Data;
   2073 
   2074   PadLen            = ISCSI_GET_PAD_LEN (Len);
   2075   if (PadLen != 0) {
   2076     Fragment[1].Len   = PadLen;
   2077     Fragment[1].Bulk  = (UINT8 *) &mDataSegPad;
   2078 
   2079     FragmentCount     = 2;
   2080   } else {
   2081     FragmentCount = 1;
   2082   }
   2083 
   2084   DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
   2085 
   2086   return DataSeg;
   2087 }
   2088 
   2089 
   2090 /**
   2091   Create a iSCSI SCSI command PDU to encapsulate the command issued
   2092   by SCSI through the EXT SCSI PASS THRU Protocol.
   2093 
   2094   @param[in]  Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
   2095   @param[in]  Lun    The LUN.
   2096   @param[in]  Tcb    The tcb assocated with this SCSI command.
   2097 
   2098   @return The  created iSCSI SCSI command PDU.
   2099   @retval NULL Other errors as indicated.
   2100 
   2101 **/
   2102 NET_BUF *
   2103 IScsiNewScsiCmdPdu (
   2104   IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
   2105   IN UINT64                                     Lun,
   2106   IN ISCSI_TCB                                  *Tcb
   2107   )
   2108 {
   2109   LIST_ENTRY                      *NbufList;
   2110   NET_BUF                         *Pdu;
   2111   NET_BUF                         *PduHeader;
   2112   NET_BUF                         *DataSeg;
   2113   SCSI_COMMAND                    *ScsiCmd;
   2114   UINT8                           AHSLength;
   2115   UINT32                          Length;
   2116   ISCSI_ADDITIONAL_HEADER         *Header;
   2117   ISCSI_BI_EXP_READ_DATA_LEN_AHS  *BiExpReadDataLenAHS;
   2118   ISCSI_SESSION                   *Session;
   2119   UINT32                          ImmediateDataLen;
   2120 
   2121   AHSLength = 0;
   2122 
   2123   if (Packet->DataDirection == DataBi) {
   2124     //
   2125     // Bidirectional Read/Write command, the bidirectional expected
   2126     // read data length AHS is required.
   2127     //
   2128     AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);
   2129   }
   2130 
   2131   if (Packet->CdbLength > 16) {
   2132     //
   2133     // The CDB exceeds 16 bytes. An extended CDB AHS is required.
   2134     //
   2135     AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER));
   2136   }
   2137 
   2138   Length    = sizeof (SCSI_COMMAND) + AHSLength;
   2139   PduHeader = NetbufAlloc (Length);
   2140   if (PduHeader == NULL) {
   2141     return NULL;
   2142   }
   2143 
   2144   ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);
   2145   if (ScsiCmd == NULL) {
   2146     NetbufFree (PduHeader);
   2147     return NULL;
   2148   }
   2149   Header  = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);
   2150 
   2151   ZeroMem (ScsiCmd, Length);
   2152 
   2153   ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);
   2154   ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);
   2155 
   2156   //
   2157   // Set the READ/WRITE flags according to the IO type of this request.
   2158   //
   2159   switch (Packet->DataDirection) {
   2160   case DataIn:
   2161     ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);
   2162     ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);
   2163     break;
   2164 
   2165   case DataOut:
   2166     ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);
   2167     ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
   2168     break;
   2169 
   2170   case DataBi:
   2171     ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);
   2172     ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
   2173 
   2174     //
   2175     // Fill the bidirectional expected read data length AHS.
   2176     //
   2177     BiExpReadDataLenAHS                     = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;
   2178     Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);
   2179 
   2180     BiExpReadDataLenAHS->Length = NTOHS (5);
   2181     BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;
   2182     BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);
   2183 
   2184     break;
   2185   }
   2186 
   2187   ScsiCmd->TotalAHSLength = AHSLength;
   2188   CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));
   2189   ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);
   2190   ScsiCmd->CmdSN            = NTOHL (Tcb->CmdSN);
   2191   ScsiCmd->ExpStatSN        = NTOHL (Tcb->Conn->ExpStatSN);
   2192 
   2193   CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));
   2194 
   2195   if (Packet->CdbLength > 16) {
   2196     Header->Length  = NTOHS ((UINT16) (Packet->CdbLength - 15));
   2197     Header->Type    = ISCSI_AHS_TYPE_EXT_CDB;
   2198 
   2199     CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);
   2200   }
   2201 
   2202   Pdu               = PduHeader;
   2203   Session           = Tcb->Conn->Session;
   2204   ImmediateDataLen  = 0;
   2205 
   2206   if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {
   2207     //
   2208     // Send immediate data in this SCSI Command PDU. The length of the immeidate
   2209     // data is the minimum of FirstBurstLength, the data length to be xfered, and
   2210     // the MaxRecvdataSegmentLength on this connection.
   2211     //
   2212     ImmediateDataLen  = MIN (Session->FirstBurstLength, Packet->OutTransferLength);
   2213     ImmediateDataLen  = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);
   2214 
   2215     //
   2216     // Update the data segment length in the PDU header.
   2217     //
   2218     ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);
   2219 
   2220     //
   2221     // Create the data segment.
   2222     //
   2223     DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);
   2224     if (DataSeg == NULL) {
   2225       NetbufFree (PduHeader);
   2226       Pdu = NULL;
   2227       goto ON_EXIT;
   2228     }
   2229 
   2230     NbufList = AllocatePool (sizeof (LIST_ENTRY));
   2231     if (NbufList == NULL) {
   2232       NetbufFree (PduHeader);
   2233       NetbufFree (DataSeg);
   2234 
   2235       Pdu = NULL;
   2236       goto ON_EXIT;
   2237     }
   2238 
   2239     InitializeListHead (NbufList);
   2240     InsertTailList (NbufList, &PduHeader->List);
   2241     InsertTailList (NbufList, &DataSeg->List);
   2242 
   2243     Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
   2244     if (Pdu == NULL) {
   2245       IScsiFreeNbufList (NbufList);
   2246     }
   2247   }
   2248 
   2249   if (Session->InitialR2T ||
   2250       (ImmediateDataLen == Session->FirstBurstLength) ||
   2251       (ImmediateDataLen == Packet->OutTransferLength)
   2252       ) {
   2253     //
   2254     // Unsolicited data out sequence is not allowed,
   2255     // or FirstBustLength data is already sent out by immediate data,
   2256     // or all the OUT data accompany this SCSI packet are sent as
   2257     // immediate data. The final flag should be set on this SCSI Command
   2258     // PDU.
   2259     //
   2260     ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);
   2261   }
   2262 
   2263 ON_EXIT:
   2264 
   2265   return Pdu;
   2266 }
   2267 
   2268 
   2269 /**
   2270   Create a new iSCSI SCSI Data Out PDU.
   2271 
   2272   @param[in]  Data   The data to put into the Data Out PDU.
   2273   @param[in]  Len    Length of the data.
   2274   @param[in]  DataSN The DataSN of the Data Out PDU.
   2275   @param[in]  Tcb    The task control block of this Data Out PDU.
   2276   @param[in]  Lun    The LUN.
   2277 
   2278   @return The net buffer wrapping the Data Out PDU.
   2279   @retval NULL Other errors as indicated.
   2280 
   2281 **/
   2282 NET_BUF *
   2283 IScsiNewDataOutPdu (
   2284   IN UINT8      *Data,
   2285   IN UINT32     Len,
   2286   IN UINT32     DataSN,
   2287   IN ISCSI_TCB  *Tcb,
   2288   IN UINT64     Lun
   2289   )
   2290 {
   2291   LIST_ENTRY          *NbufList;
   2292   NET_BUF             *PduHdr;
   2293   NET_BUF             *DataSeg;
   2294   NET_BUF             *Pdu;
   2295   ISCSI_SCSI_DATA_OUT *DataOutHdr;
   2296   ISCSI_XFER_CONTEXT  *XferContext;
   2297 
   2298   NbufList = AllocatePool (sizeof (LIST_ENTRY));
   2299   if (NbufList == NULL) {
   2300     return NULL;
   2301   }
   2302 
   2303   InitializeListHead (NbufList);
   2304 
   2305   //
   2306   // Allocate memory for the BHS.
   2307   //
   2308   PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));
   2309   if (PduHdr == NULL) {
   2310     FreePool (NbufList);
   2311     return NULL;
   2312   }
   2313   //
   2314   // Insert the BHS into the buffer list.
   2315   //
   2316   InsertTailList (NbufList, &PduHdr->List);
   2317 
   2318   DataOutHdr  = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);
   2319   ASSERT (DataOutHdr != NULL);
   2320   XferContext = &Tcb->XferContext;
   2321 
   2322   ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));
   2323 
   2324   //
   2325   // Set the flags and fields of the Data Out PDU BHS.
   2326   //
   2327   ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);
   2328   ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);
   2329 
   2330   DataOutHdr->InitiatorTaskTag  = HTONL (Tcb->InitiatorTaskTag);
   2331   DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);
   2332   DataOutHdr->ExpStatSN         = HTONL (Tcb->Conn->ExpStatSN);
   2333   DataOutHdr->DataSN            = HTONL (DataSN);
   2334   DataOutHdr->BufferOffset      = HTONL (XferContext->Offset);
   2335 
   2336   if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {
   2337     CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));
   2338   }
   2339   //
   2340   // Build the data segment for this Data Out PDU.
   2341   //
   2342   DataSeg = IScsiNewDataSegment (Data, Len, FALSE);
   2343   if (DataSeg == NULL) {
   2344     IScsiFreeNbufList (NbufList);
   2345     return NULL;
   2346   }
   2347   //
   2348   // Put the data segment into the buffer list and combine it with the BHS
   2349   // into a full Data Out PDU.
   2350   //
   2351   InsertTailList (NbufList, &DataSeg->List);
   2352   Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
   2353   if (Pdu == NULL) {
   2354     IScsiFreeNbufList (NbufList);
   2355   }
   2356 
   2357   return Pdu;
   2358 }
   2359 
   2360 
   2361 /**
   2362   Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
   2363 
   2364   @param[in]  Data The data  which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
   2365   @param[in]  Tcb  The task control block of the data to send out.
   2366   @param[in]  Lun  The LUN the data will be sent to.
   2367 
   2368   @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.
   2369   @retval NULL Other errors as indicated.
   2370 
   2371 **/
   2372 LIST_ENTRY *
   2373 IScsiGenerateDataOutPduSequence (
   2374   IN UINT8      *Data,
   2375   IN ISCSI_TCB  *Tcb,
   2376   IN UINT64     Lun
   2377   )
   2378 {
   2379   LIST_ENTRY          *PduList;
   2380   UINT32              DataSN;
   2381   UINT32              DataLen;
   2382   NET_BUF             *DataOutPdu;
   2383   ISCSI_CONNECTION    *Conn;
   2384   ISCSI_XFER_CONTEXT  *XferContext;
   2385   UINT8               *DataOutPacket;
   2386 
   2387   PduList = AllocatePool (sizeof (LIST_ENTRY));
   2388   if (PduList == NULL) {
   2389     return NULL;
   2390   }
   2391 
   2392   InitializeListHead (PduList);
   2393 
   2394   DataSN      = 0;
   2395   Conn        = Tcb->Conn;
   2396   DataOutPdu  = NULL;
   2397   XferContext = &Tcb->XferContext;
   2398 
   2399   while (XferContext->DesiredLength > 0) {
   2400     //
   2401     // Determine the length of data this Data Out PDU can carry.
   2402     //
   2403     DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);
   2404 
   2405     //
   2406     // Create a Data Out PDU.
   2407     //
   2408     DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);
   2409     if (DataOutPdu == NULL) {
   2410       IScsiFreeNbufList (PduList);
   2411       PduList = NULL;
   2412 
   2413       goto ON_EXIT;
   2414     }
   2415 
   2416     InsertTailList (PduList, &DataOutPdu->List);
   2417 
   2418     //
   2419     // Update the context and DataSN.
   2420     //
   2421     Data += DataLen;
   2422     XferContext->Offset += DataLen;
   2423     XferContext->DesiredLength -= DataLen;
   2424     DataSN++;
   2425   }
   2426   //
   2427   // Set the F bit for the last data out PDU in this sequence.
   2428   //
   2429   DataOutPacket = NetbufGetByte (DataOutPdu, 0, NULL);
   2430   if (DataOutPacket == NULL) {
   2431     IScsiFreeNbufList (PduList);
   2432     PduList = NULL;
   2433     goto ON_EXIT;
   2434   }
   2435 
   2436   ISCSI_SET_FLAG (DataOutPacket, ISCSI_BHS_FLAG_FINAL);
   2437 
   2438 ON_EXIT:
   2439 
   2440   return PduList;
   2441 }
   2442 
   2443 /**
   2444   Send the Data in a sequence of Data Out PDUs one by one.
   2445 
   2446   @param[in]  Data            The data to carry by Data Out PDUs.
   2447   @param[in]  Lun             The LUN the data will be sent to.
   2448   @param[in]  Tcb             The task control block.
   2449 
   2450   @retval EFI_SUCCES           The data is sent out to the LUN.
   2451   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
   2452   @retval Others               Other errors as indicated.
   2453 
   2454 **/
   2455 EFI_STATUS
   2456 IScsiSendDataOutPduSequence (
   2457   IN UINT8      *Data,
   2458   IN UINT64     Lun,
   2459   IN ISCSI_TCB  *Tcb
   2460   )
   2461 {
   2462   LIST_ENTRY      *DataOutPduList;
   2463   LIST_ENTRY      *Entry;
   2464   NET_BUF         *Pdu;
   2465   EFI_STATUS      Status;
   2466 
   2467   //
   2468   // Generate the Data Out PDU sequence.
   2469   //
   2470   DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);
   2471   if (DataOutPduList == NULL) {
   2472     return EFI_OUT_OF_RESOURCES;
   2473   }
   2474 
   2475   Status = EFI_SUCCESS;
   2476 
   2477   //
   2478   // Send the Data Out PDU's one by one.
   2479   //
   2480   NET_LIST_FOR_EACH (Entry, DataOutPduList) {
   2481     Pdu     = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
   2482 
   2483     Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu);
   2484 
   2485     if (EFI_ERROR (Status)) {
   2486       break;
   2487     }
   2488   }
   2489 
   2490   IScsiFreeNbufList (DataOutPduList);
   2491 
   2492   return Status;
   2493 }
   2494 
   2495 
   2496 /**
   2497   Process the received iSCSI SCSI Data In PDU.
   2498 
   2499   @param[in]        Pdu      The Data In PDU received.
   2500   @param[in]        Tcb      The task control block.
   2501   @param[in, out]   Packet   The EXT SCSI PASS THRU request packet.
   2502 
   2503   @retval EFI_SUCCES           The check on the Data IN PDU is passed and some update
   2504                                actions are taken.
   2505   @retval EFI_PROTOCOL_ERROR   Some kind of iSCSI protocol errror occurred.
   2506   @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
   2507   @retval Others               Other errors as indicated.
   2508 
   2509 **/
   2510 EFI_STATUS
   2511 IScsiOnDataInRcvd (
   2512   IN NET_BUF                                         *Pdu,
   2513   IN ISCSI_TCB                                       *Tcb,
   2514   IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet
   2515   )
   2516 {
   2517   ISCSI_SCSI_DATA_IN  *DataInHdr;
   2518   EFI_STATUS          Status;
   2519 
   2520   DataInHdr                   = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);
   2521   if (DataInHdr == NULL) {
   2522     return EFI_PROTOCOL_ERROR;
   2523   }
   2524 
   2525   DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);
   2526   DataInHdr->ExpCmdSN         = NTOHL (DataInHdr->ExpCmdSN);
   2527   DataInHdr->MaxCmdSN         = NTOHL (DataInHdr->MaxCmdSN);
   2528   DataInHdr->DataSN           = NTOHL (DataInHdr->DataSN);
   2529 
   2530   //
   2531   // Check the DataSN.
   2532   //
   2533   Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);
   2534   if (EFI_ERROR (Status)) {
   2535     return Status;
   2536   }
   2537 
   2538   if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
   2539     return EFI_PROTOCOL_ERROR;
   2540   }
   2541   //
   2542   // Update the command related sequence numbers.
   2543   //
   2544   IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);
   2545 
   2546   if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {
   2547     if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {
   2548       //
   2549       // The S bit is on but the F bit is off.
   2550       //
   2551       return EFI_PROTOCOL_ERROR;
   2552     }
   2553 
   2554     Tcb->StatusXferd = TRUE;
   2555 
   2556     if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {
   2557       //
   2558       // Underflow and Overflow are mutual flags.
   2559       //
   2560       return EFI_PROTOCOL_ERROR;
   2561     }
   2562     //
   2563     // S bit is on, the StatSN is valid.
   2564     //
   2565     Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));
   2566     if (EFI_ERROR (Status)) {
   2567       return Status;
   2568     }
   2569 
   2570     Packet->HostAdapterStatus = 0;
   2571     Packet->TargetStatus      = DataInHdr->Status;
   2572 
   2573     if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
   2574       Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);
   2575       Status = EFI_BAD_BUFFER_SIZE;
   2576     }
   2577 
   2578     if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
   2579       Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);
   2580     }
   2581   }
   2582 
   2583   return Status;
   2584 }
   2585 
   2586 
   2587 /**
   2588   Process the received iSCSI R2T PDU.
   2589 
   2590   @param[in]       Pdu       The R2T PDU received.
   2591   @param[in]       Tcb       The task control block.
   2592   @param[in]       Lun       The Lun.
   2593   @param[in, out]  Packet    The EXT SCSI PASS THRU request packet.
   2594 
   2595   @retval EFI_SUCCES         The R2T PDU is valid and the solicited data is sent out.
   2596   @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
   2597   @retval Others             Other errors as indicated.
   2598 
   2599 **/
   2600 EFI_STATUS
   2601 IScsiOnR2TRcvd (
   2602   IN NET_BUF                                         *Pdu,
   2603   IN ISCSI_TCB                                       *Tcb,
   2604   IN UINT64                                          Lun,
   2605   IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet
   2606   )
   2607 {
   2608   ISCSI_READY_TO_TRANSFER *R2THdr;
   2609   EFI_STATUS              Status;
   2610   ISCSI_XFER_CONTEXT      *XferContext;
   2611   UINT8                   *Data;
   2612 
   2613   R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);
   2614   if (R2THdr == NULL) {
   2615     return EFI_PROTOCOL_ERROR;
   2616   }
   2617 
   2618   R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);
   2619   R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);
   2620   R2THdr->StatSN = NTOHL (R2THdr->StatSN);
   2621   R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum);
   2622   R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);
   2623   R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);
   2624 
   2625   if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {
   2626     return EFI_PROTOCOL_ERROR;;
   2627   }
   2628   //
   2629   // Check the sequence number.
   2630   //
   2631   Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);
   2632   if (EFI_ERROR (Status)) {
   2633     return Status;
   2634   }
   2635 
   2636   XferContext                     = &Tcb->XferContext;
   2637   XferContext->TargetTransferTag  = R2THdr->TargetTransferTag;
   2638   XferContext->Offset             = R2THdr->BufferOffset;
   2639   XferContext->DesiredLength      = R2THdr->DesiredDataTransferLength;
   2640 
   2641   if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||
   2642       (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)
   2643       ) {
   2644     return EFI_PROTOCOL_ERROR;
   2645   }
   2646   //
   2647   // Send the data solicited by this R2T.
   2648   //
   2649   Data    = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
   2650   Status  = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
   2651 
   2652   return Status;
   2653 }
   2654 
   2655 
   2656 /**
   2657   Process the received iSCSI SCSI Response PDU.
   2658 
   2659   @param[in]       Pdu      The Response PDU received.
   2660   @param[in]       Tcb      The task control block.
   2661   @param[in, out]  Packet   The EXT SCSI PASS THRU request packet.
   2662 
   2663   @retval EFI_SUCCES         The Response PDU is processed.
   2664   @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
   2665   @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
   2666   @retval Others             Other errors as indicated.
   2667 
   2668 **/
   2669 EFI_STATUS
   2670 IScsiOnScsiRspRcvd (
   2671   IN NET_BUF                                         *Pdu,
   2672   IN ISCSI_TCB                                       *Tcb,
   2673   IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet
   2674   )
   2675 {
   2676   SCSI_RESPONSE     *ScsiRspHdr;
   2677   ISCSI_SENSE_DATA  *SenseData;
   2678   EFI_STATUS        Status;
   2679   UINT32            DataSegLen;
   2680 
   2681   ScsiRspHdr                    = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
   2682   if (ScsiRspHdr == NULL) {
   2683     return EFI_PROTOCOL_ERROR;
   2684   }
   2685 
   2686   ScsiRspHdr->InitiatorTaskTag  = NTOHL (ScsiRspHdr->InitiatorTaskTag);
   2687   if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
   2688     return EFI_PROTOCOL_ERROR;
   2689   }
   2690 
   2691   ScsiRspHdr->StatSN  = NTOHL (ScsiRspHdr->StatSN);
   2692 
   2693   Status              = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);
   2694   if (EFI_ERROR (Status)) {
   2695     return Status;
   2696   }
   2697 
   2698   ScsiRspHdr->MaxCmdSN  = NTOHL (ScsiRspHdr->MaxCmdSN);
   2699   ScsiRspHdr->ExpCmdSN  = NTOHL (ScsiRspHdr->ExpCmdSN);
   2700   IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);
   2701 
   2702   Tcb->StatusXferd          = TRUE;
   2703 
   2704   Packet->HostAdapterStatus = ScsiRspHdr->Response;
   2705   if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {
   2706     return EFI_SUCCESS;
   2707   }
   2708 
   2709   Packet->TargetStatus = ScsiRspHdr->Status;
   2710 
   2711   if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||
   2712       ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)
   2713         ) {
   2714     return EFI_PROTOCOL_ERROR;
   2715   }
   2716 
   2717   if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {
   2718     Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);
   2719     Status = EFI_BAD_BUFFER_SIZE;
   2720   }
   2721 
   2722   if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {
   2723     Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);
   2724   }
   2725 
   2726   if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
   2727     if (Packet->DataDirection == DataIn) {
   2728       Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
   2729     } else {
   2730       Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
   2731     }
   2732 
   2733     Status = EFI_BAD_BUFFER_SIZE;
   2734   }
   2735 
   2736   if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
   2737     if (Packet->DataDirection == DataIn) {
   2738       Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
   2739     } else {
   2740       Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
   2741     }
   2742   }
   2743 
   2744   DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);
   2745   if (DataSegLen != 0) {
   2746     SenseData               = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);
   2747     if (SenseData == NULL) {
   2748       return EFI_PROTOCOL_ERROR;
   2749     }
   2750 
   2751     SenseData->Length       = NTOHS (SenseData->Length);
   2752 
   2753     Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);
   2754     if (Packet->SenseDataLength != 0) {
   2755       CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);
   2756     }
   2757   } else {
   2758     Packet->SenseDataLength = 0;
   2759   }
   2760 
   2761   return Status;
   2762 }
   2763 
   2764 
   2765 /**
   2766   Process the received NOP In PDU.
   2767 
   2768   @param[in]  Pdu            The NOP In PDU received.
   2769   @param[in]  Tcb            The task control block.
   2770 
   2771   @retval EFI_SUCCES         The NOP In PDU is processed and the related sequence
   2772                              numbers are updated.
   2773   @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
   2774 
   2775 **/
   2776 EFI_STATUS
   2777 IScsiOnNopInRcvd (
   2778   IN NET_BUF    *Pdu,
   2779   IN ISCSI_TCB  *Tcb
   2780   )
   2781 {
   2782   ISCSI_NOP_IN  *NopInHdr;
   2783   EFI_STATUS    Status;
   2784 
   2785   NopInHdr            = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);
   2786   if (NopInHdr == NULL) {
   2787     return EFI_PROTOCOL_ERROR;
   2788   }
   2789 
   2790   NopInHdr->StatSN    = NTOHL (NopInHdr->StatSN);
   2791   NopInHdr->ExpCmdSN  = NTOHL (NopInHdr->ExpCmdSN);
   2792   NopInHdr->MaxCmdSN  = NTOHL (NopInHdr->MaxCmdSN);
   2793 
   2794   if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {
   2795     if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {
   2796       return EFI_PROTOCOL_ERROR;
   2797     }
   2798   } else {
   2799     Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);
   2800     if (EFI_ERROR (Status)) {
   2801       return Status;
   2802     }
   2803   }
   2804 
   2805   IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);
   2806 
   2807   return EFI_SUCCESS;
   2808 }
   2809 
   2810 
   2811 /**
   2812   Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
   2813 
   2814   @param[in]       PassThru  The EXT SCSI PASS THRU protocol.
   2815   @param[in]       Target    The target ID.
   2816   @param[in]       Lun       The LUN.
   2817   @param[in, out]  Packet    The request packet containing IO request, SCSI command
   2818                              buffer and buffers to read/write.
   2819 
   2820   @retval EFI_SUCCES           The SCSI command is executed and the result is updated to
   2821                                the Packet.
   2822   @retval EFI_DEVICE_ERROR     Session state was not as required.
   2823   @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
   2824   @retval EFI_PROTOCOL_ERROR   There is no such data in the net buffer.
   2825   @retval EFI_NOT_READY        The target can not accept new commands.
   2826   @retval Others               Other errors as indicated.
   2827 
   2828 **/
   2829 EFI_STATUS
   2830 IScsiExecuteScsiCommand (
   2831   IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL                 *PassThru,
   2832   IN UINT8                                           *Target,
   2833   IN UINT64                                          Lun,
   2834   IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET  *Packet
   2835   )
   2836 {
   2837   EFI_STATUS              Status;
   2838   ISCSI_DRIVER_DATA       *Private;
   2839   ISCSI_SESSION           *Session;
   2840   EFI_EVENT               TimeoutEvent;
   2841   ISCSI_CONNECTION        *Conn;
   2842   ISCSI_TCB               *Tcb;
   2843   NET_BUF                 *Pdu;
   2844   ISCSI_XFER_CONTEXT      *XferContext;
   2845   UINT8                   *Data;
   2846   ISCSI_IN_BUFFER_CONTEXT InBufferContext;
   2847   UINT64                  Timeout;
   2848   UINT8                   *PduHdr;
   2849 
   2850   Private       = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
   2851   Session       = Private->Session;
   2852   Status        = EFI_SUCCESS;
   2853   Tcb           = NULL;
   2854   TimeoutEvent  = NULL;
   2855   Timeout       = 0;
   2856 
   2857   if (Session->State != SESSION_STATE_LOGGED_IN) {
   2858     Status = EFI_DEVICE_ERROR;
   2859     goto ON_EXIT;
   2860   }
   2861 
   2862   Conn = NET_LIST_USER_STRUCT_S (
   2863            Session->Conns.ForwardLink,
   2864            ISCSI_CONNECTION,
   2865            Link,
   2866            ISCSI_CONNECTION_SIGNATURE
   2867            );
   2868 
   2869   if (Packet->Timeout != 0) {
   2870     Timeout = MultU64x32 (Packet->Timeout, 4);
   2871   }
   2872 
   2873   Status = IScsiNewTcb (Conn, &Tcb);
   2874   if (EFI_ERROR (Status)) {
   2875     goto ON_EXIT;
   2876   }
   2877   //
   2878   // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
   2879   //
   2880   Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);
   2881   if (Pdu == NULL) {
   2882     Status = EFI_OUT_OF_RESOURCES;
   2883     goto ON_EXIT;
   2884   }
   2885 
   2886   XferContext         = &Tcb->XferContext;
   2887   PduHdr              = NetbufGetByte (Pdu, 0, NULL);
   2888   if (PduHdr == NULL) {
   2889     Status = EFI_PROTOCOL_ERROR;
   2890     NetbufFree (Pdu);
   2891     goto ON_EXIT;
   2892   }
   2893   XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);
   2894 
   2895   //
   2896   // Transmit the SCSI Command PDU.
   2897   //
   2898   Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
   2899 
   2900   NetbufFree (Pdu);
   2901 
   2902   if (EFI_ERROR (Status)) {
   2903     goto ON_EXIT;
   2904   }
   2905 
   2906   if (!Session->InitialR2T &&
   2907       (XferContext->Offset < Session->FirstBurstLength) &&
   2908       (XferContext->Offset < Packet->OutTransferLength)
   2909       ) {
   2910     //
   2911     // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
   2912     // OUT data, and the limit of FirstBurstLength is not reached.
   2913     //
   2914     XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;
   2915     XferContext->DesiredLength = MIN (
   2916                                    Session->FirstBurstLength,
   2917                                    Packet->OutTransferLength - XferContext->Offset
   2918                                    );
   2919 
   2920     Data    = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
   2921     Status  = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
   2922     if (EFI_ERROR (Status)) {
   2923       goto ON_EXIT;
   2924     }
   2925   }
   2926 
   2927   InBufferContext.InData    = (UINT8 *) Packet->InDataBuffer;
   2928   InBufferContext.InDataLen = Packet->InTransferLength;
   2929 
   2930   while (!Tcb->StatusXferd) {
   2931     //
   2932     // Start the timeout timer.
   2933     //
   2934     if (Timeout != 0) {
   2935       Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);
   2936       if (EFI_ERROR (Status)) {
   2937         goto ON_EXIT;
   2938       }
   2939 
   2940       TimeoutEvent = Conn->TimeoutEvent;
   2941     }
   2942 
   2943     //
   2944     // Try to receive PDU from target.
   2945     //
   2946     Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);
   2947     if (EFI_ERROR (Status)) {
   2948       goto ON_EXIT;
   2949     }
   2950 
   2951     PduHdr = NetbufGetByte (Pdu, 0, NULL);
   2952     if (PduHdr == NULL) {
   2953       Status = EFI_PROTOCOL_ERROR;
   2954       NetbufFree (Pdu);
   2955       goto ON_EXIT;
   2956     }
   2957     switch (ISCSI_GET_OPCODE (PduHdr)) {
   2958     case ISCSI_OPCODE_SCSI_DATA_IN:
   2959       Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);
   2960       break;
   2961 
   2962     case ISCSI_OPCODE_R2T:
   2963       Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);
   2964       break;
   2965 
   2966     case ISCSI_OPCODE_SCSI_RSP:
   2967       Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);
   2968       break;
   2969 
   2970     case ISCSI_OPCODE_NOP_IN:
   2971       Status = IScsiOnNopInRcvd (Pdu, Tcb);
   2972       break;
   2973 
   2974     case ISCSI_OPCODE_VENDOR_T0:
   2975     case ISCSI_OPCODE_VENDOR_T1:
   2976     case ISCSI_OPCODE_VENDOR_T2:
   2977       //
   2978       // These messages are vendor specific. Skip them.
   2979       //
   2980       break;
   2981 
   2982     default:
   2983       Status = EFI_PROTOCOL_ERROR;
   2984       break;
   2985     }
   2986 
   2987     NetbufFree (Pdu);
   2988 
   2989     if (EFI_ERROR (Status)) {
   2990       break;
   2991     }
   2992   }
   2993 
   2994 ON_EXIT:
   2995 
   2996   if (TimeoutEvent != NULL) {
   2997     gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
   2998   }
   2999 
   3000   if (Tcb != NULL) {
   3001     IScsiDelTcb (Tcb);
   3002   }
   3003 
   3004   return Status;
   3005 }
   3006 
   3007 
   3008 /**
   3009   Reinstate the session on some error.
   3010 
   3011   @param[in]  Session           The iSCSI session
   3012 
   3013   @retval EFI_SUCCESS           The session is reinstated from some error.
   3014   @retval Other                 Reinstatement failed.
   3015 
   3016 **/
   3017 EFI_STATUS
   3018 IScsiSessionReinstatement (
   3019   IN ISCSI_SESSION  *Session
   3020   )
   3021 {
   3022   EFI_STATUS    Status;
   3023 
   3024   ASSERT (Session->State != SESSION_STATE_FREE);
   3025 
   3026   //
   3027   // Abort the session and re-init it.
   3028   //
   3029   IScsiSessionAbort (Session);
   3030   IScsiSessionInit (Session, TRUE);
   3031 
   3032   //
   3033   // Login again.
   3034   //
   3035   Status = IScsiSessionLogin (Session);
   3036 
   3037   return Status;
   3038 }
   3039 
   3040 
   3041 /**
   3042   Initialize some session parameters before login.
   3043 
   3044   @param[in, out]  Session  The iSCSI session.
   3045   @param[in]       Recovery Whether the request is from a fresh new start or recovery.
   3046 
   3047 **/
   3048 VOID
   3049 IScsiSessionInit (
   3050   IN OUT ISCSI_SESSION  *Session,
   3051   IN BOOLEAN            Recovery
   3052   )
   3053 {
   3054   if (!Recovery) {
   3055     Session->Signature  = ISCSI_SESSION_SIGNATURE;
   3056     Session->State      = SESSION_STATE_FREE;
   3057 
   3058     InitializeListHead (&Session->Conns);
   3059     InitializeListHead (&Session->TcbList);
   3060   }
   3061 
   3062   Session->Tsih                 = 0;
   3063 
   3064   Session->CmdSN                = 1;
   3065   Session->InitiatorTaskTag     = 1;
   3066   Session->NextCid              = 1;
   3067 
   3068   Session->TargetPortalGroupTag = 0;
   3069   Session->MaxConnections       = ISCSI_MAX_CONNS_PER_SESSION;
   3070   Session->InitialR2T           = FALSE;
   3071   Session->ImmediateData        = TRUE;
   3072   Session->MaxBurstLength       = 262144;
   3073   Session->FirstBurstLength     = MAX_RECV_DATA_SEG_LEN_IN_FFP;
   3074   Session->DefaultTime2Wait     = 2;
   3075   Session->DefaultTime2Retain   = 20;
   3076   Session->MaxOutstandingR2T    = DEFAULT_MAX_OUTSTANDING_R2T;
   3077   Session->DataPDUInOrder       = TRUE;
   3078   Session->DataSequenceInOrder  = TRUE;
   3079   Session->ErrorRecoveryLevel   = 0;
   3080 }
   3081 
   3082 
   3083 /**
   3084   Abort the iSCSI session. That is, reset all the connection(s), and free the
   3085   resources.
   3086 
   3087   @param[in, out]  Session The iSCSI session.
   3088 
   3089 **/
   3090 VOID
   3091 IScsiSessionAbort (
   3092   IN OUT ISCSI_SESSION  *Session
   3093   )
   3094 {
   3095   ISCSI_CONNECTION  *Conn;
   3096   EFI_GUID          *ProtocolGuid;
   3097 
   3098   if (Session->State != SESSION_STATE_LOGGED_IN) {
   3099     return ;
   3100   }
   3101 
   3102   ASSERT (!IsListEmpty (&Session->Conns));
   3103 
   3104   while (!IsListEmpty (&Session->Conns)) {
   3105     Conn = NET_LIST_USER_STRUCT_S (
   3106              Session->Conns.ForwardLink,
   3107              ISCSI_CONNECTION,
   3108              Link,
   3109              ISCSI_CONNECTION_SIGNATURE
   3110              );
   3111     if (!Conn->Ipv6Flag) {
   3112       ProtocolGuid = &gEfiTcp4ProtocolGuid;
   3113     } else {
   3114       ProtocolGuid = &gEfiTcp6ProtocolGuid;
   3115     }
   3116 
   3117     gBS->CloseProtocol (
   3118            Conn->TcpIo.Handle,
   3119            ProtocolGuid,
   3120            Session->Private->Image,
   3121            Session->Private->ExtScsiPassThruHandle
   3122            );
   3123 
   3124     IScsiConnReset (Conn);
   3125 
   3126     IScsiDetatchConnection (Conn);
   3127     IScsiDestroyConnection (Conn);
   3128   }
   3129 
   3130   Session->State = SESSION_STATE_FAILED;
   3131 
   3132   return ;
   3133 }
   3134