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