2 The implementation of iSCSI protocol based on RFC3720.
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
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.
15 #include "IScsiImpl.h"
17 UINT32 mDataSegPad
= 0;
20 Attach the iSCSI connection to the iSCSI session.
22 @param[in, out] Session The iSCSI session.
23 @param[in, out] Conn The iSCSI connection.
27 IScsiAttatchConnection (
28 IN OUT ISCSI_SESSION
*Session
,
29 IN OUT ISCSI_CONNECTION
*Conn
32 InsertTailList (&Session
->Conns
, &Conn
->Link
);
33 Conn
->Session
= Session
;
38 Detach the iSCSI connection from the session it belongs to.
40 @param[in, out] Conn The iSCSI connection.
44 IScsiDetatchConnection (
45 IN OUT ISCSI_CONNECTION
*Conn
48 RemoveEntryList (&Conn
->Link
);
49 Conn
->Session
->NumConns
--;
55 Check the sequence number according to RFC3720.
57 @param[in, out] ExpSN The currently expected sequence number.
58 @param[in] NewSN The sequence number to check.
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.
71 if (!ISCSI_SEQ_EQ (NewSN
, *ExpSN
)) {
72 if (ISCSI_SEQ_LT (NewSN
, *ExpSN
)) {
78 return EFI_PROTOCOL_ERROR
;
91 Update the sequence numbers for the iSCSI command.
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.
100 IN OUT ISCSI_SESSION
*Session
,
105 if (ISCSI_SEQ_LT (MaxCmdSN
, ExpCmdSN
- 1)) {
109 if (ISCSI_SEQ_GT (MaxCmdSN
, Session
->MaxCmdSN
)) {
110 Session
->MaxCmdSN
= MaxCmdSN
;
113 if (ISCSI_SEQ_GT (ExpCmdSN
, Session
->ExpCmdSN
)) {
114 Session
->ExpCmdSN
= ExpCmdSN
;
120 This function does the iSCSI connection login.
122 @param[in, out] Conn The iSCSI connection to login.
123 @param Timeout The timeout value in millisecond.
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.
132 IN OUT ISCSI_CONNECTION
*Conn
,
139 // Start the timer, and wait Timeout seconds to establish the TCP connection.
141 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, Timeout
* TICKS_PER_MS
);
142 if (EFI_ERROR (Status
)) {
147 // Try to establish the tcp connection.
149 Status
= TcpIoConnect (&Conn
->TcpIo
, Conn
->TimeoutEvent
);
150 gBS
->SetTimer (Conn
->TimeoutEvent
, TimerCancel
, 0);
152 if (EFI_ERROR (Status
)) {
156 Conn
->State
= CONN_STATE_IN_LOGIN
;
159 // Connection is established, start the iSCSI Login.
162 Status
= IScsiSendLoginReq (Conn
);
163 if (EFI_ERROR (Status
)) {
167 Status
= IScsiReceiveLoginRsp (Conn
);
168 if (EFI_ERROR (Status
)) {
171 } while (Conn
->CurrentStage
!= ISCSI_FULL_FEATURE_PHASE
);
178 Reset the iSCSI connection.
180 @param[in, out] Conn The iSCSI connection to reset.
185 IN OUT ISCSI_CONNECTION
*Conn
188 TcpIoReset (&Conn
->TcpIo
);
193 Create a TCP connection for the iSCSI session.
195 @param[in] Session Points to the iSCSI session.
197 @return The newly created iSCSI connection.
201 IScsiCreateConnection (
202 IN ISCSI_SESSION
*Session
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
;
213 Private
= Session
->Private
;
214 NvData
= &Session
->ConfigData
->SessionConfigData
;
216 Conn
= AllocateZeroPool (sizeof (ISCSI_CONNECTION
));
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
;
227 Conn
->PartialReqSent
= FALSE
;
228 Conn
->PartialRspRcvd
= FALSE
;
229 Conn
->ParamNegotiated
= FALSE
;
230 Conn
->Cid
= Session
->NextCid
++;
231 Conn
->Ipv6Flag
= mPrivate
->Ipv6Flag
;
233 Status
= gBS
->CreateEvent (
240 if (EFI_ERROR (Status
)) {
245 NetbufQueInit (&Conn
->RspQue
);
248 // Set the default connection-only parameters.
250 Conn
->MaxRecvDataSegmentLength
= DEFAULT_MAX_RECV_DATA_SEG_LEN
;
251 Conn
->HeaderDigest
= IScsiDigestNone
;
252 Conn
->DataDigest
= IScsiDigestNone
;
254 if (!Conn
->Ipv6Flag
) {
255 Tcp4IoConfig
= &TcpIoConfig
.Tcp4IoConfigData
;
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
));
262 Tcp4IoConfig
->RemotePort
= NvData
->TargetPort
;
263 Tcp4IoConfig
->ActiveFlag
= TRUE
;
266 Tcp6IoConfig
= &TcpIoConfig
.Tcp6IoConfigData
;
268 CopyMem (&Tcp6IoConfig
->RemoteIp
, &NvData
->TargetIp
, sizeof (EFI_IPv6_ADDRESS
));
269 Tcp6IoConfig
->RemotePort
= NvData
->TargetPort
;
270 Tcp6IoConfig
->ActiveFlag
= TRUE
;
274 // Create the TCP IO for this connection.
276 Status
= TcpIoCreateSocket (
279 (UINT8
) (!Conn
->Ipv6Flag
? TCP_VERSION_4
: TCP_VERSION_6
),
283 if (EFI_ERROR (Status
)) {
284 gBS
->CloseEvent (Conn
->TimeoutEvent
);
294 Destroy an iSCSI connection.
296 @param[in] Conn The connection to destroy.
300 IScsiDestroyConnection (
301 IN ISCSI_CONNECTION
*Conn
304 TcpIoDestroySocket (&Conn
->TcpIo
);
306 NetbufQueFlush (&Conn
->RspQue
);
307 gBS
->CloseEvent (Conn
->TimeoutEvent
);
313 Login the iSCSI session.
315 @param[in] Session The iSCSI session.
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.
325 IN ISCSI_SESSION
*Session
329 ISCSI_CONNECTION
*Conn
;
331 EFI_GUID
*ProtocolGuid
;
333 BOOLEAN MediaPresent
;
336 // Check media status before session login.
339 NetLibDetectMedia (Session
->Private
->Controller
, &MediaPresent
);
345 // Set session identifier
347 CopyMem (Session
->Isid
, Session
->ConfigData
->SessionConfigData
.IsId
, 6);
353 // Create a connection for the session.
355 Conn
= IScsiCreateConnection (Session
);
357 return EFI_OUT_OF_RESOURCES
;
360 IScsiAttatchConnection (Session
, Conn
);
363 // Login througth the newly created connection.
365 Status
= IScsiConnLogin (Conn
, Session
->ConfigData
->SessionConfigData
.ConnectTimeout
);
366 if (EFI_ERROR (Status
)) {
367 IScsiConnReset (Conn
);
368 IScsiDetatchConnection (Conn
);
369 IScsiDestroyConnection (Conn
);
372 if (Status
!= EFI_TIMEOUT
) {
377 } while (RetryCount
<= Session
->ConfigData
->SessionConfigData
.ConnectRetryCount
);
379 if (!EFI_ERROR (Status
)) {
380 Session
->State
= SESSION_STATE_LOGGED_IN
;
382 if (!mPrivate
->Ipv6Flag
) {
383 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
385 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
388 Status
= gBS
->OpenProtocol (
392 Session
->Private
->Image
,
393 Session
->Private
->ExtScsiPassThruHandle
,
394 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
397 ASSERT_EFI_ERROR (Status
);
405 Wait for IPsec negotiation, then try to login the iSCSI session again.
407 @param[in] Session The iSCSI session.
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.
415 IScsiSessionReLogin (
416 IN ISCSI_SESSION
*Session
421 EFI_STATUS TimerStatus
;
424 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
425 if (EFI_ERROR (Status
)) {
429 Status
= gBS
->SetTimer (
432 ISCSI_WAIT_IPSEC_TIMEOUT
435 if (EFI_ERROR (Status
)) {
436 gBS
->CloseEvent (Timer
);
442 TimerStatus
= gBS
->CheckEvent (Timer
);
444 if (!EFI_ERROR (TimerStatus
)) {
445 Status
= IScsiSessionLogin (Session
);
448 } while (TimerStatus
== EFI_NOT_READY
);
450 gBS
->CloseEvent (Timer
);
456 Build and send the iSCSI login request to the iSCSI target according to
457 the current login stage.
459 @param[in] Conn The connection in the iSCSI login phase.
461 @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
463 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
464 @retval EFI_DEVICE_ERROR Some kind of device error occurred.
469 IN ISCSI_CONNECTION
*Conn
476 // Build the Login Request PDU.
478 Pdu
= IScsiPrepareLoginReq (Conn
);
480 return EFI_DEVICE_ERROR
;
483 // Send it to the iSCSI target.
485 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
494 Receive and process the iSCSI login response.
496 @param[in] Conn The connection in the iSCSI login phase.
498 @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
499 @retval Others Other errors as indicated.
503 IScsiReceiveLoginRsp (
504 IN ISCSI_CONNECTION
*Conn
511 // Receive the iSCSI login response.
513 Status
= IScsiReceivePdu (Conn
, &Pdu
, NULL
, FALSE
, FALSE
, NULL
);
514 if (EFI_ERROR (Status
)) {
517 ASSERT (Pdu
!= NULL
);
520 // A Login Response is received; process it.
522 Status
= IScsiProcessLoginRsp (Conn
, Pdu
);
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
535 @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
537 @param[in] Key The key name string.
538 @param[in] Value The value string.
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
544 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
547 IScsiAddKeyValuePair (
557 ISCSI_LOGIN_REQUEST
*LoginReq
;
560 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufGetByte (Pdu
, 0, NULL
);
561 if (LoginReq
== NULL
) {
562 return EFI_PROTOCOL_ERROR
;
564 DataSegLen
= NTOH24 (LoginReq
->DataSegmentLength
);
566 KeyLen
= (UINT32
) AsciiStrLen (Key
);
567 ValueLen
= (UINT32
) AsciiStrLen (Value
);
570 // 1 byte for the key value separator '=' and 1 byte for the null
571 // delimiter after the value.
573 TotalLen
= KeyLen
+ 1 + ValueLen
+ 1;
576 // Allocate the space for the key-value pair.
578 Data
= (CHAR8
*) NetbufAllocSpace (Pdu
, TotalLen
, NET_BUF_TAIL
);
580 return EFI_OUT_OF_RESOURCES
;
585 CopyMem (Data
, Key
, KeyLen
);
594 CopyMem (Data
, Value
, ValueLen
);
600 // Update the DataSegmentLength
602 ISCSI_SET_DATASEG_LEN (LoginReq
, DataSegLen
+ TotalLen
);
609 Prepare the iSCSI login request to be sent according to the current login status.
611 @param[in, out] Conn The connection in the iSCSI login phase.
613 @return The pointer to the net buffer containing the iSCSI login request built.
614 @retval NULL Other errors as indicated.
618 IScsiPrepareLoginReq (
619 IN OUT ISCSI_CONNECTION
*Conn
622 ISCSI_SESSION
*Session
;
624 ISCSI_LOGIN_REQUEST
*LoginReq
;
627 Session
= Conn
->Session
;
629 Nbuf
= NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST
) + DEFAULT_MAX_RECV_DATA_SEG_LEN
);
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
));
639 // Init the login request pdu
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
);
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
654 // For subsequent Login Requests it is used to acknowledge the Login Responses
655 // with their increasing StatSN values.
657 LoginReq
->ExpStatSN
= HTONL (Conn
->ExpStatSN
);
658 CopyMem (LoginReq
->Isid
, Session
->Isid
, sizeof (LoginReq
->Isid
));
660 if (Conn
->PartialRspRcvd
) {
662 // A partial response. The initiator must send an empty Login Request.
667 Status
= EFI_SUCCESS
;
669 switch (Conn
->CurrentStage
) {
670 case ISCSI_SECURITY_NEGOTIATION
:
672 // Both none authentication and CHAP authentication share the CHAP path.
675 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
676 Status
= IScsiCHAPToSendReq (Conn
, Nbuf
);
681 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
683 // Only negotiate the paramter once.
685 if (!Conn
->ParamNegotiated
) {
686 IScsiFillOpParams (Conn
, Nbuf
);
689 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
694 // An error occurs...
696 Status
= EFI_DEVICE_ERROR
;
700 if (EFI_ERROR (Status
)) {
705 // Pad the data segment if needed.
707 IScsiPadSegment (Nbuf
, ISCSI_GET_DATASEG_LEN (LoginReq
));
709 // Check whether we will issue the stage transition signal?
711 Conn
->TransitInitiated
= ISCSI_FLAG_ON (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
719 Process the iSCSI Login Response.
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.
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.
731 IScsiProcessLoginRsp (
732 IN OUT ISCSI_CONNECTION
*Conn
,
737 ISCSI_SESSION
*Session
;
738 ISCSI_LOGIN_RESPONSE
*LoginRsp
;
746 Status
= EFI_SUCCESS
;
747 Session
= Conn
->Session
;
749 LoginRsp
= (ISCSI_LOGIN_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
750 if (LoginRsp
== NULL
) {
751 return EFI_PROTOCOL_ERROR
;
753 if (!ISCSI_CHECK_OPCODE (LoginRsp
, ISCSI_OPCODE_LOGIN_RSP
)) {
755 // It is not a Login Response.
757 return EFI_PROTOCOL_ERROR
;
760 // Get the data segment, if any.
762 DataSegLen
= ISCSI_GET_DATASEG_LEN (LoginRsp
);
763 if (DataSegLen
!= 0) {
764 DataSeg
= NetbufGetByte (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NULL
);
769 // Check the status class in the login response PDU.
771 switch (LoginRsp
->StatusClass
) {
772 case ISCSI_LOGIN_STATUS_SUCCESS
:
774 // Just break here; the response and the data segment will be processed later.
778 case ISCSI_LOGIN_STATUS_REDIRECTION
:
780 // The target may be moved to a different address.
782 if (DataSeg
== NULL
) {
783 return EFI_PROTOCOL_ERROR
;
786 // Process the TargetAddress key-value strings in the data segment to update the
787 // target address info.
789 Status
= IScsiUpdateTargetAddress (Session
, (CHAR8
*) DataSeg
, DataSegLen
);
790 if (EFI_ERROR (Status
)) {
794 // Session will be restarted on this error status because the Target is
795 // redirected by this Login Response.
797 return EFI_MEDIA_CHANGED
;
801 // Initiator Error, Target Error, or any other undefined error code.
803 return EFI_PROTOCOL_ERROR
;
806 // The status is success; extract the wanted fields from the header segment.
808 Transit
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT
);
809 Continue
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE
);
811 CurrentStage
= ISCSI_GET_CURRENT_STAGE (LoginRsp
);
812 NextStage
= ISCSI_GET_NEXT_STAGE (LoginRsp
);
814 LoginRsp
->InitiatorTaskTag
= NTOHL (LoginRsp
->InitiatorTaskTag
);
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
)
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.
831 return EFI_PROTOCOL_ERROR
;
834 LoginRsp
->StatSN
= NTOHL (LoginRsp
->StatSN
);
835 LoginRsp
->ExpCmdSN
= NTOHL (LoginRsp
->ExpCmdSN
);
836 LoginRsp
->MaxCmdSN
= NTOHL (LoginRsp
->MaxCmdSN
);
838 if ((Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
) && (Conn
->AuthStep
== ISCSI_AUTH_INITIAL
)) {
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.
843 if ((Session
->State
== SESSION_STATE_FREE
) && (Session
->CmdSN
!= LoginRsp
->ExpCmdSN
)) {
844 return EFI_PROTOCOL_ERROR
;
848 // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
851 Conn
->ExpStatSN
= LoginRsp
->StatSN
+ 1;
852 Session
->MaxCmdSN
= LoginRsp
->MaxCmdSN
;
853 Session
->ExpCmdSN
= LoginRsp
->ExpCmdSN
;
856 // Check the StatSN of this PDU.
858 Status
= IScsiCheckSN (&Conn
->ExpStatSN
, LoginRsp
->StatSN
);
859 if (!EFI_ERROR (Status
)) {
861 // Update the MaxCmdSN and ExpCmdSN.
863 IScsiUpdateCmdSN (Session
, LoginRsp
->MaxCmdSN
, LoginRsp
->ExpCmdSN
);
869 // Trim off the header segment.
871 NetbufTrim (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NET_BUF_HEAD
);
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.
879 NetbufQueAppend (&Conn
->RspQue
, Pdu
);
881 Conn
->PartialRspRcvd
= Continue
;
884 // It is a partial response; must wait for another or more Request/Response
885 // conversations to get the full response.
890 switch (CurrentStage
) {
891 case ISCSI_SECURITY_NEGOTIATION
:
893 // In security negotiation stage, let CHAP module handle it.
895 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
896 Status
= IScsiCHAPOnRspReceived (Conn
);
900 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
902 // Response received with negotiation response on iSCSI parameters: check them.
904 Status
= IScsiCheckOpParams (Conn
);
905 if (!EFI_ERROR (Status
)) {
906 Conn
->ParamNegotiated
= TRUE
;
913 // Should never get here.
915 Status
= EFI_PROTOCOL_ERROR
;
919 if (Transit
&& (Status
== EFI_SUCCESS
)) {
921 // Do the state transition.
923 Conn
->CurrentStage
= Conn
->NextStage
;
925 if (Conn
->CurrentStage
== ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
) {
926 Conn
->NextStage
= ISCSI_FULL_FEATURE_PHASE
;
929 // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
930 // get the TSIH from the Login Response.
932 Session
->Tsih
= NTOHS (LoginRsp
->Tsih
);
936 // Flush the response(s) received.
938 NetbufQueFlush (&Conn
->RspQue
);
945 Updated the target information according the data received in the iSCSI
946 login response with an target redirection status.
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.
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.
960 IScsiUpdateTargetAddress (
961 IN OUT ISCSI_SESSION
*Session
,
966 LIST_ENTRY
*KeyValueList
;
967 CHAR8
*TargetAddress
;
973 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
974 if (KeyValueList
== NULL
) {
975 return EFI_OUT_OF_RESOURCES
;
978 Status
= EFI_NOT_FOUND
;
981 TargetAddress
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ADDRESS
);
982 if (TargetAddress
== NULL
) {
986 if (!NET_IS_DIGIT (TargetAddress
[0])) {
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
995 IpStr
= TargetAddress
;
997 while ((*TargetAddress
!= 0) && (*TargetAddress
!= ':') && (*TargetAddress
!= ',')) {
999 // NULL, ':', or ',' ends the IPv4 string.
1004 if (*TargetAddress
== ',') {
1006 // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
1007 // as the result of a redirection.
1010 } else if (*TargetAddress
== ':') {
1011 *TargetAddress
= '\0';
1015 Number
= AsciiStrDecimalToUintn (TargetAddress
);
1016 if (Number
> 0xFFFF) {
1019 Session
->ConfigData
->SessionConfigData
.TargetPort
= (UINT16
) Number
;
1023 // The string only contains the IPv4 address. Use the well-known port.
1025 Session
->ConfigData
->SessionConfigData
.TargetPort
= ISCSI_WELL_KNOWN_PORT
;
1028 // Update the target IP address.
1030 if (Session
->ConfigData
->SessionConfigData
.IpMode
< IP_MODE_AUTOCONFIG
) {
1031 IpMode
= Session
->ConfigData
->SessionConfigData
.IpMode
;
1033 IpMode
= Session
->ConfigData
->AutoConfigureMode
;
1036 Status
= IScsiAsciiStrToIp (
1039 &Session
->ConfigData
->SessionConfigData
.TargetIp
1042 if (EFI_ERROR (Status
)) {
1049 IScsiFreeKeyValueList (KeyValueList
);
1056 The callback function to free the net buffer list.
1058 @param[in] Arg The opaque parameter.
1067 ASSERT (Arg
!= NULL
);
1069 NetbufFreeList ((LIST_ENTRY
*) Arg
);
1075 The callback function called in NetBufFree; it does nothing.
1077 @param[in] Arg The opaque parameter.
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.
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.
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.
1111 IN ISCSI_CONNECTION
*Conn
,
1113 IN ISCSI_IN_BUFFER_CONTEXT
*Context
, OPTIONAL
1114 IN BOOLEAN HeaderDigest
,
1115 IN BOOLEAN DataDigest
,
1116 IN EFI_EVENT TimeoutEvent OPTIONAL
1119 LIST_ENTRY
*NbufList
;
1125 UINT32 InDataOffset
;
1126 NET_FRAGMENT Fragment
[2];
1127 UINT32 FragmentCount
;
1129 UINT32 PadAndCRC32
[2];
1131 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
1132 if (NbufList
== NULL
) {
1133 return EFI_OUT_OF_RESOURCES
;
1136 InitializeListHead (NbufList
);
1139 // The header digest will be received together with the PDU header, if exists.
1141 Len
= sizeof (ISCSI_BASIC_HEADER
) + (HeaderDigest
? sizeof (UINT32
) : 0);
1142 PduHdr
= NetbufAlloc (Len
);
1143 if (PduHdr
== NULL
) {
1144 Status
= EFI_OUT_OF_RESOURCES
;
1148 Header
= NetbufAllocSpace (PduHdr
, Len
, NET_BUF_TAIL
);
1149 ASSERT (Header
!= NULL
);
1150 InsertTailList (NbufList
, &PduHdr
->List
);
1153 // First step, receive the BHS of the PDU.
1155 Status
= TcpIoReceive (&Conn
->TcpIo
, PduHdr
, FALSE
, TimeoutEvent
);
1157 if (EFI_ERROR (Status
)) {
1163 // TODO: check the header-digest.
1166 // Trim off the digest.
1168 NetbufTrim (PduHdr
, sizeof (UINT32
), NET_BUF_TAIL
);
1171 Len
= ISCSI_GET_DATASEG_LEN (Header
);
1179 // Get the length of the padding bytes of the data segment.
1181 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1183 switch (ISCSI_GET_OPCODE (Header
)) {
1184 case ISCSI_OPCODE_SCSI_DATA_IN
:
1186 // To reduce memory copy overhead, try to use the buffer described by Context
1187 // if the PDU is an iSCSI SCSI data.
1189 InDataOffset
= ISCSI_GET_BUFFER_OFFSET (Header
);
1190 if ((Context
== NULL
) || ((InDataOffset
+ Len
) > Context
->InDataLen
)) {
1191 Status
= EFI_PROTOCOL_ERROR
;
1195 Fragment
[0].Len
= Len
;
1196 Fragment
[0].Bulk
= Context
->InData
+ InDataOffset
;
1198 if (DataDigest
|| (PadLen
!= 0)) {
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.
1203 Fragment
[1].Len
= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1204 Fragment
[1].Bulk
= (UINT8
*)PadAndCRC32
+ (4 - PadLen
);
1211 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1212 if (DataSeg
== NULL
) {
1213 Status
= EFI_OUT_OF_RESOURCES
;
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
:
1229 // Allocate buffer to receive the data segment.
1231 Len
+= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1232 DataSeg
= NetbufAlloc (Len
);
1233 if (DataSeg
== NULL
) {
1234 Status
= EFI_OUT_OF_RESOURCES
;
1238 NetbufAllocSpace (DataSeg
, Len
, NET_BUF_TAIL
);
1242 Status
= EFI_PROTOCOL_ERROR
;
1246 InsertTailList (NbufList
, &DataSeg
->List
);
1249 // Receive the data segment with the data digest, if any.
1251 Status
= TcpIoReceive (&Conn
->TcpIo
, DataSeg
, FALSE
, TimeoutEvent
);
1253 if (EFI_ERROR (Status
)) {
1259 // TODO: Check the data digest.
1261 NetbufTrim (DataSeg
, sizeof (UINT32
), NET_BUF_TAIL
);
1266 // Trim off the padding bytes in the data segment.
1268 NetbufTrim (DataSeg
, PadLen
, NET_BUF_TAIL
);
1273 // Form the pdu from a list of pdu segments.
1275 *Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
1277 Status
= EFI_OUT_OF_RESOURCES
;
1282 if (EFI_ERROR (Status
)) {
1284 // Free the Nbufs in this NbufList and the NbufList itself.
1286 IScsiFreeNbufList (NbufList
);
1294 Check and get the result of the parameter negotiation.
1296 @param[in, out] Conn The connection in iSCSI login.
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.
1304 IScsiCheckOpParams (
1305 IN OUT ISCSI_CONNECTION
*Conn
1309 LIST_ENTRY
*KeyValueList
;
1312 ISCSI_SESSION
*Session
;
1316 ASSERT (Conn
->RspQue
.BufNum
!= 0);
1318 Session
= Conn
->Session
;
1320 Len
= Conn
->RspQue
.BufSize
;
1321 Data
= AllocatePool (Len
);
1323 return EFI_OUT_OF_RESOURCES
;
1326 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, (UINT8
*) Data
);
1328 Status
= EFI_PROTOCOL_ERROR
;
1331 // Extract the Key-Value pairs into a list.
1333 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1334 if (KeyValueList
== NULL
) {
1341 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_HEADER_DIGEST
);
1342 if (Value
== NULL
) {
1346 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1347 if (Conn
->HeaderDigest
!= IScsiDigestCRC32
) {
1350 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1351 Conn
->HeaderDigest
= IScsiDigestNone
;
1358 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_DIGEST
);
1359 if (Value
== NULL
) {
1363 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1364 if (Conn
->DataDigest
!= IScsiDigestCRC32
) {
1367 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1368 Conn
->DataDigest
= IScsiDigestNone
;
1373 // ErrorRecoveryLevel: result fuction is Minimum.
1375 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
);
1376 if (Value
== NULL
) {
1380 NumericValue
= IScsiNetNtoi (Value
);
1381 if (NumericValue
> 2) {
1385 Session
->ErrorRecoveryLevel
= (UINT8
) MIN (Session
->ErrorRecoveryLevel
, NumericValue
);
1388 // InitialR2T: result function is OR.
1390 if (!Session
->InitialR2T
) {
1391 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1392 if (Value
== NULL
) {
1396 Session
->InitialR2T
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1400 // ImmediateData: result function is AND.
1402 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_IMMEDIATE_DATA
);
1403 if (Value
== NULL
) {
1407 Session
->ImmediateData
= (BOOLEAN
) (Session
->ImmediateData
&& (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0));
1410 // MaxRecvDataSegmentLength is declarative.
1412 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
);
1413 if (Value
!= NULL
) {
1414 Conn
->MaxRecvDataSegmentLength
= (UINT32
) IScsiNetNtoi (Value
);
1417 // MaxBurstLength: result funtion is Mininum.
1419 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_BURST_LENGTH
);
1420 if (Value
== NULL
) {
1424 NumericValue
= IScsiNetNtoi (Value
);
1425 Session
->MaxBurstLength
= (UINT32
) MIN (Session
->MaxBurstLength
, NumericValue
);
1428 // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
1429 // ImmediateData=No.
1431 if (!(Session
->InitialR2T
&& !Session
->ImmediateData
)) {
1432 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1433 if (Value
== NULL
) {
1437 NumericValue
= IScsiNetNtoi (Value
);
1438 Session
->FirstBurstLength
= (UINT32
) MIN (Session
->FirstBurstLength
, NumericValue
);
1442 // MaxConnections: result function is Minimum.
1444 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_CONNECTIONS
);
1445 if (Value
== NULL
) {
1449 NumericValue
= IScsiNetNtoi (Value
);
1450 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1454 Session
->MaxConnections
= (UINT32
) MIN (Session
->MaxConnections
, NumericValue
);
1457 // DataPDUInOrder: result function is OR.
1459 if (!Session
->DataPDUInOrder
) {
1460 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1461 if (Value
== NULL
) {
1465 Session
->DataPDUInOrder
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1469 // DataSequenceInorder: result function is OR.
1471 if (!Session
->DataSequenceInOrder
) {
1472 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1473 if (Value
== NULL
) {
1477 Session
->DataSequenceInOrder
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1481 // DefaultTime2Wait: result function is Maximum.
1483 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2WAIT
);
1484 if (Value
== NULL
) {
1488 NumericValue
= IScsiNetNtoi (Value
);
1489 if (NumericValue
== 0) {
1490 Session
->DefaultTime2Wait
= 0;
1491 } else if (NumericValue
> 3600) {
1494 Session
->DefaultTime2Wait
= (UINT32
) MAX (Session
->DefaultTime2Wait
, NumericValue
);
1497 // DefaultTime2Retain: result function is Minimum.
1499 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2RETAIN
);
1500 if (Value
== NULL
) {
1504 NumericValue
= IScsiNetNtoi (Value
);
1505 if (NumericValue
== 0) {
1506 Session
->DefaultTime2Retain
= 0;
1507 } else if (NumericValue
> 3600) {
1510 Session
->DefaultTime2Retain
= (UINT32
) MIN (Session
->DefaultTime2Retain
, NumericValue
);
1513 // MaxOutstandingR2T: result function is Minimum.
1515 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_OUTSTANDING_R2T
);
1516 if (Value
== NULL
) {
1520 NumericValue
= IScsiNetNtoi (Value
);
1521 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1525 Session
->MaxOutstandingR2T
= (UINT16
) MIN (Session
->MaxOutstandingR2T
, NumericValue
);
1528 // Remove declarative key-value pairs, if any.
1530 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_SESSION_TYPE
);
1531 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ALIAS
);
1532 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
);
1536 // Remove the key-value that may not needed for result function is OR.
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
);
1543 // Remove irrelevant parameter, if any.
1545 if (Session
->InitialR2T
&& !Session
->ImmediateData
) {
1546 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1549 if (IsListEmpty (KeyValueList
)) {
1551 // Succeed if no more keys in the list.
1553 Status
= EFI_SUCCESS
;
1558 IScsiFreeKeyValueList (KeyValueList
);
1567 Fill the operational parameters.
1569 @param[in] Conn The connection in iSCSI login.
1570 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1575 IN ISCSI_CONNECTION
*Conn
,
1579 ISCSI_SESSION
*Session
;
1582 Session
= Conn
->Session
;
1584 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->HeaderDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1585 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_HEADER_DIGEST
, Value
);
1587 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->DataDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1588 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_DIGEST
, Value
);
1590 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->ErrorRecoveryLevel
);
1591 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
, Value
);
1593 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->InitialR2T
? "Yes" : "No");
1594 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_INITIAL_R2T
, Value
);
1596 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->ImmediateData
? "Yes" : "No");
1597 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_IMMEDIATE_DATA
, Value
);
1599 AsciiSPrint (Value
, sizeof (Value
), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP
);
1600 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
, Value
);
1602 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxBurstLength
);
1603 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_BURST_LENGTH
, Value
);
1605 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->FirstBurstLength
);
1606 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_FIRST_BURST_LENGTH
, Value
);
1608 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxConnections
);
1609 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_CONNECTIONS
, Value
);
1611 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataPDUInOrder
? "Yes" : "No");
1612 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_PDU_IN_ORDER
, Value
);
1614 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataSequenceInOrder
? "Yes" : "No");
1615 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
, Value
);
1617 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Wait
);
1618 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2WAIT
, Value
);
1620 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Retain
);
1621 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2RETAIN
, Value
);
1623 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxOutstandingR2T
);
1624 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_OUTSTANDING_R2T
, Value
);
1629 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
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.
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
1640 IN OUT NET_BUF
*Pdu
,
1647 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1650 Data
= NetbufAllocSpace (Pdu
, PadLen
, NET_BUF_TAIL
);
1652 return EFI_OUT_OF_RESOURCES
;
1655 ZeroMem (Data
, PadLen
);
1663 Build a key-value list from the data segment.
1665 @param[in] Data The data segment containing the key-value pairs.
1666 @param[in] Len Length of the data segment.
1668 @return The key-value list.
1669 @retval NULL Other errors as indicated.
1673 IScsiBuildKeyValueList (
1678 LIST_ENTRY
*ListHead
;
1679 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1681 ListHead
= AllocatePool (sizeof (LIST_ENTRY
));
1682 if (ListHead
== NULL
) {
1686 InitializeListHead (ListHead
);
1689 KeyValuePair
= AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR
));
1690 if (KeyValuePair
== NULL
) {
1694 InitializeListHead (&KeyValuePair
->List
);
1696 KeyValuePair
->Key
= Data
;
1698 while ((Len
> 0) && (*Data
!= '=')) {
1709 FreePool (KeyValuePair
);
1713 KeyValuePair
->Value
= Data
;
1715 InsertTailList (ListHead
, &KeyValuePair
->List
);;
1717 Data
+= AsciiStrLen (KeyValuePair
->Value
) + 1;
1718 Len
-= (UINT32
) AsciiStrLen (KeyValuePair
->Value
) + 1;
1725 IScsiFreeKeyValueList (ListHead
);
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.
1735 @param[in, out] KeyValueList The key-value list.
1736 @param[in] Key The key name to find.
1738 @return The value string.
1739 @retval NULL The key value pair cannot be found.
1743 IScsiGetValueByKeyFromList (
1744 IN OUT LIST_ENTRY
*KeyValueList
,
1749 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1754 NET_LIST_FOR_EACH (Entry
, KeyValueList
) {
1755 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1757 if (AsciiStrCmp (KeyValuePair
->Key
, Key
) == 0) {
1758 Value
= KeyValuePair
->Value
;
1760 RemoveEntryList (&KeyValuePair
->List
);
1761 FreePool (KeyValuePair
);
1771 Free the key-value list.
1773 @param[in] KeyValueList The key-value list.
1777 IScsiFreeKeyValueList (
1778 IN LIST_ENTRY
*KeyValueList
1782 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1784 while (!IsListEmpty (KeyValueList
)) {
1785 Entry
= NetListRemoveHead (KeyValueList
);
1786 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1788 FreePool (KeyValuePair
);
1791 FreePool (KeyValueList
);
1796 Normalize the iSCSI name according to RFC.
1798 @param[in, out] Name The iSCSI name.
1799 @param[in] Len Length of the iSCSI name.
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.
1806 IScsiNormalizeName (
1813 for (Index
= 0; Index
< Len
; Index
++) {
1814 if (NET_IS_UPPER_CASE_CHAR (Name
[Index
])) {
1816 // Convert the upper-case characters to lower-case ones.
1818 Name
[Index
] = (CHAR8
) (Name
[Index
] - 'A' + 'a');
1821 if (!NET_IS_LOWER_CASE_CHAR (Name
[Index
]) &&
1822 !NET_IS_DIGIT (Name
[Index
]) &&
1823 (Name
[Index
] != '-') &&
1824 (Name
[Index
] != '.') &&
1825 (Name
[Index
] != ':')
1828 // ASCII dash, dot, colon lower-case characters and digit characters
1831 return EFI_PROTOCOL_ERROR
;
1835 if ((Len
< 4) || (CompareMem (Name
, "iqn.", 4) != 0)) {
1837 // Only IQN format is accepted now.
1839 return EFI_PROTOCOL_ERROR
;
1847 Create an iSCSI task control block.
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.
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.
1859 IN ISCSI_CONNECTION
*Conn
,
1863 ISCSI_SESSION
*Session
;
1866 ASSERT (Tcb
!= NULL
);
1868 Session
= Conn
->Session
;
1870 if (ISCSI_SEQ_GT (Session
->CmdSN
, Session
->MaxCmdSN
)) {
1871 return EFI_NOT_READY
;
1874 NewTcb
= AllocateZeroPool (sizeof (ISCSI_TCB
));
1875 if (NewTcb
== NULL
) {
1876 return EFI_OUT_OF_RESOURCES
;
1879 InitializeListHead (&NewTcb
->Link
);
1881 NewTcb
->SoFarInOrder
= TRUE
;
1882 NewTcb
->InitiatorTaskTag
= Session
->InitiatorTaskTag
;
1883 NewTcb
->CmdSN
= Session
->CmdSN
;
1884 NewTcb
->Conn
= Conn
;
1886 InsertTailList (&Session
->TcbList
, &NewTcb
->Link
);
1889 // Advance the initiator task tag.
1891 Session
->InitiatorTaskTag
++;
1901 Delete the tcb from the connection and destroy it.
1903 @param[in] Tcb The tcb to delete.
1911 RemoveEntryList (&Tcb
->Link
);
1918 Find the task control block by the initator task tag.
1920 @param[in] TcbList The tcb list.
1921 @param[in] InitiatorTaskTag The initiator task tag.
1923 @return The task control block found.
1924 @retval NULL The task control block cannot be found.
1929 IN LIST_ENTRY
*TcbList
,
1930 IN UINT32 InitiatorTaskTag
1938 NET_LIST_FOR_EACH (Entry
, TcbList
) {
1939 Tcb
= NET_LIST_USER_STRUCT (Entry
, ISCSI_TCB
, Link
);
1941 if (Tcb
->InitiatorTaskTag
== InitiatorTaskTag
) {
1951 Create a data segment, pad it, and calculate the CRC if needed.
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.
1957 @return The net buffer wrapping the data segment.
1961 IScsiNewDataSegment (
1964 IN BOOLEAN DataDigest
1967 NET_FRAGMENT Fragment
[2];
1968 UINT32 FragmentCount
;
1972 Fragment
[0].Len
= Len
;
1973 Fragment
[0].Bulk
= Data
;
1975 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1977 Fragment
[1].Len
= PadLen
;
1978 Fragment
[1].Bulk
= (UINT8
*) &mDataSegPad
;
1985 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1992 Create a iSCSI SCSI command PDU to encapsulate the command issued
1993 by SCSI through the EXT SCSI PASS THRU Protocol.
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.
1999 @return The created iSCSI SCSI command PDU.
2000 @retval NULL Other errors as indicated.
2004 IScsiNewScsiCmdPdu (
2005 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
2010 LIST_ENTRY
*NbufList
;
2014 SCSI_COMMAND
*ScsiCmd
;
2017 ISCSI_ADDITIONAL_HEADER
*Header
;
2018 ISCSI_BI_EXP_READ_DATA_LEN_AHS
*BiExpReadDataLenAHS
;
2019 ISCSI_SESSION
*Session
;
2020 UINT32 ImmediateDataLen
;
2024 if (Packet
->DataDirection
== DataBi
) {
2026 // Bidirectional Read/Write command, the bidirectional expected
2027 // read data length AHS is required.
2029 AHSLength
+= sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS
);
2032 if (Packet
->CdbLength
> 16) {
2034 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2036 AHSLength
= (UINT8
) (AHSLength
+ ISCSI_ROUNDUP (Packet
->CdbLength
- 16) + sizeof (ISCSI_ADDITIONAL_HEADER
));
2039 Length
= sizeof (SCSI_COMMAND
) + AHSLength
;
2040 PduHeader
= NetbufAlloc (Length
);
2041 if (PduHeader
== NULL
) {
2045 ScsiCmd
= (SCSI_COMMAND
*) NetbufAllocSpace (PduHeader
, Length
, NET_BUF_TAIL
);
2046 if (ScsiCmd
== NULL
) {
2047 NetbufFree (PduHeader
);
2050 Header
= (ISCSI_ADDITIONAL_HEADER
*) (ScsiCmd
+ 1);
2052 ZeroMem (ScsiCmd
, Length
);
2054 ISCSI_SET_OPCODE (ScsiCmd
, ISCSI_OPCODE_SCSI_CMD
, 0);
2055 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_TASK_ATTR_SIMPLE
);
2058 // Set the READ/WRITE flags according to the IO type of this request.
2060 switch (Packet
->DataDirection
) {
2062 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
);
2063 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->InTransferLength
);
2067 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_WRITE
);
2068 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2072 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
| SCSI_CMD_PDU_FLAG_WRITE
);
2073 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2076 // Fill the bidirectional expected read data length AHS.
2078 BiExpReadDataLenAHS
= (ISCSI_BI_EXP_READ_DATA_LEN_AHS
*) Header
;
2079 Header
= (ISCSI_ADDITIONAL_HEADER
*) (BiExpReadDataLenAHS
+ 1);
2081 BiExpReadDataLenAHS
->Length
= NTOHS (5);
2082 BiExpReadDataLenAHS
->Type
= ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN
;
2083 BiExpReadDataLenAHS
->ExpReadDataLength
= NTOHL (Packet
->InTransferLength
);
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
);
2094 CopyMem (ScsiCmd
->Cdb
, Packet
->Cdb
, sizeof (ScsiCmd
->Cdb
));
2096 if (Packet
->CdbLength
> 16) {
2097 Header
->Length
= NTOHS ((UINT16
) (Packet
->CdbLength
- 15));
2098 Header
->Type
= ISCSI_AHS_TYPE_EXT_CDB
;
2100 CopyMem (Header
+ 1, (UINT8
*) Packet
->Cdb
+ 16, Packet
->CdbLength
- 16);
2104 Session
= Tcb
->Conn
->Session
;
2105 ImmediateDataLen
= 0;
2107 if (Session
->ImmediateData
&& (Packet
->OutTransferLength
!= 0)) {
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.
2113 ImmediateDataLen
= MIN (Session
->FirstBurstLength
, Packet
->OutTransferLength
);
2114 ImmediateDataLen
= MIN (ImmediateDataLen
, Tcb
->Conn
->MaxRecvDataSegmentLength
);
2117 // Update the data segment length in the PDU header.
2119 ISCSI_SET_DATASEG_LEN (ScsiCmd
, ImmediateDataLen
);
2122 // Create the data segment.
2124 DataSeg
= IScsiNewDataSegment ((UINT8
*) Packet
->OutDataBuffer
, ImmediateDataLen
, FALSE
);
2125 if (DataSeg
== NULL
) {
2126 NetbufFree (PduHeader
);
2131 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2132 if (NbufList
== NULL
) {
2133 NetbufFree (PduHeader
);
2134 NetbufFree (DataSeg
);
2140 InitializeListHead (NbufList
);
2141 InsertTailList (NbufList
, &PduHeader
->List
);
2142 InsertTailList (NbufList
, &DataSeg
->List
);
2144 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2146 IScsiFreeNbufList (NbufList
);
2150 if (Session
->InitialR2T
||
2151 (ImmediateDataLen
== Session
->FirstBurstLength
) ||
2152 (ImmediateDataLen
== Packet
->OutTransferLength
)
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
2161 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_BHS_FLAG_FINAL
);
2171 Create a new iSCSI SCSI Data Out PDU.
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.
2179 @return The net buffer wrapping the Data Out PDU.
2180 @retval NULL Other errors as indicated.
2184 IScsiNewDataOutPdu (
2192 LIST_ENTRY
*NbufList
;
2196 ISCSI_SCSI_DATA_OUT
*DataOutHdr
;
2197 ISCSI_XFER_CONTEXT
*XferContext
;
2199 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2200 if (NbufList
== NULL
) {
2204 InitializeListHead (NbufList
);
2207 // Allocate memory for the BHS.
2209 PduHdr
= NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT
));
2210 if (PduHdr
== NULL
) {
2211 FreePool (NbufList
);
2215 // Insert the BHS into the buffer list.
2217 InsertTailList (NbufList
, &PduHdr
->List
);
2219 DataOutHdr
= (ISCSI_SCSI_DATA_OUT
*) NetbufAllocSpace (PduHdr
, sizeof (ISCSI_SCSI_DATA_OUT
), NET_BUF_TAIL
);
2220 ASSERT (DataOutHdr
!= NULL
);
2221 XferContext
= &Tcb
->XferContext
;
2223 ZeroMem (DataOutHdr
, sizeof (ISCSI_SCSI_DATA_OUT
));
2226 // Set the flags and fields of the Data Out PDU BHS.
2228 ISCSI_SET_OPCODE (DataOutHdr
, ISCSI_OPCODE_SCSI_DATA_OUT
, 0);
2229 ISCSI_SET_DATASEG_LEN (DataOutHdr
, Len
);
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
);
2237 if (XferContext
->TargetTransferTag
!= ISCSI_RESERVED_TAG
) {
2238 CopyMem (&DataOutHdr
->Lun
, &Lun
, sizeof (DataOutHdr
->Lun
));
2241 // Build the data segment for this Data Out PDU.
2243 DataSeg
= IScsiNewDataSegment (Data
, Len
, FALSE
);
2244 if (DataSeg
== NULL
) {
2245 IScsiFreeNbufList (NbufList
);
2249 // Put the data segment into the buffer list and combine it with the BHS
2250 // into a full Data Out PDU.
2252 InsertTailList (NbufList
, &DataSeg
->List
);
2253 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2255 IScsiFreeNbufList (NbufList
);
2263 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
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.
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.
2274 IScsiGenerateDataOutPduSequence (
2280 LIST_ENTRY
*PduList
;
2283 NET_BUF
*DataOutPdu
;
2284 ISCSI_CONNECTION
*Conn
;
2285 ISCSI_XFER_CONTEXT
*XferContext
;
2286 UINT8
*DataOutPacket
;
2288 PduList
= AllocatePool (sizeof (LIST_ENTRY
));
2289 if (PduList
== NULL
) {
2293 InitializeListHead (PduList
);
2298 XferContext
= &Tcb
->XferContext
;
2300 while (XferContext
->DesiredLength
> 0) {
2302 // Determine the length of data this Data Out PDU can carry.
2304 DataLen
= MIN (XferContext
->DesiredLength
, Conn
->MaxRecvDataSegmentLength
);
2307 // Create a Data Out PDU.
2309 DataOutPdu
= IScsiNewDataOutPdu (Data
, DataLen
, DataSN
, Tcb
, Lun
);
2310 if (DataOutPdu
== NULL
) {
2311 IScsiFreeNbufList (PduList
);
2317 InsertTailList (PduList
, &DataOutPdu
->List
);
2320 // Update the context and DataSN.
2323 XferContext
->Offset
+= DataLen
;
2324 XferContext
->DesiredLength
-= DataLen
;
2328 // Set the F bit for the last data out PDU in this sequence.
2330 DataOutPacket
= NetbufGetByte (DataOutPdu
, 0, NULL
);
2331 if (DataOutPacket
== NULL
) {
2332 IScsiFreeNbufList (PduList
);
2337 ISCSI_SET_FLAG (DataOutPacket
, ISCSI_BHS_FLAG_FINAL
);
2345 Send the Data in a sequence of Data Out PDUs one by one.
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.
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.
2357 IScsiSendDataOutPduSequence (
2363 LIST_ENTRY
*DataOutPduList
;
2369 // Generate the Data Out PDU sequence.
2371 DataOutPduList
= IScsiGenerateDataOutPduSequence (Data
, Tcb
, Lun
);
2372 if (DataOutPduList
== NULL
) {
2373 return EFI_OUT_OF_RESOURCES
;
2376 Status
= EFI_SUCCESS
;
2379 // Send the Data Out PDU's one by one.
2381 NET_LIST_FOR_EACH (Entry
, DataOutPduList
) {
2382 Pdu
= NET_LIST_USER_STRUCT (Entry
, NET_BUF
, List
);
2384 Status
= TcpIoTransmit (&Tcb
->Conn
->TcpIo
, Pdu
);
2386 if (EFI_ERROR (Status
)) {
2391 IScsiFreeNbufList (DataOutPduList
);
2398 Process the received iSCSI SCSI Data In PDU.
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.
2404 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
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.
2415 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2418 ISCSI_SCSI_DATA_IN
*DataInHdr
;
2421 DataInHdr
= (ISCSI_SCSI_DATA_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2422 if (DataInHdr
== NULL
) {
2423 return EFI_PROTOCOL_ERROR
;
2426 DataInHdr
->InitiatorTaskTag
= NTOHL (DataInHdr
->InitiatorTaskTag
);
2427 DataInHdr
->ExpCmdSN
= NTOHL (DataInHdr
->ExpCmdSN
);
2428 DataInHdr
->MaxCmdSN
= NTOHL (DataInHdr
->MaxCmdSN
);
2429 DataInHdr
->DataSN
= NTOHL (DataInHdr
->DataSN
);
2432 // Check the DataSN.
2434 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, DataInHdr
->DataSN
);
2435 if (EFI_ERROR (Status
)) {
2439 if (DataInHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2440 return EFI_PROTOCOL_ERROR
;
2443 // Update the command related sequence numbers.
2445 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, DataInHdr
->MaxCmdSN
, DataInHdr
->ExpCmdSN
);
2447 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID
)) {
2448 if (!ISCSI_FLAG_ON (DataInHdr
, ISCSI_BHS_FLAG_FINAL
)) {
2450 // The S bit is on but the F bit is off.
2452 return EFI_PROTOCOL_ERROR
;
2455 Tcb
->StatusXferd
= TRUE
;
2457 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_OVERFLOW
| SCSI_DATA_IN_PDU_FLAG_UNDERFLOW
)) {
2459 // Underflow and Overflow are mutual flags.
2461 return EFI_PROTOCOL_ERROR
;
2464 // S bit is on, the StatSN is valid.
2466 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NTOHL (DataInHdr
->StatSN
));
2467 if (EFI_ERROR (Status
)) {
2471 Packet
->HostAdapterStatus
= 0;
2472 Packet
->TargetStatus
= DataInHdr
->Status
;
2474 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2475 Packet
->InTransferLength
+= NTOHL (DataInHdr
->ResidualCount
);
2476 Status
= EFI_BAD_BUFFER_SIZE
;
2479 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2480 Packet
->InTransferLength
-= NTOHL (DataInHdr
->ResidualCount
);
2489 Process the received iSCSI R2T PDU.
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.
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.
2506 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2509 ISCSI_READY_TO_TRANSFER
*R2THdr
;
2511 ISCSI_XFER_CONTEXT
*XferContext
;
2514 R2THdr
= (ISCSI_READY_TO_TRANSFER
*) NetbufGetByte (Pdu
, 0, NULL
);
2515 if (R2THdr
== NULL
) {
2516 return EFI_PROTOCOL_ERROR
;
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
);
2526 if ((R2THdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) || !ISCSI_SEQ_EQ (R2THdr
->StatSN
, Tcb
->Conn
->ExpStatSN
)) {
2527 return EFI_PROTOCOL_ERROR
;;
2530 // Check the sequence number.
2532 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, R2THdr
->R2TSeqNum
);
2533 if (EFI_ERROR (Status
)) {
2537 XferContext
= &Tcb
->XferContext
;
2538 XferContext
->TargetTransferTag
= R2THdr
->TargetTransferTag
;
2539 XferContext
->Offset
= R2THdr
->BufferOffset
;
2540 XferContext
->DesiredLength
= R2THdr
->DesiredDataTransferLength
;
2542 if (((XferContext
->Offset
+ XferContext
->DesiredLength
) > Packet
->OutTransferLength
) ||
2543 (XferContext
->DesiredLength
> Tcb
->Conn
->Session
->MaxBurstLength
)
2545 return EFI_PROTOCOL_ERROR
;
2548 // Send the data solicited by this R2T.
2550 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2551 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2558 Process the received iSCSI SCSI Response PDU.
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.
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.
2571 IScsiOnScsiRspRcvd (
2574 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2577 SCSI_RESPONSE
*ScsiRspHdr
;
2578 ISCSI_SENSE_DATA
*SenseData
;
2582 ScsiRspHdr
= (SCSI_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
2583 if (ScsiRspHdr
== NULL
) {
2584 return EFI_PROTOCOL_ERROR
;
2587 ScsiRspHdr
->InitiatorTaskTag
= NTOHL (ScsiRspHdr
->InitiatorTaskTag
);
2588 if (ScsiRspHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2589 return EFI_PROTOCOL_ERROR
;
2592 ScsiRspHdr
->StatSN
= NTOHL (ScsiRspHdr
->StatSN
);
2594 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, ScsiRspHdr
->StatSN
);
2595 if (EFI_ERROR (Status
)) {
2599 ScsiRspHdr
->MaxCmdSN
= NTOHL (ScsiRspHdr
->MaxCmdSN
);
2600 ScsiRspHdr
->ExpCmdSN
= NTOHL (ScsiRspHdr
->ExpCmdSN
);
2601 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, ScsiRspHdr
->MaxCmdSN
, ScsiRspHdr
->ExpCmdSN
);
2603 Tcb
->StatusXferd
= TRUE
;
2605 Packet
->HostAdapterStatus
= ScsiRspHdr
->Response
;
2606 if (Packet
->HostAdapterStatus
!= ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET
) {
2610 Packet
->TargetStatus
= ScsiRspHdr
->Status
;
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
)
2615 return EFI_PROTOCOL_ERROR
;
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
;
2623 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
)) {
2624 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2627 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2628 if (Packet
->DataDirection
== DataIn
) {
2629 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2631 Packet
->OutTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2634 Status
= EFI_BAD_BUFFER_SIZE
;
2637 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2638 if (Packet
->DataDirection
== DataIn
) {
2639 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2641 Packet
->OutTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
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
;
2652 SenseData
->Length
= NTOHS (SenseData
->Length
);
2654 Packet
->SenseDataLength
= (UINT8
) MIN (SenseData
->Length
, Packet
->SenseDataLength
);
2655 if (Packet
->SenseDataLength
!= 0) {
2656 CopyMem (Packet
->SenseData
, &SenseData
->Data
[0], Packet
->SenseDataLength
);
2659 Packet
->SenseDataLength
= 0;
2667 Process the received NOP In PDU.
2669 @param[in] Pdu The NOP In PDU received.
2670 @param[in] Tcb The task control block.
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.
2683 ISCSI_NOP_IN
*NopInHdr
;
2686 NopInHdr
= (ISCSI_NOP_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2687 if (NopInHdr
== NULL
) {
2688 return EFI_PROTOCOL_ERROR
;
2691 NopInHdr
->StatSN
= NTOHL (NopInHdr
->StatSN
);
2692 NopInHdr
->ExpCmdSN
= NTOHL (NopInHdr
->ExpCmdSN
);
2693 NopInHdr
->MaxCmdSN
= NTOHL (NopInHdr
->MaxCmdSN
);
2695 if (NopInHdr
->InitiatorTaskTag
== ISCSI_RESERVED_TAG
) {
2696 if (NopInHdr
->StatSN
!= Tcb
->Conn
->ExpStatSN
) {
2697 return EFI_PROTOCOL_ERROR
;
2700 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NopInHdr
->StatSN
);
2701 if (EFI_ERROR (Status
)) {
2706 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, NopInHdr
->MaxCmdSN
, NopInHdr
->ExpCmdSN
);
2713 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
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.
2721 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
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.
2730 IScsiExecuteScsiCommand (
2731 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
,
2734 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2738 ISCSI_DRIVER_DATA
*Private
;
2739 ISCSI_SESSION
*Session
;
2740 EFI_EVENT TimeoutEvent
;
2741 ISCSI_CONNECTION
*Conn
;
2744 ISCSI_XFER_CONTEXT
*XferContext
;
2746 ISCSI_IN_BUFFER_CONTEXT InBufferContext
;
2750 Private
= ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru
);
2751 Session
= Private
->Session
;
2752 Status
= EFI_SUCCESS
;
2754 TimeoutEvent
= NULL
;
2757 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
2758 return EFI_DEVICE_ERROR
;
2761 Conn
= NET_LIST_USER_STRUCT_S (
2762 Session
->Conns
.ForwardLink
,
2765 ISCSI_CONNECTION_SIGNATURE
2768 if (Packet
->Timeout
!= 0) {
2769 Timeout
= MultU64x32 (Packet
->Timeout
, 4);
2772 Status
= IScsiNewTcb (Conn
, &Tcb
);
2773 if (EFI_ERROR (Status
)) {
2777 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2779 Pdu
= IScsiNewScsiCmdPdu (Packet
, Lun
, Tcb
);
2781 Status
= EFI_OUT_OF_RESOURCES
;
2785 XferContext
= &Tcb
->XferContext
;
2786 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2787 if (PduHdr
== NULL
) {
2788 Status
= EFI_PROTOCOL_ERROR
;
2792 XferContext
->Offset
= ISCSI_GET_DATASEG_LEN (PduHdr
);
2795 // Transmit the SCSI Command PDU.
2797 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
2801 if (EFI_ERROR (Status
)) {
2805 if (!Session
->InitialR2T
&&
2806 (XferContext
->Offset
< Session
->FirstBurstLength
) &&
2807 (XferContext
->Offset
< Packet
->OutTransferLength
)
2810 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2811 // OUT data, and the limit of FirstBurstLength is not reached.
2813 XferContext
->TargetTransferTag
= ISCSI_RESERVED_TAG
;
2814 XferContext
->DesiredLength
= MIN (
2815 Session
->FirstBurstLength
,
2816 Packet
->OutTransferLength
- XferContext
->Offset
2819 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2820 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2821 if (EFI_ERROR (Status
)) {
2826 InBufferContext
.InData
= (UINT8
*) Packet
->InDataBuffer
;
2827 InBufferContext
.InDataLen
= Packet
->InTransferLength
;
2829 while (!Tcb
->StatusXferd
) {
2831 // Start the timeout timer.
2834 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, Timeout
);
2835 if (EFI_ERROR (Status
)) {
2839 TimeoutEvent
= Conn
->TimeoutEvent
;
2843 // Try to receive PDU from target.
2845 Status
= IScsiReceivePdu (Conn
, &Pdu
, &InBufferContext
, FALSE
, FALSE
, TimeoutEvent
);
2846 if (EFI_ERROR (Status
)) {
2850 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2851 if (PduHdr
== NULL
) {
2852 Status
= EFI_PROTOCOL_ERROR
;
2856 switch (ISCSI_GET_OPCODE (PduHdr
)) {
2857 case ISCSI_OPCODE_SCSI_DATA_IN
:
2858 Status
= IScsiOnDataInRcvd (Pdu
, Tcb
, Packet
);
2861 case ISCSI_OPCODE_R2T
:
2862 Status
= IScsiOnR2TRcvd (Pdu
, Tcb
, Lun
, Packet
);
2865 case ISCSI_OPCODE_SCSI_RSP
:
2866 Status
= IScsiOnScsiRspRcvd (Pdu
, Tcb
, Packet
);
2869 case ISCSI_OPCODE_NOP_IN
:
2870 Status
= IScsiOnNopInRcvd (Pdu
, Tcb
);
2873 case ISCSI_OPCODE_VENDOR_T0
:
2874 case ISCSI_OPCODE_VENDOR_T1
:
2875 case ISCSI_OPCODE_VENDOR_T2
:
2877 // These messages are vendor specific. Skip them.
2882 Status
= EFI_PROTOCOL_ERROR
;
2888 if (EFI_ERROR (Status
)) {
2895 if (TimeoutEvent
!= NULL
) {
2896 gBS
->SetTimer (TimeoutEvent
, TimerCancel
, 0);
2903 if ((Status
!= EFI_SUCCESS
) && (Status
!= EFI_NOT_READY
)) {
2905 // Reinstate the session.
2907 if (EFI_ERROR (IScsiSessionReinstatement (Session
))) {
2908 Status
= EFI_DEVICE_ERROR
;
2917 Reinstate the session on some error.
2919 @param[in] Session The iSCSI session
2921 @retval EFI_SUCCESS The session is reinstated from some error.
2922 @retval Other Reinstatement failed.
2926 IScsiSessionReinstatement (
2927 IN ISCSI_SESSION
*Session
2932 ASSERT (Session
->State
== SESSION_STATE_LOGGED_IN
);
2935 // Abort the session and re-init it.
2937 IScsiSessionAbort (Session
);
2938 IScsiSessionInit (Session
, TRUE
);
2943 Status
= IScsiSessionLogin (Session
);
2950 Initialize some session parameters before login.
2952 @param[in, out] Session The iSCSI session.
2953 @param[in] Recovery Whether the request is from a fresh new start or recovery.
2958 IN OUT ISCSI_SESSION
*Session
,
2963 Session
->Signature
= ISCSI_SESSION_SIGNATURE
;
2964 Session
->State
= SESSION_STATE_FREE
;
2966 InitializeListHead (&Session
->Conns
);
2967 InitializeListHead (&Session
->TcbList
);
2973 Session
->InitiatorTaskTag
= 1;
2974 Session
->NextCid
= 1;
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;
2992 Abort the iSCSI session. That is, reset all the connection(s), and free the
2995 @param[in, out] Session The iSCSI session.
3000 IN OUT ISCSI_SESSION
*Session
3003 ISCSI_CONNECTION
*Conn
;
3004 EFI_GUID
*ProtocolGuid
;
3006 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
3010 ASSERT (!IsListEmpty (&Session
->Conns
));
3012 while (!IsListEmpty (&Session
->Conns
)) {
3013 Conn
= NET_LIST_USER_STRUCT_S (
3014 Session
->Conns
.ForwardLink
,
3017 ISCSI_CONNECTION_SIGNATURE
3019 if (!Conn
->Ipv6Flag
) {
3020 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
3022 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
3025 gBS
->CloseProtocol (
3028 Session
->Private
->Image
,
3029 Session
->Private
->ExtScsiPassThruHandle
3032 IScsiConnReset (Conn
);
3034 IScsiDetatchConnection (Conn
);
3035 IScsiDestroyConnection (Conn
);
3038 Session
->State
= SESSION_STATE_FAILED
;