1/** @file
2  The implementation of iSCSI protocol based on RFC3720.
3
4Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "IScsiImpl.h"
16
17UINT32 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**/
25VOID
26IScsiAttatchConnection (
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**/
41VOID
42IScsiDetatchConnection (
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**/
61EFI_STATUS
62IScsiCheckSN (
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**/
92VOID
93IScsiUpdateCmdSN (
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**/
121EFI_STATUS
122IScsiConnLogin (
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**/
169VOID
170IScsiConnReset (
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**/
185ISCSI_CONNECTION *
186IScsiCreateConnection (
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**/
261VOID
262IScsiDestroyConnection (
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**/
283EFI_STATUS
284IScsiSessionLogin (
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**/
355EFI_STATUS
356IScsiSendLoginReq (
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**/
388EFI_STATUS
389IScsiReceiveLoginRsp (
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**/
431EFI_STATUS
432IScsiAddKeyValuePair (
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**/
500NET_BUF *
501IScsiPrepareLoginReq (
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**/
596EFI_STATUS
597IScsiProcessLoginRsp (
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**/
816EFI_STATUS
817IScsiUpdateTargetAddress (
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**/
904VOID
905EFIAPI
906IScsiFreeNbufList (
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**/
921VOID
922EFIAPI
923IScsiNbufExtFree (
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**/
948EFI_STATUS
949IScsiReceivePdu (
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
1120ON_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**/
1141EFI_STATUS
1142IScsiCheckOpParams (
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
1393ON_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**/
1410EFI_STATUS
1411IScsiFillOpParams (
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**/
1476EFI_STATUS
1477IScsiPadSegment (
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**/
1508LIST_ENTRY *
1509IScsiBuildKeyValueList (
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
1559ON_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**/
1575CHAR8 *
1576IScsiGetValueByKeyFromList (
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**/
1607VOID
1608IScsiFreeKeyValueList (
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**/
1634EFI_STATUS
1635IScsiNormalizeName (
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**/
1684EFI_STATUS
1685IScsiNewTcb (
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**/
1731VOID
1732IScsiDelTcb (
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**/
1749ISCSI_TCB *
1750IScsiFindTcbByITT (
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**/
1782NET_BUF *
1783IScsiNewDataSegment (
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**/
1823NET_BUF *
1824IScsiNewScsiCmdPdu (
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
1984ON_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**/
2001NET_BUF *
2002IScsiNewDataOutPdu (
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**/
2089LIST_ENTRY *
2090IScsiGenerateDataOutPduSequence (
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
2155ON_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**/
2171EFI_STATUS
2172IScsiSendDataOutPduSequence (
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**/
2223EFI_STATUS
2224IScsiOnDataInRcvd (
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**/
2311EFI_STATUS
2312IScsiOnR2TRcvd (
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**/
2378EFI_STATUS
2379IScsiOnScsiRspRcvd (
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**/
2483EFI_STATUS
2484IScsiOnNopInRcvd (
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**/
2533EFI_STATUS
2534IScsiExecuteScsiCommand (
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
2696ON_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**/
2717EFI_STATUS
2718IScsiSessionReinstatement (
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**/
2748VOID
2749IScsiSessionInit (
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**/
2790EFI_STATUS
2791IScsiSessionAbort (
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