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 prameter 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 if (IsListEmpty (KeyValueList
)) {
1545 // Succeed if no more keys in the list.
1547 Status
= EFI_SUCCESS
;
1552 IScsiFreeKeyValueList (KeyValueList
);
1561 Fill the oprational parameters.
1563 @param[in] Conn The connection in iSCSI login.
1564 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1569 IN ISCSI_CONNECTION
*Conn
,
1573 ISCSI_SESSION
*Session
;
1576 Session
= Conn
->Session
;
1578 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->HeaderDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1579 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_HEADER_DIGEST
, Value
);
1581 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->DataDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1582 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_DIGEST
, Value
);
1584 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->ErrorRecoveryLevel
);
1585 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
, Value
);
1587 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->InitialR2T
? "Yes" : "No");
1588 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_INITIAL_R2T
, Value
);
1590 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->ImmediateData
? "Yes" : "No");
1591 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_IMMEDIATE_DATA
, Value
);
1593 AsciiSPrint (Value
, sizeof (Value
), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP
);
1594 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
, Value
);
1596 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxBurstLength
);
1597 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_BURST_LENGTH
, Value
);
1599 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->FirstBurstLength
);
1600 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_FIRST_BURST_LENGTH
, Value
);
1602 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxConnections
);
1603 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_CONNECTIONS
, Value
);
1605 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataPDUInOrder
? "Yes" : "No");
1606 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_PDU_IN_ORDER
, Value
);
1608 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataSequenceInOrder
? "Yes" : "No");
1609 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
, Value
);
1611 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Wait
);
1612 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2WAIT
, Value
);
1614 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Retain
);
1615 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2RETAIN
, Value
);
1617 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxOutstandingR2T
);
1618 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_OUTSTANDING_R2T
, Value
);
1623 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
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.
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
1634 IN OUT NET_BUF
*Pdu
,
1641 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1644 Data
= NetbufAllocSpace (Pdu
, PadLen
, NET_BUF_TAIL
);
1646 return EFI_OUT_OF_RESOURCES
;
1649 ZeroMem (Data
, PadLen
);
1657 Build a key-value list from the data segment.
1659 @param[in] Data The data segment containing the key-value pairs.
1660 @param[in] Len Length of the data segment.
1662 @return The key-value list.
1663 @retval NULL Other errors as indicated.
1667 IScsiBuildKeyValueList (
1672 LIST_ENTRY
*ListHead
;
1673 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1675 ListHead
= AllocatePool (sizeof (LIST_ENTRY
));
1676 if (ListHead
== NULL
) {
1680 InitializeListHead (ListHead
);
1683 KeyValuePair
= AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR
));
1684 if (KeyValuePair
== NULL
) {
1688 InitializeListHead (&KeyValuePair
->List
);
1690 KeyValuePair
->Key
= Data
;
1692 while ((Len
> 0) && (*Data
!= '=')) {
1703 FreePool (KeyValuePair
);
1707 KeyValuePair
->Value
= Data
;
1709 InsertTailList (ListHead
, &KeyValuePair
->List
);;
1711 Data
+= AsciiStrLen (KeyValuePair
->Value
) + 1;
1712 Len
-= (UINT32
) AsciiStrLen (KeyValuePair
->Value
) + 1;
1719 IScsiFreeKeyValueList (ListHead
);
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.
1729 @param[in, out] KeyValueList The key-value list.
1730 @param[in] Key The key name to find.
1732 @return The value string.
1733 @retval NULL The key value pair cannot be found.
1737 IScsiGetValueByKeyFromList (
1738 IN OUT LIST_ENTRY
*KeyValueList
,
1743 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1748 NET_LIST_FOR_EACH (Entry
, KeyValueList
) {
1749 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1751 if (AsciiStrCmp (KeyValuePair
->Key
, Key
) == 0) {
1752 Value
= KeyValuePair
->Value
;
1754 RemoveEntryList (&KeyValuePair
->List
);
1755 FreePool (KeyValuePair
);
1765 Free the key-value list.
1767 @param[in] KeyValueList The key-value list.
1771 IScsiFreeKeyValueList (
1772 IN LIST_ENTRY
*KeyValueList
1776 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1778 while (!IsListEmpty (KeyValueList
)) {
1779 Entry
= NetListRemoveHead (KeyValueList
);
1780 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1782 FreePool (KeyValuePair
);
1785 FreePool (KeyValueList
);
1790 Normalize the iSCSI name according to RFC.
1792 @param[in, out] Name The iSCSI name.
1793 @param[in] Len Length of the iSCSI name.
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.
1800 IScsiNormalizeName (
1807 for (Index
= 0; Index
< Len
; Index
++) {
1808 if (NET_IS_UPPER_CASE_CHAR (Name
[Index
])) {
1810 // Convert the upper-case characters to lower-case ones.
1812 Name
[Index
] = (CHAR8
) (Name
[Index
] - 'A' + 'a');
1815 if (!NET_IS_LOWER_CASE_CHAR (Name
[Index
]) &&
1816 !NET_IS_DIGIT (Name
[Index
]) &&
1817 (Name
[Index
] != '-') &&
1818 (Name
[Index
] != '.') &&
1819 (Name
[Index
] != ':')
1822 // ASCII dash, dot, colon lower-case characters and digit characters
1825 return EFI_PROTOCOL_ERROR
;
1829 if ((Len
< 4) || (CompareMem (Name
, "iqn.", 4) != 0)) {
1831 // Only IQN format is accepted now.
1833 return EFI_PROTOCOL_ERROR
;
1841 Create an iSCSI task control block.
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.
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.
1853 IN ISCSI_CONNECTION
*Conn
,
1857 ISCSI_SESSION
*Session
;
1860 ASSERT (Tcb
!= NULL
);
1862 Session
= Conn
->Session
;
1864 if (ISCSI_SEQ_GT (Session
->CmdSN
, Session
->MaxCmdSN
)) {
1865 return EFI_NOT_READY
;
1868 NewTcb
= AllocateZeroPool (sizeof (ISCSI_TCB
));
1869 if (NewTcb
== NULL
) {
1870 return EFI_OUT_OF_RESOURCES
;
1873 InitializeListHead (&NewTcb
->Link
);
1875 NewTcb
->SoFarInOrder
= TRUE
;
1876 NewTcb
->InitiatorTaskTag
= Session
->InitiatorTaskTag
;
1877 NewTcb
->CmdSN
= Session
->CmdSN
;
1878 NewTcb
->Conn
= Conn
;
1880 InsertTailList (&Session
->TcbList
, &NewTcb
->Link
);
1883 // Advance the initiator task tag.
1885 Session
->InitiatorTaskTag
++;
1895 Delete the tcb from the connection and destroy it.
1897 @param[in] Tcb The tcb to delete.
1905 RemoveEntryList (&Tcb
->Link
);
1912 Find the task control block by the initator task tag.
1914 @param[in] TcbList The tcb list.
1915 @param[in] InitiatorTaskTag The initiator task tag.
1917 @return The task control block found.
1918 @retval NULL The task control block cannot be found.
1923 IN LIST_ENTRY
*TcbList
,
1924 IN UINT32 InitiatorTaskTag
1932 NET_LIST_FOR_EACH (Entry
, TcbList
) {
1933 Tcb
= NET_LIST_USER_STRUCT (Entry
, ISCSI_TCB
, Link
);
1935 if (Tcb
->InitiatorTaskTag
== InitiatorTaskTag
) {
1945 Create a data segment, pad it, and calculate the CRC if needed.
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.
1951 @return The net buffer wrapping the data segment.
1955 IScsiNewDataSegment (
1958 IN BOOLEAN DataDigest
1961 NET_FRAGMENT Fragment
[2];
1962 UINT32 FragmentCount
;
1966 Fragment
[0].Len
= Len
;
1967 Fragment
[0].Bulk
= Data
;
1969 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1971 Fragment
[1].Len
= PadLen
;
1972 Fragment
[1].Bulk
= (UINT8
*) &mDataSegPad
;
1979 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1986 Create a iSCSI SCSI command PDU to encapsulate the command issued
1987 by SCSI through the EXT SCSI PASS THRU Protocol.
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.
1993 @return The created iSCSI SCSI command PDU.
1994 @retval NULL Other errors as indicated.
1998 IScsiNewScsiCmdPdu (
1999 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
2004 LIST_ENTRY
*NbufList
;
2008 SCSI_COMMAND
*ScsiCmd
;
2011 ISCSI_ADDITIONAL_HEADER
*Header
;
2012 ISCSI_BI_EXP_READ_DATA_LEN_AHS
*BiExpReadDataLenAHS
;
2013 ISCSI_SESSION
*Session
;
2014 UINT32 ImmediateDataLen
;
2018 if (Packet
->DataDirection
== DataBi
) {
2020 // Bidirectional Read/Write command, the bidirectional expected
2021 // read data length AHS is required.
2023 AHSLength
+= sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS
);
2026 if (Packet
->CdbLength
> 16) {
2028 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2030 AHSLength
= (UINT8
) (AHSLength
+ ISCSI_ROUNDUP (Packet
->CdbLength
- 16) + sizeof (ISCSI_ADDITIONAL_HEADER
));
2033 Length
= sizeof (SCSI_COMMAND
) + AHSLength
;
2034 PduHeader
= NetbufAlloc (Length
);
2035 if (PduHeader
== NULL
) {
2039 ScsiCmd
= (SCSI_COMMAND
*) NetbufAllocSpace (PduHeader
, Length
, NET_BUF_TAIL
);
2040 if (ScsiCmd
== NULL
) {
2041 NetbufFree (PduHeader
);
2044 Header
= (ISCSI_ADDITIONAL_HEADER
*) (ScsiCmd
+ 1);
2046 ZeroMem (ScsiCmd
, Length
);
2048 ISCSI_SET_OPCODE (ScsiCmd
, ISCSI_OPCODE_SCSI_CMD
, 0);
2049 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_TASK_ATTR_SIMPLE
);
2052 // Set the READ/WRITE flags according to the IO type of this request.
2054 switch (Packet
->DataDirection
) {
2056 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
);
2057 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->InTransferLength
);
2061 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_WRITE
);
2062 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2066 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
| SCSI_CMD_PDU_FLAG_WRITE
);
2067 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2070 // Fill the bidirectional expected read data length AHS.
2072 BiExpReadDataLenAHS
= (ISCSI_BI_EXP_READ_DATA_LEN_AHS
*) Header
;
2073 Header
= (ISCSI_ADDITIONAL_HEADER
*) (BiExpReadDataLenAHS
+ 1);
2075 BiExpReadDataLenAHS
->Length
= NTOHS (5);
2076 BiExpReadDataLenAHS
->Type
= ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN
;
2077 BiExpReadDataLenAHS
->ExpReadDataLength
= NTOHL (Packet
->InTransferLength
);
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
);
2088 CopyMem (ScsiCmd
->Cdb
, Packet
->Cdb
, sizeof (ScsiCmd
->Cdb
));
2090 if (Packet
->CdbLength
> 16) {
2091 Header
->Length
= NTOHS ((UINT16
) (Packet
->CdbLength
- 15));
2092 Header
->Type
= ISCSI_AHS_TYPE_EXT_CDB
;
2094 CopyMem (Header
+ 1, (UINT8
*) Packet
->Cdb
+ 16, Packet
->CdbLength
- 16);
2098 Session
= Tcb
->Conn
->Session
;
2099 ImmediateDataLen
= 0;
2101 if (Session
->ImmediateData
&& (Packet
->OutTransferLength
!= 0)) {
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.
2107 ImmediateDataLen
= MIN (Session
->FirstBurstLength
, Packet
->OutTransferLength
);
2108 ImmediateDataLen
= MIN (ImmediateDataLen
, Tcb
->Conn
->MaxRecvDataSegmentLength
);
2111 // Update the data segment length in the PDU header.
2113 ISCSI_SET_DATASEG_LEN (ScsiCmd
, ImmediateDataLen
);
2116 // Create the data segment.
2118 DataSeg
= IScsiNewDataSegment ((UINT8
*) Packet
->OutDataBuffer
, ImmediateDataLen
, FALSE
);
2119 if (DataSeg
== NULL
) {
2120 NetbufFree (PduHeader
);
2125 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2126 if (NbufList
== NULL
) {
2127 NetbufFree (PduHeader
);
2128 NetbufFree (DataSeg
);
2134 InitializeListHead (NbufList
);
2135 InsertTailList (NbufList
, &PduHeader
->List
);
2136 InsertTailList (NbufList
, &DataSeg
->List
);
2138 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2140 IScsiFreeNbufList (NbufList
);
2144 if (Session
->InitialR2T
||
2145 (ImmediateDataLen
== Session
->FirstBurstLength
) ||
2146 (ImmediateDataLen
== Packet
->OutTransferLength
)
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
2155 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_BHS_FLAG_FINAL
);
2165 Create a new iSCSI SCSI Data Out PDU.
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.
2173 @return The net buffer wrapping the Data Out PDU.
2174 @retval NULL Other errors as indicated.
2178 IScsiNewDataOutPdu (
2186 LIST_ENTRY
*NbufList
;
2190 ISCSI_SCSI_DATA_OUT
*DataOutHdr
;
2191 ISCSI_XFER_CONTEXT
*XferContext
;
2193 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2194 if (NbufList
== NULL
) {
2198 InitializeListHead (NbufList
);
2201 // Allocate memory for the BHS.
2203 PduHdr
= NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT
));
2204 if (PduHdr
== NULL
) {
2205 FreePool (NbufList
);
2209 // Insert the BHS into the buffer list.
2211 InsertTailList (NbufList
, &PduHdr
->List
);
2213 DataOutHdr
= (ISCSI_SCSI_DATA_OUT
*) NetbufAllocSpace (PduHdr
, sizeof (ISCSI_SCSI_DATA_OUT
), NET_BUF_TAIL
);
2214 ASSERT (DataOutHdr
!= NULL
);
2215 XferContext
= &Tcb
->XferContext
;
2217 ZeroMem (DataOutHdr
, sizeof (ISCSI_SCSI_DATA_OUT
));
2220 // Set the flags and fields of the Data Out PDU BHS.
2222 ISCSI_SET_OPCODE (DataOutHdr
, ISCSI_OPCODE_SCSI_DATA_OUT
, 0);
2223 ISCSI_SET_DATASEG_LEN (DataOutHdr
, Len
);
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
);
2231 if (XferContext
->TargetTransferTag
!= ISCSI_RESERVED_TAG
) {
2232 CopyMem (&DataOutHdr
->Lun
, &Lun
, sizeof (DataOutHdr
->Lun
));
2235 // Build the data segment for this Data Out PDU.
2237 DataSeg
= IScsiNewDataSegment (Data
, Len
, FALSE
);
2238 if (DataSeg
== NULL
) {
2239 IScsiFreeNbufList (NbufList
);
2243 // Put the data segment into the buffer list and combine it with the BHS
2244 // into a full Data Out PDU.
2246 InsertTailList (NbufList
, &DataSeg
->List
);
2247 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2249 IScsiFreeNbufList (NbufList
);
2257 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
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.
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.
2268 IScsiGenerateDataOutPduSequence (
2274 LIST_ENTRY
*PduList
;
2277 NET_BUF
*DataOutPdu
;
2278 ISCSI_CONNECTION
*Conn
;
2279 ISCSI_XFER_CONTEXT
*XferContext
;
2280 UINT8
*DataOutPacket
;
2282 PduList
= AllocatePool (sizeof (LIST_ENTRY
));
2283 if (PduList
== NULL
) {
2287 InitializeListHead (PduList
);
2292 XferContext
= &Tcb
->XferContext
;
2294 while (XferContext
->DesiredLength
> 0) {
2296 // Determine the length of data this Data Out PDU can carry.
2298 DataLen
= MIN (XferContext
->DesiredLength
, Conn
->MaxRecvDataSegmentLength
);
2301 // Create a Data Out PDU.
2303 DataOutPdu
= IScsiNewDataOutPdu (Data
, DataLen
, DataSN
, Tcb
, Lun
);
2304 if (DataOutPdu
== NULL
) {
2305 IScsiFreeNbufList (PduList
);
2311 InsertTailList (PduList
, &DataOutPdu
->List
);
2314 // Update the context and DataSN.
2317 XferContext
->Offset
+= DataLen
;
2318 XferContext
->DesiredLength
-= DataLen
;
2322 // Set the F bit for the last data out PDU in this sequence.
2324 DataOutPacket
= NetbufGetByte (DataOutPdu
, 0, NULL
);
2325 if (DataOutPacket
== NULL
) {
2326 IScsiFreeNbufList (PduList
);
2331 ISCSI_SET_FLAG (DataOutPacket
, ISCSI_BHS_FLAG_FINAL
);
2339 Send the Data in a sequence of Data Out PDUs one by one.
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.
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.
2351 IScsiSendDataOutPduSequence (
2357 LIST_ENTRY
*DataOutPduList
;
2363 // Generate the Data Out PDU sequence.
2365 DataOutPduList
= IScsiGenerateDataOutPduSequence (Data
, Tcb
, Lun
);
2366 if (DataOutPduList
== NULL
) {
2367 return EFI_OUT_OF_RESOURCES
;
2370 Status
= EFI_SUCCESS
;
2373 // Send the Data Out PDU's one by one.
2375 NET_LIST_FOR_EACH (Entry
, DataOutPduList
) {
2376 Pdu
= NET_LIST_USER_STRUCT (Entry
, NET_BUF
, List
);
2378 Status
= TcpIoTransmit (&Tcb
->Conn
->TcpIo
, Pdu
);
2380 if (EFI_ERROR (Status
)) {
2385 IScsiFreeNbufList (DataOutPduList
);
2392 Process the received iSCSI SCSI Data In PDU.
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.
2398 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
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.
2409 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2412 ISCSI_SCSI_DATA_IN
*DataInHdr
;
2415 DataInHdr
= (ISCSI_SCSI_DATA_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2416 if (DataInHdr
== NULL
) {
2417 return EFI_PROTOCOL_ERROR
;
2420 DataInHdr
->InitiatorTaskTag
= NTOHL (DataInHdr
->InitiatorTaskTag
);
2421 DataInHdr
->ExpCmdSN
= NTOHL (DataInHdr
->ExpCmdSN
);
2422 DataInHdr
->MaxCmdSN
= NTOHL (DataInHdr
->MaxCmdSN
);
2423 DataInHdr
->DataSN
= NTOHL (DataInHdr
->DataSN
);
2426 // Check the DataSN.
2428 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, DataInHdr
->DataSN
);
2429 if (EFI_ERROR (Status
)) {
2433 if (DataInHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2434 return EFI_PROTOCOL_ERROR
;
2437 // Update the command related sequence numbers.
2439 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, DataInHdr
->MaxCmdSN
, DataInHdr
->ExpCmdSN
);
2441 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID
)) {
2442 if (!ISCSI_FLAG_ON (DataInHdr
, ISCSI_BHS_FLAG_FINAL
)) {
2444 // The S bit is on but the F bit is off.
2446 return EFI_PROTOCOL_ERROR
;
2449 Tcb
->StatusXferd
= TRUE
;
2451 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_OVERFLOW
| SCSI_DATA_IN_PDU_FLAG_UNDERFLOW
)) {
2453 // Underflow and Overflow are mutual flags.
2455 return EFI_PROTOCOL_ERROR
;
2458 // S bit is on, the StatSN is valid.
2460 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NTOHL (DataInHdr
->StatSN
));
2461 if (EFI_ERROR (Status
)) {
2465 Packet
->HostAdapterStatus
= 0;
2466 Packet
->TargetStatus
= DataInHdr
->Status
;
2468 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2469 Packet
->InTransferLength
+= NTOHL (DataInHdr
->ResidualCount
);
2470 Status
= EFI_BAD_BUFFER_SIZE
;
2473 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2474 Packet
->InTransferLength
-= NTOHL (DataInHdr
->ResidualCount
);
2483 Process the received iSCSI R2T PDU.
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.
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.
2500 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2503 ISCSI_READY_TO_TRANSFER
*R2THdr
;
2505 ISCSI_XFER_CONTEXT
*XferContext
;
2508 R2THdr
= (ISCSI_READY_TO_TRANSFER
*) NetbufGetByte (Pdu
, 0, NULL
);
2509 if (R2THdr
== NULL
) {
2510 return EFI_PROTOCOL_ERROR
;
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
);
2520 if ((R2THdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) || !ISCSI_SEQ_EQ (R2THdr
->StatSN
, Tcb
->Conn
->ExpStatSN
)) {
2521 return EFI_PROTOCOL_ERROR
;;
2524 // Check the sequence number.
2526 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, R2THdr
->R2TSeqNum
);
2527 if (EFI_ERROR (Status
)) {
2531 XferContext
= &Tcb
->XferContext
;
2532 XferContext
->TargetTransferTag
= R2THdr
->TargetTransferTag
;
2533 XferContext
->Offset
= R2THdr
->BufferOffset
;
2534 XferContext
->DesiredLength
= R2THdr
->DesiredDataTransferLength
;
2536 if (((XferContext
->Offset
+ XferContext
->DesiredLength
) > Packet
->OutTransferLength
) ||
2537 (XferContext
->DesiredLength
> Tcb
->Conn
->Session
->MaxBurstLength
)
2539 return EFI_PROTOCOL_ERROR
;
2542 // Send the data solicited by this R2T.
2544 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2545 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2552 Process the received iSCSI SCSI Response PDU.
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.
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.
2565 IScsiOnScsiRspRcvd (
2568 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2571 SCSI_RESPONSE
*ScsiRspHdr
;
2572 ISCSI_SENSE_DATA
*SenseData
;
2576 ScsiRspHdr
= (SCSI_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
2577 if (ScsiRspHdr
== NULL
) {
2578 return EFI_PROTOCOL_ERROR
;
2581 ScsiRspHdr
->InitiatorTaskTag
= NTOHL (ScsiRspHdr
->InitiatorTaskTag
);
2582 if (ScsiRspHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2583 return EFI_PROTOCOL_ERROR
;
2586 ScsiRspHdr
->StatSN
= NTOHL (ScsiRspHdr
->StatSN
);
2588 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, ScsiRspHdr
->StatSN
);
2589 if (EFI_ERROR (Status
)) {
2593 ScsiRspHdr
->MaxCmdSN
= NTOHL (ScsiRspHdr
->MaxCmdSN
);
2594 ScsiRspHdr
->ExpCmdSN
= NTOHL (ScsiRspHdr
->ExpCmdSN
);
2595 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, ScsiRspHdr
->MaxCmdSN
, ScsiRspHdr
->ExpCmdSN
);
2597 Tcb
->StatusXferd
= TRUE
;
2599 Packet
->HostAdapterStatus
= ScsiRspHdr
->Response
;
2600 if (Packet
->HostAdapterStatus
!= ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET
) {
2604 Packet
->TargetStatus
= ScsiRspHdr
->Status
;
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
)
2609 return EFI_PROTOCOL_ERROR
;
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
;
2617 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
)) {
2618 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2621 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2622 if (Packet
->DataDirection
== DataIn
) {
2623 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2625 Packet
->OutTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2628 Status
= EFI_BAD_BUFFER_SIZE
;
2631 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2632 if (Packet
->DataDirection
== DataIn
) {
2633 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2635 Packet
->OutTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
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
;
2646 SenseData
->Length
= NTOHS (SenseData
->Length
);
2648 Packet
->SenseDataLength
= (UINT8
) MIN (SenseData
->Length
, Packet
->SenseDataLength
);
2649 if (Packet
->SenseDataLength
!= 0) {
2650 CopyMem (Packet
->SenseData
, &SenseData
->Data
[0], Packet
->SenseDataLength
);
2653 Packet
->SenseDataLength
= 0;
2661 Process the received NOP In PDU.
2663 @param[in] Pdu The NOP In PDU received.
2664 @param[in] Tcb The task control block.
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.
2677 ISCSI_NOP_IN
*NopInHdr
;
2680 NopInHdr
= (ISCSI_NOP_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2681 if (NopInHdr
== NULL
) {
2682 return EFI_PROTOCOL_ERROR
;
2685 NopInHdr
->StatSN
= NTOHL (NopInHdr
->StatSN
);
2686 NopInHdr
->ExpCmdSN
= NTOHL (NopInHdr
->ExpCmdSN
);
2687 NopInHdr
->MaxCmdSN
= NTOHL (NopInHdr
->MaxCmdSN
);
2689 if (NopInHdr
->InitiatorTaskTag
== ISCSI_RESERVED_TAG
) {
2690 if (NopInHdr
->StatSN
!= Tcb
->Conn
->ExpStatSN
) {
2691 return EFI_PROTOCOL_ERROR
;
2694 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NopInHdr
->StatSN
);
2695 if (EFI_ERROR (Status
)) {
2700 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, NopInHdr
->MaxCmdSN
, NopInHdr
->ExpCmdSN
);
2707 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
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.
2715 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
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.
2724 IScsiExecuteScsiCommand (
2725 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
,
2728 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2732 ISCSI_DRIVER_DATA
*Private
;
2733 ISCSI_SESSION
*Session
;
2734 EFI_EVENT TimeoutEvent
;
2735 ISCSI_CONNECTION
*Conn
;
2738 ISCSI_XFER_CONTEXT
*XferContext
;
2740 ISCSI_IN_BUFFER_CONTEXT InBufferContext
;
2744 Private
= ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru
);
2745 Session
= Private
->Session
;
2746 Status
= EFI_SUCCESS
;
2748 TimeoutEvent
= NULL
;
2751 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
2752 return EFI_DEVICE_ERROR
;
2755 Conn
= NET_LIST_USER_STRUCT_S (
2756 Session
->Conns
.ForwardLink
,
2759 ISCSI_CONNECTION_SIGNATURE
2762 if (Packet
->Timeout
!= 0) {
2763 Timeout
= MultU64x32 (Packet
->Timeout
, 4);
2766 Status
= IScsiNewTcb (Conn
, &Tcb
);
2767 if (EFI_ERROR (Status
)) {
2771 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2773 Pdu
= IScsiNewScsiCmdPdu (Packet
, Lun
, Tcb
);
2775 Status
= EFI_OUT_OF_RESOURCES
;
2779 XferContext
= &Tcb
->XferContext
;
2780 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2781 if (PduHdr
== NULL
) {
2782 Status
= EFI_PROTOCOL_ERROR
;
2786 XferContext
->Offset
= ISCSI_GET_DATASEG_LEN (PduHdr
);
2789 // Transmit the SCSI Command PDU.
2791 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
2795 if (EFI_ERROR (Status
)) {
2799 if (!Session
->InitialR2T
&&
2800 (XferContext
->Offset
< Session
->FirstBurstLength
) &&
2801 (XferContext
->Offset
< Packet
->OutTransferLength
)
2804 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2805 // OUT data, and the limit of FirstBurstLength is not reached.
2807 XferContext
->TargetTransferTag
= ISCSI_RESERVED_TAG
;
2808 XferContext
->DesiredLength
= MIN (
2809 Session
->FirstBurstLength
,
2810 Packet
->OutTransferLength
- XferContext
->Offset
2813 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2814 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2815 if (EFI_ERROR (Status
)) {
2820 InBufferContext
.InData
= (UINT8
*) Packet
->InDataBuffer
;
2821 InBufferContext
.InDataLen
= Packet
->InTransferLength
;
2823 while (!Tcb
->StatusXferd
) {
2825 // Start the timeout timer.
2828 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, Timeout
);
2829 if (EFI_ERROR (Status
)) {
2833 TimeoutEvent
= Conn
->TimeoutEvent
;
2837 // Try to receive PDU from target.
2839 Status
= IScsiReceivePdu (Conn
, &Pdu
, &InBufferContext
, FALSE
, FALSE
, TimeoutEvent
);
2840 if (EFI_ERROR (Status
)) {
2844 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2845 if (PduHdr
== NULL
) {
2846 Status
= EFI_PROTOCOL_ERROR
;
2850 switch (ISCSI_GET_OPCODE (PduHdr
)) {
2851 case ISCSI_OPCODE_SCSI_DATA_IN
:
2852 Status
= IScsiOnDataInRcvd (Pdu
, Tcb
, Packet
);
2855 case ISCSI_OPCODE_R2T
:
2856 Status
= IScsiOnR2TRcvd (Pdu
, Tcb
, Lun
, Packet
);
2859 case ISCSI_OPCODE_SCSI_RSP
:
2860 Status
= IScsiOnScsiRspRcvd (Pdu
, Tcb
, Packet
);
2863 case ISCSI_OPCODE_NOP_IN
:
2864 Status
= IScsiOnNopInRcvd (Pdu
, Tcb
);
2867 case ISCSI_OPCODE_VENDOR_T0
:
2868 case ISCSI_OPCODE_VENDOR_T1
:
2869 case ISCSI_OPCODE_VENDOR_T2
:
2871 // These messages are vendor specific. Skip them.
2876 Status
= EFI_PROTOCOL_ERROR
;
2882 if (EFI_ERROR (Status
)) {
2889 if (TimeoutEvent
!= NULL
) {
2890 gBS
->SetTimer (TimeoutEvent
, TimerCancel
, 0);
2897 if ((Status
!= EFI_SUCCESS
) && (Status
!= EFI_NOT_READY
)) {
2899 // Reinstate the session.
2901 if (EFI_ERROR (IScsiSessionReinstatement (Session
))) {
2902 Status
= EFI_DEVICE_ERROR
;
2911 Reinstate the session on some error.
2913 @param[in] Session The iSCSI session
2915 @retval EFI_SUCCESS The session is reinstated from some error.
2916 @retval Other Reinstatement failed.
2920 IScsiSessionReinstatement (
2921 IN ISCSI_SESSION
*Session
2926 ASSERT (Session
->State
== SESSION_STATE_LOGGED_IN
);
2929 // Abort the session and re-init it.
2931 IScsiSessionAbort (Session
);
2932 IScsiSessionInit (Session
, TRUE
);
2937 Status
= IScsiSessionLogin (Session
);
2944 Initialize some session parameters before login.
2946 @param[in, out] Session The iSCSI session.
2947 @param[in] Recovery Whether the request is from a fresh new start or recovery.
2952 IN OUT ISCSI_SESSION
*Session
,
2957 Session
->Signature
= ISCSI_SESSION_SIGNATURE
;
2958 Session
->State
= SESSION_STATE_FREE
;
2960 InitializeListHead (&Session
->Conns
);
2961 InitializeListHead (&Session
->TcbList
);
2967 Session
->InitiatorTaskTag
= 1;
2968 Session
->NextCid
= 1;
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;
2986 Abort the iSCSI session. That is, reset all the connection(s), and free the
2989 @param[in, out] Session The iSCSI session.
2994 IN OUT ISCSI_SESSION
*Session
2997 ISCSI_CONNECTION
*Conn
;
2998 EFI_GUID
*ProtocolGuid
;
3000 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
3004 ASSERT (!IsListEmpty (&Session
->Conns
));
3006 while (!IsListEmpty (&Session
->Conns
)) {
3007 Conn
= NET_LIST_USER_STRUCT_S (
3008 Session
->Conns
.ForwardLink
,
3011 ISCSI_CONNECTION_SIGNATURE
3013 if (!Conn
->Ipv6Flag
) {
3014 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
3016 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
3019 gBS
->CloseProtocol (
3022 Session
->Private
->Image
,
3023 Session
->Private
->ExtScsiPassThruHandle
3026 IScsiConnReset (Conn
);
3028 IScsiDetatchConnection (Conn
);
3029 IScsiDestroyConnection (Conn
);
3032 Session
->State
= SESSION_STATE_FAILED
;