]> git.proxmox.com Git - mirror_edk2.git/blame - NetworkPkg/IScsiDxe/IScsiProto.c
Update code to avoid potential access violation.
[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
1294 Check and get the result of the prameter negotiation.\r
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
1542\r
1543 if (IsListEmpty (KeyValueList)) {\r
1544 //\r
1545 // Succeed if no more keys in the list.\r
1546 //\r
1547 Status = EFI_SUCCESS;\r
1548 }\r
1549\r
1550ON_ERROR:\r
1551\r
1552 IScsiFreeKeyValueList (KeyValueList);\r
1553\r
1554 FreePool (Data);\r
1555\r
1556 return Status;\r
1557}\r
1558\r
1559\r
1560/**\r
1561 Fill the oprational parameters.\r
1562\r
1563 @param[in] Conn The connection in iSCSI login.\r
1564 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.\r
1565\r
1566**/\r
1567VOID\r
1568IScsiFillOpParams (\r
1569 IN ISCSI_CONNECTION *Conn,\r
1570 IN OUT NET_BUF *Pdu\r
1571 )\r
1572{\r
1573 ISCSI_SESSION *Session;\r
1574 CHAR8 Value[256];\r
1575\r
1576 Session = Conn->Session;\r
1577\r
1578 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");\r
1579 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);\r
1580\r
1581 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");\r
1582 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);\r
1583\r
1584 AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);\r
1585 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);\r
1586\r
1587 AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");\r
1588 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);\r
1589\r
1590 AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");\r
1591 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);\r
1592\r
1593 AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);\r
1594 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);\r
1595\r
1596 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);\r
1597 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);\r
1598\r
1599 AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);\r
1600 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);\r
1601\r
1602 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);\r
1603 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);\r
1604\r
1605 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");\r
1606 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);\r
1607\r
1608 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");\r
1609 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);\r
1610\r
1611 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);\r
1612 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);\r
1613\r
1614 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);\r
1615 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);\r
1616\r
1617 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);\r
1618 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);\r
1619}\r
1620\r
1621\r
1622/**\r
1623 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.\r
1624\r
1625 @param[in, out] Pdu The iSCSI pdu which contains segments to pad.\r
1626 @param[in] Len The length of the last segment in the PDU.\r
1627\r
1628 @retval EFI_SUCCESS The segment is padded or there is no need to pad it.\r
1629 @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the\r
1630 padding bytes.\r
1631**/\r
1632EFI_STATUS\r
1633IScsiPadSegment (\r
1634 IN OUT NET_BUF *Pdu,\r
1635 IN UINT32 Len\r
1636 )\r
1637{\r
1638 UINT32 PadLen;\r
1639 UINT8 *Data;\r
1640\r
1641 PadLen = ISCSI_GET_PAD_LEN (Len);\r
1642\r
1643 if (PadLen != 0) {\r
1644 Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);\r
1645 if (Data == NULL) {\r
1646 return EFI_OUT_OF_RESOURCES;\r
1647 }\r
1648\r
1649 ZeroMem (Data, PadLen);\r
1650 }\r
1651\r
1652 return EFI_SUCCESS;\r
1653}\r
1654\r
1655\r
1656/**\r
1657 Build a key-value list from the data segment.\r
1658\r
1659 @param[in] Data The data segment containing the key-value pairs.\r
1660 @param[in] Len Length of the data segment.\r
1661\r
1662 @return The key-value list.\r
1663 @retval NULL Other errors as indicated.\r
1664\r
1665**/\r
1666LIST_ENTRY *\r
1667IScsiBuildKeyValueList (\r
1668 IN CHAR8 *Data,\r
1669 IN UINT32 Len\r
1670 )\r
1671{\r
1672 LIST_ENTRY *ListHead;\r
1673 ISCSI_KEY_VALUE_PAIR *KeyValuePair;\r
1674\r
1675 ListHead = AllocatePool (sizeof (LIST_ENTRY));\r
1676 if (ListHead == NULL) {\r
1677 return NULL;\r
1678 }\r
1679\r
1680 InitializeListHead (ListHead);\r
1681\r
1682 while (Len > 0) {\r
1683 KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));\r
1684 if (KeyValuePair == NULL) {\r
1685 goto ON_ERROR;\r
1686 }\r
1687\r
1688 InitializeListHead (&KeyValuePair->List);\r
1689\r
1690 KeyValuePair->Key = Data;\r
1691\r
1692 while ((Len > 0) && (*Data != '=')) {\r
1693 Len--;\r
1694 Data++;\r
1695 }\r
1696\r
1697 if (*Data == '=') {\r
1698 *Data = '\0';\r
1699\r
1700 Data++;\r
1701 Len--;\r
1702 } else {\r
1703 FreePool (KeyValuePair);\r
1704 goto ON_ERROR;\r
1705 }\r
1706\r
1707 KeyValuePair->Value = Data;\r
1708\r
1709 InsertTailList (ListHead, &KeyValuePair->List);;\r
1710\r
1711 Data += AsciiStrLen (KeyValuePair->Value) + 1;\r
1712 Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;\r
1713 }\r
1714\r
1715 return ListHead;\r
1716\r
1717ON_ERROR:\r
1718\r
1719 IScsiFreeKeyValueList (ListHead);\r
1720\r
1721 return NULL;\r
1722}\r
1723\r
1724\r
1725/**\r
1726 Get the value string by the key name from the key-value list. If found,\r
1727 the key-value entry will be removed from the list.\r
1728\r
1729 @param[in, out] KeyValueList The key-value list.\r
1730 @param[in] Key The key name to find.\r
1731\r
1732 @return The value string.\r
1733 @retval NULL The key value pair cannot be found.\r
1734\r
1735**/\r
1736CHAR8 *\r
1737IScsiGetValueByKeyFromList (\r
1738 IN OUT LIST_ENTRY *KeyValueList,\r
1739 IN CHAR8 *Key\r
1740 )\r
1741{\r
1742 LIST_ENTRY *Entry;\r
1743 ISCSI_KEY_VALUE_PAIR *KeyValuePair;\r
1744 CHAR8 *Value;\r
1745\r
1746 Value = NULL;\r
1747\r
1748 NET_LIST_FOR_EACH (Entry, KeyValueList) {\r
1749 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);\r
1750\r
1751 if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {\r
1752 Value = KeyValuePair->Value;\r
1753\r
1754 RemoveEntryList (&KeyValuePair->List);\r
1755 FreePool (KeyValuePair);\r
1756 break;\r
1757 }\r
1758 }\r
1759\r
1760 return Value;\r
1761}\r
1762\r
1763\r
1764/**\r
1765 Free the key-value list.\r
1766\r
1767 @param[in] KeyValueList The key-value list.\r
1768\r
1769**/\r
1770VOID\r
1771IScsiFreeKeyValueList (\r
1772 IN LIST_ENTRY *KeyValueList\r
1773 )\r
1774{\r
1775 LIST_ENTRY *Entry;\r
1776 ISCSI_KEY_VALUE_PAIR *KeyValuePair;\r
1777\r
1778 while (!IsListEmpty (KeyValueList)) {\r
1779 Entry = NetListRemoveHead (KeyValueList);\r
1780 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);\r
1781\r
1782 FreePool (KeyValuePair);\r
1783 }\r
1784\r
1785 FreePool (KeyValueList);\r
1786}\r
1787\r
1788\r
1789/**\r
1790 Normalize the iSCSI name according to RFC.\r
1791\r
1792 @param[in, out] Name The iSCSI name.\r
1793 @param[in] Len Length of the iSCSI name.\r
1794\r
1795 @retval EFI_SUCCESS The iSCSI name is valid and normalized.\r
1796 @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.\r
1797\r
1798**/\r
1799EFI_STATUS\r
1800IScsiNormalizeName (\r
1801 IN OUT CHAR8 *Name,\r
1802 IN UINTN Len\r
1803 )\r
1804{\r
1805 UINTN Index;\r
1806\r
1807 for (Index = 0; Index < Len; Index++) {\r
1808 if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {\r
1809 //\r
1810 // Convert the upper-case characters to lower-case ones.\r
1811 //\r
1812 Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');\r
1813 }\r
1814\r
1815 if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&\r
1816 !NET_IS_DIGIT (Name[Index]) &&\r
1817 (Name[Index] != '-') &&\r
1818 (Name[Index] != '.') &&\r
1819 (Name[Index] != ':')\r
1820 ) {\r
1821 //\r
1822 // ASCII dash, dot, colon lower-case characters and digit characters\r
1823 // are allowed.\r
1824 //\r
1825 return EFI_PROTOCOL_ERROR;\r
1826 }\r
1827 }\r
1828\r
1829 if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {\r
1830 //\r
1831 // Only IQN format is accepted now.\r
1832 //\r
1833 return EFI_PROTOCOL_ERROR;\r
1834 }\r
1835\r
1836 return EFI_SUCCESS;\r
1837}\r
1838\r
1839\r
1840/**\r
1841 Create an iSCSI task control block.\r
1842\r
1843 @param[in] Conn The connection on which the task control block will be created.\r
1844 @param[out] Tcb The newly created task control block.\r
1845\r
1846 @retval EFI_SUCCESS The task control block is created.\r
1847 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
1848 @retval EFI_NOT_READY The target cannot accept new commands.\r
1849\r
1850**/\r
1851EFI_STATUS\r
1852IScsiNewTcb (\r
1853 IN ISCSI_CONNECTION *Conn,\r
1854 OUT ISCSI_TCB **Tcb\r
1855 )\r
1856{\r
1857 ISCSI_SESSION *Session;\r
1858 ISCSI_TCB *NewTcb;\r
1859\r
1860 ASSERT (Tcb != NULL);\r
1861\r
1862 Session = Conn->Session;\r
1863\r
1864 if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {\r
1865 return EFI_NOT_READY;\r
1866 }\r
1867\r
1868 NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));\r
1869 if (NewTcb == NULL) {\r
1870 return EFI_OUT_OF_RESOURCES;\r
1871 }\r
1872\r
1873 InitializeListHead (&NewTcb->Link);\r
1874\r
1875 NewTcb->SoFarInOrder = TRUE;\r
1876 NewTcb->InitiatorTaskTag = Session->InitiatorTaskTag;\r
1877 NewTcb->CmdSN = Session->CmdSN;\r
1878 NewTcb->Conn = Conn;\r
1879\r
1880 InsertTailList (&Session->TcbList, &NewTcb->Link);\r
1881\r
1882 //\r
1883 // Advance the initiator task tag.\r
1884 //\r
1885 Session->InitiatorTaskTag++;\r
1886 Session->CmdSN++;\r
1887\r
1888 *Tcb = NewTcb;\r
1889\r
1890 return EFI_SUCCESS;\r
1891}\r
1892\r
1893\r
1894/**\r
1895 Delete the tcb from the connection and destroy it.\r
1896\r
1897 @param[in] Tcb The tcb to delete.\r
1898\r
1899**/\r
1900VOID\r
1901IScsiDelTcb (\r
1902 IN ISCSI_TCB *Tcb\r
1903 )\r
1904{\r
1905 RemoveEntryList (&Tcb->Link);\r
1906\r
1907 FreePool (Tcb);\r
1908}\r
1909\r
1910\r
1911/**\r
1912 Find the task control block by the initator task tag.\r
1913\r
1914 @param[in] TcbList The tcb list.\r
1915 @param[in] InitiatorTaskTag The initiator task tag.\r
1916\r
1917 @return The task control block found.\r
1918 @retval NULL The task control block cannot be found.\r
1919\r
1920**/\r
1921ISCSI_TCB *\r
1922IScsiFindTcbByITT (\r
1923 IN LIST_ENTRY *TcbList,\r
1924 IN UINT32 InitiatorTaskTag\r
1925 )\r
1926{\r
1927 ISCSI_TCB *Tcb;\r
1928 LIST_ENTRY *Entry;\r
1929\r
1930 Tcb = NULL;\r
1931\r
1932 NET_LIST_FOR_EACH (Entry, TcbList) {\r
1933 Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);\r
1934\r
1935 if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {\r
1936 break;\r
1937 }\r
1938 }\r
1939\r
1940 return Tcb;\r
1941}\r
1942\r
1943\r
1944/**\r
1945 Create a data segment, pad it, and calculate the CRC if needed.\r
1946\r
1947 @param[in] Data The data to fill into the data segment.\r
1948 @param[in] Len Length of the data.\r
1949 @param[in] DataDigest Whether to calculate CRC for this data segment.\r
1950\r
1951 @return The net buffer wrapping the data segment.\r
1952\r
1953**/\r
1954NET_BUF *\r
1955IScsiNewDataSegment (\r
1956 IN UINT8 *Data,\r
1957 IN UINT32 Len,\r
1958 IN BOOLEAN DataDigest\r
1959 )\r
1960{\r
1961 NET_FRAGMENT Fragment[2];\r
1962 UINT32 FragmentCount;\r
1963 UINT32 PadLen;\r
1964 NET_BUF *DataSeg;\r
1965\r
1966 Fragment[0].Len = Len;\r
1967 Fragment[0].Bulk = Data;\r
1968\r
1969 PadLen = ISCSI_GET_PAD_LEN (Len);\r
1970 if (PadLen != 0) {\r
1971 Fragment[1].Len = PadLen;\r
1972 Fragment[1].Bulk = (UINT8 *) &mDataSegPad;\r
1973\r
1974 FragmentCount = 2;\r
1975 } else {\r
1976 FragmentCount = 1;\r
1977 }\r
1978\r
1979 DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);\r
1980\r
1981 return DataSeg;\r
1982}\r
1983\r
1984\r
1985/**\r
1986 Create a iSCSI SCSI command PDU to encapsulate the command issued\r
1987 by SCSI through the EXT SCSI PASS THRU Protocol.\r
1988\r
1989 @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.\r
1990 @param[in] Lun The LUN.\r
1991 @param[in] Tcb The tcb assocated with this SCSI command.\r
1992\r
1993 @return The created iSCSI SCSI command PDU.\r
1994 @retval NULL Other errors as indicated.\r
1995\r
1996**/\r
1997NET_BUF *\r
1998IScsiNewScsiCmdPdu (\r
1999 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,\r
2000 IN UINT64 Lun,\r
2001 IN ISCSI_TCB *Tcb\r
2002 )\r
2003{\r
2004 LIST_ENTRY *NbufList;\r
2005 NET_BUF *Pdu;\r
2006 NET_BUF *PduHeader;\r
2007 NET_BUF *DataSeg;\r
2008 SCSI_COMMAND *ScsiCmd;\r
2009 UINT8 AHSLength;\r
2010 UINT32 Length;\r
2011 ISCSI_ADDITIONAL_HEADER *Header;\r
2012 ISCSI_BI_EXP_READ_DATA_LEN_AHS *BiExpReadDataLenAHS;\r
2013 ISCSI_SESSION *Session;\r
2014 UINT32 ImmediateDataLen;\r
2015\r
2016 AHSLength = 0;\r
2017\r
2018 if (Packet->DataDirection == DataBi) {\r
2019 //\r
2020 // Bidirectional Read/Write command, the bidirectional expected\r
2021 // read data length AHS is required.\r
2022 //\r
2023 AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);\r
2024 }\r
2025\r
2026 if (Packet->CdbLength > 16) {\r
2027 //\r
2028 // The CDB exceeds 16 bytes. An extended CDB AHS is required.\r
2029 //\r
2030 AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER));\r
2031 }\r
2032\r
2033 Length = sizeof (SCSI_COMMAND) + AHSLength;\r
2034 PduHeader = NetbufAlloc (Length);\r
2035 if (PduHeader == NULL) {\r
2036 return NULL;\r
2037 }\r
2038\r
2039 ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);\r
2040 if (ScsiCmd == NULL) {\r
2041 NetbufFree (PduHeader);\r
2042 return NULL;\r
2043 } \r
2044 Header = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);\r
2045\r
2046 ZeroMem (ScsiCmd, Length);\r
2047\r
2048 ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);\r
2049 ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);\r
2050\r
2051 //\r
2052 // Set the READ/WRITE flags according to the IO type of this request.\r
2053 //\r
2054 switch (Packet->DataDirection) {\r
2055 case DataIn:\r
2056 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);\r
2057 ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);\r
2058 break;\r
2059\r
2060 case DataOut:\r
2061 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);\r
2062 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);\r
2063 break;\r
2064\r
2065 case DataBi:\r
2066 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);\r
2067 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);\r
2068\r
2069 //\r
2070 // Fill the bidirectional expected read data length AHS.\r
2071 //\r
2072 BiExpReadDataLenAHS = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;\r
2073 Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);\r
2074\r
2075 BiExpReadDataLenAHS->Length = NTOHS (5);\r
2076 BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;\r
2077 BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);\r
2078\r
2079 break;\r
2080 }\r
2081\r
2082 ScsiCmd->TotalAHSLength = AHSLength;\r
2083 CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));\r
2084 ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);\r
2085 ScsiCmd->CmdSN = NTOHL (Tcb->CmdSN);\r
2086 ScsiCmd->ExpStatSN = NTOHL (Tcb->Conn->ExpStatSN);\r
2087\r
2088 CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));\r
2089\r
2090 if (Packet->CdbLength > 16) {\r
2091 Header->Length = NTOHS ((UINT16) (Packet->CdbLength - 15));\r
2092 Header->Type = ISCSI_AHS_TYPE_EXT_CDB;\r
2093\r
2094 CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);\r
2095 }\r
2096\r
2097 Pdu = PduHeader;\r
2098 Session = Tcb->Conn->Session;\r
2099 ImmediateDataLen = 0;\r
2100\r
2101 if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {\r
2102 //\r
2103 // Send immediate data in this SCSI Command PDU. The length of the immeidate\r
2104 // data is the minimum of FirstBurstLength, the data length to be xfered, and\r
2105 // the MaxRecvdataSegmentLength on this connection.\r
2106 //\r
2107 ImmediateDataLen = MIN (Session->FirstBurstLength, Packet->OutTransferLength);\r
2108 ImmediateDataLen = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);\r
2109\r
2110 //\r
2111 // Update the data segment length in the PDU header.\r
2112 //\r
2113 ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);\r
2114\r
2115 //\r
2116 // Create the data segment.\r
2117 //\r
2118 DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);\r
2119 if (DataSeg == NULL) {\r
2120 NetbufFree (PduHeader);\r
2121 Pdu = NULL;\r
2122 goto ON_EXIT;\r
2123 }\r
2124\r
2125 NbufList = AllocatePool (sizeof (LIST_ENTRY));\r
2126 if (NbufList == NULL) {\r
2127 NetbufFree (PduHeader);\r
2128 NetbufFree (DataSeg);\r
2129\r
2130 Pdu = NULL;\r
2131 goto ON_EXIT;\r
2132 }\r
2133\r
2134 InitializeListHead (NbufList);\r
2135 InsertTailList (NbufList, &PduHeader->List);\r
2136 InsertTailList (NbufList, &DataSeg->List);\r
2137\r
2138 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);\r
2139 if (Pdu == NULL) {\r
2140 IScsiFreeNbufList (NbufList);\r
2141 }\r
2142 }\r
2143\r
2144 if (Session->InitialR2T ||\r
2145 (ImmediateDataLen == Session->FirstBurstLength) ||\r
2146 (ImmediateDataLen == Packet->OutTransferLength)\r
2147 ) {\r
2148 //\r
2149 // Unsolicited data out sequence is not allowed,\r
2150 // or FirstBustLength data is already sent out by immediate data,\r
2151 // or all the OUT data accompany this SCSI packet are sent as\r
2152 // immediate data. The final flag should be set on this SCSI Command\r
2153 // PDU.\r
2154 //\r
2155 ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);\r
2156 }\r
2157\r
2158ON_EXIT:\r
2159\r
2160 return Pdu;\r
2161}\r
2162\r
2163\r
2164/**\r
2165 Create a new iSCSI SCSI Data Out PDU.\r
2166\r
2167 @param[in] Data The data to put into the Data Out PDU.\r
2168 @param[in] Len Length of the data.\r
2169 @param[in] DataSN The DataSN of the Data Out PDU.\r
2170 @param[in] Tcb The task control block of this Data Out PDU.\r
2171 @param[in] Lun The LUN.\r
2172\r
2173 @return The net buffer wrapping the Data Out PDU.\r
2174 @retval NULL Other errors as indicated.\r
2175\r
2176**/\r
2177NET_BUF *\r
2178IScsiNewDataOutPdu (\r
2179 IN UINT8 *Data,\r
2180 IN UINT32 Len,\r
2181 IN UINT32 DataSN,\r
2182 IN ISCSI_TCB *Tcb,\r
2183 IN UINT64 Lun\r
2184 )\r
2185{\r
2186 LIST_ENTRY *NbufList;\r
2187 NET_BUF *PduHdr;\r
2188 NET_BUF *DataSeg;\r
2189 NET_BUF *Pdu;\r
2190 ISCSI_SCSI_DATA_OUT *DataOutHdr;\r
2191 ISCSI_XFER_CONTEXT *XferContext;\r
2192\r
2193 NbufList = AllocatePool (sizeof (LIST_ENTRY));\r
2194 if (NbufList == NULL) {\r
2195 return NULL;\r
2196 }\r
2197\r
2198 InitializeListHead (NbufList);\r
2199\r
2200 //\r
2201 // Allocate memory for the BHS.\r
2202 //\r
2203 PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));\r
2204 if (PduHdr == NULL) {\r
2205 FreePool (NbufList);\r
2206 return NULL;\r
2207 }\r
2208 //\r
2209 // Insert the BHS into the buffer list.\r
2210 //\r
2211 InsertTailList (NbufList, &PduHdr->List);\r
2212\r
2213 DataOutHdr = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);\r
2214 ASSERT (DataOutHdr != NULL);\r
2215 XferContext = &Tcb->XferContext;\r
2216\r
2217 ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));\r
2218\r
2219 //\r
2220 // Set the flags and fields of the Data Out PDU BHS.\r
2221 //\r
2222 ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);\r
2223 ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);\r
2224\r
2225 DataOutHdr->InitiatorTaskTag = HTONL (Tcb->InitiatorTaskTag);\r
2226 DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);\r
2227 DataOutHdr->ExpStatSN = HTONL (Tcb->Conn->ExpStatSN);\r
2228 DataOutHdr->DataSN = HTONL (DataSN);\r
2229 DataOutHdr->BufferOffset = HTONL (XferContext->Offset);\r
2230\r
2231 if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {\r
2232 CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));\r
2233 }\r
2234 //\r
2235 // Build the data segment for this Data Out PDU.\r
2236 //\r
2237 DataSeg = IScsiNewDataSegment (Data, Len, FALSE);\r
2238 if (DataSeg == NULL) {\r
2239 IScsiFreeNbufList (NbufList);\r
2240 return NULL;\r
2241 }\r
2242 //\r
2243 // Put the data segment into the buffer list and combine it with the BHS\r
2244 // into a full Data Out PDU.\r
2245 //\r
2246 InsertTailList (NbufList, &DataSeg->List);\r
2247 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);\r
2248 if (Pdu == NULL) {\r
2249 IScsiFreeNbufList (NbufList);\r
2250 }\r
2251\r
2252 return Pdu;\r
2253}\r
2254\r
2255\r
2256/**\r
2257 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.\r
2258\r
2259 @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.\r
2260 @param[in] Tcb The task control block of the data to send out.\r
2261 @param[in] Lun The LUN the data will be sent to.\r
2262\r
2263 @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.\r
2264 @retval NULL Other errors as indicated.\r
2265\r
2266**/\r
2267LIST_ENTRY *\r
2268IScsiGenerateDataOutPduSequence (\r
2269 IN UINT8 *Data,\r
2270 IN ISCSI_TCB *Tcb,\r
2271 IN UINT64 Lun\r
2272 )\r
2273{\r
2274 LIST_ENTRY *PduList;\r
2275 UINT32 DataSN;\r
2276 UINT32 DataLen;\r
2277 NET_BUF *DataOutPdu;\r
2278 ISCSI_CONNECTION *Conn;\r
2279 ISCSI_XFER_CONTEXT *XferContext;\r
7a49cd08 2280 UINT8 *DataOutPacket;\r
4c5a5e0c 2281\r
2282 PduList = AllocatePool (sizeof (LIST_ENTRY));\r
2283 if (PduList == NULL) {\r
2284 return NULL;\r
2285 }\r
2286\r
2287 InitializeListHead (PduList);\r
2288\r
2289 DataSN = 0;\r
2290 Conn = Tcb->Conn;\r
2291 DataOutPdu = NULL;\r
2292 XferContext = &Tcb->XferContext;\r
2293\r
2294 while (XferContext->DesiredLength > 0) {\r
2295 //\r
2296 // Determine the length of data this Data Out PDU can carry.\r
2297 //\r
2298 DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);\r
2299\r
2300 //\r
2301 // Create a Data Out PDU.\r
2302 //\r
2303 DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);\r
2304 if (DataOutPdu == NULL) {\r
2305 IScsiFreeNbufList (PduList);\r
2306 PduList = NULL;\r
2307\r
2308 goto ON_EXIT;\r
2309 }\r
2310\r
2311 InsertTailList (PduList, &DataOutPdu->List);\r
2312\r
2313 //\r
2314 // Update the context and DataSN.\r
2315 //\r
2316 Data += DataLen;\r
2317 XferContext->Offset += DataLen;\r
2318 XferContext->DesiredLength -= DataLen;\r
2319 DataSN++;\r
2320 }\r
2321 //\r
2322 // Set the F bit for the last data out PDU in this sequence.\r
2323 //\r
7a49cd08
ED
2324 DataOutPacket = NetbufGetByte (DataOutPdu, 0, NULL);\r
2325 if (DataOutPacket == NULL) {\r
2326 IScsiFreeNbufList (PduList);\r
2327 PduList = NULL;\r
2328 goto ON_EXIT;\r
2329 }\r
2330\r
2331 ISCSI_SET_FLAG (DataOutPacket, ISCSI_BHS_FLAG_FINAL);\r
4c5a5e0c 2332\r
2333ON_EXIT:\r
2334\r
2335 return PduList;\r
2336}\r
2337\r
2338/**\r
2339 Send the Data in a sequence of Data Out PDUs one by one.\r
2340\r
2341 @param[in] Data The data to carry by Data Out PDUs.\r
2342 @param[in] Lun The LUN the data will be sent to.\r
2343 @param[in] Tcb The task control block.\r
2344\r
2345 @retval EFI_SUCCES The data is sent out to the LUN.\r
2346 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
2347 @retval Others Other errors as indicated.\r
2348\r
2349**/\r
2350EFI_STATUS\r
2351IScsiSendDataOutPduSequence (\r
2352 IN UINT8 *Data,\r
2353 IN UINT64 Lun,\r
2354 IN ISCSI_TCB *Tcb\r
2355 )\r
2356{\r
2357 LIST_ENTRY *DataOutPduList;\r
2358 LIST_ENTRY *Entry;\r
2359 NET_BUF *Pdu;\r
2360 EFI_STATUS Status;\r
2361\r
2362 //\r
2363 // Generate the Data Out PDU sequence.\r
2364 //\r
2365 DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);\r
2366 if (DataOutPduList == NULL) {\r
2367 return EFI_OUT_OF_RESOURCES;\r
2368 }\r
2369\r
2370 Status = EFI_SUCCESS;\r
2371\r
2372 //\r
2373 // Send the Data Out PDU's one by one.\r
2374 //\r
2375 NET_LIST_FOR_EACH (Entry, DataOutPduList) {\r
2376 Pdu = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);\r
2377\r
2378 Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu);\r
2379\r
2380 if (EFI_ERROR (Status)) {\r
2381 break;\r
2382 }\r
2383 }\r
2384\r
2385 IScsiFreeNbufList (DataOutPduList);\r
2386\r
2387 return Status;\r
2388}\r
2389\r
2390\r
2391/**\r
2392 Process the received iSCSI SCSI Data In PDU.\r
2393\r
2394 @param[in] Pdu The Data In PDU received.\r
2395 @param[in] Tcb The task control block.\r
2396 @param[in, out] Packet The EXT SCSI PASS THRU request packet.\r
2397\r
2398 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update\r
2399 actions are taken.\r
2400 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.\r
2401 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.\r
2402 @retval Others Other errors as indicated.\r
2403\r
2404**/\r
2405EFI_STATUS\r
2406IScsiOnDataInRcvd (\r
2407 IN NET_BUF *Pdu,\r
2408 IN ISCSI_TCB *Tcb,\r
2409 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
2410 )\r
2411{\r
2412 ISCSI_SCSI_DATA_IN *DataInHdr;\r
2413 EFI_STATUS Status;\r
2414\r
2415 DataInHdr = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);\r
7a49cd08
ED
2416 if (DataInHdr == NULL) {\r
2417 return EFI_PROTOCOL_ERROR;\r
2418 }\r
4c5a5e0c 2419\r
2420 DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);\r
2421 DataInHdr->ExpCmdSN = NTOHL (DataInHdr->ExpCmdSN);\r
2422 DataInHdr->MaxCmdSN = NTOHL (DataInHdr->MaxCmdSN);\r
2423 DataInHdr->DataSN = NTOHL (DataInHdr->DataSN);\r
2424\r
2425 //\r
2426 // Check the DataSN.\r
2427 //\r
2428 Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);\r
2429 if (EFI_ERROR (Status)) {\r
2430 return Status;\r
2431 }\r
2432\r
2433 if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {\r
2434 return EFI_PROTOCOL_ERROR;\r
2435 }\r
2436 //\r
2437 // Update the command related sequence numbers.\r
2438 //\r
2439 IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);\r
2440\r
2441 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {\r
2442 if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {\r
2443 //\r
2444 // The S bit is on but the F bit is off.\r
2445 //\r
2446 return EFI_PROTOCOL_ERROR;\r
2447 }\r
2448\r
2449 Tcb->StatusXferd = TRUE;\r
2450\r
2451 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {\r
2452 //\r
2453 // Underflow and Overflow are mutual flags.\r
2454 //\r
2455 return EFI_PROTOCOL_ERROR;\r
2456 }\r
2457 //\r
2458 // S bit is on, the StatSN is valid.\r
2459 //\r
2460 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));\r
2461 if (EFI_ERROR (Status)) {\r
2462 return Status;\r
2463 }\r
2464\r
2465 Packet->HostAdapterStatus = 0;\r
2466 Packet->TargetStatus = DataInHdr->Status;\r
2467\r
2468 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {\r
2469 Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);\r
2470 Status = EFI_BAD_BUFFER_SIZE;\r
2471 }\r
2472\r
2473 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {\r
2474 Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);\r
2475 }\r
2476 }\r
2477\r
2478 return Status;\r
2479}\r
2480\r
2481\r
2482/**\r
2483 Process the received iSCSI R2T PDU.\r
2484\r
2485 @param[in] Pdu The R2T PDU received.\r
2486 @param[in] Tcb The task control block.\r
2487 @param[in] Lun The Lun.\r
2488 @param[in, out] Packet The EXT SCSI PASS THRU request packet.\r
2489\r
2490 @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.\r
2491 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.\r
2492 @retval Others Other errors as indicated.\r
2493\r
2494**/\r
2495EFI_STATUS\r
2496IScsiOnR2TRcvd (\r
2497 IN NET_BUF *Pdu,\r
2498 IN ISCSI_TCB *Tcb,\r
2499 IN UINT64 Lun,\r
2500 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
2501 )\r
2502{\r
2503 ISCSI_READY_TO_TRANSFER *R2THdr;\r
2504 EFI_STATUS Status;\r
2505 ISCSI_XFER_CONTEXT *XferContext;\r
2506 UINT8 *Data;\r
2507\r
2508 R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);\r
7a49cd08
ED
2509 if (R2THdr == NULL) {\r
2510 return EFI_PROTOCOL_ERROR;\r
2511 }\r
4c5a5e0c 2512\r
2513 R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);\r
2514 R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);\r
2515 R2THdr->StatSN = NTOHL (R2THdr->StatSN);\r
2516 R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum);\r
2517 R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);\r
2518 R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);\r
2519\r
2520 if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {\r
2521 return EFI_PROTOCOL_ERROR;;\r
2522 }\r
2523 //\r
2524 // Check the sequence number.\r
2525 //\r
2526 Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);\r
2527 if (EFI_ERROR (Status)) {\r
2528 return Status;\r
2529 }\r
2530\r
2531 XferContext = &Tcb->XferContext;\r
2532 XferContext->TargetTransferTag = R2THdr->TargetTransferTag;\r
2533 XferContext->Offset = R2THdr->BufferOffset;\r
2534 XferContext->DesiredLength = R2THdr->DesiredDataTransferLength;\r
2535\r
2536 if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||\r
2537 (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)\r
2538 ) {\r
2539 return EFI_PROTOCOL_ERROR;\r
2540 }\r
2541 //\r
2542 // Send the data solicited by this R2T.\r
2543 //\r
2544 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;\r
2545 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);\r
2546\r
2547 return Status;\r
2548}\r
2549\r
2550\r
2551/**\r
2552 Process the received iSCSI SCSI Response PDU.\r
2553\r
2554 @param[in] Pdu The Response PDU received.\r
2555 @param[in] Tcb The task control block.\r
2556 @param[in, out] Packet The EXT SCSI PASS THRU request packet.\r
2557\r
2558 @retval EFI_SUCCES The Response PDU is processed.\r
2559 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.\r
2560 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.\r
2561 @retval Others Other errors as indicated.\r
2562\r
2563**/\r
2564EFI_STATUS\r
2565IScsiOnScsiRspRcvd (\r
2566 IN NET_BUF *Pdu,\r
2567 IN ISCSI_TCB *Tcb,\r
2568 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
2569 )\r
2570{\r
2571 SCSI_RESPONSE *ScsiRspHdr;\r
2572 ISCSI_SENSE_DATA *SenseData;\r
2573 EFI_STATUS Status;\r
2574 UINT32 DataSegLen;\r
2575\r
2576 ScsiRspHdr = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);\r
7a49cd08
ED
2577 if (ScsiRspHdr == NULL) {\r
2578 return EFI_PROTOCOL_ERROR;\r
2579 }\r
4c5a5e0c 2580\r
2581 ScsiRspHdr->InitiatorTaskTag = NTOHL (ScsiRspHdr->InitiatorTaskTag);\r
2582 if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {\r
2583 return EFI_PROTOCOL_ERROR;\r
2584 }\r
2585\r
2586 ScsiRspHdr->StatSN = NTOHL (ScsiRspHdr->StatSN);\r
2587\r
2588 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);\r
2589 if (EFI_ERROR (Status)) {\r
2590 return Status;\r
2591 }\r
2592\r
2593 ScsiRspHdr->MaxCmdSN = NTOHL (ScsiRspHdr->MaxCmdSN);\r
2594 ScsiRspHdr->ExpCmdSN = NTOHL (ScsiRspHdr->ExpCmdSN);\r
2595 IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);\r
2596\r
2597 Tcb->StatusXferd = TRUE;\r
2598\r
2599 Packet->HostAdapterStatus = ScsiRspHdr->Response;\r
2600 if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {\r
2601 return EFI_SUCCESS;\r
2602 }\r
2603\r
2604 Packet->TargetStatus = ScsiRspHdr->Status;\r
2605\r
2606 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||\r
2607 ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)\r
2608 ) {\r
2609 return EFI_PROTOCOL_ERROR;\r
2610 }\r
2611\r
2612 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {\r
2613 Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);\r
2614 Status = EFI_BAD_BUFFER_SIZE;\r
2615 }\r
2616\r
2617 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {\r
2618 Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);\r
2619 }\r
2620\r
2621 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {\r
2622 if (Packet->DataDirection == DataIn) {\r
2623 Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);\r
2624 } else {\r
2625 Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);\r
2626 }\r
2627\r
2628 Status = EFI_BAD_BUFFER_SIZE;\r
2629 }\r
2630\r
2631 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {\r
2632 if (Packet->DataDirection == DataIn) {\r
2633 Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);\r
2634 } else {\r
2635 Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);\r
2636 }\r
2637 }\r
2638\r
2639 DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);\r
2640 if (DataSegLen != 0) {\r
2641 SenseData = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);\r
7a49cd08
ED
2642 if (SenseData == NULL) {\r
2643 return EFI_PROTOCOL_ERROR;\r
2644 }\r
4c5a5e0c 2645\r
2646 SenseData->Length = NTOHS (SenseData->Length);\r
2647\r
2648 Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);\r
2649 if (Packet->SenseDataLength != 0) {\r
2650 CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);\r
2651 }\r
2652 } else {\r
2653 Packet->SenseDataLength = 0;\r
2654 }\r
2655\r
2656 return Status;\r
2657}\r
2658\r
2659\r
2660/**\r
2661 Process the received NOP In PDU.\r
2662\r
2663 @param[in] Pdu The NOP In PDU received.\r
2664 @param[in] Tcb The task control block.\r
2665\r
2666 @retval EFI_SUCCES The NOP In PDU is processed and the related sequence\r
2667 numbers are updated.\r
2668 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.\r
2669\r
2670**/\r
2671EFI_STATUS\r
2672IScsiOnNopInRcvd (\r
2673 IN NET_BUF *Pdu,\r
2674 IN ISCSI_TCB *Tcb\r
2675 )\r
2676{\r
2677 ISCSI_NOP_IN *NopInHdr;\r
2678 EFI_STATUS Status;\r
2679\r
2680 NopInHdr = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);\r
7a49cd08
ED
2681 if (NopInHdr == NULL) {\r
2682 return EFI_PROTOCOL_ERROR;\r
2683 }\r
4c5a5e0c 2684\r
2685 NopInHdr->StatSN = NTOHL (NopInHdr->StatSN);\r
2686 NopInHdr->ExpCmdSN = NTOHL (NopInHdr->ExpCmdSN);\r
2687 NopInHdr->MaxCmdSN = NTOHL (NopInHdr->MaxCmdSN);\r
2688\r
2689 if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {\r
2690 if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {\r
2691 return EFI_PROTOCOL_ERROR;\r
2692 }\r
2693 } else {\r
2694 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);\r
2695 if (EFI_ERROR (Status)) {\r
2696 return Status;\r
2697 }\r
2698 }\r
2699\r
2700 IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);\r
2701\r
2702 return EFI_SUCCESS;\r
2703}\r
2704\r
2705\r
2706/**\r
2707 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.\r
2708\r
2709 @param[in] PassThru The EXT SCSI PASS THRU protocol.\r
2710 @param[in] Target The target ID.\r
2711 @param[in] Lun The LUN.\r
2712 @param[in, out] Packet The request packet containing IO request, SCSI command\r
2713 buffer and buffers to read/write.\r
2714 \r
2715 @retval EFI_SUCCES The SCSI command is executed and the result is updated to \r
2716 the Packet.\r
2717 @retval EFI_DEVICE_ERROR Session state was not as required.\r
2718 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.\r
7a49cd08 2719 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.\r
4c5a5e0c 2720 @retval Others Other errors as indicated.\r
2721\r
2722**/\r
2723EFI_STATUS\r
2724IScsiExecuteScsiCommand (\r
2725 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,\r
2726 IN UINT8 *Target,\r
2727 IN UINT64 Lun,\r
2728 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet\r
2729 )\r
2730{\r
2731 EFI_STATUS Status;\r
2732 ISCSI_DRIVER_DATA *Private;\r
2733 ISCSI_SESSION *Session;\r
2734 EFI_EVENT TimeoutEvent;\r
2735 ISCSI_CONNECTION *Conn;\r
2736 ISCSI_TCB *Tcb;\r
2737 NET_BUF *Pdu;\r
2738 ISCSI_XFER_CONTEXT *XferContext;\r
2739 UINT8 *Data;\r
2740 ISCSI_IN_BUFFER_CONTEXT InBufferContext;\r
2741 UINT64 Timeout;\r
2742 UINT8 *PduHdr;\r
2743\r
2744 Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);\r
2745 Session = Private->Session;\r
2746 Status = EFI_SUCCESS;\r
2747 Tcb = NULL;\r
2748 TimeoutEvent = NULL;\r
2749 Timeout = 0;\r
2750\r
2751 if (Session->State != SESSION_STATE_LOGGED_IN) {\r
2752 return EFI_DEVICE_ERROR;\r
2753 }\r
2754\r
2755 Conn = NET_LIST_USER_STRUCT_S (\r
2756 Session->Conns.ForwardLink,\r
2757 ISCSI_CONNECTION,\r
2758 Link,\r
2759 ISCSI_CONNECTION_SIGNATURE\r
2760 );\r
2761\r
2762 if (Packet->Timeout != 0) {\r
2763 Timeout = MultU64x32 (Packet->Timeout, 4);\r
2764 }\r
2765\r
2766 Status = IScsiNewTcb (Conn, &Tcb);\r
2767 if (EFI_ERROR (Status)) {\r
2768 goto ON_EXIT;\r
2769 }\r
2770 //\r
2771 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.\r
2772 //\r
2773 Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);\r
2774 if (Pdu == NULL) {\r
2775 Status = EFI_OUT_OF_RESOURCES;\r
2776 goto ON_EXIT;\r
2777 }\r
2778\r
2779 XferContext = &Tcb->XferContext;\r
2780 PduHdr = NetbufGetByte (Pdu, 0, NULL);\r
7a49cd08
ED
2781 if (PduHdr == NULL) {\r
2782 Status = EFI_PROTOCOL_ERROR;\r
2783 NetbufFree (Pdu);\r
2784 goto ON_EXIT;\r
2785 }\r
4c5a5e0c 2786 XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);\r
2787\r
2788 //\r
2789 // Transmit the SCSI Command PDU.\r
2790 //\r
2791 Status = TcpIoTransmit (&Conn->TcpIo, Pdu);\r
2792\r
2793 NetbufFree (Pdu);\r
2794\r
2795 if (EFI_ERROR (Status)) {\r
2796 goto ON_EXIT;\r
2797 }\r
2798\r
2799 if (!Session->InitialR2T &&\r
2800 (XferContext->Offset < Session->FirstBurstLength) &&\r
2801 (XferContext->Offset < Packet->OutTransferLength)\r
2802 ) {\r
2803 //\r
2804 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI\r
2805 // OUT data, and the limit of FirstBurstLength is not reached.\r
2806 //\r
2807 XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;\r
2808 XferContext->DesiredLength = MIN (\r
2809 Session->FirstBurstLength,\r
2810 Packet->OutTransferLength - XferContext->Offset\r
2811 );\r
2812\r
2813 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;\r
2814 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);\r
2815 if (EFI_ERROR (Status)) {\r
2816 goto ON_EXIT;\r
2817 }\r
2818 }\r
2819\r
2820 InBufferContext.InData = (UINT8 *) Packet->InDataBuffer;\r
2821 InBufferContext.InDataLen = Packet->InTransferLength;\r
2822\r
2823 while (!Tcb->StatusXferd) {\r
2824 //\r
2825 // Start the timeout timer.\r
2826 //\r
2827 if (Timeout != 0) {\r
2828 Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);\r
2829 if (EFI_ERROR (Status)) {\r
2830 goto ON_EXIT;\r
2831 }\r
2832\r
2833 TimeoutEvent = Conn->TimeoutEvent;\r
2834 }\r
2835\r
2836 //\r
2837 // Try to receive PDU from target.\r
2838 //\r
2839 Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);\r
2840 if (EFI_ERROR (Status)) {\r
2841 goto ON_EXIT;\r
2842 }\r
2843\r
7a49cd08
ED
2844 PduHdr = NetbufGetByte (Pdu, 0, NULL);\r
2845 if (PduHdr == NULL) {\r
2846 Status = EFI_PROTOCOL_ERROR;\r
2847 NetbufFree (Pdu);\r
2848 goto ON_EXIT;\r
2849 }\r
2850 switch (ISCSI_GET_OPCODE (PduHdr)) {\r
4c5a5e0c 2851 case ISCSI_OPCODE_SCSI_DATA_IN:\r
2852 Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);\r
2853 break;\r
2854\r
2855 case ISCSI_OPCODE_R2T:\r
2856 Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);\r
2857 break;\r
2858\r
2859 case ISCSI_OPCODE_SCSI_RSP:\r
2860 Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);\r
2861 break;\r
2862\r
2863 case ISCSI_OPCODE_NOP_IN:\r
2864 Status = IScsiOnNopInRcvd (Pdu, Tcb);\r
2865 break;\r
2866\r
2867 case ISCSI_OPCODE_VENDOR_T0:\r
2868 case ISCSI_OPCODE_VENDOR_T1:\r
2869 case ISCSI_OPCODE_VENDOR_T2:\r
2870 //\r
2871 // These messages are vendor specific. Skip them.\r
2872 //\r
2873 break;\r
2874\r
2875 default:\r
2876 Status = EFI_PROTOCOL_ERROR;\r
2877 break;\r
2878 }\r
2879\r
2880 NetbufFree (Pdu);\r
2881\r
2882 if (EFI_ERROR (Status)) {\r
2883 break;\r
2884 }\r
2885 }\r
2886\r
2887ON_EXIT:\r
2888\r
2889 if (TimeoutEvent != NULL) {\r
2890 gBS->SetTimer (TimeoutEvent, TimerCancel, 0);\r
2891 }\r
2892\r
2893 if (Tcb != NULL) {\r
2894 IScsiDelTcb (Tcb);\r
2895 }\r
2896\r
2897 if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {\r
2898 //\r
2899 // Reinstate the session.\r
2900 //\r
2901 if (EFI_ERROR (IScsiSessionReinstatement (Session))) {\r
2902 Status = EFI_DEVICE_ERROR;\r
2903 }\r
2904 }\r
2905\r
2906 return Status;\r
2907}\r
2908\r
2909\r
2910/**\r
2911 Reinstate the session on some error.\r
2912\r
2913 @param[in] Session The iSCSI session\r
2914\r
2915 @retval EFI_SUCCESS The session is reinstated from some error.\r
2916 @retval Other Reinstatement failed.\r
2917\r
2918**/\r
2919EFI_STATUS\r
2920IScsiSessionReinstatement (\r
2921 IN ISCSI_SESSION *Session\r
2922 )\r
2923{\r
2924 EFI_STATUS Status;\r
2925\r
2926 ASSERT (Session->State == SESSION_STATE_LOGGED_IN);\r
2927\r
2928 //\r
2929 // Abort the session and re-init it.\r
2930 //\r
2931 IScsiSessionAbort (Session);\r
2932 IScsiSessionInit (Session, TRUE);\r
2933\r
2934 //\r
2935 // Login again.\r
2936 //\r
2937 Status = IScsiSessionLogin (Session);\r
2938\r
2939 return Status;\r
2940}\r
2941\r
2942\r
2943/**\r
2944 Initialize some session parameters before login.\r
2945\r
2946 @param[in, out] Session The iSCSI session.\r
2947 @param[in] Recovery Whether the request is from a fresh new start or recovery.\r
2948\r
2949**/\r
2950VOID\r
2951IScsiSessionInit (\r
2952 IN OUT ISCSI_SESSION *Session,\r
2953 IN BOOLEAN Recovery\r
2954 )\r
2955{\r
2956 if (!Recovery) {\r
2957 Session->Signature = ISCSI_SESSION_SIGNATURE;\r
2958 Session->State = SESSION_STATE_FREE;\r
2959\r
2960 InitializeListHead (&Session->Conns);\r
2961 InitializeListHead (&Session->TcbList);\r
2962 }\r
2963\r
2964 Session->Tsih = 0;\r
2965\r
2966 Session->CmdSN = 1;\r
2967 Session->InitiatorTaskTag = 1;\r
2968 Session->NextCid = 1;\r
2969\r
2970 Session->TargetPortalGroupTag = 0;\r
2971 Session->MaxConnections = ISCSI_MAX_CONNS_PER_SESSION;\r
2972 Session->InitialR2T = FALSE;\r
2973 Session->ImmediateData = TRUE;\r
2974 Session->MaxBurstLength = 262144;\r
2975 Session->FirstBurstLength = MAX_RECV_DATA_SEG_LEN_IN_FFP;\r
2976 Session->DefaultTime2Wait = 2;\r
2977 Session->DefaultTime2Retain = 20;\r
2978 Session->MaxOutstandingR2T = DEFAULT_MAX_OUTSTANDING_R2T;\r
2979 Session->DataPDUInOrder = TRUE;\r
2980 Session->DataSequenceInOrder = TRUE;\r
2981 Session->ErrorRecoveryLevel = 0;\r
2982}\r
2983\r
2984\r
2985/**\r
2986 Abort the iSCSI session. That is, reset all the connection(s), and free the\r
2987 resources.\r
2988\r
2989 @param[in, out] Session The iSCSI session.\r
2990\r
2991**/\r
2992VOID\r
2993IScsiSessionAbort (\r
2994 IN OUT ISCSI_SESSION *Session\r
2995 )\r
2996{\r
2997 ISCSI_CONNECTION *Conn;\r
2998 EFI_GUID *ProtocolGuid;\r
2999\r
3000 if (Session->State != SESSION_STATE_LOGGED_IN) {\r
3001 return ;\r
3002 }\r
3003\r
3004 ASSERT (!IsListEmpty (&Session->Conns));\r
3005\r
3006 while (!IsListEmpty (&Session->Conns)) {\r
3007 Conn = NET_LIST_USER_STRUCT_S (\r
3008 Session->Conns.ForwardLink,\r
3009 ISCSI_CONNECTION,\r
3010 Link,\r
3011 ISCSI_CONNECTION_SIGNATURE\r
3012 );\r
3013 if (!Conn->Ipv6Flag) {\r
3014 ProtocolGuid = &gEfiTcp4ProtocolGuid;\r
3015 } else {\r
3016 ProtocolGuid = &gEfiTcp6ProtocolGuid; \r
3017 }\r
3018\r
3019 gBS->CloseProtocol (\r
3020 Conn->TcpIo.Handle,\r
3021 ProtocolGuid,\r
3022 Session->Private->Image,\r
3023 Session->Private->ExtScsiPassThruHandle\r
3024 );\r
3025\r
3026 IScsiConnReset (Conn);\r
3027\r
3028 IScsiDetatchConnection (Conn);\r
3029 IScsiDestroyConnection (Conn);\r
3030 }\r
3031\r
3032 Session->State = SESSION_STATE_FAILED;\r
3033\r
3034 return ;\r
3035}\r