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