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