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