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