2 The implementation of IScsi protocol based on RFC3720
4 Copyright (c) 2004 - 2008, Intel Corporation
5 All rights reserved. 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.
18 The implementation of IScsi protocol based on RFC3720
22 #include "IScsiImpl.h"
24 UINT32 mDataSegPad
= 0;
27 Attach the iSCSI connection to the iSCSI session.
29 @param Session[in] The iSCSI session.
31 @param Conn[in] The iSCSI connection.
37 IScsiAttatchConnection (
38 IN ISCSI_SESSION
*Session
,
39 IN ISCSI_CONNECTION
*Conn
42 InsertTailList (&Session
->Conns
, &Conn
->Link
);
43 Conn
->Session
= Session
;
48 Detach the iSCSI connection from the session it belongs to.
50 @param Conn[in] The iSCSI connection.
56 IScsiDetatchConnection (
57 IN ISCSI_CONNECTION
*Conn
60 RemoveEntryList (&Conn
->Link
);
61 Conn
->Session
->NumConns
--;
66 Check the sequence number according to RFC3720.
68 @param ExpSN[in] The currently expected sequence number.
70 @param NewSN[in] The sequence number to check.
72 @retval EFI_SUCCESS The check passed and the ExpSN is increased.
81 if (!ISCSI_SEQ_EQ (NewSN
, *ExpSN
)) {
82 if (ISCSI_SEQ_LT (NewSN
, *ExpSN
)) {
88 return EFI_PROTOCOL_ERROR
;
100 Update the sequence numbers for the iSCSI command.
102 @param Session[in] The iSCSI session.
104 @param MaxCmdSN[in] Maximum CmdSN from the target.
106 @param ExpCmdSN[in] Next expected CmdSN from the target.
113 IN ISCSI_SESSION
*Session
,
118 if (ISCSI_SEQ_LT (MaxCmdSN
, ExpCmdSN
- 1)) {
122 if (ISCSI_SEQ_GT (MaxCmdSN
, Session
->MaxCmdSN
)) {
123 Session
->MaxCmdSN
= MaxCmdSN
;
126 if (ISCSI_SEQ_GT (ExpCmdSN
, Session
->ExpCmdSN
)) {
127 Session
->ExpCmdSN
= ExpCmdSN
;
132 This function does the iSCSI connection login.
134 @param Conn[in] The iSCSI connection to login.
136 @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target.
138 @retval EFI_TIMEOUT Timeout happened during the login procedure.
140 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
145 IN ISCSI_CONNECTION
*Conn
151 // Start the timer, wait 16 seconds to establish the TCP connection.
153 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, 16 * TICKS_PER_SECOND
);
154 if (EFI_ERROR (Status
)) {
158 // try to establish the tcp connection
160 Status
= Tcp4IoConnect (&Conn
->Tcp4Io
, Conn
->TimeoutEvent
);
161 if (EFI_ERROR (Status
)) {
165 gBS
->SetTimer (Conn
->TimeoutEvent
, TimerCancel
, 0);
166 Conn
->State
= CONN_STATE_IN_LOGIN
;
169 // connection is established, start the iSCSI Login
172 Status
= IScsiSendLoginReq (Conn
);
173 if (EFI_ERROR (Status
)) {
177 Status
= IScsiReceiveLoginRsp (Conn
);
178 if (EFI_ERROR (Status
)) {
181 } while (Conn
->CurrentStage
!= ISCSI_FULL_FEATURE_PHASE
);
187 Reset the iSCSI connection.
189 @param Conn[in] The iSCSI connection to reset.
196 IN ISCSI_CONNECTION
*Conn
199 Tcp4IoReset (&Conn
->Tcp4Io
);
203 Create a TCP connection for the iSCSI session.
205 @param Private[in] The iSCSI driver data.
207 @param Session[in] Maximum CmdSN from the target.
209 @retval The newly created iSCSI connection.
213 IScsiCreateConnection (
214 IN ISCSI_DRIVER_DATA
*Private
,
215 IN ISCSI_SESSION
*Session
218 ISCSI_CONNECTION
*Conn
;
219 TCP4_IO_CONFIG_DATA Tcp4IoConfig
;
222 Conn
= AllocatePool (sizeof (ISCSI_CONNECTION
));
227 Conn
->Signature
= ISCSI_CONNECTION_SIGNATURE
;
228 Conn
->State
= CONN_STATE_FREE
;
229 Conn
->CurrentStage
= ISCSI_SECURITY_NEGOTIATION
;
230 Conn
->NextStage
= ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
;
231 Conn
->CHAPStep
= ISCSI_CHAP_INITIAL
;
233 Conn
->PartialReqSent
= FALSE
;
234 Conn
->PartialRspRcvd
= FALSE
;
235 Conn
->CID
= Session
->NextCID
++;
237 Status
= gBS
->CreateEvent (
244 if (EFI_ERROR (Status
)) {
245 gBS
->FreePool (Conn
);
249 NetbufQueInit (&Conn
->RspQue
);
252 // set the default connection-only parameters
254 Conn
->MaxRecvDataSegmentLength
= MAX_RECV_DATA_SEG_LEN_IN_FFP
;
255 Conn
->HeaderDigest
= ISCSI_DIGEST_NONE
;
256 Conn
->DataDigest
= ISCSI_DIGEST_NONE
;
258 CopyMem (&Tcp4IoConfig
.LocalIp
, &Session
->ConfigData
.NvData
.LocalIp
, sizeof (EFI_IPv4_ADDRESS
));
259 CopyMem (&Tcp4IoConfig
.SubnetMask
, &Session
->ConfigData
.NvData
.SubnetMask
, sizeof (EFI_IPv4_ADDRESS
));
260 CopyMem (&Tcp4IoConfig
.Gateway
, &Session
->ConfigData
.NvData
.Gateway
, sizeof (EFI_IPv4_ADDRESS
));
261 CopyMem (&Tcp4IoConfig
.RemoteIp
, &Session
->ConfigData
.NvData
.TargetIp
, sizeof (EFI_IPv4_ADDRESS
));
263 Tcp4IoConfig
.RemotePort
= Session
->ConfigData
.NvData
.TargetPort
;
266 // Create the tcp4 IO for this connection
268 Status
= Tcp4IoCreateSocket (
274 if (EFI_ERROR (Status
)) {
275 gBS
->CloseEvent (Conn
->TimeoutEvent
);
276 gBS
->FreePool (Conn
);
284 Destroy an iSCSI connection.
286 @param Conn[in] The connection to destroy.
292 IScsiDestroyConnection (
293 IN ISCSI_CONNECTION
*Conn
296 Tcp4IoDestroySocket (&Conn
->Tcp4Io
);
297 NetbufQueFlush (&Conn
->RspQue
);
298 gBS
->CloseEvent (Conn
->TimeoutEvent
);
299 gBS
->FreePool (Conn
);
303 Login the iSCSI session.
305 @param Private[in] The iSCSI driver data.
307 @retval EFI_SUCCESS The iSCSI session login procedure finished.
309 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
311 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
316 IN ISCSI_DRIVER_DATA
*Private
320 ISCSI_SESSION
*Session
;
321 ISCSI_CONNECTION
*Conn
;
322 EFI_TCP4_PROTOCOL
*Tcp4
;
324 Session
= &Private
->Session
;
327 // Create a connection for the session.
329 Conn
= IScsiCreateConnection (Private
, Session
);
331 return EFI_OUT_OF_RESOURCES
;
334 IScsiAttatchConnection (Session
, Conn
);
337 // Login througth the newly created connection.
339 Status
= IScsiConnLogin (Conn
);
340 if (EFI_ERROR (Status
)) {
341 IScsiConnReset (Conn
);
342 IScsiDetatchConnection (Conn
);
343 IScsiDestroyConnection (Conn
);
345 Session
->State
= SESSION_STATE_LOGGED_IN
;
349 &gEfiTcp4ProtocolGuid
,
352 Private
->ExtScsiPassThruHandle
,
353 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
361 Build and send the iSCSI login request to the iSCSI target according to
362 the current login stage.
364 @param Conn[in] The connection in the iSCSI login phase.
366 @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
369 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
371 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
376 IN ISCSI_CONNECTION
*Conn
383 // build the Login Request PDU
385 Pdu
= IScsiPrepareLoginReq (Conn
);
387 return EFI_DEVICE_ERROR
;
390 // Send it to the iSCSI target.
392 Status
= Tcp4IoTransmit (&Conn
->Tcp4Io
, Pdu
);
400 Receive and process the iSCSI login response.
402 @param Conn[in] The connection in the iSCSI login phase.
404 @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
406 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
408 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
412 IScsiReceiveLoginRsp (
413 IN ISCSI_CONNECTION
*Conn
420 // Receive the iSCSI login response.
422 Status
= IScsiReceivePdu (Conn
, &Pdu
, NULL
, FALSE
, FALSE
, NULL
);
423 if (EFI_ERROR (Status
)) {
427 // A Login Response is received, process it.
429 Status
= IScsiProcessLoginRsp (Conn
, Pdu
);
437 Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
438 The DataSegmentLength and the actual size of the net buffer containing this PDU will be
441 @param Pdu[in] The iSCSI PDU whose data segment the key-value pair will
444 @param Key[in] The key name string.
446 @param Value[in] The value string.
448 @retval EFI_SUCCESS The key-valu pair is added to the PDU's datasegment and
449 the correspondence length fields are updated.
451 @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
456 IScsiAddKeyValuePair (
466 ISCSI_LOGIN_REQUEST
*LoginReq
;
469 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufGetByte (Pdu
, 0, NULL
);
470 DataSegLen
= NTOH24 (LoginReq
->DataSegmentLength
);
472 KeyLen
= (UINT32
) AsciiStrLen (Key
);
473 ValueLen
= (UINT32
) AsciiStrLen (Value
);
476 // 1 byte for the key value separator '=' and 1 byte for the null
477 // delimiter after the value.
479 TotalLen
= KeyLen
+ 1 + ValueLen
+ 1;
482 // Allocate the space for the key-value pair.
484 Data
= (CHAR8
*)NetbufAllocSpace (Pdu
, TotalLen
, NET_BUF_TAIL
);
486 return EFI_OUT_OF_RESOURCES
;
491 CopyMem (Data
, Key
, KeyLen
);
500 CopyMem (Data
, Value
, ValueLen
);
506 // update the DataSegmentLength
508 ISCSI_SET_DATASEG_LEN (LoginReq
, DataSegLen
+ TotalLen
);
514 Prepare the iSCSI login request to be sent according to the current login status.
516 @param Conn[in] The connection in the iSCSI login phase.
518 @retval The pointer to the net buffer containing the iSCSI login request built.
522 IScsiPrepareLoginReq (
523 IN ISCSI_CONNECTION
*Conn
526 ISCSI_SESSION
*Session
;
528 ISCSI_LOGIN_REQUEST
*LoginReq
;
531 Session
= Conn
->Session
;
533 Nbuf
= NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST
) + DEFAULT_MAX_RECV_DATA_SEG_LEN
);
538 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufAllocSpace (Nbuf
, sizeof (ISCSI_LOGIN_REQUEST
), NET_BUF_TAIL
);
539 ZeroMem (LoginReq
, sizeof (ISCSI_LOGIN_REQUEST
));
542 // Init the login request pdu
544 ISCSI_SET_OPCODE (LoginReq
, ISCSI_OPCODE_LOGIN_REQ
, ISCSI_REQ_IMMEDIATE
);
545 ISCSI_SET_STAGES (LoginReq
, Conn
->CurrentStage
, Conn
->NextStage
);
546 LoginReq
->VersionMax
= ISCSI_VERSION_MAX
;
547 LoginReq
->VersionMin
= ISCSI_VERSION_MIN
;
548 LoginReq
->TSIH
= HTONS (Session
->TSIH
);
549 LoginReq
->InitiatorTaskTag
= HTONL (Session
->InitiatorTaskTag
);
550 LoginReq
->CID
= HTONS (Conn
->CID
);
551 LoginReq
->CmdSN
= HTONL (Session
->CmdSN
);
554 // For the first Login Request on a coonection this is ExpStatSN for the
555 // old connection and this field is only valid if the Login Request restarts
557 // For subsequent Login Requests it is used to acknowledge the Login Responses
558 // with their increasing StatSN values.
560 LoginReq
->ExpStatSN
= HTONL (Conn
->ExpStatSN
);
561 CopyMem (LoginReq
->ISID
, Session
->ISID
, sizeof (LoginReq
->ISID
));
563 if (Conn
->PartialRspRcvd
) {
565 // A partial response, initiator must send an empty Login Request.
570 switch (Conn
->CurrentStage
) {
571 case ISCSI_SECURITY_NEGOTIATION
:
572 Status
= IScsiCHAPToSendReq (Conn
, Nbuf
);
575 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
576 Status
= IScsiFillOpParams (Conn
, Nbuf
);
577 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
582 // something error happens...
584 Status
= EFI_DEVICE_ERROR
;
588 if (EFI_ERROR (Status
)) {
593 // Pad the data segment if needed.
595 IScsiPadSegment (Nbuf
, ISCSI_GET_DATASEG_LEN (LoginReq
));
597 // Check whether we will issue the stage transition signal?
599 Conn
->TransitInitiated
= (BOOLEAN
) ISCSI_FLAG_ON (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
606 Process the iSCSI Login Response.
608 @param Conn[in] The connection on which the iSCSI login response is received.
610 @param Pdu[in] The iSCSI login response PDU.
612 @retval EFI_SUCCESS The iSCSI login response PDU is processed and all check are passed.
614 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
618 IScsiProcessLoginRsp (
619 IN ISCSI_CONNECTION
*Conn
,
624 ISCSI_SESSION
*Session
;
625 ISCSI_LOGIN_RESPONSE
*LoginRsp
;
633 Session
= Conn
->Session
;
635 LoginRsp
= (ISCSI_LOGIN_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
636 if (!ISCSI_CHECK_OPCODE (LoginRsp
, ISCSI_OPCODE_LOGIN_RSP
)) {
638 // It's not a Login Response
640 return EFI_PROTOCOL_ERROR
;
643 // Get the data segment if any.
645 DataSegLen
= ISCSI_GET_DATASEG_LEN (LoginRsp
);
646 if (DataSegLen
!= 0) {
647 DataSeg
= NetbufGetByte (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NULL
);
652 // Check the status class in the login response PDU.
654 switch (LoginRsp
->StatusClass
) {
655 case ISCSI_LOGIN_STATUS_SUCCESS
:
657 // Just break here, the response and the data segment will be processed later.
661 case ISCSI_LOGIN_STATUS_REDIRECTION
:
663 // The target may be moved to a different address
665 if (DataSeg
== NULL
) {
666 return EFI_PROTOCOL_ERROR
;
669 // Process the TargetAddress key-value strings in the data segment to update the
670 // target address info.
672 Status
= IScsiUpdateTargetAddress (Session
, (CHAR8
*)DataSeg
, DataSegLen
);
673 if (EFI_ERROR (Status
)) {
677 // Session will be restarted on this error status because the Target is
678 // redirected by this Login Response.
680 return EFI_MEDIA_CHANGED
;
684 // Initiator Error, Target Error, or any other undefined error code.
686 return EFI_PROTOCOL_ERROR
;
689 // The status is sucess, extract the wanted fields from the header segment.
691 Transit
= (BOOLEAN
) ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT
);
692 Continue
= (BOOLEAN
) ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE
);
694 CurrentStage
= (UINT8
) ISCSI_GET_CURRENT_STAGE (LoginRsp
);
695 NextStage
= (UINT8
) ISCSI_GET_NEXT_STAGE (LoginRsp
);
697 LoginRsp
->InitiatorTaskTag
= NTOHL (LoginRsp
->InitiatorTaskTag
);
699 if ((Transit
&& Continue
) ||
700 (CurrentStage
!= Conn
->CurrentStage
) ||
701 (!Conn
->TransitInitiated
&& Transit
) ||
702 (Transit
&& (NextStage
!= Conn
->NextStage
)) ||
703 (CompareMem (Session
->ISID
, LoginRsp
->ISID
, sizeof (LoginRsp
->ISID
)) != 0) ||
704 (LoginRsp
->InitiatorTaskTag
!= Session
->InitiatorTaskTag
)
707 // A Login Response with the C bit set to 1 MUST have the T bit set to 0;
708 // The CSG in the Login Response MUST be the same with the I-end of this connection;
709 // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
710 // initiate the transistion;
711 // The NSG MUST be the same with the I-end of this connection if Transit is required.
712 // The ISID in the Login Response MUST be the same with this session.
714 return EFI_PROTOCOL_ERROR
;
717 LoginRsp
->StatSN
= NTOHL (LoginRsp
->StatSN
);
718 LoginRsp
->ExpCmdSN
= NTOHL (LoginRsp
->ExpCmdSN
);
719 LoginRsp
->MaxCmdSN
= NTOHL (LoginRsp
->MaxCmdSN
);
721 if ((Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
) && (Conn
->CHAPStep
== ISCSI_CHAP_INITIAL
)) {
723 // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
726 Conn
->ExpStatSN
= LoginRsp
->StatSN
+ 1;
727 Session
->MaxCmdSN
= LoginRsp
->MaxCmdSN
;
728 Session
->ExpCmdSN
= LoginRsp
->ExpCmdSN
;
731 // Check the StatSN of this PDU
733 Status
= IScsiCheckSN (&Conn
->ExpStatSN
, LoginRsp
->StatSN
);
734 if (!EFI_ERROR (Status
)) {
736 // Update the MaxCmdSN and ExpCmdSN
738 IScsiUpdateCmdSN (Session
, LoginRsp
->MaxCmdSN
, LoginRsp
->ExpCmdSN
);
744 // Trim off the header segment.
746 NetbufTrim (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NET_BUF_HEAD
);
749 // Queue this login response first in case it's a partial response so that
750 // later when the full response list is received we can combine these scattered
751 // responses' data segment and then process it.
754 NetbufQueAppend (&Conn
->RspQue
, Pdu
);
756 Conn
->PartialRspRcvd
= Continue
;
759 // It's a partial response, have to wait for another or more Request/Response
760 // conversations to get the full response.
765 switch (CurrentStage
) {
766 case ISCSI_SECURITY_NEGOTIATION
:
768 // In security negotiation stage, let CHAP module handle it.
770 Status
= IScsiCHAPOnRspReceived (Conn
, Transit
);
773 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
775 // Response received with negotiation resonse on iSCSI parameters, check them.
777 Status
= IScsiCheckOpParams (Conn
, Transit
);
782 // Should never get here.
784 Status
= EFI_PROTOCOL_ERROR
;
788 if (Transit
&& (Status
== EFI_SUCCESS
)) {
790 // Do the state transition.
792 Conn
->CurrentStage
= Conn
->NextStage
;
794 if (Conn
->CurrentStage
== ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
) {
795 Conn
->NextStage
= ISCSI_FULL_FEATURE_PHASE
;
798 // CurrentStage is iSCSI Full Feature, it's the Login-Final Response,
799 // get the TSIH from the Login Response.
801 Session
->TSIH
= NTOHS (LoginRsp
->TSIH
);
805 // Flush the response(s) received.
807 NetbufQueFlush (&Conn
->RspQue
);
813 Updated the target information according the data received in the iSCSI
814 login response with an target redirection status.
816 @param Session[in] The iSCSI session.
818 @param Data[in] The data segment which should contain the
819 TargetAddress key-value list.
821 @param Len[in] Length of the data.
823 @retval EFI_SUCCESS The target address is updated.
825 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
827 @retval EFI_NOT_FOUND The TargetAddress key is not found.
831 IScsiUpdateTargetAddress (
832 IN ISCSI_SESSION
*Session
,
837 LIST_ENTRY
*KeyValueList
;
838 CHAR8
*TargetAddress
;
843 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
844 if (KeyValueList
== NULL
) {
845 return EFI_OUT_OF_RESOURCES
;
848 Status
= EFI_NOT_FOUND
;
851 TargetAddress
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ADDRESS
);
852 if (TargetAddress
== NULL
) {
856 if (!NET_IS_DIGIT (TargetAddress
[0])) {
858 // The domainname of the target may be presented in three formats: a DNS host name,
859 // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted
865 IpStr
= TargetAddress
;
867 while (*TargetAddress
&& (*TargetAddress
!= ':') && (*TargetAddress
!= ',')) {
869 // NULL, ':' or ',' ends the IPv4 string.
874 if (*TargetAddress
== ',') {
876 // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
877 // as the result of a redirection.
880 } else if (*TargetAddress
== ':') {
881 *TargetAddress
= '\0';
885 Number
= AsciiStrDecimalToUintn (TargetAddress
);
886 if (Number
> 0xFFFF) {
889 Session
->ConfigData
.NvData
.TargetPort
= (UINT16
) Number
;
893 // The string only contains the IPv4 address. Use the well known port.
895 Session
->ConfigData
.NvData
.TargetPort
= ISCSI_WELL_KNOWN_PORT
;
898 // Update the target IP address.
900 Status
= IScsiAsciiStrToIp (IpStr
, &Session
->ConfigData
.NvData
.TargetIp
);
901 if (EFI_ERROR (Status
)) {
908 IScsiFreeKeyValueList (KeyValueList
);
914 The callback function to free the net buffer list.
916 @param Arg[in] The opaque parameter.
926 ASSERT (Arg
!= NULL
);
928 NetbufFreeList ((LIST_ENTRY
*) Arg
);
933 The callback function called in NetBufFree, it does nothing.
935 @param Arg[in] The opaque parameter.
948 Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
949 an optional data segment. The two parts will be put into two blocks of buffers in the
950 net buffer. The digest check will be conducted in this function if needed and the digests
951 will be trimmed from the PDU buffer.
953 @param Conn[in] The iSCSI connection to receive data from.
955 @param Pdu[out] The received iSCSI pdu.
957 @param Context[in] The context used to describe information on the caller provided
958 buffer to receive data segment of the iSCSI pdu, it's optional.
960 @param HeaderDigest[in] Whether there will be header digest received.
962 @param DataDigest[in] Whether there will be data digest.
964 @param TimeoutEvent[in] The timeout event, it's optional.
966 @retval EFI_SUCCESS An iSCSI pdu is received.
968 @retval EFI_TIMEOUT Timeout happenend.
973 IN ISCSI_CONNECTION
*Conn
,
975 IN ISCSI_IN_BUFFER_CONTEXT
*Context
, OPTIONAL
976 IN BOOLEAN HeaderDigest
,
977 IN BOOLEAN DataDigest
,
978 IN EFI_EVENT TimeoutEvent OPTIONAL
981 LIST_ENTRY
*NbufList
;
988 NET_FRAGMENT Fragment
[2];
989 UINT32 FragmentCount
;
991 UINT32 PadAndCRC32
[2];
993 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
994 if (NbufList
== NULL
) {
995 return EFI_OUT_OF_RESOURCES
;
998 InitializeListHead (NbufList
);
1001 // The header digest will be received together with the PDU header if exists.
1003 Len
= sizeof (ISCSI_BASIC_HEADER
) + (HeaderDigest
? sizeof (UINT32
) : 0);
1004 PduHdr
= NetbufAlloc (Len
);
1005 if (PduHdr
== NULL
) {
1006 Status
= EFI_OUT_OF_RESOURCES
;
1010 Header
= NetbufAllocSpace (PduHdr
, Len
, NET_BUF_TAIL
);
1011 InsertTailList (NbufList
, &PduHdr
->List
);
1014 // First step, receive the BHS of the PDU.
1016 Status
= Tcp4IoReceive (&Conn
->Tcp4Io
, PduHdr
, FALSE
, TimeoutEvent
);
1017 if (EFI_ERROR (Status
)) {
1023 // TODO: check the header-digest.
1026 // Trim off the digest.
1028 NetbufTrim (PduHdr
, sizeof (UINT32
), NET_BUF_TAIL
);
1031 Len
= ISCSI_GET_DATASEG_LEN (Header
);
1039 // Get the length of the padding bytes of the data segment.
1041 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1043 switch (ISCSI_GET_OPCODE (Header
)) {
1044 case ISCSI_OPCODE_SCSI_DATA_IN
:
1046 // Try to use the buffer described by Context if the PDU is an
1047 // iSCSI SCSI data in pdu so as to reduce memory copy overhead.
1049 InDataOffset
= ISCSI_GET_BUFFER_OFFSET (Header
);
1050 if ((Context
== NULL
) || ((InDataOffset
+ Len
) > Context
->InDataLen
)) {
1051 Status
= EFI_PROTOCOL_ERROR
;
1055 Fragment
[0].Len
= Len
;
1056 Fragment
[0].Bulk
= Context
->InData
+ InDataOffset
;
1058 if (DataDigest
|| (PadLen
!= 0)) {
1060 // The data segment is padded, use two fragments to receive it.
1061 // The first to receive the useful data. The second to receive the padding.
1063 Fragment
[1].Len
= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1064 Fragment
[1].Bulk
= (UINT8
*) ((UINTN
) &PadAndCRC32
[1] - PadLen
);
1071 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1072 if (DataSeg
== NULL
) {
1073 Status
= EFI_OUT_OF_RESOURCES
;
1079 case ISCSI_OPCODE_SCSI_RSP
:
1080 case ISCSI_OPCODE_NOP_IN
:
1081 case ISCSI_OPCODE_LOGIN_RSP
:
1082 case ISCSI_OPCODE_TEXT_RSP
:
1083 case ISCSI_OPCODE_ASYNC_MSG
:
1084 case ISCSI_OPCODE_REJECT
:
1085 case ISCSI_OPCODE_VENDOR_T0
:
1086 case ISCSI_OPCODE_VENDOR_T1
:
1087 case ISCSI_OPCODE_VENDOR_T2
:
1089 // Allocate buffer to receive the data segment.
1091 Len
+= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1092 DataSeg
= NetbufAlloc (Len
);
1093 if (DataSeg
== NULL
) {
1094 Status
= EFI_OUT_OF_RESOURCES
;
1098 NetbufAllocSpace (DataSeg
, Len
, NET_BUF_TAIL
);
1102 Status
= EFI_PROTOCOL_ERROR
;
1106 InsertTailList (NbufList
, &DataSeg
->List
);
1109 // Receive the data segment with the data digest if any.
1111 Status
= Tcp4IoReceive (&Conn
->Tcp4Io
, DataSeg
, FALSE
, TimeoutEvent
);
1112 if (EFI_ERROR (Status
)) {
1118 // TODO: Check the data digest.
1120 NetbufTrim (DataSeg
, sizeof (UINT32
), NET_BUF_TAIL
);
1125 // Trim off the padding bytes in the data segment.
1127 NetbufTrim (DataSeg
, PadLen
, NET_BUF_TAIL
);
1132 // Form the pdu from a list of pdu segments.
1134 *Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
1136 Status
= EFI_OUT_OF_RESOURCES
;
1141 if (EFI_ERROR (Status
)) {
1143 // Free the Nbufs in this NbufList and the NbufList itself.
1145 IScsiFreeNbufList (NbufList
);
1152 Check and get the result of the prameter negotiation.
1154 @param Conn[in] The connection in iSCSI login.
1156 @param Pdu[in] The iSCSI response PDU containing the parameter list.
1158 @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
1160 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error happened.
1164 IScsiCheckOpParams (
1165 IN ISCSI_CONNECTION
*Conn
,
1170 LIST_ENTRY
*KeyValueList
;
1173 ISCSI_SESSION
*Session
;
1177 ASSERT (Conn
->RspQue
.BufNum
!= 0);
1179 Session
= Conn
->Session
;
1181 Len
= Conn
->RspQue
.BufSize
;
1182 Data
= AllocatePool (Len
);
1184 return EFI_OUT_OF_RESOURCES
;
1187 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, (UINT8
*) Data
);
1189 Status
= EFI_PROTOCOL_ERROR
;
1192 // Extract the Key-Value pairs into a list.
1194 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1195 if (KeyValueList
== NULL
) {
1196 gBS
->FreePool (Data
);
1202 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_HEADER_DIGEST
);
1203 if (Value
== NULL
) {
1207 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1208 if (Conn
->HeaderDigest
!= ISCSI_DIGEST_CRC32
) {
1211 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1212 Conn
->HeaderDigest
= ISCSI_DIGEST_NONE
;
1219 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_DIGEST
);
1220 if (Value
== NULL
) {
1224 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1225 if (Conn
->DataDigest
!= ISCSI_DIGEST_CRC32
) {
1228 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1229 Conn
->DataDigest
= ISCSI_DIGEST_NONE
;
1234 // ErrorRecoveryLevel, result fuction is Minimum.
1236 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
);
1237 if (Value
== NULL
) {
1241 NumericValue
= AsciiStrDecimalToUintn (Value
);
1242 if (NumericValue
> 2) {
1246 Session
->ErrorRecoveryLevel
= (UINT8
) MIN (Session
->ErrorRecoveryLevel
, NumericValue
);
1249 // InitialR2T, result function is OR.
1251 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1252 if (Value
== NULL
) {
1256 Session
->InitialR2T
= (BOOLEAN
) (Session
->InitialR2T
|| (AsciiStrCmp (Value
, "Yes") == 0));
1259 // ImmediateData, result function is AND.
1261 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_IMMEDIATE_DATA
);
1262 if (Value
== NULL
) {
1266 Session
->ImmediateData
= (BOOLEAN
) (Session
->ImmediateData
&& (AsciiStrCmp (Value
, "Yes") == 0));
1269 // MaxRecvDataSegmentLength, result function is Mininum.
1271 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
);
1272 if (Value
!= NULL
) {
1274 // MaxRecvDataSegmentLength is declarative.
1276 NumericValue
= AsciiStrDecimalToUintn (Value
);
1278 Conn
->MaxRecvDataSegmentLength
= (UINT32
) MIN (Conn
->MaxRecvDataSegmentLength
, NumericValue
);
1281 // MaxBurstLength, result funtion is Mininum.
1283 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_BURST_LENGTH
);
1284 if (Value
== NULL
) {
1288 NumericValue
= AsciiStrDecimalToUintn (Value
);
1289 Session
->MaxBurstLength
= (UINT32
) MIN (Session
->MaxBurstLength
, NumericValue
);
1292 // FirstBurstLength, result function is Minimum. Irrelevant when InitialR2T=Yes and
1293 // ImmediateData=No.
1295 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1296 if ((Value
== NULL
) && !(Session
->InitialR2T
&& !Session
->ImmediateData
)) {
1300 NumericValue
= AsciiStrDecimalToUintn (Value
);
1301 Session
->FirstBurstLength
= (UINT32
) MIN (Session
->FirstBurstLength
, NumericValue
);
1304 // MaxConnections, result function is Minimum.
1306 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_CONNECTIONS
);
1307 if (Value
== NULL
) {
1311 NumericValue
= AsciiStrDecimalToUintn (Value
);
1312 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1316 Session
->MaxConnections
= (UINT32
) MIN (Session
->MaxConnections
, NumericValue
);
1319 // DataPDUInOrder, result function is OR.
1321 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1322 if (Value
== NULL
) {
1326 Session
->DataPDUInOrder
= (BOOLEAN
) (Session
->DataPDUInOrder
|| (AsciiStrCmp (Value
, "Yes") == 0));
1329 // DataSequenceInorder, result function is OR.
1331 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1332 if (Value
== NULL
) {
1336 Session
->DataSequenceInOrder
= (BOOLEAN
) (Session
->DataSequenceInOrder
|| (AsciiStrCmp (Value
, "Yes") == 0));
1339 // DefaultTime2Wait, result function is Maximum.
1341 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2WAIT
);
1342 if (Value
== NULL
) {
1346 NumericValue
= AsciiStrDecimalToUintn (Value
);
1347 if (NumericValue
== 0) {
1348 Session
->DefaultTime2Wait
= 0;
1349 } else if (NumericValue
> 3600) {
1352 Session
->DefaultTime2Wait
= (UINT32
) MAX (Session
->DefaultTime2Wait
, NumericValue
);
1355 // DefaultTime2Retain, result function is Minimum.
1357 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2RETAIN
);
1358 if (Value
== NULL
) {
1362 NumericValue
= AsciiStrDecimalToUintn (Value
);
1363 if (NumericValue
== 0) {
1364 Session
->DefaultTime2Retain
= 0;
1365 } else if (NumericValue
> 3600) {
1368 Session
->DefaultTime2Retain
= (UINT32
) MIN (Session
->DefaultTime2Retain
, NumericValue
);
1371 // MaxOutstandingR2T, result function is Minimum.
1373 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_OUTSTANDING_R2T
);
1374 if (Value
== NULL
) {
1378 NumericValue
= AsciiStrDecimalToUintn (Value
);
1379 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1383 Session
->MaxOutstandingR2T
= (UINT16
) MIN (Session
->MaxOutstandingR2T
, NumericValue
);
1386 // Remove declarative key-value paris if any.
1388 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_SESSION_TYPE
);
1389 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ALIAS
);
1390 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
);
1392 if (IsListEmpty (KeyValueList
)) {
1394 // Succeed if no more keys in the list.
1396 Status
= EFI_SUCCESS
;
1401 IScsiFreeKeyValueList (KeyValueList
);
1403 gBS
->FreePool (Data
);
1409 Fill the oprational prameters.
1411 @param Conn[in] The connection in iSCSI login.
1413 @param Pdu[in] The iSCSI login request PDU to fill the parameters.
1415 @retval EFI_SUCCESS The parmeters are filled into the iSCSI login request PDU.
1417 @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to hold the parameters.
1422 IN ISCSI_CONNECTION
*Conn
,
1426 ISCSI_SESSION
*Session
;
1429 Session
= Conn
->Session
;
1431 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->HeaderDigest
== ISCSI_DIGEST_CRC32
) ? "None,CRC32" : "None");
1432 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_HEADER_DIGEST
, Value
);
1434 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->DataDigest
== ISCSI_DIGEST_CRC32
) ? "None,CRC32" : "None");
1435 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_DIGEST
, Value
);
1437 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->ErrorRecoveryLevel
);
1438 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
, Value
);
1440 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->InitialR2T
? "Yes" : "No");
1441 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_INITIAL_R2T
, Value
);
1443 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->ImmediateData
? "Yes" : "No");
1444 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_IMMEDIATE_DATA
, Value
);
1446 AsciiSPrint (Value
, sizeof (Value
), "%d", Conn
->MaxRecvDataSegmentLength
);
1447 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
, Value
);
1449 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxBurstLength
);
1450 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_BURST_LENGTH
, Value
);
1452 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->FirstBurstLength
);
1453 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_FIRST_BURST_LENGTH
, Value
);
1455 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxConnections
);
1456 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_CONNECTIONS
, Value
);
1458 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataPDUInOrder
? "Yes" : "No");
1459 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_PDU_IN_ORDER
, Value
);
1461 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataSequenceInOrder
? "Yes" : "No");
1462 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
, Value
);
1464 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Wait
);
1465 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2WAIT
, Value
);
1467 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Retain
);
1468 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2RETAIN
, Value
);
1470 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxOutstandingR2T
);
1471 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_OUTSTANDING_R2T
, Value
);
1477 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
1479 @param Pdu[in] The iSCSI pdu which contains segments to pad.
1481 @param Len[in] The length of the last semgnet in the PDU.
1483 @retval EFI_SUCCESS The segment is padded or no need to pad it.
1485 @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
1498 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1501 Data
= NetbufAllocSpace (Pdu
, PadLen
, NET_BUF_TAIL
);
1503 return EFI_OUT_OF_RESOURCES
;
1506 ZeroMem (Data
, PadLen
);
1513 Build a key-value list from the data segment.
1515 @param Data[in] The data segment containing the key-value pairs.
1517 @param Len[in] Length of the data segment.
1519 @retval The key-value list.
1523 IScsiBuildKeyValueList (
1528 LIST_ENTRY
*ListHead
;
1529 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1531 ListHead
= AllocatePool (sizeof (LIST_ENTRY
));
1532 if (ListHead
== NULL
) {
1536 InitializeListHead (ListHead
);
1539 KeyValuePair
= AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR
));
1540 if (KeyValuePair
== NULL
) {
1544 InitializeListHead (&KeyValuePair
->List
);
1546 KeyValuePair
->Key
= Data
;
1548 while ((Len
> 0) && (*Data
!= '=')) {
1559 gBS
->FreePool (KeyValuePair
);
1563 KeyValuePair
->Value
= Data
;
1565 InsertTailList (ListHead
, &KeyValuePair
->List
);;
1567 Data
+= AsciiStrLen (KeyValuePair
->Value
) + 1;
1568 Len
-= (UINT32
) AsciiStrLen (KeyValuePair
->Value
) + 1;
1575 IScsiFreeKeyValueList (ListHead
);
1581 Get the value string by the key name from the key-value list. If found,
1582 the key-value entry will be removed from the list.
1584 @param KeyValueList[in] The key-value list.
1586 @param Key[in] The key name to find.
1588 @retval The value string.
1592 IScsiGetValueByKeyFromList (
1593 IN LIST_ENTRY
*KeyValueList
,
1598 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1603 NET_LIST_FOR_EACH (Entry
, KeyValueList
) {
1604 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1606 if (AsciiStrCmp (KeyValuePair
->Key
, Key
) == 0) {
1607 Value
= KeyValuePair
->Value
;
1609 RemoveEntryList (&KeyValuePair
->List
);
1610 gBS
->FreePool (KeyValuePair
);
1619 Free the key-value list.
1621 @param KeyValueList[in] The key-value list.
1627 IScsiFreeKeyValueList (
1628 IN LIST_ENTRY
*KeyValueList
1632 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1634 while (!IsListEmpty (KeyValueList
)) {
1635 Entry
= NetListRemoveHead (KeyValueList
);
1636 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1638 gBS
->FreePool (KeyValuePair
);
1641 gBS
->FreePool (KeyValueList
);
1645 Normalize the iSCSI name according to RFC.
1647 @param Name[in] The iSCSI name.
1649 @param Len[in] length of the iSCSI name.
1651 @retval EFI_SUCCESS The iSCSI name is valid and normalized.
1653 @retval EFI_PROTOCOL_ERROR The iSCSI name is mal-formatted or not in the IQN format.
1657 IScsiNormalizeName (
1664 for (Index
= 0; Index
< Len
; Index
++) {
1665 if (NET_IS_UPPER_CASE_CHAR (Name
[Index
])) {
1667 // Convert the upper-case characters to lower-case ones
1669 Name
[Index
] = (CHAR8
) (Name
[Index
] - 'A' + 'a');
1672 if (!NET_IS_LOWER_CASE_CHAR (Name
[Index
]) &&
1673 !NET_IS_DIGIT (Name
[Index
]) &&
1674 (Name
[Index
] != '-') &&
1675 (Name
[Index
] != '.') &&
1676 (Name
[Index
] != ':')
1679 // ASCII dash, dot, colon lower-case characters and digit characters
1682 return EFI_PROTOCOL_ERROR
;
1686 if ((Len
< 4) || (CompareMem (Name
, "iqn.", 4) != 0)) {
1688 // Only IQN format is accepted now.
1690 return EFI_PROTOCOL_ERROR
;
1697 Create an iSCSI task control block.
1699 @param Conn[in] The connection on which the task control block will be created.
1701 @param Tcb[out] The newly created task control block.
1703 @retval EFI_SUCCESS The task control block is created.
1705 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1710 IN ISCSI_CONNECTION
*Conn
,
1714 ISCSI_SESSION
*Session
;
1717 ASSERT (Tcb
!= NULL
);
1719 Session
= Conn
->Session
;
1721 if (ISCSI_SEQ_GT (Session
->CmdSN
, Session
->MaxCmdSN
)) {
1722 return EFI_NOT_READY
;
1725 NewTcb
= AllocateZeroPool (sizeof (ISCSI_TCB
));
1726 if (NewTcb
== NULL
) {
1727 return EFI_OUT_OF_RESOURCES
;
1730 InitializeListHead (&NewTcb
->Link
);
1732 NewTcb
->SoFarInOrder
= TRUE
;
1733 NewTcb
->InitiatorTaskTag
= Session
->InitiatorTaskTag
;
1734 NewTcb
->CmdSN
= Session
->CmdSN
;
1735 NewTcb
->Conn
= Conn
;
1737 InsertTailList (&Session
->TcbList
, &NewTcb
->Link
);
1740 // Advance the initiator task tag.
1742 Session
->InitiatorTaskTag
++;
1751 Delete the tcb from the connection and destroy it.
1753 @param Tcb The tcb to delete.
1763 RemoveEntryList (&Tcb
->Link
);
1765 gBS
->FreePool (Tcb
);
1769 Find the task control block by the initator task tag.
1771 @param TcbList[in] The tcb list.
1773 @param InitiatorTaskTag[in] The initiator task tag.
1775 @retval The task control block found.
1780 IN LIST_ENTRY
*TcbList
,
1781 IN UINT32 InitiatorTaskTag
1789 NET_LIST_FOR_EACH (Entry
, TcbList
) {
1790 Tcb
= NET_LIST_USER_STRUCT (Entry
, ISCSI_TCB
, Link
);
1792 if (Tcb
->InitiatorTaskTag
== InitiatorTaskTag
) {
1803 Create a data segment, pad it and calculate the CRC if needed.
1805 @param Data[in] The data to fill into the data segment.
1807 @param Len[in] Length of the data.
1809 @param DataDigest[in] Whether to calculate CRC for this data segment.
1811 @retval The net buffer wrapping the data segment.
1815 IScsiNewDataSegment (
1818 IN BOOLEAN DataDigest
1821 NET_FRAGMENT Fragment
[2];
1822 UINT32 FragmentCount
;
1826 Fragment
[0].Len
= Len
;
1827 Fragment
[0].Bulk
= Data
;
1829 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1831 Fragment
[1].Len
= PadLen
;
1832 Fragment
[1].Bulk
= (UINT8
*) &mDataSegPad
;
1839 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1845 Create a iSCSI SCSI command PDU to encapsulate the command issued
1846 by SCSI through the EXT SCSI PASS THRU Protocol.
1848 @param Packet[in] The EXT SCSI PASS THRU request packet containing the SCSI command.
1850 @param Lun[in] The LUN.
1852 @param Tcb[in] The tcb assocated with this SCSI command.
1854 @retval The created iSCSI SCSI command PDU.
1858 IScsiNewScsiCmdPdu (
1859 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
1864 LIST_ENTRY
*NbufList
;
1868 SCSI_COMMAND
*ScsiCmd
;
1871 ISCSI_ADDITIONAL_HEADER
*Header
;
1872 ISCSI_BI_EXP_READ_DATA_LEN_AHS
*BiExpReadDataLenAHS
;
1873 ISCSI_SESSION
*Session
;
1874 UINT32 ImmediateDataLen
;
1878 if (Packet
->DataDirection
== DataBi
) {
1880 // Bi directional Read/Write command, the bidirectional expected
1881 // read data length AHS is required.
1883 AHSLength
+= sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS
);
1886 if (Packet
->CdbLength
> 16) {
1888 // The CDB exceeds 16 bytes, an extended CDB AHS is required.
1890 AHSLength
= (UINT8
) (AHSLength
+ (ISCSI_ROUNDUP (Packet
->CdbLength
- 16) + sizeof (ISCSI_ADDITIONAL_HEADER
)));
1893 Length
= sizeof (SCSI_COMMAND
) + AHSLength
;
1894 PduHeader
= NetbufAlloc (Length
);
1895 if (PduHeader
== NULL
) {
1899 ScsiCmd
= (SCSI_COMMAND
*) NetbufAllocSpace (PduHeader
, Length
, NET_BUF_TAIL
);
1900 Header
= (ISCSI_ADDITIONAL_HEADER
*) (ScsiCmd
+ 1);
1902 ZeroMem (ScsiCmd
, Length
);
1904 ISCSI_SET_OPCODE (ScsiCmd
, ISCSI_OPCODE_SCSI_CMD
, 0);
1905 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_TASK_ATTR_SIMPLE
);
1908 // Set the READ/WRITE flags according to the IO type of this request.
1910 switch (Packet
->DataDirection
) {
1912 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
);
1913 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->InTransferLength
);
1917 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_WRITE
);
1918 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
1922 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
| SCSI_CMD_PDU_FLAG_WRITE
);
1923 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
1926 // Fill the bidirectional expected read data length AHS.
1928 BiExpReadDataLenAHS
= (ISCSI_BI_EXP_READ_DATA_LEN_AHS
*) Header
;
1929 Header
= (ISCSI_ADDITIONAL_HEADER
*) (BiExpReadDataLenAHS
+ 1);
1931 BiExpReadDataLenAHS
->Length
= NTOHS (5);
1932 BiExpReadDataLenAHS
->Type
= ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN
;
1933 BiExpReadDataLenAHS
->ExpReadDataLength
= NTOHL (Packet
->InTransferLength
);
1938 ScsiCmd
->TotalAHSLength
= AHSLength
;
1939 CopyMem (ScsiCmd
->Lun
, &Lun
, sizeof (ScsiCmd
->Lun
));
1940 ScsiCmd
->InitiatorTaskTag
= NTOHL (Tcb
->InitiatorTaskTag
);
1941 ScsiCmd
->CmdSN
= NTOHL (Tcb
->CmdSN
);
1942 ScsiCmd
->ExpStatSN
= NTOHL (Tcb
->Conn
->ExpStatSN
);
1944 CopyMem (ScsiCmd
->CDB
, Packet
->Cdb
, sizeof (ScsiCmd
->CDB
));
1946 if (Packet
->CdbLength
> 16) {
1947 Header
->Length
= NTOHS (Packet
->CdbLength
- 15);
1948 Header
->Type
= ISCSI_AHS_TYPE_EXT_CDB
;
1950 CopyMem (Header
+ 1, (UINT8
*) Packet
->Cdb
+ 16, Packet
->CdbLength
- 16);
1954 Session
= Tcb
->Conn
->Session
;
1955 ImmediateDataLen
= 0;
1957 if (Session
->ImmediateData
&& (Packet
->OutTransferLength
!= 0)) {
1959 // Send immediate data in this SCSI Command PDU. The length of the immeidate
1960 // data is the minimum of FirstBurstLength, the data length to be xfered and
1961 // the MaxRecvdataSegmentLength on this connection.
1963 ImmediateDataLen
= MIN (Session
->FirstBurstLength
, Packet
->OutTransferLength
);
1964 ImmediateDataLen
= MIN (ImmediateDataLen
, Tcb
->Conn
->MaxRecvDataSegmentLength
);
1967 // Update the data segment length in the PDU header.
1969 ISCSI_SET_DATASEG_LEN (ScsiCmd
, ImmediateDataLen
);
1972 // Create the data segment.
1974 DataSeg
= IScsiNewDataSegment ((UINT8
*) Packet
->OutDataBuffer
, ImmediateDataLen
, FALSE
);
1975 if (DataSeg
== NULL
) {
1976 NetbufFree (PduHeader
);
1981 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
1982 if (NbufList
== NULL
) {
1983 NetbufFree (PduHeader
);
1984 NetbufFree (DataSeg
);
1990 InitializeListHead (NbufList
);
1991 InsertTailList (NbufList
, &PduHeader
->List
);
1992 InsertTailList (NbufList
, &DataSeg
->List
);
1994 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
1996 IScsiFreeNbufList (NbufList
);
2000 if (Session
->InitialR2T
||
2001 (ImmediateDataLen
== Session
->FirstBurstLength
) ||
2002 (ImmediateDataLen
== Packet
->OutTransferLength
)
2005 // Unsolicited data out sequence is not allowed,
2006 // or FirstBustLength data is already sent out by immediate data
2007 // or all the OUT data accompany this SCSI packet is sent as
2008 // immediate data, the final flag should be set on this SCSI Command
2011 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_BHS_FLAG_FINAL
);
2020 Create a new iSCSI SCSI Data Out PDU.
2022 @param Data[in] The data to put into the Data Out PDU.
2024 @param Len[in] Length of the data.
2026 @param DataSN[in] The DataSN of the Data Out PDU.
2028 @param Tcb[in] The task control block of this Data Out PDU.
2030 @param Lun[in] The LUN.
2032 @retval The net buffer wrapping the Data Out PDU.
2036 IScsiNewDataOutPdu (
2044 LIST_ENTRY
*NbufList
;
2048 ISCSI_SCSI_DATA_OUT
*DataOutHdr
;
2049 ISCSI_XFER_CONTEXT
*XferContext
;
2051 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2052 if (NbufList
== NULL
) {
2056 InitializeListHead (NbufList
);
2059 // Allocate memory for the BHS.
2061 PduHdr
= NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT
));
2062 if (PduHdr
== NULL
) {
2063 gBS
->FreePool (NbufList
);
2067 // Insert the BHS into the buffer list.
2069 InsertTailList (NbufList
, &PduHdr
->List
);
2071 DataOutHdr
= (ISCSI_SCSI_DATA_OUT
*) NetbufAllocSpace (PduHdr
, sizeof (ISCSI_SCSI_DATA_OUT
), NET_BUF_TAIL
);
2072 XferContext
= &Tcb
->XferContext
;
2074 ZeroMem (DataOutHdr
, sizeof (ISCSI_SCSI_DATA_OUT
));
2077 // Set the flags and fields of the Data Out PDU BHS.
2079 ISCSI_SET_OPCODE (DataOutHdr
, ISCSI_OPCODE_SCSI_DATA_OUT
, 0);
2080 ISCSI_SET_DATASEG_LEN (DataOutHdr
, Len
);
2082 DataOutHdr
->InitiatorTaskTag
= HTONL (Tcb
->InitiatorTaskTag
);
2083 DataOutHdr
->TargetTransferTag
= HTONL (XferContext
->TargetTransferTag
);
2084 DataOutHdr
->ExpStatSN
= HTONL (Tcb
->Conn
->ExpStatSN
);
2085 DataOutHdr
->DataSN
= HTONL (DataSN
);
2086 DataOutHdr
->BufferOffset
= HTONL (XferContext
->Offset
);
2088 if (XferContext
->TargetTransferTag
!= ISCSI_RESERVED_TAG
) {
2089 CopyMem (&DataOutHdr
->Lun
, &Lun
, sizeof (DataOutHdr
->Lun
));
2092 // Build the data segment for this Data Out PDU.
2094 DataSeg
= IScsiNewDataSegment (Data
, Len
, FALSE
);
2095 if (DataSeg
== NULL
) {
2096 IScsiFreeNbufList (NbufList
);
2100 // Put the data segment into the buffer list and combine it with the BHS
2101 // into a full Data Out PDU.
2103 InsertTailList (NbufList
, &DataSeg
->List
);
2104 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2106 IScsiFreeNbufList (NbufList
);
2113 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
2115 @param Data[in] The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
2117 @param Tcb[in] The task control block of the data to send out.
2119 @param Lun[in] The LUN the data will be sent to.
2121 @retval A list of net buffers with each of them wraps an iSCSI SCSI Data Out PDU.
2125 IScsiGenerateDataOutPduSequence (
2131 LIST_ENTRY
*PduList
;
2134 NET_BUF
*DataOutPdu
;
2135 ISCSI_CONNECTION
*Conn
;
2136 ISCSI_XFER_CONTEXT
*XferContext
;
2138 PduList
= AllocatePool (sizeof (LIST_ENTRY
));
2139 if (PduList
== NULL
) {
2143 InitializeListHead (PduList
);
2148 XferContext
= &Tcb
->XferContext
;
2150 while (XferContext
->DesiredLength
> 0) {
2152 // Determine the length of data this Data Out PDU can carry.
2154 DataLen
= MIN (XferContext
->DesiredLength
, Conn
->MaxRecvDataSegmentLength
);
2157 // Create a Data Out PDU.
2159 DataOutPdu
= IScsiNewDataOutPdu (Data
, DataLen
, DataSN
, Tcb
, Lun
);
2160 if (DataOutPdu
== NULL
) {
2161 IScsiFreeNbufList (PduList
);
2167 InsertTailList (PduList
, &DataOutPdu
->List
);
2170 // Update the context and DataSN.
2172 XferContext
->Offset
+= DataLen
;
2173 XferContext
->DesiredLength
-= DataLen
;
2178 // Set the F bit for the last data out PDU in this sequence.
2180 ISCSI_SET_FLAG (NetbufGetByte (DataOutPdu
, 0, NULL
), ISCSI_BHS_FLAG_FINAL
);
2188 Send the Data in a sequence of Data Out PDUs one by one.
2190 @param Data[in] The data to carry by Data Out PDUs.
2192 @param Lun[in] The LUN the data will be sent to.
2194 @param Tcb[in] The task control block.
2196 @retval EFI_SUCCES The data is sent out to the LUN.
2198 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2202 IScsiSendDataOutPduSequence (
2208 LIST_ENTRY
*DataOutPduList
;
2214 // Generate the Data Out PDU sequence.
2216 DataOutPduList
= IScsiGenerateDataOutPduSequence (Data
, Tcb
, Lun
);
2217 if (DataOutPduList
== NULL
) {
2218 return EFI_OUT_OF_RESOURCES
;
2221 Status
= EFI_SUCCESS
;
2224 // Send the Data Out PDU's one by one.
2226 NET_LIST_FOR_EACH (Entry
, DataOutPduList
) {
2227 Pdu
= NET_LIST_USER_STRUCT (Entry
, NET_BUF
, List
);
2229 Status
= Tcp4IoTransmit (&Tcb
->Conn
->Tcp4Io
, Pdu
);
2230 if (EFI_ERROR (Status
)) {
2235 IScsiFreeNbufList (DataOutPduList
);
2241 Process the received iSCSI SCSI Data In PDU.
2243 @param Pdu[in] The Data In PDU received.
2245 @param Tcb[in] The task control block.
2247 @param Packet[in][out] The EXT SCSI PASS THRU request packet.
2249 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
2252 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
2259 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2262 ISCSI_SCSI_DATA_IN
*DataInHdr
;
2265 DataInHdr
= (ISCSI_SCSI_DATA_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2267 DataInHdr
->InitiatorTaskTag
= NTOHL (DataInHdr
->InitiatorTaskTag
);
2268 DataInHdr
->ExpCmdSN
= NTOHL (DataInHdr
->ExpCmdSN
);
2269 DataInHdr
->MaxCmdSN
= NTOHL (DataInHdr
->MaxCmdSN
);
2270 DataInHdr
->DataSN
= NTOHL (DataInHdr
->DataSN
);
2273 // Check the DataSN.
2275 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, DataInHdr
->DataSN
);
2276 if (EFI_ERROR (Status
)) {
2280 if (DataInHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2281 return EFI_PROTOCOL_ERROR
;
2284 // Update the command related sequence numbers.
2286 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, DataInHdr
->MaxCmdSN
, DataInHdr
->ExpCmdSN
);
2288 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID
)) {
2289 if (!ISCSI_FLAG_ON (DataInHdr
, ISCSI_BHS_FLAG_FINAL
)) {
2291 // The S bit is on but the F bit is off.
2293 return EFI_PROTOCOL_ERROR
;
2296 Tcb
->StatusXferd
= TRUE
;
2298 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_OVERFLOW
| SCSI_DATA_IN_PDU_FLAG_UNDERFLOW
)) {
2300 // Underflow and Overflow are mutual flags.
2302 return EFI_PROTOCOL_ERROR
;
2305 // S bit is on, the StatSN is valid.
2307 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NTOHL (DataInHdr
->StatSN
));
2308 if (EFI_ERROR (Status
)) {
2312 Packet
->HostAdapterStatus
= 0;
2313 Packet
->TargetStatus
= DataInHdr
->Status
;
2315 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2316 Packet
->InTransferLength
+= NTOHL (DataInHdr
->ResidualCount
);
2317 Status
= EFI_BAD_BUFFER_SIZE
;
2320 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2321 Packet
->InTransferLength
-= NTOHL (DataInHdr
->ResidualCount
);
2329 Process the received iSCSI R2T PDU.
2331 @param Pdu[in] The R2T PDU received.
2333 @param Tcb[in] The task control block.
2335 @param Lun[in] The Lun.
2337 @param Packet[in][out] The EXT SCSI PASS THRU request packet.
2339 @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.
2341 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
2349 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2352 ISCSI_READY_TO_TRANSFER
*R2THdr
;
2354 ISCSI_XFER_CONTEXT
*XferContext
;
2357 R2THdr
= (ISCSI_READY_TO_TRANSFER
*) NetbufGetByte (Pdu
, 0, NULL
);
2359 R2THdr
->InitiatorTaskTag
= NTOHL (R2THdr
->InitiatorTaskTag
);
2360 R2THdr
->TargetTransferTag
= NTOHL (R2THdr
->TargetTransferTag
);
2361 R2THdr
->StatSN
= NTOHL (R2THdr
->StatSN
);
2362 R2THdr
->R2TSN
= NTOHL (R2THdr
->R2TSN
);
2363 R2THdr
->BufferOffset
= NTOHL (R2THdr
->BufferOffset
);
2364 R2THdr
->DesiredDataTransferLength
= NTOHL (R2THdr
->DesiredDataTransferLength
);
2366 if ((R2THdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) || !ISCSI_SEQ_EQ (R2THdr
->StatSN
, Tcb
->Conn
->ExpStatSN
)) {
2367 return EFI_PROTOCOL_ERROR
;;
2370 // Check the sequence number.
2372 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, R2THdr
->R2TSN
);
2373 if (EFI_ERROR (Status
)) {
2377 XferContext
= &Tcb
->XferContext
;
2378 XferContext
->TargetTransferTag
= R2THdr
->TargetTransferTag
;
2379 XferContext
->Offset
= R2THdr
->BufferOffset
;
2380 XferContext
->DesiredLength
= R2THdr
->DesiredDataTransferLength
;
2382 if (((XferContext
->Offset
+ XferContext
->DesiredLength
) > Packet
->OutTransferLength
) ||
2383 (XferContext
->DesiredLength
> Tcb
->Conn
->Session
->MaxBurstLength
)
2385 return EFI_PROTOCOL_ERROR
;
2388 // Send the data solicited by this R2T.
2390 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2391 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2397 Process the received iSCSI SCSI Response PDU.
2399 @param Pdu[in] The Response PDU received.
2401 @param Tcb[in] The task control block.
2403 @param Packet[in][out] The EXT SCSI PASS THRU request packet.
2405 @retval EFI_SUCCES The Response PDU is processed.
2407 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
2411 IScsiOnScsiRspRcvd (
2414 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2417 SCSI_RESPONSE
*ScsiRspHdr
;
2418 ISCSI_SENSE_DATA
*SenseData
;
2422 ScsiRspHdr
= (SCSI_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
2424 ScsiRspHdr
->InitiatorTaskTag
= NTOHL (ScsiRspHdr
->InitiatorTaskTag
);
2425 if (ScsiRspHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2426 return EFI_PROTOCOL_ERROR
;
2429 ScsiRspHdr
->StatSN
= NTOHL (ScsiRspHdr
->StatSN
);
2431 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, ScsiRspHdr
->StatSN
);
2432 if (EFI_ERROR (Status
)) {
2436 ScsiRspHdr
->MaxCmdSN
= NTOHL (ScsiRspHdr
->MaxCmdSN
);
2437 ScsiRspHdr
->ExpCmdSN
= NTOHL (ScsiRspHdr
->ExpCmdSN
);
2438 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, ScsiRspHdr
->MaxCmdSN
, ScsiRspHdr
->ExpCmdSN
);
2440 Tcb
->StatusXferd
= TRUE
;
2442 Packet
->HostAdapterStatus
= ScsiRspHdr
->Response
;
2443 if (Packet
->HostAdapterStatus
!= ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET
) {
2447 Packet
->TargetStatus
= ScsiRspHdr
->Status
;
2449 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
| SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
) ||
2450 ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
| SCSI_RSP_PDU_FLAG_UNDERFLOW
)
2452 return EFI_PROTOCOL_ERROR
;
2455 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
)) {
2456 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2457 Status
= EFI_BAD_BUFFER_SIZE
;
2460 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
)) {
2461 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2464 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2465 if (Packet
->DataDirection
== DataIn
) {
2466 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2468 Packet
->OutTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2471 Status
= EFI_BAD_BUFFER_SIZE
;
2474 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2475 if (Packet
->DataDirection
== DataIn
) {
2476 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2478 Packet
->OutTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2482 DataSegLen
= ISCSI_GET_DATASEG_LEN (ScsiRspHdr
);
2483 if (DataSegLen
!= 0) {
2484 SenseData
= (ISCSI_SENSE_DATA
*) NetbufGetByte (Pdu
, sizeof (SCSI_RESPONSE
), NULL
);
2486 SenseData
->Length
= NTOHS (SenseData
->Length
);
2488 Packet
->SenseDataLength
= (UINT8
) MIN (SenseData
->Length
, Packet
->SenseDataLength
);
2489 if (Packet
->SenseDataLength
!= 0) {
2490 CopyMem (Packet
->SenseData
, &SenseData
->Data
[0], Packet
->SenseDataLength
);
2493 Packet
->SenseDataLength
= 0;
2500 Process the received NOP In PDU.
2502 @param Pdu[in] The NOP In PDU received.
2504 @param Tcb[in] The task control block.
2506 @retval EFI_SUCCES The NOP In PDU is processed and the related sequence
2507 numbers are updated.
2509 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror happened.
2518 ISCSI_NOP_IN
*NopInHdr
;
2521 NopInHdr
= (ISCSI_NOP_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2523 NopInHdr
->StatSN
= NTOHL (NopInHdr
->StatSN
);
2524 NopInHdr
->ExpCmdSN
= NTOHL (NopInHdr
->ExpCmdSN
);
2525 NopInHdr
->MaxCmdSN
= NTOHL (NopInHdr
->MaxCmdSN
);
2527 if (NopInHdr
->InitiatorTaskTag
== ISCSI_RESERVED_TAG
) {
2528 if (NopInHdr
->StatSN
!= Tcb
->Conn
->ExpStatSN
) {
2529 return EFI_PROTOCOL_ERROR
;
2532 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NopInHdr
->StatSN
);
2533 if (EFI_ERROR (Status
)) {
2538 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, NopInHdr
->MaxCmdSN
, NopInHdr
->ExpCmdSN
);
2544 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
2546 @param PassThru[in] The EXT SCSI PASS THRU protocol.
2548 @param Target[in] The target ID.
2550 @param Lun[in] The LUN.
2552 @param Packet[in][out] The request packet containing IO request, SCSI command
2553 buffer and buffers to read/write.
2555 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
2558 @retval EFI_DEVICE_ERROR Some unexpected error happened.
2562 IScsiExecuteScsiCommand (
2563 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
,
2566 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2570 ISCSI_DRIVER_DATA
*Private
;
2571 ISCSI_SESSION
*Session
;
2572 EFI_EVENT TimeoutEvent
;
2573 ISCSI_CONNECTION
*Conn
;
2576 ISCSI_XFER_CONTEXT
*XferContext
;
2578 ISCSI_IN_BUFFER_CONTEXT InBufferContext
;
2582 Private
= ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru
);
2583 Session
= &Private
->Session
;
2584 Status
= EFI_SUCCESS
;
2586 TimeoutEvent
= NULL
;
2589 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
2590 return EFI_DEVICE_ERROR
;
2593 Conn
= NET_LIST_USER_STRUCT_S (
2594 Session
->Conns
.ForwardLink
,
2597 ISCSI_CONNECTION_SIGNATURE
2600 if (Packet
->Timeout
!= 0) {
2601 Timeout
= MultU64x32 (Packet
->Timeout
, 2);
2604 Status
= IScsiNewTcb (Conn
, &Tcb
);
2605 if (EFI_ERROR (Status
)) {
2609 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2611 Pdu
= IScsiNewScsiCmdPdu (Packet
, Lun
, Tcb
);
2613 Status
= EFI_OUT_OF_RESOURCES
;
2617 XferContext
= &Tcb
->XferContext
;
2618 Buffer
= NetbufGetByte (Pdu
, 0, NULL
);
2619 XferContext
->Offset
= ISCSI_GET_DATASEG_LEN (Buffer
);
2622 // Transmit the SCSI Command PDU.
2624 Status
= Tcp4IoTransmit (&Conn
->Tcp4Io
, Pdu
);
2628 if (EFI_ERROR (Status
)) {
2632 if (!Session
->InitialR2T
&&
2633 (XferContext
->Offset
< Session
->FirstBurstLength
) &&
2634 (XferContext
->Offset
< Packet
->OutTransferLength
)
2637 // Unsolicited Data-Out sequence is allowed, there is remaining SCSI
2638 // OUT data and the limit of FirstBurstLength is not reached.
2640 XferContext
->TargetTransferTag
= ISCSI_RESERVED_TAG
;
2641 XferContext
->DesiredLength
= MIN (
2642 Session
->FirstBurstLength
,
2643 Packet
->OutTransferLength
- XferContext
->Offset
2646 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2647 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2648 if (EFI_ERROR (Status
)) {
2653 InBufferContext
.InData
= (UINT8
*) Packet
->InDataBuffer
;
2654 InBufferContext
.InDataLen
= Packet
->InTransferLength
;
2656 while (!Tcb
->StatusXferd
) {
2658 // Start the timeout timer.
2661 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, Timeout
);
2662 if (EFI_ERROR (Status
)) {
2665 TimeoutEvent
= Conn
->TimeoutEvent
;
2668 // try to receive PDU from target.
2670 Status
= IScsiReceivePdu (Conn
, &Pdu
, &InBufferContext
, FALSE
, FALSE
, TimeoutEvent
);
2671 if (EFI_ERROR (Status
)) {
2675 switch (ISCSI_GET_OPCODE (NetbufGetByte (Pdu
, 0, NULL
))) {
2676 case ISCSI_OPCODE_SCSI_DATA_IN
:
2677 Status
= IScsiOnDataInRcvd (Pdu
, Tcb
, Packet
);
2680 case ISCSI_OPCODE_R2T
:
2681 Status
= IScsiOnR2TRcvd (Pdu
, Tcb
, Lun
, Packet
);
2684 case ISCSI_OPCODE_SCSI_RSP
:
2685 Status
= IScsiOnScsiRspRcvd (Pdu
, Tcb
, Packet
);
2688 case ISCSI_OPCODE_NOP_IN
:
2689 Status
= IScsiOnNopInRcvd (Pdu
, Tcb
);
2692 case ISCSI_OPCODE_VENDOR_T0
:
2693 case ISCSI_OPCODE_VENDOR_T1
:
2694 case ISCSI_OPCODE_VENDOR_T2
:
2696 // These messages are vendor specific, skip them.
2701 Status
= EFI_PROTOCOL_ERROR
;
2707 if (EFI_ERROR (Status
)) {
2714 if (TimeoutEvent
!= NULL
) {
2715 gBS
->SetTimer (TimeoutEvent
, TimerCancel
, 0);
2722 if ((Status
!= EFI_SUCCESS
) && (Status
!= EFI_NOT_READY
)) {
2724 // Reinstate the session.
2726 if (EFI_ERROR (IScsiSessionReinstatement (Private
))) {
2727 Status
= EFI_DEVICE_ERROR
;
2735 Reinstate the session on some error.
2737 @param Private[in] The iSCSI driver data.
2739 @retval EFI_SUCCES The session is reinstated from some error.
2741 @retval other Reinstatement failed.
2745 IScsiSessionReinstatement (
2746 IN ISCSI_DRIVER_DATA
*Private
2749 ISCSI_SESSION
*Session
;
2752 Session
= &Private
->Session
;
2753 ASSERT (Session
->State
== SESSION_STATE_LOGGED_IN
);
2756 // Abort the session and re-init it.
2758 IScsiSessionAbort (Session
);
2759 IScsiSessionInit (Session
, TRUE
);
2764 Status
= IScsiSessionLogin (Private
);
2770 Initialize some session parameters before login.
2772 @param Session[in] The iSCSI session.
2774 @param Recovery[in] Whether the request is from a fresh new start or recovery.
2781 IN ISCSI_SESSION
*Session
,
2788 Session
->Signature
= ISCSI_SESSION_SIGNATURE
;
2789 Session
->State
= SESSION_STATE_FREE
;
2791 Random
= NET_RANDOM (NetRandomInitSeed ());
2793 Session
->ISID
[0] = ISID_BYTE_0
;
2794 Session
->ISID
[1] = ISID_BYTE_1
;
2795 Session
->ISID
[2] = ISID_BYTE_2
;
2796 Session
->ISID
[3] = ISID_BYTE_3
;
2797 Session
->ISID
[4] = (UINT8
) Random
;
2798 Session
->ISID
[5] = (UINT8
) (Random
>> 8);
2800 InitializeListHead (&Session
->Conns
);
2801 InitializeListHead (&Session
->TcbList
);
2807 Session
->InitiatorTaskTag
= 1;
2808 Session
->NextCID
= 1;
2810 Session
->TargetPortalGroupTag
= 0;
2811 Session
->MaxConnections
= ISCSI_MAX_CONNS_PER_SESSION
;
2812 Session
->InitialR2T
= FALSE
;
2813 Session
->ImmediateData
= TRUE
;
2814 Session
->MaxBurstLength
= 262144;
2815 Session
->FirstBurstLength
= MAX_RECV_DATA_SEG_LEN_IN_FFP
;
2816 Session
->DefaultTime2Wait
= 2;
2817 Session
->DefaultTime2Retain
= 20;
2818 Session
->MaxOutstandingR2T
= DEFAULT_MAX_OUTSTANDING_R2T
;
2819 Session
->DataPDUInOrder
= TRUE
;
2820 Session
->DataSequenceInOrder
= TRUE
;
2821 Session
->ErrorRecoveryLevel
= 0;
2825 Abort the iSCSI session, that is, reset all the connection and free the
2828 @param Session[in] The iSCSI session.
2830 @retval EFI_SUCCES The session is aborted.
2835 IN ISCSI_SESSION
*Session
2838 ISCSI_DRIVER_DATA
*Private
;
2839 ISCSI_CONNECTION
*Conn
;
2841 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
2845 ASSERT (!IsListEmpty (&Session
->Conns
));
2847 Private
= ISCSI_DRIVER_DATA_FROM_SESSION (Session
);
2849 while (!IsListEmpty (&Session
->Conns
)) {
2850 Conn
= NET_LIST_USER_STRUCT_S (
2851 Session
->Conns
.ForwardLink
,
2854 ISCSI_CONNECTION_SIGNATURE
2857 gBS
->CloseProtocol (
2858 Conn
->Tcp4Io
.Handle
,
2859 &gEfiTcp4ProtocolGuid
,
2861 Private
->ExtScsiPassThruHandle
2864 IScsiConnReset (Conn
);
2866 IScsiDetatchConnection (Conn
);
2867 IScsiDestroyConnection (Conn
);
2870 Session
->State
= SESSION_STATE_FAILED
;