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