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