]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiProto.c
Update code to avoid potential access violation.
[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 prameter 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 if (IsListEmpty (KeyValueList)) {
1544 //
1545 // Succeed if no more keys in the list.
1546 //
1547 Status = EFI_SUCCESS;
1548 }
1549
1550 ON_ERROR:
1551
1552 IScsiFreeKeyValueList (KeyValueList);
1553
1554 FreePool (Data);
1555
1556 return Status;
1557 }
1558
1559
1560 /**
1561 Fill the oprational parameters.
1562
1563 @param[in] Conn The connection in iSCSI login.
1564 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1565
1566 **/
1567 VOID
1568 IScsiFillOpParams (
1569 IN ISCSI_CONNECTION *Conn,
1570 IN OUT NET_BUF *Pdu
1571 )
1572 {
1573 ISCSI_SESSION *Session;
1574 CHAR8 Value[256];
1575
1576 Session = Conn->Session;
1577
1578 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
1579 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);
1580
1581 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
1582 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);
1583
1584 AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);
1585 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);
1586
1587 AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");
1588 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);
1589
1590 AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");
1591 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);
1592
1593 AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);
1594 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);
1595
1596 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);
1597 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);
1598
1599 AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);
1600 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);
1601
1602 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);
1603 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);
1604
1605 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");
1606 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);
1607
1608 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");
1609 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);
1610
1611 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);
1612 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);
1613
1614 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);
1615 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);
1616
1617 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);
1618 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);
1619 }
1620
1621
1622 /**
1623 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
1624
1625 @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
1626 @param[in] Len The length of the last segment in the PDU.
1627
1628 @retval EFI_SUCCESS The segment is padded or there is no need to pad it.
1629 @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
1630 padding bytes.
1631 **/
1632 EFI_STATUS
1633 IScsiPadSegment (
1634 IN OUT NET_BUF *Pdu,
1635 IN UINT32 Len
1636 )
1637 {
1638 UINT32 PadLen;
1639 UINT8 *Data;
1640
1641 PadLen = ISCSI_GET_PAD_LEN (Len);
1642
1643 if (PadLen != 0) {
1644 Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);
1645 if (Data == NULL) {
1646 return EFI_OUT_OF_RESOURCES;
1647 }
1648
1649 ZeroMem (Data, PadLen);
1650 }
1651
1652 return EFI_SUCCESS;
1653 }
1654
1655
1656 /**
1657 Build a key-value list from the data segment.
1658
1659 @param[in] Data The data segment containing the key-value pairs.
1660 @param[in] Len Length of the data segment.
1661
1662 @return The key-value list.
1663 @retval NULL Other errors as indicated.
1664
1665 **/
1666 LIST_ENTRY *
1667 IScsiBuildKeyValueList (
1668 IN CHAR8 *Data,
1669 IN UINT32 Len
1670 )
1671 {
1672 LIST_ENTRY *ListHead;
1673 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1674
1675 ListHead = AllocatePool (sizeof (LIST_ENTRY));
1676 if (ListHead == NULL) {
1677 return NULL;
1678 }
1679
1680 InitializeListHead (ListHead);
1681
1682 while (Len > 0) {
1683 KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));
1684 if (KeyValuePair == NULL) {
1685 goto ON_ERROR;
1686 }
1687
1688 InitializeListHead (&KeyValuePair->List);
1689
1690 KeyValuePair->Key = Data;
1691
1692 while ((Len > 0) && (*Data != '=')) {
1693 Len--;
1694 Data++;
1695 }
1696
1697 if (*Data == '=') {
1698 *Data = '\0';
1699
1700 Data++;
1701 Len--;
1702 } else {
1703 FreePool (KeyValuePair);
1704 goto ON_ERROR;
1705 }
1706
1707 KeyValuePair->Value = Data;
1708
1709 InsertTailList (ListHead, &KeyValuePair->List);;
1710
1711 Data += AsciiStrLen (KeyValuePair->Value) + 1;
1712 Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;
1713 }
1714
1715 return ListHead;
1716
1717 ON_ERROR:
1718
1719 IScsiFreeKeyValueList (ListHead);
1720
1721 return NULL;
1722 }
1723
1724
1725 /**
1726 Get the value string by the key name from the key-value list. If found,
1727 the key-value entry will be removed from the list.
1728
1729 @param[in, out] KeyValueList The key-value list.
1730 @param[in] Key The key name to find.
1731
1732 @return The value string.
1733 @retval NULL The key value pair cannot be found.
1734
1735 **/
1736 CHAR8 *
1737 IScsiGetValueByKeyFromList (
1738 IN OUT LIST_ENTRY *KeyValueList,
1739 IN CHAR8 *Key
1740 )
1741 {
1742 LIST_ENTRY *Entry;
1743 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1744 CHAR8 *Value;
1745
1746 Value = NULL;
1747
1748 NET_LIST_FOR_EACH (Entry, KeyValueList) {
1749 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
1750
1751 if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {
1752 Value = KeyValuePair->Value;
1753
1754 RemoveEntryList (&KeyValuePair->List);
1755 FreePool (KeyValuePair);
1756 break;
1757 }
1758 }
1759
1760 return Value;
1761 }
1762
1763
1764 /**
1765 Free the key-value list.
1766
1767 @param[in] KeyValueList The key-value list.
1768
1769 **/
1770 VOID
1771 IScsiFreeKeyValueList (
1772 IN LIST_ENTRY *KeyValueList
1773 )
1774 {
1775 LIST_ENTRY *Entry;
1776 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1777
1778 while (!IsListEmpty (KeyValueList)) {
1779 Entry = NetListRemoveHead (KeyValueList);
1780 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
1781
1782 FreePool (KeyValuePair);
1783 }
1784
1785 FreePool (KeyValueList);
1786 }
1787
1788
1789 /**
1790 Normalize the iSCSI name according to RFC.
1791
1792 @param[in, out] Name The iSCSI name.
1793 @param[in] Len Length of the iSCSI name.
1794
1795 @retval EFI_SUCCESS The iSCSI name is valid and normalized.
1796 @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.
1797
1798 **/
1799 EFI_STATUS
1800 IScsiNormalizeName (
1801 IN OUT CHAR8 *Name,
1802 IN UINTN Len
1803 )
1804 {
1805 UINTN Index;
1806
1807 for (Index = 0; Index < Len; Index++) {
1808 if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {
1809 //
1810 // Convert the upper-case characters to lower-case ones.
1811 //
1812 Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');
1813 }
1814
1815 if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&
1816 !NET_IS_DIGIT (Name[Index]) &&
1817 (Name[Index] != '-') &&
1818 (Name[Index] != '.') &&
1819 (Name[Index] != ':')
1820 ) {
1821 //
1822 // ASCII dash, dot, colon lower-case characters and digit characters
1823 // are allowed.
1824 //
1825 return EFI_PROTOCOL_ERROR;
1826 }
1827 }
1828
1829 if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {
1830 //
1831 // Only IQN format is accepted now.
1832 //
1833 return EFI_PROTOCOL_ERROR;
1834 }
1835
1836 return EFI_SUCCESS;
1837 }
1838
1839
1840 /**
1841 Create an iSCSI task control block.
1842
1843 @param[in] Conn The connection on which the task control block will be created.
1844 @param[out] Tcb The newly created task control block.
1845
1846 @retval EFI_SUCCESS The task control block is created.
1847 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1848 @retval EFI_NOT_READY The target cannot accept new commands.
1849
1850 **/
1851 EFI_STATUS
1852 IScsiNewTcb (
1853 IN ISCSI_CONNECTION *Conn,
1854 OUT ISCSI_TCB **Tcb
1855 )
1856 {
1857 ISCSI_SESSION *Session;
1858 ISCSI_TCB *NewTcb;
1859
1860 ASSERT (Tcb != NULL);
1861
1862 Session = Conn->Session;
1863
1864 if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {
1865 return EFI_NOT_READY;
1866 }
1867
1868 NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));
1869 if (NewTcb == NULL) {
1870 return EFI_OUT_OF_RESOURCES;
1871 }
1872
1873 InitializeListHead (&NewTcb->Link);
1874
1875 NewTcb->SoFarInOrder = TRUE;
1876 NewTcb->InitiatorTaskTag = Session->InitiatorTaskTag;
1877 NewTcb->CmdSN = Session->CmdSN;
1878 NewTcb->Conn = Conn;
1879
1880 InsertTailList (&Session->TcbList, &NewTcb->Link);
1881
1882 //
1883 // Advance the initiator task tag.
1884 //
1885 Session->InitiatorTaskTag++;
1886 Session->CmdSN++;
1887
1888 *Tcb = NewTcb;
1889
1890 return EFI_SUCCESS;
1891 }
1892
1893
1894 /**
1895 Delete the tcb from the connection and destroy it.
1896
1897 @param[in] Tcb The tcb to delete.
1898
1899 **/
1900 VOID
1901 IScsiDelTcb (
1902 IN ISCSI_TCB *Tcb
1903 )
1904 {
1905 RemoveEntryList (&Tcb->Link);
1906
1907 FreePool (Tcb);
1908 }
1909
1910
1911 /**
1912 Find the task control block by the initator task tag.
1913
1914 @param[in] TcbList The tcb list.
1915 @param[in] InitiatorTaskTag The initiator task tag.
1916
1917 @return The task control block found.
1918 @retval NULL The task control block cannot be found.
1919
1920 **/
1921 ISCSI_TCB *
1922 IScsiFindTcbByITT (
1923 IN LIST_ENTRY *TcbList,
1924 IN UINT32 InitiatorTaskTag
1925 )
1926 {
1927 ISCSI_TCB *Tcb;
1928 LIST_ENTRY *Entry;
1929
1930 Tcb = NULL;
1931
1932 NET_LIST_FOR_EACH (Entry, TcbList) {
1933 Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);
1934
1935 if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {
1936 break;
1937 }
1938 }
1939
1940 return Tcb;
1941 }
1942
1943
1944 /**
1945 Create a data segment, pad it, and calculate the CRC if needed.
1946
1947 @param[in] Data The data to fill into the data segment.
1948 @param[in] Len Length of the data.
1949 @param[in] DataDigest Whether to calculate CRC for this data segment.
1950
1951 @return The net buffer wrapping the data segment.
1952
1953 **/
1954 NET_BUF *
1955 IScsiNewDataSegment (
1956 IN UINT8 *Data,
1957 IN UINT32 Len,
1958 IN BOOLEAN DataDigest
1959 )
1960 {
1961 NET_FRAGMENT Fragment[2];
1962 UINT32 FragmentCount;
1963 UINT32 PadLen;
1964 NET_BUF *DataSeg;
1965
1966 Fragment[0].Len = Len;
1967 Fragment[0].Bulk = Data;
1968
1969 PadLen = ISCSI_GET_PAD_LEN (Len);
1970 if (PadLen != 0) {
1971 Fragment[1].Len = PadLen;
1972 Fragment[1].Bulk = (UINT8 *) &mDataSegPad;
1973
1974 FragmentCount = 2;
1975 } else {
1976 FragmentCount = 1;
1977 }
1978
1979 DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
1980
1981 return DataSeg;
1982 }
1983
1984
1985 /**
1986 Create a iSCSI SCSI command PDU to encapsulate the command issued
1987 by SCSI through the EXT SCSI PASS THRU Protocol.
1988
1989 @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
1990 @param[in] Lun The LUN.
1991 @param[in] Tcb The tcb assocated with this SCSI command.
1992
1993 @return The created iSCSI SCSI command PDU.
1994 @retval NULL Other errors as indicated.
1995
1996 **/
1997 NET_BUF *
1998 IScsiNewScsiCmdPdu (
1999 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
2000 IN UINT64 Lun,
2001 IN ISCSI_TCB *Tcb
2002 )
2003 {
2004 LIST_ENTRY *NbufList;
2005 NET_BUF *Pdu;
2006 NET_BUF *PduHeader;
2007 NET_BUF *DataSeg;
2008 SCSI_COMMAND *ScsiCmd;
2009 UINT8 AHSLength;
2010 UINT32 Length;
2011 ISCSI_ADDITIONAL_HEADER *Header;
2012 ISCSI_BI_EXP_READ_DATA_LEN_AHS *BiExpReadDataLenAHS;
2013 ISCSI_SESSION *Session;
2014 UINT32 ImmediateDataLen;
2015
2016 AHSLength = 0;
2017
2018 if (Packet->DataDirection == DataBi) {
2019 //
2020 // Bidirectional Read/Write command, the bidirectional expected
2021 // read data length AHS is required.
2022 //
2023 AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);
2024 }
2025
2026 if (Packet->CdbLength > 16) {
2027 //
2028 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2029 //
2030 AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER));
2031 }
2032
2033 Length = sizeof (SCSI_COMMAND) + AHSLength;
2034 PduHeader = NetbufAlloc (Length);
2035 if (PduHeader == NULL) {
2036 return NULL;
2037 }
2038
2039 ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);
2040 if (ScsiCmd == NULL) {
2041 NetbufFree (PduHeader);
2042 return NULL;
2043 }
2044 Header = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);
2045
2046 ZeroMem (ScsiCmd, Length);
2047
2048 ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);
2049 ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);
2050
2051 //
2052 // Set the READ/WRITE flags according to the IO type of this request.
2053 //
2054 switch (Packet->DataDirection) {
2055 case DataIn:
2056 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);
2057 ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);
2058 break;
2059
2060 case DataOut:
2061 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);
2062 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
2063 break;
2064
2065 case DataBi:
2066 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);
2067 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
2068
2069 //
2070 // Fill the bidirectional expected read data length AHS.
2071 //
2072 BiExpReadDataLenAHS = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;
2073 Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);
2074
2075 BiExpReadDataLenAHS->Length = NTOHS (5);
2076 BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;
2077 BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);
2078
2079 break;
2080 }
2081
2082 ScsiCmd->TotalAHSLength = AHSLength;
2083 CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));
2084 ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);
2085 ScsiCmd->CmdSN = NTOHL (Tcb->CmdSN);
2086 ScsiCmd->ExpStatSN = NTOHL (Tcb->Conn->ExpStatSN);
2087
2088 CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));
2089
2090 if (Packet->CdbLength > 16) {
2091 Header->Length = NTOHS ((UINT16) (Packet->CdbLength - 15));
2092 Header->Type = ISCSI_AHS_TYPE_EXT_CDB;
2093
2094 CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);
2095 }
2096
2097 Pdu = PduHeader;
2098 Session = Tcb->Conn->Session;
2099 ImmediateDataLen = 0;
2100
2101 if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {
2102 //
2103 // Send immediate data in this SCSI Command PDU. The length of the immeidate
2104 // data is the minimum of FirstBurstLength, the data length to be xfered, and
2105 // the MaxRecvdataSegmentLength on this connection.
2106 //
2107 ImmediateDataLen = MIN (Session->FirstBurstLength, Packet->OutTransferLength);
2108 ImmediateDataLen = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);
2109
2110 //
2111 // Update the data segment length in the PDU header.
2112 //
2113 ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);
2114
2115 //
2116 // Create the data segment.
2117 //
2118 DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);
2119 if (DataSeg == NULL) {
2120 NetbufFree (PduHeader);
2121 Pdu = NULL;
2122 goto ON_EXIT;
2123 }
2124
2125 NbufList = AllocatePool (sizeof (LIST_ENTRY));
2126 if (NbufList == NULL) {
2127 NetbufFree (PduHeader);
2128 NetbufFree (DataSeg);
2129
2130 Pdu = NULL;
2131 goto ON_EXIT;
2132 }
2133
2134 InitializeListHead (NbufList);
2135 InsertTailList (NbufList, &PduHeader->List);
2136 InsertTailList (NbufList, &DataSeg->List);
2137
2138 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
2139 if (Pdu == NULL) {
2140 IScsiFreeNbufList (NbufList);
2141 }
2142 }
2143
2144 if (Session->InitialR2T ||
2145 (ImmediateDataLen == Session->FirstBurstLength) ||
2146 (ImmediateDataLen == Packet->OutTransferLength)
2147 ) {
2148 //
2149 // Unsolicited data out sequence is not allowed,
2150 // or FirstBustLength data is already sent out by immediate data,
2151 // or all the OUT data accompany this SCSI packet are sent as
2152 // immediate data. The final flag should be set on this SCSI Command
2153 // PDU.
2154 //
2155 ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);
2156 }
2157
2158 ON_EXIT:
2159
2160 return Pdu;
2161 }
2162
2163
2164 /**
2165 Create a new iSCSI SCSI Data Out PDU.
2166
2167 @param[in] Data The data to put into the Data Out PDU.
2168 @param[in] Len Length of the data.
2169 @param[in] DataSN The DataSN of the Data Out PDU.
2170 @param[in] Tcb The task control block of this Data Out PDU.
2171 @param[in] Lun The LUN.
2172
2173 @return The net buffer wrapping the Data Out PDU.
2174 @retval NULL Other errors as indicated.
2175
2176 **/
2177 NET_BUF *
2178 IScsiNewDataOutPdu (
2179 IN UINT8 *Data,
2180 IN UINT32 Len,
2181 IN UINT32 DataSN,
2182 IN ISCSI_TCB *Tcb,
2183 IN UINT64 Lun
2184 )
2185 {
2186 LIST_ENTRY *NbufList;
2187 NET_BUF *PduHdr;
2188 NET_BUF *DataSeg;
2189 NET_BUF *Pdu;
2190 ISCSI_SCSI_DATA_OUT *DataOutHdr;
2191 ISCSI_XFER_CONTEXT *XferContext;
2192
2193 NbufList = AllocatePool (sizeof (LIST_ENTRY));
2194 if (NbufList == NULL) {
2195 return NULL;
2196 }
2197
2198 InitializeListHead (NbufList);
2199
2200 //
2201 // Allocate memory for the BHS.
2202 //
2203 PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));
2204 if (PduHdr == NULL) {
2205 FreePool (NbufList);
2206 return NULL;
2207 }
2208 //
2209 // Insert the BHS into the buffer list.
2210 //
2211 InsertTailList (NbufList, &PduHdr->List);
2212
2213 DataOutHdr = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);
2214 ASSERT (DataOutHdr != NULL);
2215 XferContext = &Tcb->XferContext;
2216
2217 ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));
2218
2219 //
2220 // Set the flags and fields of the Data Out PDU BHS.
2221 //
2222 ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);
2223 ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);
2224
2225 DataOutHdr->InitiatorTaskTag = HTONL (Tcb->InitiatorTaskTag);
2226 DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);
2227 DataOutHdr->ExpStatSN = HTONL (Tcb->Conn->ExpStatSN);
2228 DataOutHdr->DataSN = HTONL (DataSN);
2229 DataOutHdr->BufferOffset = HTONL (XferContext->Offset);
2230
2231 if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {
2232 CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));
2233 }
2234 //
2235 // Build the data segment for this Data Out PDU.
2236 //
2237 DataSeg = IScsiNewDataSegment (Data, Len, FALSE);
2238 if (DataSeg == NULL) {
2239 IScsiFreeNbufList (NbufList);
2240 return NULL;
2241 }
2242 //
2243 // Put the data segment into the buffer list and combine it with the BHS
2244 // into a full Data Out PDU.
2245 //
2246 InsertTailList (NbufList, &DataSeg->List);
2247 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
2248 if (Pdu == NULL) {
2249 IScsiFreeNbufList (NbufList);
2250 }
2251
2252 return Pdu;
2253 }
2254
2255
2256 /**
2257 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
2258
2259 @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
2260 @param[in] Tcb The task control block of the data to send out.
2261 @param[in] Lun The LUN the data will be sent to.
2262
2263 @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.
2264 @retval NULL Other errors as indicated.
2265
2266 **/
2267 LIST_ENTRY *
2268 IScsiGenerateDataOutPduSequence (
2269 IN UINT8 *Data,
2270 IN ISCSI_TCB *Tcb,
2271 IN UINT64 Lun
2272 )
2273 {
2274 LIST_ENTRY *PduList;
2275 UINT32 DataSN;
2276 UINT32 DataLen;
2277 NET_BUF *DataOutPdu;
2278 ISCSI_CONNECTION *Conn;
2279 ISCSI_XFER_CONTEXT *XferContext;
2280 UINT8 *DataOutPacket;
2281
2282 PduList = AllocatePool (sizeof (LIST_ENTRY));
2283 if (PduList == NULL) {
2284 return NULL;
2285 }
2286
2287 InitializeListHead (PduList);
2288
2289 DataSN = 0;
2290 Conn = Tcb->Conn;
2291 DataOutPdu = NULL;
2292 XferContext = &Tcb->XferContext;
2293
2294 while (XferContext->DesiredLength > 0) {
2295 //
2296 // Determine the length of data this Data Out PDU can carry.
2297 //
2298 DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);
2299
2300 //
2301 // Create a Data Out PDU.
2302 //
2303 DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);
2304 if (DataOutPdu == NULL) {
2305 IScsiFreeNbufList (PduList);
2306 PduList = NULL;
2307
2308 goto ON_EXIT;
2309 }
2310
2311 InsertTailList (PduList, &DataOutPdu->List);
2312
2313 //
2314 // Update the context and DataSN.
2315 //
2316 Data += DataLen;
2317 XferContext->Offset += DataLen;
2318 XferContext->DesiredLength -= DataLen;
2319 DataSN++;
2320 }
2321 //
2322 // Set the F bit for the last data out PDU in this sequence.
2323 //
2324 DataOutPacket = NetbufGetByte (DataOutPdu, 0, NULL);
2325 if (DataOutPacket == NULL) {
2326 IScsiFreeNbufList (PduList);
2327 PduList = NULL;
2328 goto ON_EXIT;
2329 }
2330
2331 ISCSI_SET_FLAG (DataOutPacket, ISCSI_BHS_FLAG_FINAL);
2332
2333 ON_EXIT:
2334
2335 return PduList;
2336 }
2337
2338 /**
2339 Send the Data in a sequence of Data Out PDUs one by one.
2340
2341 @param[in] Data The data to carry by Data Out PDUs.
2342 @param[in] Lun The LUN the data will be sent to.
2343 @param[in] Tcb The task control block.
2344
2345 @retval EFI_SUCCES The data is sent out to the LUN.
2346 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2347 @retval Others Other errors as indicated.
2348
2349 **/
2350 EFI_STATUS
2351 IScsiSendDataOutPduSequence (
2352 IN UINT8 *Data,
2353 IN UINT64 Lun,
2354 IN ISCSI_TCB *Tcb
2355 )
2356 {
2357 LIST_ENTRY *DataOutPduList;
2358 LIST_ENTRY *Entry;
2359 NET_BUF *Pdu;
2360 EFI_STATUS Status;
2361
2362 //
2363 // Generate the Data Out PDU sequence.
2364 //
2365 DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);
2366 if (DataOutPduList == NULL) {
2367 return EFI_OUT_OF_RESOURCES;
2368 }
2369
2370 Status = EFI_SUCCESS;
2371
2372 //
2373 // Send the Data Out PDU's one by one.
2374 //
2375 NET_LIST_FOR_EACH (Entry, DataOutPduList) {
2376 Pdu = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
2377
2378 Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu);
2379
2380 if (EFI_ERROR (Status)) {
2381 break;
2382 }
2383 }
2384
2385 IScsiFreeNbufList (DataOutPduList);
2386
2387 return Status;
2388 }
2389
2390
2391 /**
2392 Process the received iSCSI SCSI Data In PDU.
2393
2394 @param[in] Pdu The Data In PDU received.
2395 @param[in] Tcb The task control block.
2396 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2397
2398 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
2399 actions are taken.
2400 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2401 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2402 @retval Others Other errors as indicated.
2403
2404 **/
2405 EFI_STATUS
2406 IScsiOnDataInRcvd (
2407 IN NET_BUF *Pdu,
2408 IN ISCSI_TCB *Tcb,
2409 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2410 )
2411 {
2412 ISCSI_SCSI_DATA_IN *DataInHdr;
2413 EFI_STATUS Status;
2414
2415 DataInHdr = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);
2416 if (DataInHdr == NULL) {
2417 return EFI_PROTOCOL_ERROR;
2418 }
2419
2420 DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);
2421 DataInHdr->ExpCmdSN = NTOHL (DataInHdr->ExpCmdSN);
2422 DataInHdr->MaxCmdSN = NTOHL (DataInHdr->MaxCmdSN);
2423 DataInHdr->DataSN = NTOHL (DataInHdr->DataSN);
2424
2425 //
2426 // Check the DataSN.
2427 //
2428 Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);
2429 if (EFI_ERROR (Status)) {
2430 return Status;
2431 }
2432
2433 if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
2434 return EFI_PROTOCOL_ERROR;
2435 }
2436 //
2437 // Update the command related sequence numbers.
2438 //
2439 IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);
2440
2441 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {
2442 if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {
2443 //
2444 // The S bit is on but the F bit is off.
2445 //
2446 return EFI_PROTOCOL_ERROR;
2447 }
2448
2449 Tcb->StatusXferd = TRUE;
2450
2451 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {
2452 //
2453 // Underflow and Overflow are mutual flags.
2454 //
2455 return EFI_PROTOCOL_ERROR;
2456 }
2457 //
2458 // S bit is on, the StatSN is valid.
2459 //
2460 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));
2461 if (EFI_ERROR (Status)) {
2462 return Status;
2463 }
2464
2465 Packet->HostAdapterStatus = 0;
2466 Packet->TargetStatus = DataInHdr->Status;
2467
2468 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
2469 Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);
2470 Status = EFI_BAD_BUFFER_SIZE;
2471 }
2472
2473 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
2474 Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);
2475 }
2476 }
2477
2478 return Status;
2479 }
2480
2481
2482 /**
2483 Process the received iSCSI R2T PDU.
2484
2485 @param[in] Pdu The R2T PDU received.
2486 @param[in] Tcb The task control block.
2487 @param[in] Lun The Lun.
2488 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2489
2490 @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.
2491 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2492 @retval Others Other errors as indicated.
2493
2494 **/
2495 EFI_STATUS
2496 IScsiOnR2TRcvd (
2497 IN NET_BUF *Pdu,
2498 IN ISCSI_TCB *Tcb,
2499 IN UINT64 Lun,
2500 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2501 )
2502 {
2503 ISCSI_READY_TO_TRANSFER *R2THdr;
2504 EFI_STATUS Status;
2505 ISCSI_XFER_CONTEXT *XferContext;
2506 UINT8 *Data;
2507
2508 R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);
2509 if (R2THdr == NULL) {
2510 return EFI_PROTOCOL_ERROR;
2511 }
2512
2513 R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);
2514 R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);
2515 R2THdr->StatSN = NTOHL (R2THdr->StatSN);
2516 R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum);
2517 R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);
2518 R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);
2519
2520 if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {
2521 return EFI_PROTOCOL_ERROR;;
2522 }
2523 //
2524 // Check the sequence number.
2525 //
2526 Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);
2527 if (EFI_ERROR (Status)) {
2528 return Status;
2529 }
2530
2531 XferContext = &Tcb->XferContext;
2532 XferContext->TargetTransferTag = R2THdr->TargetTransferTag;
2533 XferContext->Offset = R2THdr->BufferOffset;
2534 XferContext->DesiredLength = R2THdr->DesiredDataTransferLength;
2535
2536 if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||
2537 (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)
2538 ) {
2539 return EFI_PROTOCOL_ERROR;
2540 }
2541 //
2542 // Send the data solicited by this R2T.
2543 //
2544 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
2545 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
2546
2547 return Status;
2548 }
2549
2550
2551 /**
2552 Process the received iSCSI SCSI Response PDU.
2553
2554 @param[in] Pdu The Response PDU received.
2555 @param[in] Tcb The task control block.
2556 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2557
2558 @retval EFI_SUCCES The Response PDU is processed.
2559 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2560 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2561 @retval Others Other errors as indicated.
2562
2563 **/
2564 EFI_STATUS
2565 IScsiOnScsiRspRcvd (
2566 IN NET_BUF *Pdu,
2567 IN ISCSI_TCB *Tcb,
2568 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2569 )
2570 {
2571 SCSI_RESPONSE *ScsiRspHdr;
2572 ISCSI_SENSE_DATA *SenseData;
2573 EFI_STATUS Status;
2574 UINT32 DataSegLen;
2575
2576 ScsiRspHdr = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
2577 if (ScsiRspHdr == NULL) {
2578 return EFI_PROTOCOL_ERROR;
2579 }
2580
2581 ScsiRspHdr->InitiatorTaskTag = NTOHL (ScsiRspHdr->InitiatorTaskTag);
2582 if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
2583 return EFI_PROTOCOL_ERROR;
2584 }
2585
2586 ScsiRspHdr->StatSN = NTOHL (ScsiRspHdr->StatSN);
2587
2588 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);
2589 if (EFI_ERROR (Status)) {
2590 return Status;
2591 }
2592
2593 ScsiRspHdr->MaxCmdSN = NTOHL (ScsiRspHdr->MaxCmdSN);
2594 ScsiRspHdr->ExpCmdSN = NTOHL (ScsiRspHdr->ExpCmdSN);
2595 IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);
2596
2597 Tcb->StatusXferd = TRUE;
2598
2599 Packet->HostAdapterStatus = ScsiRspHdr->Response;
2600 if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {
2601 return EFI_SUCCESS;
2602 }
2603
2604 Packet->TargetStatus = ScsiRspHdr->Status;
2605
2606 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||
2607 ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)
2608 ) {
2609 return EFI_PROTOCOL_ERROR;
2610 }
2611
2612 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {
2613 Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);
2614 Status = EFI_BAD_BUFFER_SIZE;
2615 }
2616
2617 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {
2618 Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);
2619 }
2620
2621 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
2622 if (Packet->DataDirection == DataIn) {
2623 Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
2624 } else {
2625 Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
2626 }
2627
2628 Status = EFI_BAD_BUFFER_SIZE;
2629 }
2630
2631 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
2632 if (Packet->DataDirection == DataIn) {
2633 Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
2634 } else {
2635 Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
2636 }
2637 }
2638
2639 DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);
2640 if (DataSegLen != 0) {
2641 SenseData = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);
2642 if (SenseData == NULL) {
2643 return EFI_PROTOCOL_ERROR;
2644 }
2645
2646 SenseData->Length = NTOHS (SenseData->Length);
2647
2648 Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);
2649 if (Packet->SenseDataLength != 0) {
2650 CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);
2651 }
2652 } else {
2653 Packet->SenseDataLength = 0;
2654 }
2655
2656 return Status;
2657 }
2658
2659
2660 /**
2661 Process the received NOP In PDU.
2662
2663 @param[in] Pdu The NOP In PDU received.
2664 @param[in] Tcb The task control block.
2665
2666 @retval EFI_SUCCES The NOP In PDU is processed and the related sequence
2667 numbers are updated.
2668 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2669
2670 **/
2671 EFI_STATUS
2672 IScsiOnNopInRcvd (
2673 IN NET_BUF *Pdu,
2674 IN ISCSI_TCB *Tcb
2675 )
2676 {
2677 ISCSI_NOP_IN *NopInHdr;
2678 EFI_STATUS Status;
2679
2680 NopInHdr = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);
2681 if (NopInHdr == NULL) {
2682 return EFI_PROTOCOL_ERROR;
2683 }
2684
2685 NopInHdr->StatSN = NTOHL (NopInHdr->StatSN);
2686 NopInHdr->ExpCmdSN = NTOHL (NopInHdr->ExpCmdSN);
2687 NopInHdr->MaxCmdSN = NTOHL (NopInHdr->MaxCmdSN);
2688
2689 if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {
2690 if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {
2691 return EFI_PROTOCOL_ERROR;
2692 }
2693 } else {
2694 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);
2695 if (EFI_ERROR (Status)) {
2696 return Status;
2697 }
2698 }
2699
2700 IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);
2701
2702 return EFI_SUCCESS;
2703 }
2704
2705
2706 /**
2707 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
2708
2709 @param[in] PassThru The EXT SCSI PASS THRU protocol.
2710 @param[in] Target The target ID.
2711 @param[in] Lun The LUN.
2712 @param[in, out] Packet The request packet containing IO request, SCSI command
2713 buffer and buffers to read/write.
2714
2715 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
2716 the Packet.
2717 @retval EFI_DEVICE_ERROR Session state was not as required.
2718 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2719 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
2720 @retval Others Other errors as indicated.
2721
2722 **/
2723 EFI_STATUS
2724 IScsiExecuteScsiCommand (
2725 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,
2726 IN UINT8 *Target,
2727 IN UINT64 Lun,
2728 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2729 )
2730 {
2731 EFI_STATUS Status;
2732 ISCSI_DRIVER_DATA *Private;
2733 ISCSI_SESSION *Session;
2734 EFI_EVENT TimeoutEvent;
2735 ISCSI_CONNECTION *Conn;
2736 ISCSI_TCB *Tcb;
2737 NET_BUF *Pdu;
2738 ISCSI_XFER_CONTEXT *XferContext;
2739 UINT8 *Data;
2740 ISCSI_IN_BUFFER_CONTEXT InBufferContext;
2741 UINT64 Timeout;
2742 UINT8 *PduHdr;
2743
2744 Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
2745 Session = Private->Session;
2746 Status = EFI_SUCCESS;
2747 Tcb = NULL;
2748 TimeoutEvent = NULL;
2749 Timeout = 0;
2750
2751 if (Session->State != SESSION_STATE_LOGGED_IN) {
2752 return EFI_DEVICE_ERROR;
2753 }
2754
2755 Conn = NET_LIST_USER_STRUCT_S (
2756 Session->Conns.ForwardLink,
2757 ISCSI_CONNECTION,
2758 Link,
2759 ISCSI_CONNECTION_SIGNATURE
2760 );
2761
2762 if (Packet->Timeout != 0) {
2763 Timeout = MultU64x32 (Packet->Timeout, 4);
2764 }
2765
2766 Status = IScsiNewTcb (Conn, &Tcb);
2767 if (EFI_ERROR (Status)) {
2768 goto ON_EXIT;
2769 }
2770 //
2771 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2772 //
2773 Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);
2774 if (Pdu == NULL) {
2775 Status = EFI_OUT_OF_RESOURCES;
2776 goto ON_EXIT;
2777 }
2778
2779 XferContext = &Tcb->XferContext;
2780 PduHdr = NetbufGetByte (Pdu, 0, NULL);
2781 if (PduHdr == NULL) {
2782 Status = EFI_PROTOCOL_ERROR;
2783 NetbufFree (Pdu);
2784 goto ON_EXIT;
2785 }
2786 XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);
2787
2788 //
2789 // Transmit the SCSI Command PDU.
2790 //
2791 Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
2792
2793 NetbufFree (Pdu);
2794
2795 if (EFI_ERROR (Status)) {
2796 goto ON_EXIT;
2797 }
2798
2799 if (!Session->InitialR2T &&
2800 (XferContext->Offset < Session->FirstBurstLength) &&
2801 (XferContext->Offset < Packet->OutTransferLength)
2802 ) {
2803 //
2804 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2805 // OUT data, and the limit of FirstBurstLength is not reached.
2806 //
2807 XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;
2808 XferContext->DesiredLength = MIN (
2809 Session->FirstBurstLength,
2810 Packet->OutTransferLength - XferContext->Offset
2811 );
2812
2813 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
2814 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
2815 if (EFI_ERROR (Status)) {
2816 goto ON_EXIT;
2817 }
2818 }
2819
2820 InBufferContext.InData = (UINT8 *) Packet->InDataBuffer;
2821 InBufferContext.InDataLen = Packet->InTransferLength;
2822
2823 while (!Tcb->StatusXferd) {
2824 //
2825 // Start the timeout timer.
2826 //
2827 if (Timeout != 0) {
2828 Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);
2829 if (EFI_ERROR (Status)) {
2830 goto ON_EXIT;
2831 }
2832
2833 TimeoutEvent = Conn->TimeoutEvent;
2834 }
2835
2836 //
2837 // Try to receive PDU from target.
2838 //
2839 Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);
2840 if (EFI_ERROR (Status)) {
2841 goto ON_EXIT;
2842 }
2843
2844 PduHdr = NetbufGetByte (Pdu, 0, NULL);
2845 if (PduHdr == NULL) {
2846 Status = EFI_PROTOCOL_ERROR;
2847 NetbufFree (Pdu);
2848 goto ON_EXIT;
2849 }
2850 switch (ISCSI_GET_OPCODE (PduHdr)) {
2851 case ISCSI_OPCODE_SCSI_DATA_IN:
2852 Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);
2853 break;
2854
2855 case ISCSI_OPCODE_R2T:
2856 Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);
2857 break;
2858
2859 case ISCSI_OPCODE_SCSI_RSP:
2860 Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);
2861 break;
2862
2863 case ISCSI_OPCODE_NOP_IN:
2864 Status = IScsiOnNopInRcvd (Pdu, Tcb);
2865 break;
2866
2867 case ISCSI_OPCODE_VENDOR_T0:
2868 case ISCSI_OPCODE_VENDOR_T1:
2869 case ISCSI_OPCODE_VENDOR_T2:
2870 //
2871 // These messages are vendor specific. Skip them.
2872 //
2873 break;
2874
2875 default:
2876 Status = EFI_PROTOCOL_ERROR;
2877 break;
2878 }
2879
2880 NetbufFree (Pdu);
2881
2882 if (EFI_ERROR (Status)) {
2883 break;
2884 }
2885 }
2886
2887 ON_EXIT:
2888
2889 if (TimeoutEvent != NULL) {
2890 gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
2891 }
2892
2893 if (Tcb != NULL) {
2894 IScsiDelTcb (Tcb);
2895 }
2896
2897 if ((Status != EFI_SUCCESS) && (Status != EFI_NOT_READY)) {
2898 //
2899 // Reinstate the session.
2900 //
2901 if (EFI_ERROR (IScsiSessionReinstatement (Session))) {
2902 Status = EFI_DEVICE_ERROR;
2903 }
2904 }
2905
2906 return Status;
2907 }
2908
2909
2910 /**
2911 Reinstate the session on some error.
2912
2913 @param[in] Session The iSCSI session
2914
2915 @retval EFI_SUCCESS The session is reinstated from some error.
2916 @retval Other Reinstatement failed.
2917
2918 **/
2919 EFI_STATUS
2920 IScsiSessionReinstatement (
2921 IN ISCSI_SESSION *Session
2922 )
2923 {
2924 EFI_STATUS Status;
2925
2926 ASSERT (Session->State == SESSION_STATE_LOGGED_IN);
2927
2928 //
2929 // Abort the session and re-init it.
2930 //
2931 IScsiSessionAbort (Session);
2932 IScsiSessionInit (Session, TRUE);
2933
2934 //
2935 // Login again.
2936 //
2937 Status = IScsiSessionLogin (Session);
2938
2939 return Status;
2940 }
2941
2942
2943 /**
2944 Initialize some session parameters before login.
2945
2946 @param[in, out] Session The iSCSI session.
2947 @param[in] Recovery Whether the request is from a fresh new start or recovery.
2948
2949 **/
2950 VOID
2951 IScsiSessionInit (
2952 IN OUT ISCSI_SESSION *Session,
2953 IN BOOLEAN Recovery
2954 )
2955 {
2956 if (!Recovery) {
2957 Session->Signature = ISCSI_SESSION_SIGNATURE;
2958 Session->State = SESSION_STATE_FREE;
2959
2960 InitializeListHead (&Session->Conns);
2961 InitializeListHead (&Session->TcbList);
2962 }
2963
2964 Session->Tsih = 0;
2965
2966 Session->CmdSN = 1;
2967 Session->InitiatorTaskTag = 1;
2968 Session->NextCid = 1;
2969
2970 Session->TargetPortalGroupTag = 0;
2971 Session->MaxConnections = ISCSI_MAX_CONNS_PER_SESSION;
2972 Session->InitialR2T = FALSE;
2973 Session->ImmediateData = TRUE;
2974 Session->MaxBurstLength = 262144;
2975 Session->FirstBurstLength = MAX_RECV_DATA_SEG_LEN_IN_FFP;
2976 Session->DefaultTime2Wait = 2;
2977 Session->DefaultTime2Retain = 20;
2978 Session->MaxOutstandingR2T = DEFAULT_MAX_OUTSTANDING_R2T;
2979 Session->DataPDUInOrder = TRUE;
2980 Session->DataSequenceInOrder = TRUE;
2981 Session->ErrorRecoveryLevel = 0;
2982 }
2983
2984
2985 /**
2986 Abort the iSCSI session. That is, reset all the connection(s), and free the
2987 resources.
2988
2989 @param[in, out] Session The iSCSI session.
2990
2991 **/
2992 VOID
2993 IScsiSessionAbort (
2994 IN OUT ISCSI_SESSION *Session
2995 )
2996 {
2997 ISCSI_CONNECTION *Conn;
2998 EFI_GUID *ProtocolGuid;
2999
3000 if (Session->State != SESSION_STATE_LOGGED_IN) {
3001 return ;
3002 }
3003
3004 ASSERT (!IsListEmpty (&Session->Conns));
3005
3006 while (!IsListEmpty (&Session->Conns)) {
3007 Conn = NET_LIST_USER_STRUCT_S (
3008 Session->Conns.ForwardLink,
3009 ISCSI_CONNECTION,
3010 Link,
3011 ISCSI_CONNECTION_SIGNATURE
3012 );
3013 if (!Conn->Ipv6Flag) {
3014 ProtocolGuid = &gEfiTcp4ProtocolGuid;
3015 } else {
3016 ProtocolGuid = &gEfiTcp6ProtocolGuid;
3017 }
3018
3019 gBS->CloseProtocol (
3020 Conn->TcpIo.Handle,
3021 ProtocolGuid,
3022 Session->Private->Image,
3023 Session->Private->ExtScsiPassThruHandle
3024 );
3025
3026 IScsiConnReset (Conn);
3027
3028 IScsiDetatchConnection (Conn);
3029 IScsiDestroyConnection (Conn);
3030 }
3031
3032 Session->State = SESSION_STATE_FAILED;
3033
3034 return ;
3035 }