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