2 The implementation of iSCSI protocol based on RFC3720.
4 Copyright (c) 2004 - 2016, 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
= NvData
->IpMode
== IP_MODE_IP6
|| Session
->ConfigData
->AutoConfigureMode
== IP_MODE_AUTOCONFIG_IP6
;
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
;
264 Tcp4IoConfig
->StationPort
= 0;
266 Tcp6IoConfig
= &TcpIoConfig
.Tcp6IoConfigData
;
268 CopyMem (&Tcp6IoConfig
->RemoteIp
, &NvData
->TargetIp
, sizeof (EFI_IPv6_ADDRESS
));
269 Tcp6IoConfig
->RemotePort
= NvData
->TargetPort
;
270 Tcp6IoConfig
->ActiveFlag
= TRUE
;
271 Tcp6IoConfig
->StationPort
= 0;
275 // Create the TCP IO for this connection.
277 Status
= TcpIoCreateSocket (
280 (UINT8
) (!Conn
->Ipv6Flag
? TCP_VERSION_4
: TCP_VERSION_6
),
284 if (EFI_ERROR (Status
)) {
285 gBS
->CloseEvent (Conn
->TimeoutEvent
);
295 Destroy an iSCSI connection.
297 @param[in] Conn The connection to destroy.
301 IScsiDestroyConnection (
302 IN ISCSI_CONNECTION
*Conn
305 TcpIoDestroySocket (&Conn
->TcpIo
);
307 NetbufQueFlush (&Conn
->RspQue
);
308 gBS
->CloseEvent (Conn
->TimeoutEvent
);
313 Retrieve the IPv6 Address/Prefix/Gateway from the established TCP connection, these informations
314 will be filled in the iSCSI Boot Firmware Table.
316 @param[in] Conn The connection used in the iSCSI login phase.
318 @retval EFI_SUCCESS Get the NIC information successfully.
319 @retval Others Other errors as indicated.
324 IN ISCSI_CONNECTION
*Conn
327 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
328 EFI_TCP6_PROTOCOL
*Tcp6
;
329 EFI_IP6_MODE_DATA Ip6ModeData
;
331 EFI_IPv6_ADDRESS
*TargetIp
;
333 UINT8 SubnetPrefixLength
;
336 NvData
= &Conn
->Session
->ConfigData
->SessionConfigData
;
337 TargetIp
= &NvData
->TargetIp
.v6
;
338 Tcp6
= Conn
->TcpIo
.Tcp
.Tcp6
;
340 ZeroMem (&Ip6ModeData
, sizeof (EFI_IP6_MODE_DATA
));
341 Status
= Tcp6
->GetModeData (
349 if (EFI_ERROR (Status
)) {
353 if (!Ip6ModeData
.IsConfigured
) {
354 Status
= EFI_ABORTED
;
358 IP6_COPY_ADDRESS (&NvData
->LocalIp
, &Ip6ModeData
.ConfigData
.StationAddress
);
360 NvData
->PrefixLength
= 0;
361 for (Index
= 0; Index
< Ip6ModeData
.AddressCount
; Index
++) {
362 if (EFI_IP6_EQUAL (&NvData
->LocalIp
.v6
, &Ip6ModeData
.AddressList
[Index
].Address
)) {
363 NvData
->PrefixLength
= Ip6ModeData
.AddressList
[Index
].PrefixLength
;
368 SubnetPrefixLength
= 0;
369 RouteEntry
= Ip6ModeData
.RouteCount
;
370 for (Index
= 0; Index
< Ip6ModeData
.RouteCount
; Index
++) {
371 if (NetIp6IsNetEqual (TargetIp
, &Ip6ModeData
.RouteTable
[Index
].Destination
, Ip6ModeData
.RouteTable
[Index
].PrefixLength
)) {
372 if (SubnetPrefixLength
< Ip6ModeData
.RouteTable
[Index
].PrefixLength
) {
373 SubnetPrefixLength
= Ip6ModeData
.RouteTable
[Index
].PrefixLength
;
378 if (RouteEntry
!= Ip6ModeData
.RouteCount
) {
379 IP6_COPY_ADDRESS (&NvData
->Gateway
, &Ip6ModeData
.RouteTable
[RouteEntry
].Gateway
);
383 if (Ip6ModeData
.AddressList
!= NULL
) {
384 FreePool (Ip6ModeData
.AddressList
);
386 if (Ip6ModeData
.GroupTable
!= NULL
) {
387 FreePool (Ip6ModeData
.GroupTable
);
389 if (Ip6ModeData
.RouteTable
!= NULL
) {
390 FreePool (Ip6ModeData
.RouteTable
);
392 if (Ip6ModeData
.NeighborCache
!= NULL
) {
393 FreePool (Ip6ModeData
.NeighborCache
);
395 if (Ip6ModeData
.PrefixTable
!= NULL
) {
396 FreePool (Ip6ModeData
.PrefixTable
);
398 if (Ip6ModeData
.IcmpTypeList
!= NULL
) {
399 FreePool (Ip6ModeData
.IcmpTypeList
);
406 Login the iSCSI session.
408 @param[in] Session The iSCSI session.
410 @retval EFI_SUCCESS The iSCSI session login procedure finished.
411 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
412 @retval EFI_NO_MEDIA There was a media error.
413 @retval Others Other errors as indicated.
418 IN ISCSI_SESSION
*Session
422 ISCSI_CONNECTION
*Conn
;
424 EFI_GUID
*ProtocolGuid
;
426 BOOLEAN MediaPresent
;
429 // Check media status before session login.
432 NetLibDetectMedia (Session
->Private
->Controller
, &MediaPresent
);
438 // Set session identifier
440 CopyMem (Session
->Isid
, Session
->ConfigData
->SessionConfigData
.IsId
, 6);
446 // Create a connection for the session.
448 Conn
= IScsiCreateConnection (Session
);
450 return EFI_OUT_OF_RESOURCES
;
453 IScsiAttatchConnection (Session
, Conn
);
456 // Login througth the newly created connection.
458 Status
= IScsiConnLogin (Conn
, Session
->ConfigData
->SessionConfigData
.ConnectTimeout
);
459 if (EFI_ERROR (Status
)) {
460 IScsiConnReset (Conn
);
461 IScsiDetatchConnection (Conn
);
462 IScsiDestroyConnection (Conn
);
465 if (Status
!= EFI_TIMEOUT
) {
470 } while (RetryCount
<= Session
->ConfigData
->SessionConfigData
.ConnectRetryCount
);
472 if (!EFI_ERROR (Status
)) {
473 Session
->State
= SESSION_STATE_LOGGED_IN
;
475 if (!Conn
->Ipv6Flag
) {
476 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
478 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
481 Status
= gBS
->OpenProtocol (
485 Session
->Private
->Image
,
486 Session
->Private
->ExtScsiPassThruHandle
,
487 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
490 ASSERT_EFI_ERROR (Status
);
492 if (Conn
->Ipv6Flag
) {
493 Status
= IScsiGetIp6NicInfo (Conn
);
502 Wait for IPsec negotiation, then try to login the iSCSI session again.
504 @param[in] Session The iSCSI session.
506 @retval EFI_SUCCESS The iSCSI session login procedure finished.
507 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
508 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
512 IScsiSessionReLogin (
513 IN ISCSI_SESSION
*Session
518 EFI_STATUS TimerStatus
;
521 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
522 if (EFI_ERROR (Status
)) {
526 Status
= gBS
->SetTimer (
529 ISCSI_WAIT_IPSEC_TIMEOUT
532 if (EFI_ERROR (Status
)) {
533 gBS
->CloseEvent (Timer
);
539 TimerStatus
= gBS
->CheckEvent (Timer
);
541 if (!EFI_ERROR (TimerStatus
)) {
542 Status
= IScsiSessionLogin (Session
);
545 } while (TimerStatus
== EFI_NOT_READY
);
547 gBS
->CloseEvent (Timer
);
553 Build and send the iSCSI login request to the iSCSI target according to
554 the current login stage.
556 @param[in] Conn The connection in the iSCSI login phase.
558 @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
560 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
561 @retval EFI_DEVICE_ERROR Some kind of device error occurred.
566 IN ISCSI_CONNECTION
*Conn
573 // Build the Login Request PDU.
575 Pdu
= IScsiPrepareLoginReq (Conn
);
577 return EFI_DEVICE_ERROR
;
580 // Send it to the iSCSI target.
582 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
591 Receive and process the iSCSI login response.
593 @param[in] Conn The connection in the iSCSI login phase.
595 @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
596 @retval Others Other errors as indicated.
600 IScsiReceiveLoginRsp (
601 IN ISCSI_CONNECTION
*Conn
610 // Receive the iSCSI login response.
612 Status
= IScsiReceivePdu (Conn
, &Pdu
, NULL
, FALSE
, FALSE
, NULL
);
613 if (EFI_ERROR (Status
)) {
616 ASSERT (Pdu
!= NULL
);
619 // A Login Response is received; process it.
621 Status
= IScsiProcessLoginRsp (Conn
, Pdu
);
630 Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
631 The DataSegmentLength and the actual size of the net buffer containing this PDU will be
634 @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
636 @param[in] Key The key name string.
637 @param[in] Value The value string.
639 @retval EFI_SUCCESS The key-value pair is added to the PDU's data segment and
640 the correspondence length fields are updated.
641 @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
643 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
646 IScsiAddKeyValuePair (
656 ISCSI_LOGIN_REQUEST
*LoginReq
;
659 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufGetByte (Pdu
, 0, NULL
);
660 if (LoginReq
== NULL
) {
661 return EFI_PROTOCOL_ERROR
;
663 DataSegLen
= NTOH24 (LoginReq
->DataSegmentLength
);
665 KeyLen
= (UINT32
) AsciiStrLen (Key
);
666 ValueLen
= (UINT32
) AsciiStrLen (Value
);
669 // 1 byte for the key value separator '=' and 1 byte for the null
670 // delimiter after the value.
672 TotalLen
= KeyLen
+ 1 + ValueLen
+ 1;
675 // Allocate the space for the key-value pair.
677 Data
= (CHAR8
*) NetbufAllocSpace (Pdu
, TotalLen
, NET_BUF_TAIL
);
679 return EFI_OUT_OF_RESOURCES
;
684 CopyMem (Data
, Key
, KeyLen
);
693 CopyMem (Data
, Value
, ValueLen
);
699 // Update the DataSegmentLength
701 ISCSI_SET_DATASEG_LEN (LoginReq
, DataSegLen
+ TotalLen
);
708 Prepare the iSCSI login request to be sent according to the current login status.
710 @param[in, out] Conn The connection in the iSCSI login phase.
712 @return The pointer to the net buffer containing the iSCSI login request built.
713 @retval NULL Other errors as indicated.
717 IScsiPrepareLoginReq (
718 IN OUT ISCSI_CONNECTION
*Conn
721 ISCSI_SESSION
*Session
;
723 ISCSI_LOGIN_REQUEST
*LoginReq
;
726 Session
= Conn
->Session
;
728 Nbuf
= NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST
) + DEFAULT_MAX_RECV_DATA_SEG_LEN
);
733 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufAllocSpace (Nbuf
, sizeof (ISCSI_LOGIN_REQUEST
), NET_BUF_TAIL
);
734 if (LoginReq
== NULL
) {
738 ZeroMem (LoginReq
, sizeof (ISCSI_LOGIN_REQUEST
));
741 // Init the login request pdu
743 ISCSI_SET_OPCODE (LoginReq
, ISCSI_OPCODE_LOGIN_REQ
, ISCSI_REQ_IMMEDIATE
);
744 ISCSI_SET_STAGES (LoginReq
, Conn
->CurrentStage
, Conn
->NextStage
);
745 LoginReq
->VersionMax
= ISCSI_VERSION_MAX
;
746 LoginReq
->VersionMin
= ISCSI_VERSION_MIN
;
747 LoginReq
->Tsih
= HTONS (Session
->Tsih
);
748 LoginReq
->InitiatorTaskTag
= HTONL (Session
->InitiatorTaskTag
);
749 LoginReq
->Cid
= HTONS (Conn
->Cid
);
750 LoginReq
->CmdSN
= HTONL (Session
->CmdSN
);
753 // For the first Login Request on a coonection this is ExpStatSN for the
754 // old connection, and this field is only valid if the Login Request restarts
756 // For subsequent Login Requests it is used to acknowledge the Login Responses
757 // with their increasing StatSN values.
759 LoginReq
->ExpStatSN
= HTONL (Conn
->ExpStatSN
);
760 CopyMem (LoginReq
->Isid
, Session
->Isid
, sizeof (LoginReq
->Isid
));
762 if (Conn
->PartialRspRcvd
) {
764 // A partial response. The initiator must send an empty Login Request.
769 Status
= EFI_SUCCESS
;
771 switch (Conn
->CurrentStage
) {
772 case ISCSI_SECURITY_NEGOTIATION
:
774 // Both none authentication and CHAP authentication share the CHAP path.
777 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
778 Status
= IScsiCHAPToSendReq (Conn
, Nbuf
);
783 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
785 // Only negotiate the paramter once.
787 if (!Conn
->ParamNegotiated
) {
788 IScsiFillOpParams (Conn
, Nbuf
);
791 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
796 // An error occurs...
798 Status
= EFI_DEVICE_ERROR
;
802 if (EFI_ERROR (Status
)) {
807 // Pad the data segment if needed.
809 IScsiPadSegment (Nbuf
, ISCSI_GET_DATASEG_LEN (LoginReq
));
811 // Check whether we will issue the stage transition signal?
813 Conn
->TransitInitiated
= ISCSI_FLAG_ON (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
821 Process the iSCSI Login Response.
823 @param[in, out] Conn The connection on which the iSCSI login response is received.
824 @param[in, out] Pdu The iSCSI login response PDU.
826 @retval EFI_SUCCESS The iSCSI login response PDU is processed, and all checks are passed.
827 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
828 @retval EFI_MEDIA_CHANGED Target is redirected.
829 @retval Others Other errors as indicated.
833 IScsiProcessLoginRsp (
834 IN OUT ISCSI_CONNECTION
*Conn
,
839 ISCSI_SESSION
*Session
;
840 ISCSI_LOGIN_RESPONSE
*LoginRsp
;
848 Status
= EFI_SUCCESS
;
849 Session
= Conn
->Session
;
851 LoginRsp
= (ISCSI_LOGIN_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
852 if (LoginRsp
== NULL
) {
853 return EFI_PROTOCOL_ERROR
;
855 if (!ISCSI_CHECK_OPCODE (LoginRsp
, ISCSI_OPCODE_LOGIN_RSP
)) {
857 // It is not a Login Response.
859 return EFI_PROTOCOL_ERROR
;
862 // Get the data segment, if any.
864 DataSegLen
= ISCSI_GET_DATASEG_LEN (LoginRsp
);
865 if (DataSegLen
!= 0) {
866 DataSeg
= NetbufGetByte (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NULL
);
871 // Check the status class in the login response PDU.
873 switch (LoginRsp
->StatusClass
) {
874 case ISCSI_LOGIN_STATUS_SUCCESS
:
876 // Just break here; the response and the data segment will be processed later.
880 case ISCSI_LOGIN_STATUS_REDIRECTION
:
882 // The target may be moved to a different address.
884 if (DataSeg
== NULL
) {
885 return EFI_PROTOCOL_ERROR
;
888 // Process the TargetAddress key-value strings in the data segment to update the
889 // target address info.
891 Status
= IScsiUpdateTargetAddress (Session
, (CHAR8
*) DataSeg
, DataSegLen
);
892 if (EFI_ERROR (Status
)) {
896 // Session will be restarted on this error status because the Target is
897 // redirected by this Login Response.
899 return EFI_MEDIA_CHANGED
;
903 // Initiator Error, Target Error, or any other undefined error code.
905 return EFI_PROTOCOL_ERROR
;
908 // The status is success; extract the wanted fields from the header segment.
910 Transit
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT
);
911 Continue
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE
);
913 CurrentStage
= ISCSI_GET_CURRENT_STAGE (LoginRsp
);
914 NextStage
= ISCSI_GET_NEXT_STAGE (LoginRsp
);
916 LoginRsp
->InitiatorTaskTag
= NTOHL (LoginRsp
->InitiatorTaskTag
);
918 if ((Transit
&& Continue
) ||
919 (CurrentStage
!= Conn
->CurrentStage
) ||
920 (!Conn
->TransitInitiated
&& Transit
) ||
921 (Transit
&& (NextStage
!= Conn
->NextStage
)) ||
922 (CompareMem (Session
->Isid
, LoginRsp
->Isid
, sizeof (LoginRsp
->Isid
)) != 0) ||
923 (LoginRsp
->InitiatorTaskTag
!= Session
->InitiatorTaskTag
)
926 // A Login Response with the C bit set to 1 MUST have the T bit set to 0.
927 // The CSG in the Login Response MUST be the same with the I-end of this connection.
928 // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
929 // initiate the transistion.
930 // The NSG MUST be the same with the I-end of this connection if Transit is required.
931 // The ISID in the Login Response MUST be the same with this session.
933 return EFI_PROTOCOL_ERROR
;
936 LoginRsp
->StatSN
= NTOHL (LoginRsp
->StatSN
);
937 LoginRsp
->ExpCmdSN
= NTOHL (LoginRsp
->ExpCmdSN
);
938 LoginRsp
->MaxCmdSN
= NTOHL (LoginRsp
->MaxCmdSN
);
940 if ((Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
) && (Conn
->AuthStep
== ISCSI_AUTH_INITIAL
)) {
942 // If the Login Request is a leading Login Request, the target MUST use
943 // the value presented in CmdSN as the target value for ExpCmdSN.
945 if ((Session
->State
== SESSION_STATE_FREE
) && (Session
->CmdSN
!= LoginRsp
->ExpCmdSN
)) {
946 return EFI_PROTOCOL_ERROR
;
950 // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
953 Conn
->ExpStatSN
= LoginRsp
->StatSN
+ 1;
954 Session
->MaxCmdSN
= LoginRsp
->MaxCmdSN
;
955 Session
->ExpCmdSN
= LoginRsp
->ExpCmdSN
;
958 // Check the StatSN of this PDU.
960 Status
= IScsiCheckSN (&Conn
->ExpStatSN
, LoginRsp
->StatSN
);
961 if (!EFI_ERROR (Status
)) {
963 // Update the MaxCmdSN and ExpCmdSN.
965 IScsiUpdateCmdSN (Session
, LoginRsp
->MaxCmdSN
, LoginRsp
->ExpCmdSN
);
971 // Trim off the header segment.
973 NetbufTrim (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NET_BUF_HEAD
);
976 // Queue this login response first in case it's a partial response so that
977 // later when the full response list is received we can combine these scattered
978 // responses' data segment and then process it.
981 NetbufQueAppend (&Conn
->RspQue
, Pdu
);
983 Conn
->PartialRspRcvd
= Continue
;
986 // It is a partial response; must wait for another or more Request/Response
987 // conversations to get the full response.
992 switch (CurrentStage
) {
993 case ISCSI_SECURITY_NEGOTIATION
:
995 // In security negotiation stage, let CHAP module handle it.
997 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
998 Status
= IScsiCHAPOnRspReceived (Conn
);
1002 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
1004 // Response received with negotiation response on iSCSI parameters: check them.
1006 Status
= IScsiCheckOpParams (Conn
);
1007 if (!EFI_ERROR (Status
)) {
1008 Conn
->ParamNegotiated
= TRUE
;
1015 // Should never get here.
1017 Status
= EFI_PROTOCOL_ERROR
;
1021 if (Transit
&& (Status
== EFI_SUCCESS
)) {
1023 // Do the state transition.
1025 Conn
->CurrentStage
= Conn
->NextStage
;
1027 if (Conn
->CurrentStage
== ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
) {
1028 Conn
->NextStage
= ISCSI_FULL_FEATURE_PHASE
;
1031 // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
1032 // get the TSIH from the Login Response.
1034 Session
->Tsih
= NTOHS (LoginRsp
->Tsih
);
1038 // Flush the response(s) received.
1040 NetbufQueFlush (&Conn
->RspQue
);
1047 Updated the target information according the data received in the iSCSI
1048 login response with an target redirection status.
1050 @param[in, out] Session The iSCSI session.
1051 @param[in] Data The data segment that should contain the
1052 TargetAddress key-value list.
1053 @param[in] Len Length of the data.
1055 @retval EFI_SUCCESS The target address is updated.
1056 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1057 @retval EFI_NOT_FOUND The TargetAddress key is not found.
1058 @retval Others Other errors as indicated.
1062 IScsiUpdateTargetAddress (
1063 IN OUT ISCSI_SESSION
*Session
,
1068 LIST_ENTRY
*KeyValueList
;
1069 CHAR8
*TargetAddress
;
1075 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1076 if (KeyValueList
== NULL
) {
1077 return EFI_OUT_OF_RESOURCES
;
1080 Status
= EFI_NOT_FOUND
;
1083 TargetAddress
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ADDRESS
);
1084 if (TargetAddress
== NULL
) {
1088 if (!NET_IS_DIGIT (TargetAddress
[0])) {
1090 // The domainname of the target may be presented in three formats: a DNS host name,
1091 // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted
1097 IpStr
= TargetAddress
;
1099 while ((*TargetAddress
!= 0) && (*TargetAddress
!= ':') && (*TargetAddress
!= ',')) {
1101 // NULL, ':', or ',' ends the IPv4 string.
1106 if (*TargetAddress
== ',') {
1108 // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
1109 // as the result of a redirection.
1112 } else if (*TargetAddress
== ':') {
1113 *TargetAddress
= '\0';
1117 Number
= AsciiStrDecimalToUintn (TargetAddress
);
1118 if (Number
> 0xFFFF) {
1121 Session
->ConfigData
->SessionConfigData
.TargetPort
= (UINT16
) Number
;
1125 // The string only contains the IPv4 address. Use the well-known port.
1127 Session
->ConfigData
->SessionConfigData
.TargetPort
= ISCSI_WELL_KNOWN_PORT
;
1130 // Update the target IP address.
1132 if (Session
->ConfigData
->SessionConfigData
.IpMode
< IP_MODE_AUTOCONFIG
) {
1133 IpMode
= Session
->ConfigData
->SessionConfigData
.IpMode
;
1135 IpMode
= Session
->ConfigData
->AutoConfigureMode
;
1138 Status
= IScsiAsciiStrToIp (
1141 &Session
->ConfigData
->SessionConfigData
.TargetIp
1144 if (EFI_ERROR (Status
)) {
1151 IScsiFreeKeyValueList (KeyValueList
);
1158 The callback function to free the net buffer list.
1160 @param[in] Arg The opaque parameter.
1169 ASSERT (Arg
!= NULL
);
1171 NetbufFreeList ((LIST_ENTRY
*) Arg
);
1177 The callback function called in NetBufFree; it does nothing.
1179 @param[in] Arg The opaque parameter.
1192 Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
1193 an optional data segment. The two parts will be put into two blocks of buffers in the
1194 net buffer. The digest check will be conducted in this function if needed and the digests
1195 will be trimmed from the PDU buffer.
1197 @param[in] Conn The iSCSI connection to receive data from.
1198 @param[out] Pdu The received iSCSI pdu.
1199 @param[in] Context The context used to describe information on the caller provided
1200 buffer to receive data segment of the iSCSI pdu. It is optional.
1201 @param[in] HeaderDigest Whether there will be header digest received.
1202 @param[in] DataDigest Whether there will be data digest.
1203 @param[in] TimeoutEvent The timeout event. It is optional.
1205 @retval EFI_SUCCESS An iSCSI pdu is received.
1206 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1207 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1208 @retval Others Other errors as indicated.
1213 IN ISCSI_CONNECTION
*Conn
,
1215 IN ISCSI_IN_BUFFER_CONTEXT
*Context
, OPTIONAL
1216 IN BOOLEAN HeaderDigest
,
1217 IN BOOLEAN DataDigest
,
1218 IN EFI_EVENT TimeoutEvent OPTIONAL
1221 LIST_ENTRY
*NbufList
;
1227 UINT32 InDataOffset
;
1228 NET_FRAGMENT Fragment
[2];
1229 UINT32 FragmentCount
;
1231 UINT32 PadAndCRC32
[2];
1233 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
1234 if (NbufList
== NULL
) {
1235 return EFI_OUT_OF_RESOURCES
;
1238 InitializeListHead (NbufList
);
1241 // The header digest will be received together with the PDU header, if exists.
1243 Len
= sizeof (ISCSI_BASIC_HEADER
) + (HeaderDigest
? sizeof (UINT32
) : 0);
1244 PduHdr
= NetbufAlloc (Len
);
1245 if (PduHdr
== NULL
) {
1246 Status
= EFI_OUT_OF_RESOURCES
;
1250 Header
= NetbufAllocSpace (PduHdr
, Len
, NET_BUF_TAIL
);
1251 if (Header
== NULL
) {
1252 Status
= EFI_OUT_OF_RESOURCES
;
1255 InsertTailList (NbufList
, &PduHdr
->List
);
1258 // First step, receive the BHS of the PDU.
1260 Status
= TcpIoReceive (&Conn
->TcpIo
, PduHdr
, FALSE
, TimeoutEvent
);
1262 if (EFI_ERROR (Status
)) {
1268 // TODO: check the header-digest.
1271 // Trim off the digest.
1273 NetbufTrim (PduHdr
, sizeof (UINT32
), NET_BUF_TAIL
);
1276 Len
= ISCSI_GET_DATASEG_LEN (Header
);
1284 // Get the length of the padding bytes of the data segment.
1286 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1288 switch (ISCSI_GET_OPCODE (Header
)) {
1289 case ISCSI_OPCODE_SCSI_DATA_IN
:
1291 // To reduce memory copy overhead, try to use the buffer described by Context
1292 // if the PDU is an iSCSI SCSI data.
1294 InDataOffset
= ISCSI_GET_BUFFER_OFFSET (Header
);
1295 if ((Context
== NULL
) || ((InDataOffset
+ Len
) > Context
->InDataLen
)) {
1296 Status
= EFI_PROTOCOL_ERROR
;
1300 Fragment
[0].Len
= Len
;
1301 Fragment
[0].Bulk
= Context
->InData
+ InDataOffset
;
1303 if (DataDigest
|| (PadLen
!= 0)) {
1305 // The data segment is padded. Use two fragments to receive it:
1306 // the first to receive the useful data; the second to receive the padding.
1308 Fragment
[1].Len
= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1309 Fragment
[1].Bulk
= (UINT8
*)PadAndCRC32
+ (4 - PadLen
);
1316 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1317 if (DataSeg
== NULL
) {
1318 Status
= EFI_OUT_OF_RESOURCES
;
1324 case ISCSI_OPCODE_SCSI_RSP
:
1325 case ISCSI_OPCODE_NOP_IN
:
1326 case ISCSI_OPCODE_LOGIN_RSP
:
1327 case ISCSI_OPCODE_TEXT_RSP
:
1328 case ISCSI_OPCODE_ASYNC_MSG
:
1329 case ISCSI_OPCODE_REJECT
:
1330 case ISCSI_OPCODE_VENDOR_T0
:
1331 case ISCSI_OPCODE_VENDOR_T1
:
1332 case ISCSI_OPCODE_VENDOR_T2
:
1334 // Allocate buffer to receive the data segment.
1336 Len
+= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1337 DataSeg
= NetbufAlloc (Len
);
1338 if (DataSeg
== NULL
) {
1339 Status
= EFI_OUT_OF_RESOURCES
;
1343 NetbufAllocSpace (DataSeg
, Len
, NET_BUF_TAIL
);
1347 Status
= EFI_PROTOCOL_ERROR
;
1351 InsertTailList (NbufList
, &DataSeg
->List
);
1354 // Receive the data segment with the data digest, if any.
1356 Status
= TcpIoReceive (&Conn
->TcpIo
, DataSeg
, FALSE
, TimeoutEvent
);
1358 if (EFI_ERROR (Status
)) {
1364 // TODO: Check the data digest.
1366 NetbufTrim (DataSeg
, sizeof (UINT32
), NET_BUF_TAIL
);
1371 // Trim off the padding bytes in the data segment.
1373 NetbufTrim (DataSeg
, PadLen
, NET_BUF_TAIL
);
1378 // Form the pdu from a list of pdu segments.
1380 *Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
1382 Status
= EFI_OUT_OF_RESOURCES
;
1387 if (EFI_ERROR (Status
)) {
1389 // Free the Nbufs in this NbufList and the NbufList itself.
1391 IScsiFreeNbufList (NbufList
);
1399 Check and get the result of the parameter negotiation.
1401 @param[in, out] Conn The connection in iSCSI login.
1403 @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
1404 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1405 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1409 IScsiCheckOpParams (
1410 IN OUT ISCSI_CONNECTION
*Conn
1414 LIST_ENTRY
*KeyValueList
;
1417 ISCSI_SESSION
*Session
;
1421 ASSERT (Conn
->RspQue
.BufNum
!= 0);
1423 Session
= Conn
->Session
;
1425 Len
= Conn
->RspQue
.BufSize
;
1426 Data
= AllocatePool (Len
);
1428 return EFI_OUT_OF_RESOURCES
;
1431 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, (UINT8
*) Data
);
1433 Status
= EFI_PROTOCOL_ERROR
;
1436 // Extract the Key-Value pairs into a list.
1438 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1439 if (KeyValueList
== NULL
) {
1446 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_HEADER_DIGEST
);
1447 if (Value
== NULL
) {
1451 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1452 if (Conn
->HeaderDigest
!= IScsiDigestCRC32
) {
1455 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1456 Conn
->HeaderDigest
= IScsiDigestNone
;
1463 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_DIGEST
);
1464 if (Value
== NULL
) {
1468 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1469 if (Conn
->DataDigest
!= IScsiDigestCRC32
) {
1472 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1473 Conn
->DataDigest
= IScsiDigestNone
;
1478 // ErrorRecoveryLevel: result fuction is Minimum.
1480 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
);
1481 if (Value
== NULL
) {
1485 NumericValue
= IScsiNetNtoi (Value
);
1486 if (NumericValue
> 2) {
1490 Session
->ErrorRecoveryLevel
= (UINT8
) MIN (Session
->ErrorRecoveryLevel
, NumericValue
);
1493 // InitialR2T: result function is OR.
1495 if (!Session
->InitialR2T
) {
1496 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1497 if (Value
== NULL
) {
1501 Session
->InitialR2T
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1505 // ImmediateData: result function is AND.
1507 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_IMMEDIATE_DATA
);
1508 if (Value
== NULL
) {
1512 Session
->ImmediateData
= (BOOLEAN
) (Session
->ImmediateData
&& (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0));
1515 // MaxRecvDataSegmentLength is declarative.
1517 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
);
1518 if (Value
!= NULL
) {
1519 Conn
->MaxRecvDataSegmentLength
= (UINT32
) IScsiNetNtoi (Value
);
1522 // MaxBurstLength: result funtion is Mininum.
1524 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_BURST_LENGTH
);
1525 if (Value
== NULL
) {
1529 NumericValue
= IScsiNetNtoi (Value
);
1530 Session
->MaxBurstLength
= (UINT32
) MIN (Session
->MaxBurstLength
, NumericValue
);
1533 // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
1534 // ImmediateData=No.
1536 if (!(Session
->InitialR2T
&& !Session
->ImmediateData
)) {
1537 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1538 if (Value
== NULL
) {
1542 NumericValue
= IScsiNetNtoi (Value
);
1543 Session
->FirstBurstLength
= (UINT32
) MIN (Session
->FirstBurstLength
, NumericValue
);
1547 // MaxConnections: result function is Minimum.
1549 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_CONNECTIONS
);
1550 if (Value
== NULL
) {
1554 NumericValue
= IScsiNetNtoi (Value
);
1555 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1559 Session
->MaxConnections
= (UINT32
) MIN (Session
->MaxConnections
, NumericValue
);
1562 // DataPDUInOrder: result function is OR.
1564 if (!Session
->DataPDUInOrder
) {
1565 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1566 if (Value
== NULL
) {
1570 Session
->DataPDUInOrder
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1574 // DataSequenceInorder: result function is OR.
1576 if (!Session
->DataSequenceInOrder
) {
1577 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1578 if (Value
== NULL
) {
1582 Session
->DataSequenceInOrder
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1586 // DefaultTime2Wait: result function is Maximum.
1588 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2WAIT
);
1589 if (Value
== NULL
) {
1593 NumericValue
= IScsiNetNtoi (Value
);
1594 if (NumericValue
== 0) {
1595 Session
->DefaultTime2Wait
= 0;
1596 } else if (NumericValue
> 3600) {
1599 Session
->DefaultTime2Wait
= (UINT32
) MAX (Session
->DefaultTime2Wait
, NumericValue
);
1602 // DefaultTime2Retain: result function is Minimum.
1604 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2RETAIN
);
1605 if (Value
== NULL
) {
1609 NumericValue
= IScsiNetNtoi (Value
);
1610 if (NumericValue
== 0) {
1611 Session
->DefaultTime2Retain
= 0;
1612 } else if (NumericValue
> 3600) {
1615 Session
->DefaultTime2Retain
= (UINT32
) MIN (Session
->DefaultTime2Retain
, NumericValue
);
1618 // MaxOutstandingR2T: result function is Minimum.
1620 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_OUTSTANDING_R2T
);
1621 if (Value
== NULL
) {
1625 NumericValue
= IScsiNetNtoi (Value
);
1626 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1630 Session
->MaxOutstandingR2T
= (UINT16
) MIN (Session
->MaxOutstandingR2T
, NumericValue
);
1633 // Remove declarative key-value pairs, if any.
1635 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_SESSION_TYPE
);
1636 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ALIAS
);
1637 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
);
1641 // Remove the key-value that may not needed for result function is OR.
1643 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1644 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1645 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1648 // Remove irrelevant parameter, if any.
1650 if (Session
->InitialR2T
&& !Session
->ImmediateData
) {
1651 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1654 if (IsListEmpty (KeyValueList
)) {
1656 // Succeed if no more keys in the list.
1658 Status
= EFI_SUCCESS
;
1663 IScsiFreeKeyValueList (KeyValueList
);
1672 Fill the operational parameters.
1674 @param[in] Conn The connection in iSCSI login.
1675 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1680 IN ISCSI_CONNECTION
*Conn
,
1684 ISCSI_SESSION
*Session
;
1687 Session
= Conn
->Session
;
1689 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->HeaderDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1690 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_HEADER_DIGEST
, Value
);
1692 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->DataDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1693 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_DIGEST
, Value
);
1695 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->ErrorRecoveryLevel
);
1696 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
, Value
);
1698 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->InitialR2T
? "Yes" : "No");
1699 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_INITIAL_R2T
, Value
);
1701 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->ImmediateData
? "Yes" : "No");
1702 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_IMMEDIATE_DATA
, Value
);
1704 AsciiSPrint (Value
, sizeof (Value
), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP
);
1705 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
, Value
);
1707 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxBurstLength
);
1708 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_BURST_LENGTH
, Value
);
1710 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->FirstBurstLength
);
1711 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_FIRST_BURST_LENGTH
, Value
);
1713 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxConnections
);
1714 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_CONNECTIONS
, Value
);
1716 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataPDUInOrder
? "Yes" : "No");
1717 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_PDU_IN_ORDER
, Value
);
1719 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataSequenceInOrder
? "Yes" : "No");
1720 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
, Value
);
1722 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Wait
);
1723 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2WAIT
, Value
);
1725 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Retain
);
1726 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2RETAIN
, Value
);
1728 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxOutstandingR2T
);
1729 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_OUTSTANDING_R2T
, Value
);
1734 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
1736 @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
1737 @param[in] Len The length of the last segment in the PDU.
1739 @retval EFI_SUCCESS The segment is padded or there is no need to pad it.
1740 @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
1745 IN OUT NET_BUF
*Pdu
,
1752 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1755 Data
= NetbufAllocSpace (Pdu
, PadLen
, NET_BUF_TAIL
);
1757 return EFI_OUT_OF_RESOURCES
;
1760 ZeroMem (Data
, PadLen
);
1768 Build a key-value list from the data segment.
1770 @param[in] Data The data segment containing the key-value pairs.
1771 @param[in] Len Length of the data segment.
1773 @return The key-value list.
1774 @retval NULL Other errors as indicated.
1778 IScsiBuildKeyValueList (
1783 LIST_ENTRY
*ListHead
;
1784 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1786 ListHead
= AllocatePool (sizeof (LIST_ENTRY
));
1787 if (ListHead
== NULL
) {
1791 InitializeListHead (ListHead
);
1794 KeyValuePair
= AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR
));
1795 if (KeyValuePair
== NULL
) {
1799 InitializeListHead (&KeyValuePair
->List
);
1801 KeyValuePair
->Key
= Data
;
1803 while ((Len
> 0) && (*Data
!= '=')) {
1814 FreePool (KeyValuePair
);
1818 KeyValuePair
->Value
= Data
;
1820 InsertTailList (ListHead
, &KeyValuePair
->List
);;
1822 Data
+= AsciiStrLen (KeyValuePair
->Value
) + 1;
1823 Len
-= (UINT32
) AsciiStrLen (KeyValuePair
->Value
) + 1;
1830 IScsiFreeKeyValueList (ListHead
);
1837 Get the value string by the key name from the key-value list. If found,
1838 the key-value entry will be removed from the list.
1840 @param[in, out] KeyValueList The key-value list.
1841 @param[in] Key The key name to find.
1843 @return The value string.
1844 @retval NULL The key value pair cannot be found.
1848 IScsiGetValueByKeyFromList (
1849 IN OUT LIST_ENTRY
*KeyValueList
,
1854 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1859 NET_LIST_FOR_EACH (Entry
, KeyValueList
) {
1860 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1862 if (AsciiStrCmp (KeyValuePair
->Key
, Key
) == 0) {
1863 Value
= KeyValuePair
->Value
;
1865 RemoveEntryList (&KeyValuePair
->List
);
1866 FreePool (KeyValuePair
);
1876 Free the key-value list.
1878 @param[in] KeyValueList The key-value list.
1882 IScsiFreeKeyValueList (
1883 IN LIST_ENTRY
*KeyValueList
1887 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1889 while (!IsListEmpty (KeyValueList
)) {
1890 Entry
= NetListRemoveHead (KeyValueList
);
1891 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1893 FreePool (KeyValuePair
);
1896 FreePool (KeyValueList
);
1901 Normalize the iSCSI name according to RFC.
1903 @param[in, out] Name The iSCSI name.
1904 @param[in] Len Length of the iSCSI name.
1906 @retval EFI_SUCCESS The iSCSI name is valid and normalized.
1907 @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.
1911 IScsiNormalizeName (
1918 for (Index
= 0; Index
< Len
; Index
++) {
1919 if (NET_IS_UPPER_CASE_CHAR (Name
[Index
])) {
1921 // Convert the upper-case characters to lower-case ones.
1923 Name
[Index
] = (CHAR8
) (Name
[Index
] - 'A' + 'a');
1926 if (!NET_IS_LOWER_CASE_CHAR (Name
[Index
]) &&
1927 !NET_IS_DIGIT (Name
[Index
]) &&
1928 (Name
[Index
] != '-') &&
1929 (Name
[Index
] != '.') &&
1930 (Name
[Index
] != ':')
1933 // ASCII dash, dot, colon lower-case characters and digit characters
1936 return EFI_PROTOCOL_ERROR
;
1940 if ((Len
< 4) || (CompareMem (Name
, "iqn.", 4) != 0)) {
1942 // Only IQN format is accepted now.
1944 return EFI_PROTOCOL_ERROR
;
1952 Create an iSCSI task control block.
1954 @param[in] Conn The connection on which the task control block will be created.
1955 @param[out] Tcb The newly created task control block.
1957 @retval EFI_SUCCESS The task control block is created.
1958 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1959 @retval EFI_NOT_READY The target cannot accept new commands.
1964 IN ISCSI_CONNECTION
*Conn
,
1968 ISCSI_SESSION
*Session
;
1971 ASSERT (Tcb
!= NULL
);
1973 Session
= Conn
->Session
;
1975 if (ISCSI_SEQ_GT (Session
->CmdSN
, Session
->MaxCmdSN
)) {
1976 return EFI_NOT_READY
;
1979 NewTcb
= AllocateZeroPool (sizeof (ISCSI_TCB
));
1980 if (NewTcb
== NULL
) {
1981 return EFI_OUT_OF_RESOURCES
;
1984 InitializeListHead (&NewTcb
->Link
);
1986 NewTcb
->SoFarInOrder
= TRUE
;
1987 NewTcb
->InitiatorTaskTag
= Session
->InitiatorTaskTag
;
1988 NewTcb
->CmdSN
= Session
->CmdSN
;
1989 NewTcb
->Conn
= Conn
;
1991 InsertTailList (&Session
->TcbList
, &NewTcb
->Link
);
1994 // Advance the initiator task tag.
1996 Session
->InitiatorTaskTag
++;
2006 Delete the tcb from the connection and destroy it.
2008 @param[in] Tcb The tcb to delete.
2016 RemoveEntryList (&Tcb
->Link
);
2023 Find the task control block by the initator task tag.
2025 @param[in] TcbList The tcb list.
2026 @param[in] InitiatorTaskTag The initiator task tag.
2028 @return The task control block found.
2029 @retval NULL The task control block cannot be found.
2034 IN LIST_ENTRY
*TcbList
,
2035 IN UINT32 InitiatorTaskTag
2043 NET_LIST_FOR_EACH (Entry
, TcbList
) {
2044 Tcb
= NET_LIST_USER_STRUCT (Entry
, ISCSI_TCB
, Link
);
2046 if (Tcb
->InitiatorTaskTag
== InitiatorTaskTag
) {
2056 Create a data segment, pad it, and calculate the CRC if needed.
2058 @param[in] Data The data to fill into the data segment.
2059 @param[in] Len Length of the data.
2060 @param[in] DataDigest Whether to calculate CRC for this data segment.
2062 @return The net buffer wrapping the data segment.
2066 IScsiNewDataSegment (
2069 IN BOOLEAN DataDigest
2072 NET_FRAGMENT Fragment
[2];
2073 UINT32 FragmentCount
;
2077 Fragment
[0].Len
= Len
;
2078 Fragment
[0].Bulk
= Data
;
2080 PadLen
= ISCSI_GET_PAD_LEN (Len
);
2082 Fragment
[1].Len
= PadLen
;
2083 Fragment
[1].Bulk
= (UINT8
*) &mDataSegPad
;
2090 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
2097 Create a iSCSI SCSI command PDU to encapsulate the command issued
2098 by SCSI through the EXT SCSI PASS THRU Protocol.
2100 @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
2101 @param[in] Lun The LUN.
2102 @param[in] Tcb The tcb assocated with this SCSI command.
2104 @return The created iSCSI SCSI command PDU.
2105 @retval NULL Other errors as indicated.
2109 IScsiNewScsiCmdPdu (
2110 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
2115 LIST_ENTRY
*NbufList
;
2119 SCSI_COMMAND
*ScsiCmd
;
2122 ISCSI_ADDITIONAL_HEADER
*Header
;
2123 ISCSI_BI_EXP_READ_DATA_LEN_AHS
*BiExpReadDataLenAHS
;
2124 ISCSI_SESSION
*Session
;
2125 UINT32 ImmediateDataLen
;
2129 if (Packet
->DataDirection
== DataBi
) {
2131 // Bidirectional Read/Write command, the bidirectional expected
2132 // read data length AHS is required.
2134 AHSLength
+= sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS
);
2137 if (Packet
->CdbLength
> 16) {
2139 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2141 AHSLength
= (UINT8
) (AHSLength
+ ISCSI_ROUNDUP (Packet
->CdbLength
- 16) + sizeof (ISCSI_ADDITIONAL_HEADER
));
2144 Length
= sizeof (SCSI_COMMAND
) + AHSLength
;
2145 PduHeader
= NetbufAlloc (Length
);
2146 if (PduHeader
== NULL
) {
2150 ScsiCmd
= (SCSI_COMMAND
*) NetbufAllocSpace (PduHeader
, Length
, NET_BUF_TAIL
);
2151 if (ScsiCmd
== NULL
) {
2152 NetbufFree (PduHeader
);
2155 Header
= (ISCSI_ADDITIONAL_HEADER
*) (ScsiCmd
+ 1);
2157 ZeroMem (ScsiCmd
, Length
);
2159 ISCSI_SET_OPCODE (ScsiCmd
, ISCSI_OPCODE_SCSI_CMD
, 0);
2160 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_TASK_ATTR_SIMPLE
);
2163 // Set the READ/WRITE flags according to the IO type of this request.
2165 switch (Packet
->DataDirection
) {
2167 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
);
2168 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->InTransferLength
);
2172 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_WRITE
);
2173 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2177 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
| SCSI_CMD_PDU_FLAG_WRITE
);
2178 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2181 // Fill the bidirectional expected read data length AHS.
2183 BiExpReadDataLenAHS
= (ISCSI_BI_EXP_READ_DATA_LEN_AHS
*) Header
;
2184 Header
= (ISCSI_ADDITIONAL_HEADER
*) (BiExpReadDataLenAHS
+ 1);
2186 BiExpReadDataLenAHS
->Length
= NTOHS (5);
2187 BiExpReadDataLenAHS
->Type
= ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN
;
2188 BiExpReadDataLenAHS
->ExpReadDataLength
= NTOHL (Packet
->InTransferLength
);
2193 ScsiCmd
->TotalAHSLength
= AHSLength
;
2194 CopyMem (ScsiCmd
->Lun
, &Lun
, sizeof (ScsiCmd
->Lun
));
2195 ScsiCmd
->InitiatorTaskTag
= NTOHL (Tcb
->InitiatorTaskTag
);
2196 ScsiCmd
->CmdSN
= NTOHL (Tcb
->CmdSN
);
2197 ScsiCmd
->ExpStatSN
= NTOHL (Tcb
->Conn
->ExpStatSN
);
2199 CopyMem (ScsiCmd
->Cdb
, Packet
->Cdb
, sizeof (ScsiCmd
->Cdb
));
2201 if (Packet
->CdbLength
> 16) {
2202 Header
->Length
= NTOHS ((UINT16
) (Packet
->CdbLength
- 15));
2203 Header
->Type
= ISCSI_AHS_TYPE_EXT_CDB
;
2205 CopyMem (Header
+ 1, (UINT8
*) Packet
->Cdb
+ 16, Packet
->CdbLength
- 16);
2209 Session
= Tcb
->Conn
->Session
;
2210 ImmediateDataLen
= 0;
2212 if (Session
->ImmediateData
&& (Packet
->OutTransferLength
!= 0)) {
2214 // Send immediate data in this SCSI Command PDU. The length of the immeidate
2215 // data is the minimum of FirstBurstLength, the data length to be xfered, and
2216 // the MaxRecvdataSegmentLength on this connection.
2218 ImmediateDataLen
= MIN (Session
->FirstBurstLength
, Packet
->OutTransferLength
);
2219 ImmediateDataLen
= MIN (ImmediateDataLen
, Tcb
->Conn
->MaxRecvDataSegmentLength
);
2222 // Update the data segment length in the PDU header.
2224 ISCSI_SET_DATASEG_LEN (ScsiCmd
, ImmediateDataLen
);
2227 // Create the data segment.
2229 DataSeg
= IScsiNewDataSegment ((UINT8
*) Packet
->OutDataBuffer
, ImmediateDataLen
, FALSE
);
2230 if (DataSeg
== NULL
) {
2231 NetbufFree (PduHeader
);
2236 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2237 if (NbufList
== NULL
) {
2238 NetbufFree (PduHeader
);
2239 NetbufFree (DataSeg
);
2245 InitializeListHead (NbufList
);
2246 InsertTailList (NbufList
, &PduHeader
->List
);
2247 InsertTailList (NbufList
, &DataSeg
->List
);
2249 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2251 IScsiFreeNbufList (NbufList
);
2255 if (Session
->InitialR2T
||
2256 (ImmediateDataLen
== Session
->FirstBurstLength
) ||
2257 (ImmediateDataLen
== Packet
->OutTransferLength
)
2260 // Unsolicited data out sequence is not allowed,
2261 // or FirstBustLength data is already sent out by immediate data,
2262 // or all the OUT data accompany this SCSI packet are sent as
2263 // immediate data. The final flag should be set on this SCSI Command
2266 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_BHS_FLAG_FINAL
);
2276 Create a new iSCSI SCSI Data Out PDU.
2278 @param[in] Data The data to put into the Data Out PDU.
2279 @param[in] Len Length of the data.
2280 @param[in] DataSN The DataSN of the Data Out PDU.
2281 @param[in] Tcb The task control block of this Data Out PDU.
2282 @param[in] Lun The LUN.
2284 @return The net buffer wrapping the Data Out PDU.
2285 @retval NULL Other errors as indicated.
2289 IScsiNewDataOutPdu (
2297 LIST_ENTRY
*NbufList
;
2301 ISCSI_SCSI_DATA_OUT
*DataOutHdr
;
2302 ISCSI_XFER_CONTEXT
*XferContext
;
2304 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2305 if (NbufList
== NULL
) {
2309 InitializeListHead (NbufList
);
2312 // Allocate memory for the BHS.
2314 PduHdr
= NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT
));
2315 if (PduHdr
== NULL
) {
2316 FreePool (NbufList
);
2320 // Insert the BHS into the buffer list.
2322 InsertTailList (NbufList
, &PduHdr
->List
);
2324 DataOutHdr
= (ISCSI_SCSI_DATA_OUT
*) NetbufAllocSpace (PduHdr
, sizeof (ISCSI_SCSI_DATA_OUT
), NET_BUF_TAIL
);
2325 if (DataOutHdr
== NULL
) {
2326 IScsiFreeNbufList (NbufList
);
2329 XferContext
= &Tcb
->XferContext
;
2331 ZeroMem (DataOutHdr
, sizeof (ISCSI_SCSI_DATA_OUT
));
2334 // Set the flags and fields of the Data Out PDU BHS.
2336 ISCSI_SET_OPCODE (DataOutHdr
, ISCSI_OPCODE_SCSI_DATA_OUT
, 0);
2337 ISCSI_SET_DATASEG_LEN (DataOutHdr
, Len
);
2339 DataOutHdr
->InitiatorTaskTag
= HTONL (Tcb
->InitiatorTaskTag
);
2340 DataOutHdr
->TargetTransferTag
= HTONL (XferContext
->TargetTransferTag
);
2341 DataOutHdr
->ExpStatSN
= HTONL (Tcb
->Conn
->ExpStatSN
);
2342 DataOutHdr
->DataSN
= HTONL (DataSN
);
2343 DataOutHdr
->BufferOffset
= HTONL (XferContext
->Offset
);
2345 if (XferContext
->TargetTransferTag
!= ISCSI_RESERVED_TAG
) {
2346 CopyMem (&DataOutHdr
->Lun
, &Lun
, sizeof (DataOutHdr
->Lun
));
2349 // Build the data segment for this Data Out PDU.
2351 DataSeg
= IScsiNewDataSegment (Data
, Len
, FALSE
);
2352 if (DataSeg
== NULL
) {
2353 IScsiFreeNbufList (NbufList
);
2357 // Put the data segment into the buffer list and combine it with the BHS
2358 // into a full Data Out PDU.
2360 InsertTailList (NbufList
, &DataSeg
->List
);
2361 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2363 IScsiFreeNbufList (NbufList
);
2371 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
2373 @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
2374 @param[in] Tcb The task control block of the data to send out.
2375 @param[in] Lun The LUN the data will be sent to.
2377 @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.
2378 @retval NULL Other errors as indicated.
2382 IScsiGenerateDataOutPduSequence (
2388 LIST_ENTRY
*PduList
;
2391 NET_BUF
*DataOutPdu
;
2392 ISCSI_CONNECTION
*Conn
;
2393 ISCSI_XFER_CONTEXT
*XferContext
;
2394 UINT8
*DataOutPacket
;
2396 PduList
= AllocatePool (sizeof (LIST_ENTRY
));
2397 if (PduList
== NULL
) {
2401 InitializeListHead (PduList
);
2406 XferContext
= &Tcb
->XferContext
;
2408 while (XferContext
->DesiredLength
> 0) {
2410 // Determine the length of data this Data Out PDU can carry.
2412 DataLen
= MIN (XferContext
->DesiredLength
, Conn
->MaxRecvDataSegmentLength
);
2415 // Create a Data Out PDU.
2417 DataOutPdu
= IScsiNewDataOutPdu (Data
, DataLen
, DataSN
, Tcb
, Lun
);
2418 if (DataOutPdu
== NULL
) {
2419 IScsiFreeNbufList (PduList
);
2425 InsertTailList (PduList
, &DataOutPdu
->List
);
2428 // Update the context and DataSN.
2431 XferContext
->Offset
+= DataLen
;
2432 XferContext
->DesiredLength
-= DataLen
;
2436 // Set the F bit for the last data out PDU in this sequence.
2438 DataOutPacket
= NetbufGetByte (DataOutPdu
, 0, NULL
);
2439 if (DataOutPacket
== NULL
) {
2440 IScsiFreeNbufList (PduList
);
2445 ISCSI_SET_FLAG (DataOutPacket
, ISCSI_BHS_FLAG_FINAL
);
2453 Send the Data in a sequence of Data Out PDUs one by one.
2455 @param[in] Data The data to carry by Data Out PDUs.
2456 @param[in] Lun The LUN the data will be sent to.
2457 @param[in] Tcb The task control block.
2459 @retval EFI_SUCCES The data is sent out to the LUN.
2460 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2461 @retval Others Other errors as indicated.
2465 IScsiSendDataOutPduSequence (
2471 LIST_ENTRY
*DataOutPduList
;
2477 // Generate the Data Out PDU sequence.
2479 DataOutPduList
= IScsiGenerateDataOutPduSequence (Data
, Tcb
, Lun
);
2480 if (DataOutPduList
== NULL
) {
2481 return EFI_OUT_OF_RESOURCES
;
2484 Status
= EFI_SUCCESS
;
2487 // Send the Data Out PDU's one by one.
2489 NET_LIST_FOR_EACH (Entry
, DataOutPduList
) {
2490 Pdu
= NET_LIST_USER_STRUCT (Entry
, NET_BUF
, List
);
2492 Status
= TcpIoTransmit (&Tcb
->Conn
->TcpIo
, Pdu
);
2494 if (EFI_ERROR (Status
)) {
2499 IScsiFreeNbufList (DataOutPduList
);
2506 Process the received iSCSI SCSI Data In PDU.
2508 @param[in] Pdu The Data In PDU received.
2509 @param[in] Tcb The task control block.
2510 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2512 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
2514 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2515 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2516 @retval Others Other errors as indicated.
2523 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2526 ISCSI_SCSI_DATA_IN
*DataInHdr
;
2529 DataInHdr
= (ISCSI_SCSI_DATA_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2530 if (DataInHdr
== NULL
) {
2531 return EFI_PROTOCOL_ERROR
;
2534 DataInHdr
->InitiatorTaskTag
= NTOHL (DataInHdr
->InitiatorTaskTag
);
2535 DataInHdr
->ExpCmdSN
= NTOHL (DataInHdr
->ExpCmdSN
);
2536 DataInHdr
->MaxCmdSN
= NTOHL (DataInHdr
->MaxCmdSN
);
2537 DataInHdr
->DataSN
= NTOHL (DataInHdr
->DataSN
);
2540 // Check the DataSN.
2542 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, DataInHdr
->DataSN
);
2543 if (EFI_ERROR (Status
)) {
2547 if (DataInHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2548 return EFI_PROTOCOL_ERROR
;
2551 // Update the command related sequence numbers.
2553 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, DataInHdr
->MaxCmdSN
, DataInHdr
->ExpCmdSN
);
2555 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID
)) {
2556 if (!ISCSI_FLAG_ON (DataInHdr
, ISCSI_BHS_FLAG_FINAL
)) {
2558 // The S bit is on but the F bit is off.
2560 return EFI_PROTOCOL_ERROR
;
2563 Tcb
->StatusXferd
= TRUE
;
2565 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_OVERFLOW
| SCSI_DATA_IN_PDU_FLAG_UNDERFLOW
)) {
2567 // Underflow and Overflow are mutual flags.
2569 return EFI_PROTOCOL_ERROR
;
2572 // S bit is on, the StatSN is valid.
2574 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NTOHL (DataInHdr
->StatSN
));
2575 if (EFI_ERROR (Status
)) {
2579 Packet
->HostAdapterStatus
= 0;
2580 Packet
->TargetStatus
= DataInHdr
->Status
;
2582 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2583 Packet
->InTransferLength
+= NTOHL (DataInHdr
->ResidualCount
);
2584 Status
= EFI_BAD_BUFFER_SIZE
;
2587 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2588 Packet
->InTransferLength
-= NTOHL (DataInHdr
->ResidualCount
);
2597 Process the received iSCSI R2T PDU.
2599 @param[in] Pdu The R2T PDU received.
2600 @param[in] Tcb The task control block.
2601 @param[in] Lun The Lun.
2602 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2604 @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.
2605 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2606 @retval Others Other errors as indicated.
2614 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2617 ISCSI_READY_TO_TRANSFER
*R2THdr
;
2619 ISCSI_XFER_CONTEXT
*XferContext
;
2622 R2THdr
= (ISCSI_READY_TO_TRANSFER
*) NetbufGetByte (Pdu
, 0, NULL
);
2623 if (R2THdr
== NULL
) {
2624 return EFI_PROTOCOL_ERROR
;
2627 R2THdr
->InitiatorTaskTag
= NTOHL (R2THdr
->InitiatorTaskTag
);
2628 R2THdr
->TargetTransferTag
= NTOHL (R2THdr
->TargetTransferTag
);
2629 R2THdr
->StatSN
= NTOHL (R2THdr
->StatSN
);
2630 R2THdr
->R2TSeqNum
= NTOHL (R2THdr
->R2TSeqNum
);
2631 R2THdr
->BufferOffset
= NTOHL (R2THdr
->BufferOffset
);
2632 R2THdr
->DesiredDataTransferLength
= NTOHL (R2THdr
->DesiredDataTransferLength
);
2634 if ((R2THdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) || !ISCSI_SEQ_EQ (R2THdr
->StatSN
, Tcb
->Conn
->ExpStatSN
)) {
2635 return EFI_PROTOCOL_ERROR
;;
2638 // Check the sequence number.
2640 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, R2THdr
->R2TSeqNum
);
2641 if (EFI_ERROR (Status
)) {
2645 XferContext
= &Tcb
->XferContext
;
2646 XferContext
->TargetTransferTag
= R2THdr
->TargetTransferTag
;
2647 XferContext
->Offset
= R2THdr
->BufferOffset
;
2648 XferContext
->DesiredLength
= R2THdr
->DesiredDataTransferLength
;
2650 if (((XferContext
->Offset
+ XferContext
->DesiredLength
) > Packet
->OutTransferLength
) ||
2651 (XferContext
->DesiredLength
> Tcb
->Conn
->Session
->MaxBurstLength
)
2653 return EFI_PROTOCOL_ERROR
;
2656 // Send the data solicited by this R2T.
2658 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2659 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2666 Process the received iSCSI SCSI Response PDU.
2668 @param[in] Pdu The Response PDU received.
2669 @param[in] Tcb The task control block.
2670 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2672 @retval EFI_SUCCES The Response PDU is processed.
2673 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2674 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2675 @retval Others Other errors as indicated.
2679 IScsiOnScsiRspRcvd (
2682 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2685 SCSI_RESPONSE
*ScsiRspHdr
;
2686 ISCSI_SENSE_DATA
*SenseData
;
2690 ScsiRspHdr
= (SCSI_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
2691 if (ScsiRspHdr
== NULL
) {
2692 return EFI_PROTOCOL_ERROR
;
2695 ScsiRspHdr
->InitiatorTaskTag
= NTOHL (ScsiRspHdr
->InitiatorTaskTag
);
2696 if (ScsiRspHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2697 return EFI_PROTOCOL_ERROR
;
2700 ScsiRspHdr
->StatSN
= NTOHL (ScsiRspHdr
->StatSN
);
2702 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, ScsiRspHdr
->StatSN
);
2703 if (EFI_ERROR (Status
)) {
2707 ScsiRspHdr
->MaxCmdSN
= NTOHL (ScsiRspHdr
->MaxCmdSN
);
2708 ScsiRspHdr
->ExpCmdSN
= NTOHL (ScsiRspHdr
->ExpCmdSN
);
2709 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, ScsiRspHdr
->MaxCmdSN
, ScsiRspHdr
->ExpCmdSN
);
2711 Tcb
->StatusXferd
= TRUE
;
2713 Packet
->HostAdapterStatus
= ScsiRspHdr
->Response
;
2714 if (Packet
->HostAdapterStatus
!= ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET
) {
2718 Packet
->TargetStatus
= ScsiRspHdr
->Status
;
2720 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
| SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
) ||
2721 ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
| SCSI_RSP_PDU_FLAG_UNDERFLOW
)
2723 return EFI_PROTOCOL_ERROR
;
2726 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
)) {
2727 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2728 Status
= EFI_BAD_BUFFER_SIZE
;
2731 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
)) {
2732 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2735 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2736 if (Packet
->DataDirection
== DataIn
) {
2737 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2739 Packet
->OutTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2742 Status
= EFI_BAD_BUFFER_SIZE
;
2745 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2746 if (Packet
->DataDirection
== DataIn
) {
2747 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2749 Packet
->OutTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2753 DataSegLen
= ISCSI_GET_DATASEG_LEN (ScsiRspHdr
);
2754 if (DataSegLen
!= 0) {
2755 SenseData
= (ISCSI_SENSE_DATA
*) NetbufGetByte (Pdu
, sizeof (SCSI_RESPONSE
), NULL
);
2756 if (SenseData
== NULL
) {
2757 return EFI_PROTOCOL_ERROR
;
2760 SenseData
->Length
= NTOHS (SenseData
->Length
);
2762 Packet
->SenseDataLength
= (UINT8
) MIN (SenseData
->Length
, Packet
->SenseDataLength
);
2763 if (Packet
->SenseDataLength
!= 0) {
2764 CopyMem (Packet
->SenseData
, &SenseData
->Data
[0], Packet
->SenseDataLength
);
2767 Packet
->SenseDataLength
= 0;
2775 Process the received NOP In PDU.
2777 @param[in] Pdu The NOP In PDU received.
2778 @param[in] Tcb The task control block.
2780 @retval EFI_SUCCES The NOP In PDU is processed and the related sequence
2781 numbers are updated.
2782 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2791 ISCSI_NOP_IN
*NopInHdr
;
2794 NopInHdr
= (ISCSI_NOP_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2795 if (NopInHdr
== NULL
) {
2796 return EFI_PROTOCOL_ERROR
;
2799 NopInHdr
->StatSN
= NTOHL (NopInHdr
->StatSN
);
2800 NopInHdr
->ExpCmdSN
= NTOHL (NopInHdr
->ExpCmdSN
);
2801 NopInHdr
->MaxCmdSN
= NTOHL (NopInHdr
->MaxCmdSN
);
2803 if (NopInHdr
->InitiatorTaskTag
== ISCSI_RESERVED_TAG
) {
2804 if (NopInHdr
->StatSN
!= Tcb
->Conn
->ExpStatSN
) {
2805 return EFI_PROTOCOL_ERROR
;
2808 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NopInHdr
->StatSN
);
2809 if (EFI_ERROR (Status
)) {
2814 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, NopInHdr
->MaxCmdSN
, NopInHdr
->ExpCmdSN
);
2821 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
2823 @param[in] PassThru The EXT SCSI PASS THRU protocol.
2824 @param[in] Target The target ID.
2825 @param[in] Lun The LUN.
2826 @param[in, out] Packet The request packet containing IO request, SCSI command
2827 buffer and buffers to read/write.
2829 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
2831 @retval EFI_DEVICE_ERROR Session state was not as required.
2832 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2833 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
2834 @retval EFI_NOT_READY The target can not accept new commands.
2835 @retval Others Other errors as indicated.
2839 IScsiExecuteScsiCommand (
2840 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
,
2843 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2847 ISCSI_DRIVER_DATA
*Private
;
2848 ISCSI_SESSION
*Session
;
2849 EFI_EVENT TimeoutEvent
;
2850 ISCSI_CONNECTION
*Conn
;
2853 ISCSI_XFER_CONTEXT
*XferContext
;
2855 ISCSI_IN_BUFFER_CONTEXT InBufferContext
;
2859 Private
= ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru
);
2860 Session
= Private
->Session
;
2861 Status
= EFI_SUCCESS
;
2863 TimeoutEvent
= NULL
;
2866 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
2867 Status
= EFI_DEVICE_ERROR
;
2871 Conn
= NET_LIST_USER_STRUCT_S (
2872 Session
->Conns
.ForwardLink
,
2875 ISCSI_CONNECTION_SIGNATURE
2878 if (Packet
->Timeout
!= 0) {
2879 Timeout
= MultU64x32 (Packet
->Timeout
, 4);
2882 Status
= IScsiNewTcb (Conn
, &Tcb
);
2883 if (EFI_ERROR (Status
)) {
2887 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2889 Pdu
= IScsiNewScsiCmdPdu (Packet
, Lun
, Tcb
);
2891 Status
= EFI_OUT_OF_RESOURCES
;
2895 XferContext
= &Tcb
->XferContext
;
2896 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2897 if (PduHdr
== NULL
) {
2898 Status
= EFI_PROTOCOL_ERROR
;
2902 XferContext
->Offset
= ISCSI_GET_DATASEG_LEN (PduHdr
);
2905 // Transmit the SCSI Command PDU.
2907 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
2911 if (EFI_ERROR (Status
)) {
2915 if (!Session
->InitialR2T
&&
2916 (XferContext
->Offset
< Session
->FirstBurstLength
) &&
2917 (XferContext
->Offset
< Packet
->OutTransferLength
)
2920 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2921 // OUT data, and the limit of FirstBurstLength is not reached.
2923 XferContext
->TargetTransferTag
= ISCSI_RESERVED_TAG
;
2924 XferContext
->DesiredLength
= MIN (
2925 Session
->FirstBurstLength
,
2926 Packet
->OutTransferLength
- XferContext
->Offset
2929 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2930 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2931 if (EFI_ERROR (Status
)) {
2936 InBufferContext
.InData
= (UINT8
*) Packet
->InDataBuffer
;
2937 InBufferContext
.InDataLen
= Packet
->InTransferLength
;
2939 while (!Tcb
->StatusXferd
) {
2941 // Start the timeout timer.
2944 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, Timeout
);
2945 if (EFI_ERROR (Status
)) {
2949 TimeoutEvent
= Conn
->TimeoutEvent
;
2953 // Try to receive PDU from target.
2955 Status
= IScsiReceivePdu (Conn
, &Pdu
, &InBufferContext
, FALSE
, FALSE
, TimeoutEvent
);
2956 if (EFI_ERROR (Status
)) {
2960 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2961 if (PduHdr
== NULL
) {
2962 Status
= EFI_PROTOCOL_ERROR
;
2966 switch (ISCSI_GET_OPCODE (PduHdr
)) {
2967 case ISCSI_OPCODE_SCSI_DATA_IN
:
2968 Status
= IScsiOnDataInRcvd (Pdu
, Tcb
, Packet
);
2971 case ISCSI_OPCODE_R2T
:
2972 Status
= IScsiOnR2TRcvd (Pdu
, Tcb
, Lun
, Packet
);
2975 case ISCSI_OPCODE_SCSI_RSP
:
2976 Status
= IScsiOnScsiRspRcvd (Pdu
, Tcb
, Packet
);
2979 case ISCSI_OPCODE_NOP_IN
:
2980 Status
= IScsiOnNopInRcvd (Pdu
, Tcb
);
2983 case ISCSI_OPCODE_VENDOR_T0
:
2984 case ISCSI_OPCODE_VENDOR_T1
:
2985 case ISCSI_OPCODE_VENDOR_T2
:
2987 // These messages are vendor specific. Skip them.
2992 Status
= EFI_PROTOCOL_ERROR
;
2998 if (EFI_ERROR (Status
)) {
3005 if (TimeoutEvent
!= NULL
) {
3006 gBS
->SetTimer (TimeoutEvent
, TimerCancel
, 0);
3018 Reinstate the session on some error.
3020 @param[in] Session The iSCSI session
3022 @retval EFI_SUCCESS The session is reinstated from some error.
3023 @retval Other Reinstatement failed.
3027 IScsiSessionReinstatement (
3028 IN ISCSI_SESSION
*Session
3033 ASSERT (Session
->State
!= SESSION_STATE_FREE
);
3036 // Abort the session and re-init it.
3038 IScsiSessionAbort (Session
);
3039 IScsiSessionInit (Session
, TRUE
);
3044 Status
= IScsiSessionLogin (Session
);
3051 Initialize some session parameters before login.
3053 @param[in, out] Session The iSCSI session.
3054 @param[in] Recovery Whether the request is from a fresh new start or recovery.
3059 IN OUT ISCSI_SESSION
*Session
,
3064 Session
->Signature
= ISCSI_SESSION_SIGNATURE
;
3065 Session
->State
= SESSION_STATE_FREE
;
3067 InitializeListHead (&Session
->Conns
);
3068 InitializeListHead (&Session
->TcbList
);
3074 Session
->InitiatorTaskTag
= 1;
3075 Session
->NextCid
= 1;
3077 Session
->TargetPortalGroupTag
= 0;
3078 Session
->MaxConnections
= ISCSI_MAX_CONNS_PER_SESSION
;
3079 Session
->InitialR2T
= FALSE
;
3080 Session
->ImmediateData
= TRUE
;
3081 Session
->MaxBurstLength
= 262144;
3082 Session
->FirstBurstLength
= MAX_RECV_DATA_SEG_LEN_IN_FFP
;
3083 Session
->DefaultTime2Wait
= 2;
3084 Session
->DefaultTime2Retain
= 20;
3085 Session
->MaxOutstandingR2T
= DEFAULT_MAX_OUTSTANDING_R2T
;
3086 Session
->DataPDUInOrder
= TRUE
;
3087 Session
->DataSequenceInOrder
= TRUE
;
3088 Session
->ErrorRecoveryLevel
= 0;
3093 Abort the iSCSI session. That is, reset all the connection(s), and free the
3096 @param[in, out] Session The iSCSI session.
3101 IN OUT ISCSI_SESSION
*Session
3104 ISCSI_CONNECTION
*Conn
;
3105 EFI_GUID
*ProtocolGuid
;
3107 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
3111 ASSERT (!IsListEmpty (&Session
->Conns
));
3113 while (!IsListEmpty (&Session
->Conns
)) {
3114 Conn
= NET_LIST_USER_STRUCT_S (
3115 Session
->Conns
.ForwardLink
,
3118 ISCSI_CONNECTION_SIGNATURE
3120 if (!Conn
->Ipv6Flag
) {
3121 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
3123 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
3126 gBS
->CloseProtocol (
3129 Session
->Private
->Image
,
3130 Session
->Private
->ExtScsiPassThruHandle
3133 IScsiConnReset (Conn
);
3135 IScsiDetatchConnection (Conn
);
3136 IScsiDestroyConnection (Conn
);
3139 Session
->State
= SESSION_STATE_FAILED
;