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