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