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