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