2 The implementation of iSCSI protocol based on RFC3720.
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
11 UINT32 mDataSegPad
= 0;
14 Attach the iSCSI connection to the iSCSI session.
16 @param[in, out] Session The iSCSI session.
17 @param[in, out] Conn The iSCSI connection.
21 IScsiAttatchConnection (
22 IN OUT ISCSI_SESSION
*Session
,
23 IN OUT ISCSI_CONNECTION
*Conn
26 InsertTailList (&Session
->Conns
, &Conn
->Link
);
27 Conn
->Session
= Session
;
32 Detach the iSCSI connection from the session it belongs to.
34 @param[in, out] Conn The iSCSI connection.
38 IScsiDetatchConnection (
39 IN OUT ISCSI_CONNECTION
*Conn
42 RemoveEntryList (&Conn
->Link
);
43 Conn
->Session
->NumConns
--;
48 Check the sequence number according to RFC3720.
50 @param[in, out] ExpSN The currently expected sequence number.
51 @param[in] NewSN The sequence number to check.
53 @retval EFI_SUCCESS The check passed and the ExpSN is increased.
54 @retval EFI_NOT_READY Response was sent due to a retransmission request.
55 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
64 if (!ISCSI_SEQ_EQ (NewSN
, *ExpSN
)) {
65 if (ISCSI_SEQ_LT (NewSN
, *ExpSN
)) {
71 return EFI_PROTOCOL_ERROR
;
83 Update the sequence numbers for the iSCSI command.
85 @param[in, out] Session The iSCSI session.
86 @param[in] MaxCmdSN Maximum CmdSN from the target.
87 @param[in] ExpCmdSN Next expected CmdSN from the target.
92 IN OUT ISCSI_SESSION
*Session
,
97 if (ISCSI_SEQ_LT (MaxCmdSN
, ExpCmdSN
- 1)) {
101 if (ISCSI_SEQ_GT (MaxCmdSN
, Session
->MaxCmdSN
)) {
102 Session
->MaxCmdSN
= MaxCmdSN
;
105 if (ISCSI_SEQ_GT (ExpCmdSN
, Session
->ExpCmdSN
)) {
106 Session
->ExpCmdSN
= ExpCmdSN
;
111 This function does the iSCSI connection login.
113 @param[in, out] Conn The iSCSI connection to login.
114 @param Timeout The timeout value in millisecond.
116 @retval EFI_SUCCESS The iSCSI connection is logged into the iSCSI target.
117 @retval EFI_TIMEOUT Timeout occurred during the login procedure.
118 @retval Others Other errors as indicated.
123 IN OUT ISCSI_CONNECTION
*Conn
,
130 // Start the timer, and wait Timeout seconds to establish the TCP connection.
132 Status
= gBS
->SetTimer (
135 MultU64x32 (Timeout
, TICKS_PER_MS
)
137 if (EFI_ERROR (Status
)) {
142 // Try to establish the tcp connection.
144 Status
= TcpIoConnect (&Conn
->TcpIo
, Conn
->TimeoutEvent
);
145 gBS
->SetTimer (Conn
->TimeoutEvent
, TimerCancel
, 0);
147 if (EFI_ERROR (Status
)) {
151 Conn
->State
= CONN_STATE_IN_LOGIN
;
154 // Connection is established, start the iSCSI Login.
157 Status
= IScsiSendLoginReq (Conn
);
158 if (EFI_ERROR (Status
)) {
162 Status
= IScsiReceiveLoginRsp (Conn
);
163 if (EFI_ERROR (Status
)) {
166 } while (Conn
->CurrentStage
!= ISCSI_FULL_FEATURE_PHASE
);
172 Reset the iSCSI connection.
174 @param[in, out] Conn The iSCSI connection to reset.
179 IN OUT ISCSI_CONNECTION
*Conn
182 TcpIoReset (&Conn
->TcpIo
);
186 Create a TCP connection for the iSCSI session.
188 @param[in] Session Points to the iSCSI session.
190 @return The newly created iSCSI connection.
194 IScsiCreateConnection (
195 IN ISCSI_SESSION
*Session
198 ISCSI_DRIVER_DATA
*Private
;
199 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
200 ISCSI_CONNECTION
*Conn
;
201 TCP_IO_CONFIG_DATA TcpIoConfig
;
202 TCP4_IO_CONFIG_DATA
*Tcp4IoConfig
;
203 TCP6_IO_CONFIG_DATA
*Tcp6IoConfig
;
206 Private
= Session
->Private
;
207 NvData
= &Session
->ConfigData
->SessionConfigData
;
209 Conn
= AllocateZeroPool (sizeof (ISCSI_CONNECTION
));
214 Conn
->Signature
= ISCSI_CONNECTION_SIGNATURE
;
215 Conn
->State
= CONN_STATE_FREE
;
216 Conn
->CurrentStage
= ISCSI_SECURITY_NEGOTIATION
;
217 Conn
->NextStage
= ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
;
218 Conn
->AuthStep
= ISCSI_AUTH_INITIAL
;
220 Conn
->PartialReqSent
= FALSE
;
221 Conn
->PartialRspRcvd
= FALSE
;
222 Conn
->ParamNegotiated
= FALSE
;
223 Conn
->Cid
= Session
->NextCid
++;
224 Conn
->Ipv6Flag
= NvData
->IpMode
== IP_MODE_IP6
|| Session
->ConfigData
->AutoConfigureMode
== IP_MODE_AUTOCONFIG_IP6
;
226 Status
= gBS
->CreateEvent (
233 if (EFI_ERROR (Status
)) {
238 NetbufQueInit (&Conn
->RspQue
);
241 // Set the default connection-only parameters.
243 Conn
->MaxRecvDataSegmentLength
= DEFAULT_MAX_RECV_DATA_SEG_LEN
;
244 Conn
->HeaderDigest
= IScsiDigestNone
;
245 Conn
->DataDigest
= IScsiDigestNone
;
247 if (NvData
->DnsMode
) {
249 // perform dns process if target address expressed by domain name.
251 if (!Conn
->Ipv6Flag
) {
252 Status
= IScsiDns4 (Private
->Image
, Private
->Controller
, NvData
);
254 Status
= IScsiDns6 (Private
->Image
, Private
->Controller
, NvData
);
257 if (EFI_ERROR (Status
)) {
258 DEBUG ((DEBUG_ERROR
, "The configuration of Target address or DNS server address is invalid!\n"));
264 if (!Conn
->Ipv6Flag
) {
265 Tcp4IoConfig
= &TcpIoConfig
.Tcp4IoConfigData
;
267 CopyMem (&Tcp4IoConfig
->LocalIp
, &NvData
->LocalIp
, sizeof (EFI_IPv4_ADDRESS
));
268 CopyMem (&Tcp4IoConfig
->SubnetMask
, &NvData
->SubnetMask
, sizeof (EFI_IPv4_ADDRESS
));
269 CopyMem (&Tcp4IoConfig
->Gateway
, &NvData
->Gateway
, sizeof (EFI_IPv4_ADDRESS
));
270 CopyMem (&Tcp4IoConfig
->RemoteIp
, &NvData
->TargetIp
, sizeof (EFI_IPv4_ADDRESS
));
272 Tcp4IoConfig
->RemotePort
= NvData
->TargetPort
;
273 Tcp4IoConfig
->ActiveFlag
= TRUE
;
274 Tcp4IoConfig
->StationPort
= 0;
276 Tcp6IoConfig
= &TcpIoConfig
.Tcp6IoConfigData
;
278 CopyMem (&Tcp6IoConfig
->RemoteIp
, &NvData
->TargetIp
, sizeof (EFI_IPv6_ADDRESS
));
279 Tcp6IoConfig
->RemotePort
= NvData
->TargetPort
;
280 Tcp6IoConfig
->ActiveFlag
= TRUE
;
281 Tcp6IoConfig
->StationPort
= 0;
285 // Create the TCP IO for this connection.
287 Status
= TcpIoCreateSocket (
290 (UINT8
)(!Conn
->Ipv6Flag
? TCP_VERSION_4
: TCP_VERSION_6
),
294 if (EFI_ERROR (Status
)) {
295 gBS
->CloseEvent (Conn
->TimeoutEvent
);
304 Destroy an iSCSI connection.
306 @param[in] Conn The connection to destroy.
310 IScsiDestroyConnection (
311 IN ISCSI_CONNECTION
*Conn
314 TcpIoDestroySocket (&Conn
->TcpIo
);
316 NetbufQueFlush (&Conn
->RspQue
);
317 gBS
->CloseEvent (Conn
->TimeoutEvent
);
322 Retrieve the IPv6 Address/Prefix/Gateway from the established TCP connection, these informations
323 will be filled in the iSCSI Boot Firmware Table.
325 @param[in] Conn The connection used in the iSCSI login phase.
327 @retval EFI_SUCCESS Get the NIC information successfully.
328 @retval Others Other errors as indicated.
333 IN ISCSI_CONNECTION
*Conn
336 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
337 EFI_TCP6_PROTOCOL
*Tcp6
;
338 EFI_IP6_MODE_DATA Ip6ModeData
;
340 EFI_IPv6_ADDRESS
*TargetIp
;
342 UINT8 SubnetPrefixLength
;
345 NvData
= &Conn
->Session
->ConfigData
->SessionConfigData
;
346 TargetIp
= &NvData
->TargetIp
.v6
;
347 Tcp6
= Conn
->TcpIo
.Tcp
.Tcp6
;
349 ZeroMem (&Ip6ModeData
, sizeof (EFI_IP6_MODE_DATA
));
350 Status
= Tcp6
->GetModeData (
358 if (EFI_ERROR (Status
)) {
362 if (!Ip6ModeData
.IsConfigured
) {
363 Status
= EFI_ABORTED
;
367 IP6_COPY_ADDRESS (&NvData
->LocalIp
, &Ip6ModeData
.ConfigData
.StationAddress
);
369 NvData
->PrefixLength
= 0;
370 for (Index
= 0; Index
< Ip6ModeData
.AddressCount
; Index
++) {
371 if (EFI_IP6_EQUAL (&NvData
->LocalIp
.v6
, &Ip6ModeData
.AddressList
[Index
].Address
)) {
372 NvData
->PrefixLength
= Ip6ModeData
.AddressList
[Index
].PrefixLength
;
377 SubnetPrefixLength
= 0;
378 RouteEntry
= Ip6ModeData
.RouteCount
;
379 for (Index
= 0; Index
< Ip6ModeData
.RouteCount
; Index
++) {
380 if (NetIp6IsNetEqual (TargetIp
, &Ip6ModeData
.RouteTable
[Index
].Destination
, Ip6ModeData
.RouteTable
[Index
].PrefixLength
)) {
381 if (SubnetPrefixLength
< Ip6ModeData
.RouteTable
[Index
].PrefixLength
) {
382 SubnetPrefixLength
= Ip6ModeData
.RouteTable
[Index
].PrefixLength
;
388 if (RouteEntry
!= Ip6ModeData
.RouteCount
) {
389 IP6_COPY_ADDRESS (&NvData
->Gateway
, &Ip6ModeData
.RouteTable
[RouteEntry
].Gateway
);
393 if (Ip6ModeData
.AddressList
!= NULL
) {
394 FreePool (Ip6ModeData
.AddressList
);
397 if (Ip6ModeData
.GroupTable
!= NULL
) {
398 FreePool (Ip6ModeData
.GroupTable
);
401 if (Ip6ModeData
.RouteTable
!= NULL
) {
402 FreePool (Ip6ModeData
.RouteTable
);
405 if (Ip6ModeData
.NeighborCache
!= NULL
) {
406 FreePool (Ip6ModeData
.NeighborCache
);
409 if (Ip6ModeData
.PrefixTable
!= NULL
) {
410 FreePool (Ip6ModeData
.PrefixTable
);
413 if (Ip6ModeData
.IcmpTypeList
!= NULL
) {
414 FreePool (Ip6ModeData
.IcmpTypeList
);
421 Re-set any stateful session-level authentication information that is used by
422 the leading login / leading connection.
424 (Note that this driver only supports a single connection per session -- see
425 ISCSI_MAX_CONNS_PER_SESSION.)
427 @param[in,out] Session The iSCSI session.
431 IScsiSessionResetAuthData (
432 IN OUT ISCSI_SESSION
*Session
435 if (Session
->AuthType
== ISCSI_AUTH_TYPE_CHAP
) {
436 Session
->AuthData
.CHAP
.Hash
= NULL
;
441 Login the iSCSI session.
443 @param[in] Session The iSCSI session.
445 @retval EFI_SUCCESS The iSCSI session login procedure finished.
446 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
447 @retval EFI_NO_MEDIA There was a media error.
448 @retval Others Other errors as indicated.
453 IN ISCSI_SESSION
*Session
457 ISCSI_CONNECTION
*Conn
;
459 EFI_GUID
*ProtocolGuid
;
461 EFI_STATUS MediaStatus
;
464 // Check media status before session login.
466 MediaStatus
= EFI_SUCCESS
;
467 NetLibDetectMediaWaitTimeout (Session
->Private
->Controller
, ISCSI_CHECK_MEDIA_LOGIN_WAITING_TIME
, &MediaStatus
);
468 if (MediaStatus
!= EFI_SUCCESS
) {
473 // Set session identifier
475 CopyMem (Session
->Isid
, Session
->ConfigData
->SessionConfigData
.IsId
, 6);
481 // Create a connection for the session.
483 Conn
= IScsiCreateConnection (Session
);
485 return EFI_OUT_OF_RESOURCES
;
488 IScsiAttatchConnection (Session
, Conn
);
491 // Login through the newly created connection.
493 IScsiSessionResetAuthData (Session
);
494 Status
= IScsiConnLogin (Conn
, Session
->ConfigData
->SessionConfigData
.ConnectTimeout
);
495 if (EFI_ERROR (Status
)) {
496 IScsiConnReset (Conn
);
497 IScsiDetatchConnection (Conn
);
498 IScsiDestroyConnection (Conn
);
501 if (Status
!= EFI_TIMEOUT
) {
506 } while (RetryCount
<= Session
->ConfigData
->SessionConfigData
.ConnectRetryCount
);
508 if (!EFI_ERROR (Status
)) {
509 Session
->State
= SESSION_STATE_LOGGED_IN
;
511 if (!Conn
->Ipv6Flag
) {
512 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
514 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
517 Status
= gBS
->OpenProtocol (
521 Session
->Private
->Image
,
522 Session
->Private
->ExtScsiPassThruHandle
,
523 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
526 ASSERT_EFI_ERROR (Status
);
528 if (Conn
->Ipv6Flag
) {
529 Status
= IScsiGetIp6NicInfo (Conn
);
537 Wait for IPsec negotiation, then try to login the iSCSI session again.
539 @param[in] Session The iSCSI session.
541 @retval EFI_SUCCESS The iSCSI session login procedure finished.
542 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
543 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
547 IScsiSessionReLogin (
548 IN ISCSI_SESSION
*Session
552 EFI_STATUS TimerStatus
;
555 Status
= gBS
->CreateEvent (EVT_TIMER
, TPL_CALLBACK
, NULL
, NULL
, &Timer
);
556 if (EFI_ERROR (Status
)) {
560 Status
= gBS
->SetTimer (
563 ISCSI_WAIT_IPSEC_TIMEOUT
566 if (EFI_ERROR (Status
)) {
567 gBS
->CloseEvent (Timer
);
572 TimerStatus
= gBS
->CheckEvent (Timer
);
574 if (!EFI_ERROR (TimerStatus
)) {
575 Status
= IScsiSessionLogin (Session
);
577 } while (TimerStatus
== EFI_NOT_READY
);
579 gBS
->CloseEvent (Timer
);
584 Build and send the iSCSI login request to the iSCSI target according to
585 the current login stage.
587 @param[in] Conn The connection in the iSCSI login phase.
589 @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
591 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
592 @retval EFI_DEVICE_ERROR Some kind of device error occurred.
597 IN ISCSI_CONNECTION
*Conn
604 // Build the Login Request PDU.
606 Pdu
= IScsiPrepareLoginReq (Conn
);
608 return EFI_DEVICE_ERROR
;
612 // Send it to the iSCSI target.
614 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
622 Receive and process the iSCSI login response.
624 @param[in] Conn The connection in the iSCSI login phase.
626 @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
627 @retval Others Other errors as indicated.
631 IScsiReceiveLoginRsp (
632 IN ISCSI_CONNECTION
*Conn
641 // Receive the iSCSI login response.
643 Status
= IScsiReceivePdu (Conn
, &Pdu
, NULL
, FALSE
, FALSE
, NULL
);
644 if (EFI_ERROR (Status
)) {
648 ASSERT (Pdu
!= NULL
);
651 // A Login Response is received; process it.
653 Status
= IScsiProcessLoginRsp (Conn
, Pdu
);
661 Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
662 The DataSegmentLength and the actual size of the net buffer containing this PDU will be
665 @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
667 @param[in] Key The key name string.
668 @param[in] Value The value string.
670 @retval EFI_SUCCESS The key-value pair is added to the PDU's data segment and
671 the correspondence length fields are updated.
672 @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
674 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
677 IScsiAddKeyValuePair (
687 ISCSI_LOGIN_REQUEST
*LoginReq
;
690 LoginReq
= (ISCSI_LOGIN_REQUEST
*)NetbufGetByte (Pdu
, 0, NULL
);
691 if (LoginReq
== NULL
) {
692 return EFI_PROTOCOL_ERROR
;
695 DataSegLen
= NTOH24 (LoginReq
->DataSegmentLength
);
697 KeyLen
= (UINT32
)AsciiStrLen (Key
);
698 ValueLen
= (UINT32
)AsciiStrLen (Value
);
701 // 1 byte for the key value separator '=' and 1 byte for the null
702 // delimiter after the value.
704 TotalLen
= KeyLen
+ 1 + ValueLen
+ 1;
707 // Allocate the space for the key-value pair.
709 Data
= (CHAR8
*)NetbufAllocSpace (Pdu
, TotalLen
, NET_BUF_TAIL
);
711 return EFI_OUT_OF_RESOURCES
;
717 CopyMem (Data
, Key
, KeyLen
);
726 CopyMem (Data
, Value
, ValueLen
);
732 // Update the DataSegmentLength
734 ISCSI_SET_DATASEG_LEN (LoginReq
, DataSegLen
+ TotalLen
);
740 Prepare the iSCSI login request to be sent according to the current login status.
742 @param[in, out] Conn The connection in the iSCSI login phase.
744 @return The pointer to the net buffer containing the iSCSI login request built.
745 @retval NULL Other errors as indicated.
749 IScsiPrepareLoginReq (
750 IN OUT ISCSI_CONNECTION
*Conn
753 ISCSI_SESSION
*Session
;
755 ISCSI_LOGIN_REQUEST
*LoginReq
;
758 Session
= Conn
->Session
;
760 Nbuf
= NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST
) + DEFAULT_MAX_RECV_DATA_SEG_LEN
);
765 LoginReq
= (ISCSI_LOGIN_REQUEST
*)NetbufAllocSpace (Nbuf
, sizeof (ISCSI_LOGIN_REQUEST
), NET_BUF_TAIL
);
766 if (LoginReq
== NULL
) {
771 ZeroMem (LoginReq
, sizeof (ISCSI_LOGIN_REQUEST
));
774 // Init the login request pdu
776 ISCSI_SET_OPCODE (LoginReq
, ISCSI_OPCODE_LOGIN_REQ
, ISCSI_REQ_IMMEDIATE
);
777 ISCSI_SET_STAGES (LoginReq
, Conn
->CurrentStage
, Conn
->NextStage
);
778 LoginReq
->VersionMax
= ISCSI_VERSION_MAX
;
779 LoginReq
->VersionMin
= ISCSI_VERSION_MIN
;
780 LoginReq
->Tsih
= HTONS (Session
->Tsih
);
781 LoginReq
->InitiatorTaskTag
= HTONL (Session
->InitiatorTaskTag
);
782 LoginReq
->Cid
= HTONS (Conn
->Cid
);
783 LoginReq
->CmdSN
= HTONL (Session
->CmdSN
);
786 // For the first Login Request on a connection this is ExpStatSN for the
787 // old connection, and this field is only valid if the Login Request restarts
789 // For subsequent Login Requests it is used to acknowledge the Login Responses
790 // with their increasing StatSN values.
792 LoginReq
->ExpStatSN
= HTONL (Conn
->ExpStatSN
);
793 CopyMem (LoginReq
->Isid
, Session
->Isid
, sizeof (LoginReq
->Isid
));
795 if (Conn
->PartialRspRcvd
) {
797 // A partial response. The initiator must send an empty Login Request.
802 Status
= EFI_SUCCESS
;
804 switch (Conn
->CurrentStage
) {
805 case ISCSI_SECURITY_NEGOTIATION
:
807 // Both none authentication and CHAP authentication share the CHAP path.
810 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
811 Status
= IScsiCHAPToSendReq (Conn
, Nbuf
);
816 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
818 // Only negotiate the parameter once.
820 if (!Conn
->ParamNegotiated
) {
821 IScsiFillOpParams (Conn
, Nbuf
);
824 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
829 // An error occurs...
831 Status
= EFI_DEVICE_ERROR
;
835 if (EFI_ERROR (Status
)) {
840 // Pad the data segment if needed.
842 IScsiPadSegment (Nbuf
, ISCSI_GET_DATASEG_LEN (LoginReq
));
844 // Check whether we will issue the stage transition signal?
846 Conn
->TransitInitiated
= ISCSI_FLAG_ON (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
853 Process the iSCSI Login Response.
855 @param[in, out] Conn The connection on which the iSCSI login response is received.
856 @param[in, out] Pdu The iSCSI login response PDU.
858 @retval EFI_SUCCESS The iSCSI login response PDU is processed, and all checks are passed.
859 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
860 @retval EFI_MEDIA_CHANGED Target is redirected.
861 @retval Others Other errors as indicated.
865 IScsiProcessLoginRsp (
866 IN OUT ISCSI_CONNECTION
*Conn
,
871 ISCSI_SESSION
*Session
;
872 ISCSI_LOGIN_RESPONSE
*LoginRsp
;
880 Status
= EFI_SUCCESS
;
881 Session
= Conn
->Session
;
883 LoginRsp
= (ISCSI_LOGIN_RESPONSE
*)NetbufGetByte (Pdu
, 0, NULL
);
884 if (LoginRsp
== NULL
) {
885 return EFI_PROTOCOL_ERROR
;
888 if (!ISCSI_CHECK_OPCODE (LoginRsp
, ISCSI_OPCODE_LOGIN_RSP
)) {
890 // It is not a Login Response.
892 return EFI_PROTOCOL_ERROR
;
896 // Get the data segment, if any.
898 DataSegLen
= ISCSI_GET_DATASEG_LEN (LoginRsp
);
899 if (DataSegLen
!= 0) {
900 DataSeg
= NetbufGetByte (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NULL
);
906 // Check the status class in the login response PDU.
908 switch (LoginRsp
->StatusClass
) {
909 case ISCSI_LOGIN_STATUS_SUCCESS
:
911 // Just break here; the response and the data segment will be processed later.
915 case ISCSI_LOGIN_STATUS_REDIRECTION
:
917 // The target may be moved to a different address.
919 if (DataSeg
== NULL
) {
920 return EFI_PROTOCOL_ERROR
;
924 // Process the TargetAddress key-value strings in the data segment to update the
925 // target address info.
927 Status
= IScsiUpdateTargetAddress (Session
, (CHAR8
*)DataSeg
, DataSegLen
);
928 if (EFI_ERROR (Status
)) {
933 // Session will be restarted on this error status because the Target is
934 // redirected by this Login Response.
936 return EFI_MEDIA_CHANGED
;
940 // Initiator Error, Target Error, or any other undefined error code.
942 return EFI_PROTOCOL_ERROR
;
946 // The status is success; extract the wanted fields from the header segment.
948 Transit
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT
);
949 Continue
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE
);
951 CurrentStage
= ISCSI_GET_CURRENT_STAGE (LoginRsp
);
952 NextStage
= ISCSI_GET_NEXT_STAGE (LoginRsp
);
954 LoginRsp
->InitiatorTaskTag
= NTOHL (LoginRsp
->InitiatorTaskTag
);
956 if ((Transit
&& Continue
) ||
957 (CurrentStage
!= Conn
->CurrentStage
) ||
958 (!Conn
->TransitInitiated
&& Transit
) ||
959 (Transit
&& (NextStage
!= Conn
->NextStage
)) ||
960 (CompareMem (Session
->Isid
, LoginRsp
->Isid
, sizeof (LoginRsp
->Isid
)) != 0) ||
961 (LoginRsp
->InitiatorTaskTag
!= Session
->InitiatorTaskTag
)
965 // A Login Response with the C bit set to 1 MUST have the T bit set to 0.
966 // The CSG in the Login Response MUST be the same with the I-end of this connection.
967 // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
968 // initiate the transition.
969 // The NSG MUST be the same with the I-end of this connection if Transit is required.
970 // The ISID in the Login Response MUST be the same with this session.
972 return EFI_PROTOCOL_ERROR
;
975 LoginRsp
->StatSN
= NTOHL (LoginRsp
->StatSN
);
976 LoginRsp
->ExpCmdSN
= NTOHL (LoginRsp
->ExpCmdSN
);
977 LoginRsp
->MaxCmdSN
= NTOHL (LoginRsp
->MaxCmdSN
);
979 if ((Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
) && (Conn
->AuthStep
== ISCSI_AUTH_INITIAL
)) {
981 // If the Login Request is a leading Login Request, the target MUST use
982 // the value presented in CmdSN as the target value for ExpCmdSN.
984 if ((Session
->State
== SESSION_STATE_FREE
) && (Session
->CmdSN
!= LoginRsp
->ExpCmdSN
)) {
985 return EFI_PROTOCOL_ERROR
;
989 // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
992 Conn
->ExpStatSN
= LoginRsp
->StatSN
+ 1;
993 Session
->MaxCmdSN
= LoginRsp
->MaxCmdSN
;
994 Session
->ExpCmdSN
= LoginRsp
->ExpCmdSN
;
997 // Check the StatSN of this PDU.
999 Status
= IScsiCheckSN (&Conn
->ExpStatSN
, LoginRsp
->StatSN
);
1000 if (!EFI_ERROR (Status
)) {
1002 // Update the MaxCmdSN and ExpCmdSN.
1004 IScsiUpdateCmdSN (Session
, LoginRsp
->MaxCmdSN
, LoginRsp
->ExpCmdSN
);
1011 // Trim off the header segment.
1013 NetbufTrim (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NET_BUF_HEAD
);
1016 // Queue this login response first in case it's a partial response so that
1017 // later when the full response list is received we can combine these scattered
1018 // responses' data segment and then process it.
1021 NetbufQueAppend (&Conn
->RspQue
, Pdu
);
1023 Conn
->PartialRspRcvd
= Continue
;
1026 // It is a partial response; must wait for another or more Request/Response
1027 // conversations to get the full response.
1032 switch (CurrentStage
) {
1033 case ISCSI_SECURITY_NEGOTIATION
:
1035 // In security negotiation stage, let CHAP module handle it.
1037 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
1038 Status
= IScsiCHAPOnRspReceived (Conn
);
1043 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
1045 // Response received with negotiation response on iSCSI parameters: check them.
1047 Status
= IScsiCheckOpParams (Conn
);
1048 if (!EFI_ERROR (Status
)) {
1049 Conn
->ParamNegotiated
= TRUE
;
1056 // Should never get here.
1058 Status
= EFI_PROTOCOL_ERROR
;
1062 if (Transit
&& (Status
== EFI_SUCCESS
)) {
1064 // Do the state transition.
1066 Conn
->CurrentStage
= Conn
->NextStage
;
1068 if (Conn
->CurrentStage
== ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
) {
1069 Conn
->NextStage
= ISCSI_FULL_FEATURE_PHASE
;
1072 // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
1073 // get the TSIH from the Login Response.
1075 Session
->Tsih
= NTOHS (LoginRsp
->Tsih
);
1080 // Flush the response(s) received.
1082 NetbufQueFlush (&Conn
->RspQue
);
1088 Updated the target information according the data received in the iSCSI
1089 login response with an target redirection status.
1091 @param[in, out] Session The iSCSI session.
1092 @param[in] Data The data segment that should contain the
1093 TargetAddress key-value list.
1094 @param[in] Len Length of the data.
1096 @retval EFI_SUCCESS The target address is updated.
1097 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1098 @retval EFI_NOT_FOUND The TargetAddress key is not found.
1099 @retval Others Other errors as indicated.
1103 IScsiUpdateTargetAddress (
1104 IN OUT ISCSI_SESSION
*Session
,
1109 LIST_ENTRY
*KeyValueList
;
1110 CHAR8
*TargetAddress
;
1115 ISCSI_SESSION_CONFIG_NVDATA
*NvData
;
1117 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1118 if (KeyValueList
== NULL
) {
1119 return EFI_OUT_OF_RESOURCES
;
1122 Status
= EFI_NOT_FOUND
;
1123 NvData
= &Session
->ConfigData
->SessionConfigData
;
1126 TargetAddress
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ADDRESS
);
1127 if (TargetAddress
== NULL
) {
1132 // RFC 3720 defines format of the TargetAddress=domainname[:port][,portal-group-tag]
1133 // The domainname can be specified as either a DNS host name, adotted-decimal IPv4 address,
1134 // or a bracketed IPv6 address as specified in [RFC2732].
1136 if (NET_IS_DIGIT (TargetAddress
[0])) {
1138 // The domainname of the target is presented in a dotted-decimal IPv4 address format.
1140 IpStr
= TargetAddress
;
1142 while ((*TargetAddress
!= '\0') && (*TargetAddress
!= ':') && (*TargetAddress
!= ',')) {
1144 // NULL, ':', or ',' ends the IPv4 string.
1148 } else if (*TargetAddress
== ISCSI_REDIRECT_ADDR_START_DELIMITER
) {
1150 // The domainname of the target is presented in a bracketed IPv6 address format.
1153 IpStr
= TargetAddress
;
1154 while ((*TargetAddress
!= '\0') && (*TargetAddress
!= ISCSI_REDIRECT_ADDR_END_DELIMITER
)) {
1156 // ']' ends the IPv6 string.
1161 if (*TargetAddress
!= ISCSI_REDIRECT_ADDR_END_DELIMITER
) {
1165 *TargetAddress
= '\0';
1169 // The domainname of the target is presented in the format of a DNS host name.
1171 IpStr
= TargetAddress
;
1173 while ((*TargetAddress
!= '\0') && (*TargetAddress
!= ':') && (*TargetAddress
!= ',')) {
1177 NvData
->DnsMode
= TRUE
;
1181 // Save the original user setting which specifies the proxy/virtual iSCSI target.
1183 NvData
->OriginalTargetPort
= NvData
->TargetPort
;
1185 if (*TargetAddress
== ',') {
1187 // Comma and the portal group tag MUST be omitted if the TargetAddress is sent
1188 // as the result of a redirection.
1191 } else if (*TargetAddress
== ':') {
1192 *TargetAddress
= '\0';
1196 Number
= AsciiStrDecimalToUintn (TargetAddress
);
1197 if (Number
> 0xFFFF) {
1200 NvData
->TargetPort
= (UINT16
)Number
;
1204 // The string only contains the Target address. Use the well-known port.
1206 NvData
->TargetPort
= ISCSI_WELL_KNOWN_PORT
;
1210 // Save the original user setting which specifies the proxy/virtual iSCSI target.
1212 CopyMem (&NvData
->OriginalTargetIp
, &NvData
->TargetIp
, sizeof (EFI_IP_ADDRESS
));
1215 // Update the target IP address.
1217 if (NvData
->IpMode
< IP_MODE_AUTOCONFIG
) {
1218 IpMode
= NvData
->IpMode
;
1220 IpMode
= Session
->ConfigData
->AutoConfigureMode
;
1223 if (NvData
->DnsMode
) {
1225 // Target address is expressed as URL format, just save it and
1226 // do DNS resolution when creating a TCP connection.
1228 if (AsciiStrSize (IpStr
) > sizeof (Session
->ConfigData
->SessionConfigData
.TargetUrl
)) {
1229 return EFI_INVALID_PARAMETER
;
1232 CopyMem (&Session
->ConfigData
->SessionConfigData
.TargetUrl
, IpStr
, AsciiStrSize (IpStr
));
1234 Status
= IScsiAsciiStrToIp (
1237 &Session
->ConfigData
->SessionConfigData
.TargetIp
1240 if (EFI_ERROR (Status
)) {
1243 NvData
->RedirectFlag
= TRUE
;
1249 IScsiFreeKeyValueList (KeyValueList
);
1255 The callback function to free the net buffer list.
1257 @param[in] Arg The opaque parameter.
1266 ASSERT (Arg
!= NULL
);
1268 NetbufFreeList ((LIST_ENTRY
*)Arg
);
1273 The callback function called in NetBufFree; it does nothing.
1275 @param[in] Arg The opaque parameter.
1287 Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
1288 an optional data segment. The two parts will be put into two blocks of buffers in the
1289 net buffer. The digest check will be conducted in this function if needed and the digests
1290 will be trimmed from the PDU buffer.
1292 @param[in] Conn The iSCSI connection to receive data from.
1293 @param[out] Pdu The received iSCSI pdu.
1294 @param[in] Context The context used to describe information on the caller provided
1295 buffer to receive data segment of the iSCSI pdu. It is optional.
1296 @param[in] HeaderDigest Whether there will be header digest received.
1297 @param[in] DataDigest Whether there will be data digest.
1298 @param[in] TimeoutEvent The timeout event. It is optional.
1300 @retval EFI_SUCCESS An iSCSI pdu is received.
1301 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1302 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1303 @retval Others Other errors as indicated.
1308 IN ISCSI_CONNECTION
*Conn
,
1310 IN ISCSI_IN_BUFFER_CONTEXT
*Context OPTIONAL
,
1311 IN BOOLEAN HeaderDigest
,
1312 IN BOOLEAN DataDigest
,
1313 IN EFI_EVENT TimeoutEvent OPTIONAL
1316 LIST_ENTRY
*NbufList
;
1322 UINT32 InDataOffset
;
1323 NET_FRAGMENT Fragment
[2];
1324 UINT32 FragmentCount
;
1326 UINT32 PadAndCRC32
[2];
1328 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
1329 if (NbufList
== NULL
) {
1330 return EFI_OUT_OF_RESOURCES
;
1333 InitializeListHead (NbufList
);
1336 // The header digest will be received together with the PDU header, if exists.
1338 Len
= sizeof (ISCSI_BASIC_HEADER
) + (HeaderDigest
? sizeof (UINT32
) : 0);
1339 PduHdr
= NetbufAlloc (Len
);
1340 if (PduHdr
== NULL
) {
1341 Status
= EFI_OUT_OF_RESOURCES
;
1345 Header
= NetbufAllocSpace (PduHdr
, Len
, NET_BUF_TAIL
);
1346 if (Header
== NULL
) {
1347 Status
= EFI_OUT_OF_RESOURCES
;
1351 InsertTailList (NbufList
, &PduHdr
->List
);
1354 // First step, receive the BHS of the PDU.
1356 Status
= TcpIoReceive (&Conn
->TcpIo
, PduHdr
, FALSE
, TimeoutEvent
);
1358 if (EFI_ERROR (Status
)) {
1364 // TODO: check the header-digest.
1367 // Trim off the digest.
1369 NetbufTrim (PduHdr
, sizeof (UINT32
), NET_BUF_TAIL
);
1372 Len
= ISCSI_GET_DATASEG_LEN (Header
);
1381 // Get the length of the padding bytes of the data segment.
1383 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1385 switch (ISCSI_GET_OPCODE (Header
)) {
1386 case ISCSI_OPCODE_SCSI_DATA_IN
:
1388 // To reduce memory copy overhead, try to use the buffer described by Context
1389 // if the PDU is an iSCSI SCSI data.
1391 InDataOffset
= ISCSI_GET_BUFFER_OFFSET (Header
);
1392 if ((Context
== NULL
) || ((InDataOffset
+ Len
) > Context
->InDataLen
)) {
1393 Status
= EFI_PROTOCOL_ERROR
;
1397 Fragment
[0].Len
= Len
;
1398 Fragment
[0].Bulk
= Context
->InData
+ InDataOffset
;
1400 if (DataDigest
|| (PadLen
!= 0)) {
1402 // The data segment is padded. Use two fragments to receive it:
1403 // the first to receive the useful data; the second to receive the padding.
1405 Fragment
[1].Len
= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1406 Fragment
[1].Bulk
= (UINT8
*)PadAndCRC32
+ (4 - PadLen
);
1413 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1414 if (DataSeg
== NULL
) {
1415 Status
= EFI_OUT_OF_RESOURCES
;
1421 case ISCSI_OPCODE_SCSI_RSP
:
1422 case ISCSI_OPCODE_NOP_IN
:
1423 case ISCSI_OPCODE_LOGIN_RSP
:
1424 case ISCSI_OPCODE_TEXT_RSP
:
1425 case ISCSI_OPCODE_ASYNC_MSG
:
1426 case ISCSI_OPCODE_REJECT
:
1427 case ISCSI_OPCODE_VENDOR_T0
:
1428 case ISCSI_OPCODE_VENDOR_T1
:
1429 case ISCSI_OPCODE_VENDOR_T2
:
1431 // Allocate buffer to receive the data segment.
1433 Len
+= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1434 DataSeg
= NetbufAlloc (Len
);
1435 if (DataSeg
== NULL
) {
1436 Status
= EFI_OUT_OF_RESOURCES
;
1440 NetbufAllocSpace (DataSeg
, Len
, NET_BUF_TAIL
);
1444 Status
= EFI_PROTOCOL_ERROR
;
1448 InsertTailList (NbufList
, &DataSeg
->List
);
1451 // Receive the data segment with the data digest, if any.
1453 Status
= TcpIoReceive (&Conn
->TcpIo
, DataSeg
, FALSE
, TimeoutEvent
);
1455 if (EFI_ERROR (Status
)) {
1461 // TODO: Check the data digest.
1463 NetbufTrim (DataSeg
, sizeof (UINT32
), NET_BUF_TAIL
);
1468 // Trim off the padding bytes in the data segment.
1470 NetbufTrim (DataSeg
, PadLen
, NET_BUF_TAIL
);
1475 // Form the pdu from a list of pdu segments.
1477 *Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
1479 Status
= EFI_OUT_OF_RESOURCES
;
1484 if (EFI_ERROR (Status
)) {
1486 // Free the Nbufs in this NbufList and the NbufList itself.
1488 IScsiFreeNbufList (NbufList
);
1495 Check and get the result of the parameter negotiation.
1497 @param[in, out] Conn The connection in iSCSI login.
1499 @retval EFI_SUCCESS The parameter check is passed and negotiation is finished.
1500 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1501 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1505 IScsiCheckOpParams (
1506 IN OUT ISCSI_CONNECTION
*Conn
1510 LIST_ENTRY
*KeyValueList
;
1513 ISCSI_SESSION
*Session
;
1517 ASSERT (Conn
->RspQue
.BufNum
!= 0);
1519 Session
= Conn
->Session
;
1521 Len
= Conn
->RspQue
.BufSize
;
1522 Data
= AllocatePool (Len
);
1524 return EFI_OUT_OF_RESOURCES
;
1527 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, (UINT8
*)Data
);
1529 Status
= EFI_PROTOCOL_ERROR
;
1532 // Extract the Key-Value pairs into a list.
1534 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1535 if (KeyValueList
== NULL
) {
1543 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_HEADER_DIGEST
);
1544 if (Value
== NULL
) {
1548 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1549 if (Conn
->HeaderDigest
!= IScsiDigestCRC32
) {
1552 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1553 Conn
->HeaderDigest
= IScsiDigestNone
;
1561 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_DIGEST
);
1562 if (Value
== NULL
) {
1566 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1567 if (Conn
->DataDigest
!= IScsiDigestCRC32
) {
1570 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1571 Conn
->DataDigest
= IScsiDigestNone
;
1577 // ErrorRecoveryLevel: result function is Minimum.
1579 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
);
1580 if (Value
== NULL
) {
1584 NumericValue
= IScsiNetNtoi (Value
);
1585 if (NumericValue
> 2) {
1589 Session
->ErrorRecoveryLevel
= (UINT8
)MIN (Session
->ErrorRecoveryLevel
, NumericValue
);
1592 // InitialR2T: result function is OR.
1594 if (!Session
->InitialR2T
) {
1595 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1596 if (Value
== NULL
) {
1600 Session
->InitialR2T
= (BOOLEAN
)(AsciiStrCmp (Value
, "Yes") == 0);
1604 // ImmediateData: result function is AND.
1606 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_IMMEDIATE_DATA
);
1607 if (Value
== NULL
) {
1611 Session
->ImmediateData
= (BOOLEAN
)(Session
->ImmediateData
&& (BOOLEAN
)(AsciiStrCmp (Value
, "Yes") == 0));
1614 // MaxRecvDataSegmentLength is declarative.
1616 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
);
1617 if (Value
!= NULL
) {
1618 Conn
->MaxRecvDataSegmentLength
= (UINT32
)IScsiNetNtoi (Value
);
1622 // MaxBurstLength: result function is Minimum.
1624 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_BURST_LENGTH
);
1625 if (Value
== NULL
) {
1629 NumericValue
= IScsiNetNtoi (Value
);
1630 Session
->MaxBurstLength
= (UINT32
)MIN (Session
->MaxBurstLength
, NumericValue
);
1633 // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
1634 // ImmediateData=No.
1636 if (!(Session
->InitialR2T
&& !Session
->ImmediateData
)) {
1637 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1638 if (Value
== NULL
) {
1642 NumericValue
= IScsiNetNtoi (Value
);
1643 Session
->FirstBurstLength
= (UINT32
)MIN (Session
->FirstBurstLength
, NumericValue
);
1647 // MaxConnections: result function is Minimum.
1649 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_CONNECTIONS
);
1650 if (Value
== NULL
) {
1654 NumericValue
= IScsiNetNtoi (Value
);
1655 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1659 Session
->MaxConnections
= (UINT32
)MIN (Session
->MaxConnections
, NumericValue
);
1662 // DataPDUInOrder: result function is OR.
1664 if (!Session
->DataPDUInOrder
) {
1665 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1666 if (Value
== NULL
) {
1670 Session
->DataPDUInOrder
= (BOOLEAN
)(AsciiStrCmp (Value
, "Yes") == 0);
1674 // DataSequenceInorder: result function is OR.
1676 if (!Session
->DataSequenceInOrder
) {
1677 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1678 if (Value
== NULL
) {
1682 Session
->DataSequenceInOrder
= (BOOLEAN
)(AsciiStrCmp (Value
, "Yes") == 0);
1686 // DefaultTime2Wait: result function is Maximum.
1688 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2WAIT
);
1689 if (Value
== NULL
) {
1693 NumericValue
= IScsiNetNtoi (Value
);
1694 if (NumericValue
== 0) {
1695 Session
->DefaultTime2Wait
= 0;
1696 } else if (NumericValue
> 3600) {
1699 Session
->DefaultTime2Wait
= (UINT32
)MAX (Session
->DefaultTime2Wait
, NumericValue
);
1703 // DefaultTime2Retain: result function is Minimum.
1705 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2RETAIN
);
1706 if (Value
== NULL
) {
1710 NumericValue
= IScsiNetNtoi (Value
);
1711 if (NumericValue
== 0) {
1712 Session
->DefaultTime2Retain
= 0;
1713 } else if (NumericValue
> 3600) {
1716 Session
->DefaultTime2Retain
= (UINT32
)MIN (Session
->DefaultTime2Retain
, NumericValue
);
1720 // MaxOutstandingR2T: result function is Minimum.
1722 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_OUTSTANDING_R2T
);
1723 if (Value
== NULL
) {
1727 NumericValue
= IScsiNetNtoi (Value
);
1728 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1732 Session
->MaxOutstandingR2T
= (UINT16
)MIN (Session
->MaxOutstandingR2T
, NumericValue
);
1735 // Remove declarative key-value pairs, if any.
1737 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_SESSION_TYPE
);
1738 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ALIAS
);
1739 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
);
1742 // Remove the key-value that may not needed for result function is OR.
1744 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1745 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1746 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1749 // Remove irrelevant parameter, if any.
1751 if (Session
->InitialR2T
&& !Session
->ImmediateData
) {
1752 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1755 if (IsListEmpty (KeyValueList
)) {
1757 // Succeed if no more keys in the list.
1759 Status
= EFI_SUCCESS
;
1764 IScsiFreeKeyValueList (KeyValueList
);
1772 Fill the operational parameters.
1774 @param[in] Conn The connection in iSCSI login.
1775 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1780 IN ISCSI_CONNECTION
*Conn
,
1784 ISCSI_SESSION
*Session
;
1787 Session
= Conn
->Session
;
1789 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->HeaderDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1790 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_HEADER_DIGEST
, Value
);
1792 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->DataDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1793 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_DIGEST
, Value
);
1795 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->ErrorRecoveryLevel
);
1796 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
, Value
);
1798 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->InitialR2T
? "Yes" : "No");
1799 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_INITIAL_R2T
, Value
);
1801 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->ImmediateData
? "Yes" : "No");
1802 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_IMMEDIATE_DATA
, Value
);
1804 AsciiSPrint (Value
, sizeof (Value
), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP
);
1805 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
, Value
);
1807 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxBurstLength
);
1808 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_BURST_LENGTH
, Value
);
1810 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->FirstBurstLength
);
1811 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_FIRST_BURST_LENGTH
, Value
);
1813 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxConnections
);
1814 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_CONNECTIONS
, Value
);
1816 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataPDUInOrder
? "Yes" : "No");
1817 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_PDU_IN_ORDER
, Value
);
1819 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataSequenceInOrder
? "Yes" : "No");
1820 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
, Value
);
1822 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Wait
);
1823 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2WAIT
, Value
);
1825 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Retain
);
1826 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2RETAIN
, Value
);
1828 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxOutstandingR2T
);
1829 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_OUTSTANDING_R2T
, Value
);
1833 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
1835 @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
1836 @param[in] Len The length of the last segment in the PDU.
1838 @retval EFI_SUCCESS The segment is padded or there is no need to pad it.
1839 @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
1844 IN OUT NET_BUF
*Pdu
,
1851 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1854 Data
= NetbufAllocSpace (Pdu
, PadLen
, NET_BUF_TAIL
);
1856 return EFI_OUT_OF_RESOURCES
;
1859 ZeroMem (Data
, PadLen
);
1866 Build a key-value list from the data segment.
1868 @param[in] Data The data segment containing the key-value pairs.
1869 @param[in] Len Length of the data segment.
1871 @return The key-value list.
1872 @retval NULL Other errors as indicated.
1876 IScsiBuildKeyValueList (
1881 LIST_ENTRY
*ListHead
;
1882 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1884 ListHead
= AllocatePool (sizeof (LIST_ENTRY
));
1885 if (ListHead
== NULL
) {
1889 InitializeListHead (ListHead
);
1892 KeyValuePair
= AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR
));
1893 if (KeyValuePair
== NULL
) {
1897 InitializeListHead (&KeyValuePair
->List
);
1899 KeyValuePair
->Key
= Data
;
1901 while ((Len
> 0) && (*Data
!= '=')) {
1912 FreePool (KeyValuePair
);
1916 KeyValuePair
->Value
= Data
;
1918 InsertTailList (ListHead
, &KeyValuePair
->List
);
1920 Data
+= AsciiStrLen (KeyValuePair
->Value
) + 1;
1921 Len
-= (UINT32
)AsciiStrLen (KeyValuePair
->Value
) + 1;
1928 IScsiFreeKeyValueList (ListHead
);
1934 Get the value string by the key name from the key-value list. If found,
1935 the key-value entry will be removed from the list.
1937 @param[in, out] KeyValueList The key-value list.
1938 @param[in] Key The key name to find.
1940 @return The value string.
1941 @retval NULL The key value pair cannot be found.
1945 IScsiGetValueByKeyFromList (
1946 IN OUT LIST_ENTRY
*KeyValueList
,
1951 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1956 NET_LIST_FOR_EACH (Entry
, KeyValueList
) {
1957 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1959 if (AsciiStrCmp (KeyValuePair
->Key
, Key
) == 0) {
1960 Value
= KeyValuePair
->Value
;
1962 RemoveEntryList (&KeyValuePair
->List
);
1963 FreePool (KeyValuePair
);
1972 Free the key-value list.
1974 @param[in] KeyValueList The key-value list.
1978 IScsiFreeKeyValueList (
1979 IN LIST_ENTRY
*KeyValueList
1983 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1985 while (!IsListEmpty (KeyValueList
)) {
1986 Entry
= NetListRemoveHead (KeyValueList
);
1987 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1989 FreePool (KeyValuePair
);
1992 FreePool (KeyValueList
);
1996 Normalize the iSCSI name according to RFC.
1998 @param[in, out] Name The iSCSI name.
1999 @param[in] Len Length of the iSCSI name.
2001 @retval EFI_SUCCESS The iSCSI name is valid and normalized.
2002 @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.
2006 IScsiNormalizeName (
2013 for (Index
= 0; Index
< Len
; Index
++) {
2014 if (NET_IS_UPPER_CASE_CHAR (Name
[Index
])) {
2016 // Convert the upper-case characters to lower-case ones.
2018 Name
[Index
] = (CHAR8
)(Name
[Index
] - 'A' + 'a');
2021 if (!NET_IS_LOWER_CASE_CHAR (Name
[Index
]) &&
2022 !NET_IS_DIGIT (Name
[Index
]) &&
2023 (Name
[Index
] != '-') &&
2024 (Name
[Index
] != '.') &&
2025 (Name
[Index
] != ':')
2029 // ASCII dash, dot, colon lower-case characters and digit characters
2032 return EFI_PROTOCOL_ERROR
;
2036 if ((Len
< 4) || (CompareMem (Name
, "iqn.", 4) != 0)) {
2038 // Only IQN format is accepted now.
2040 return EFI_PROTOCOL_ERROR
;
2047 Create an iSCSI task control block.
2049 @param[in] Conn The connection on which the task control block will be created.
2050 @param[out] Tcb The newly created task control block.
2052 @retval EFI_SUCCESS The task control block is created.
2053 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2054 @retval EFI_NOT_READY The target cannot accept new commands.
2059 IN ISCSI_CONNECTION
*Conn
,
2063 ISCSI_SESSION
*Session
;
2066 ASSERT (Tcb
!= NULL
);
2068 Session
= Conn
->Session
;
2070 if (ISCSI_SEQ_GT (Session
->CmdSN
, Session
->MaxCmdSN
)) {
2071 return EFI_NOT_READY
;
2074 NewTcb
= AllocateZeroPool (sizeof (ISCSI_TCB
));
2075 if (NewTcb
== NULL
) {
2076 return EFI_OUT_OF_RESOURCES
;
2079 InitializeListHead (&NewTcb
->Link
);
2081 NewTcb
->SoFarInOrder
= TRUE
;
2082 NewTcb
->InitiatorTaskTag
= Session
->InitiatorTaskTag
;
2083 NewTcb
->CmdSN
= Session
->CmdSN
;
2084 NewTcb
->Conn
= Conn
;
2086 InsertTailList (&Session
->TcbList
, &NewTcb
->Link
);
2089 // Advance the initiator task tag.
2091 Session
->InitiatorTaskTag
++;
2100 Delete the tcb from the connection and destroy it.
2102 @param[in] Tcb The tcb to delete.
2110 RemoveEntryList (&Tcb
->Link
);
2116 Create a data segment, pad it, and calculate the CRC if needed.
2118 @param[in] Data The data to fill into the data segment.
2119 @param[in] Len Length of the data.
2120 @param[in] DataDigest Whether to calculate CRC for this data segment.
2122 @return The net buffer wrapping the data segment.
2126 IScsiNewDataSegment (
2129 IN BOOLEAN DataDigest
2132 NET_FRAGMENT Fragment
[2];
2133 UINT32 FragmentCount
;
2137 Fragment
[0].Len
= Len
;
2138 Fragment
[0].Bulk
= Data
;
2140 PadLen
= ISCSI_GET_PAD_LEN (Len
);
2142 Fragment
[1].Len
= PadLen
;
2143 Fragment
[1].Bulk
= (UINT8
*)&mDataSegPad
;
2150 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
2156 Create a iSCSI SCSI command PDU to encapsulate the command issued
2157 by SCSI through the EXT SCSI PASS THRU Protocol.
2159 @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
2160 @param[in] Lun The LUN.
2161 @param[in] Tcb The tcb associated with this SCSI command.
2163 @return The created iSCSI SCSI command PDU.
2164 @retval NULL Other errors as indicated.
2168 IScsiNewScsiCmdPdu (
2169 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
2174 LIST_ENTRY
*NbufList
;
2178 SCSI_COMMAND
*ScsiCmd
;
2181 ISCSI_ADDITIONAL_HEADER
*Header
;
2182 ISCSI_BI_EXP_READ_DATA_LEN_AHS
*BiExpReadDataLenAHS
;
2183 ISCSI_SESSION
*Session
;
2184 UINT32 ImmediateDataLen
;
2188 if (Packet
->DataDirection
== DataBi
) {
2190 // Bidirectional Read/Write command, the bidirectional expected
2191 // read data length AHS is required.
2193 AHSLength
+= sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS
);
2196 if (Packet
->CdbLength
> 16) {
2198 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2200 AHSLength
= (UINT8
)(AHSLength
+ ISCSI_ROUNDUP (Packet
->CdbLength
- 16) + sizeof (ISCSI_ADDITIONAL_HEADER
));
2203 Length
= sizeof (SCSI_COMMAND
) + AHSLength
;
2204 PduHeader
= NetbufAlloc (Length
);
2205 if (PduHeader
== NULL
) {
2209 ScsiCmd
= (SCSI_COMMAND
*)NetbufAllocSpace (PduHeader
, Length
, NET_BUF_TAIL
);
2210 if (ScsiCmd
== NULL
) {
2211 NetbufFree (PduHeader
);
2215 Header
= (ISCSI_ADDITIONAL_HEADER
*)(ScsiCmd
+ 1);
2217 ZeroMem (ScsiCmd
, Length
);
2219 ISCSI_SET_OPCODE (ScsiCmd
, ISCSI_OPCODE_SCSI_CMD
, 0);
2220 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_TASK_ATTR_SIMPLE
);
2223 // Set the READ/WRITE flags according to the IO type of this request.
2225 switch (Packet
->DataDirection
) {
2227 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
);
2228 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->InTransferLength
);
2232 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_WRITE
);
2233 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2237 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
| SCSI_CMD_PDU_FLAG_WRITE
);
2238 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2241 // Fill the bidirectional expected read data length AHS.
2243 BiExpReadDataLenAHS
= (ISCSI_BI_EXP_READ_DATA_LEN_AHS
*)Header
;
2244 Header
= (ISCSI_ADDITIONAL_HEADER
*)(BiExpReadDataLenAHS
+ 1);
2246 BiExpReadDataLenAHS
->Length
= NTOHS (5);
2247 BiExpReadDataLenAHS
->Type
= ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN
;
2248 BiExpReadDataLenAHS
->ExpReadDataLength
= NTOHL (Packet
->InTransferLength
);
2253 ScsiCmd
->TotalAHSLength
= AHSLength
;
2254 CopyMem (ScsiCmd
->Lun
, &Lun
, sizeof (ScsiCmd
->Lun
));
2255 ScsiCmd
->InitiatorTaskTag
= NTOHL (Tcb
->InitiatorTaskTag
);
2256 ScsiCmd
->CmdSN
= NTOHL (Tcb
->CmdSN
);
2257 ScsiCmd
->ExpStatSN
= NTOHL (Tcb
->Conn
->ExpStatSN
);
2259 CopyMem (ScsiCmd
->Cdb
, Packet
->Cdb
, sizeof (ScsiCmd
->Cdb
));
2261 if (Packet
->CdbLength
> 16) {
2262 Header
->Length
= NTOHS ((UINT16
)(Packet
->CdbLength
- 15));
2263 Header
->Type
= ISCSI_AHS_TYPE_EXT_CDB
;
2265 CopyMem (Header
+ 1, (UINT8
*)Packet
->Cdb
+ 16, Packet
->CdbLength
- 16);
2269 Session
= Tcb
->Conn
->Session
;
2270 ImmediateDataLen
= 0;
2272 if (Session
->ImmediateData
&& (Packet
->OutTransferLength
!= 0)) {
2274 // Send immediate data in this SCSI Command PDU. The length of the immediate
2275 // data is the minimum of FirstBurstLength, the data length to be xfered, and
2276 // the MaxRecvdataSegmentLength on this connection.
2278 ImmediateDataLen
= MIN (Session
->FirstBurstLength
, Packet
->OutTransferLength
);
2279 ImmediateDataLen
= MIN (ImmediateDataLen
, Tcb
->Conn
->MaxRecvDataSegmentLength
);
2282 // Update the data segment length in the PDU header.
2284 ISCSI_SET_DATASEG_LEN (ScsiCmd
, ImmediateDataLen
);
2287 // Create the data segment.
2289 DataSeg
= IScsiNewDataSegment ((UINT8
*)Packet
->OutDataBuffer
, ImmediateDataLen
, FALSE
);
2290 if (DataSeg
== NULL
) {
2291 NetbufFree (PduHeader
);
2296 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2297 if (NbufList
== NULL
) {
2298 NetbufFree (PduHeader
);
2299 NetbufFree (DataSeg
);
2305 InitializeListHead (NbufList
);
2306 InsertTailList (NbufList
, &PduHeader
->List
);
2307 InsertTailList (NbufList
, &DataSeg
->List
);
2309 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2311 IScsiFreeNbufList (NbufList
);
2315 if (Session
->InitialR2T
||
2316 (ImmediateDataLen
== Session
->FirstBurstLength
) ||
2317 (ImmediateDataLen
== Packet
->OutTransferLength
)
2321 // Unsolicited data out sequence is not allowed,
2322 // or FirstBustLength data is already sent out by immediate data,
2323 // or all the OUT data accompany this SCSI packet are sent as
2324 // immediate data. The final flag should be set on this SCSI Command
2327 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_BHS_FLAG_FINAL
);
2336 Create a new iSCSI SCSI Data Out PDU.
2338 @param[in] Data The data to put into the Data Out PDU.
2339 @param[in] Len Length of the data.
2340 @param[in] DataSN The DataSN of the Data Out PDU.
2341 @param[in] Tcb The task control block of this Data Out PDU.
2342 @param[in] Lun The LUN.
2344 @return The net buffer wrapping the Data Out PDU.
2345 @retval NULL Other errors as indicated.
2349 IScsiNewDataOutPdu (
2357 LIST_ENTRY
*NbufList
;
2361 ISCSI_SCSI_DATA_OUT
*DataOutHdr
;
2362 ISCSI_XFER_CONTEXT
*XferContext
;
2364 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2365 if (NbufList
== NULL
) {
2369 InitializeListHead (NbufList
);
2372 // Allocate memory for the BHS.
2374 PduHdr
= NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT
));
2375 if (PduHdr
== NULL
) {
2376 FreePool (NbufList
);
2381 // Insert the BHS into the buffer list.
2383 InsertTailList (NbufList
, &PduHdr
->List
);
2385 DataOutHdr
= (ISCSI_SCSI_DATA_OUT
*)NetbufAllocSpace (PduHdr
, sizeof (ISCSI_SCSI_DATA_OUT
), NET_BUF_TAIL
);
2386 if (DataOutHdr
== NULL
) {
2387 IScsiFreeNbufList (NbufList
);
2391 XferContext
= &Tcb
->XferContext
;
2393 ZeroMem (DataOutHdr
, sizeof (ISCSI_SCSI_DATA_OUT
));
2396 // Set the flags and fields of the Data Out PDU BHS.
2398 ISCSI_SET_OPCODE (DataOutHdr
, ISCSI_OPCODE_SCSI_DATA_OUT
, 0);
2399 ISCSI_SET_DATASEG_LEN (DataOutHdr
, Len
);
2401 DataOutHdr
->InitiatorTaskTag
= HTONL (Tcb
->InitiatorTaskTag
);
2402 DataOutHdr
->TargetTransferTag
= HTONL (XferContext
->TargetTransferTag
);
2403 DataOutHdr
->ExpStatSN
= HTONL (Tcb
->Conn
->ExpStatSN
);
2404 DataOutHdr
->DataSN
= HTONL (DataSN
);
2405 DataOutHdr
->BufferOffset
= HTONL (XferContext
->Offset
);
2407 if (XferContext
->TargetTransferTag
!= ISCSI_RESERVED_TAG
) {
2408 CopyMem (&DataOutHdr
->Lun
, &Lun
, sizeof (DataOutHdr
->Lun
));
2412 // Build the data segment for this Data Out PDU.
2414 DataSeg
= IScsiNewDataSegment (Data
, Len
, FALSE
);
2415 if (DataSeg
== NULL
) {
2416 IScsiFreeNbufList (NbufList
);
2421 // Put the data segment into the buffer list and combine it with the BHS
2422 // into a full Data Out PDU.
2424 InsertTailList (NbufList
, &DataSeg
->List
);
2425 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2427 IScsiFreeNbufList (NbufList
);
2434 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
2436 @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
2437 @param[in] Tcb The task control block of the data to send out.
2438 @param[in] Lun The LUN the data will be sent to.
2440 @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.
2441 @retval NULL Other errors as indicated.
2445 IScsiGenerateDataOutPduSequence (
2451 LIST_ENTRY
*PduList
;
2454 NET_BUF
*DataOutPdu
;
2455 ISCSI_CONNECTION
*Conn
;
2456 ISCSI_XFER_CONTEXT
*XferContext
;
2457 UINT8
*DataOutPacket
;
2459 PduList
= AllocatePool (sizeof (LIST_ENTRY
));
2460 if (PduList
== NULL
) {
2464 InitializeListHead (PduList
);
2469 XferContext
= &Tcb
->XferContext
;
2471 while (XferContext
->DesiredLength
> 0) {
2473 // Determine the length of data this Data Out PDU can carry.
2475 DataLen
= MIN (XferContext
->DesiredLength
, Conn
->MaxRecvDataSegmentLength
);
2478 // Create a Data Out PDU.
2480 DataOutPdu
= IScsiNewDataOutPdu (Data
, DataLen
, DataSN
, Tcb
, Lun
);
2481 if (DataOutPdu
== NULL
) {
2482 IScsiFreeNbufList (PduList
);
2488 InsertTailList (PduList
, &DataOutPdu
->List
);
2491 // Update the context and DataSN.
2494 XferContext
->Offset
+= DataLen
;
2495 XferContext
->DesiredLength
-= DataLen
;
2500 // Set the F bit for the last data out PDU in this sequence.
2502 DataOutPacket
= NetbufGetByte (DataOutPdu
, 0, NULL
);
2503 if (DataOutPacket
== NULL
) {
2504 IScsiFreeNbufList (PduList
);
2509 ISCSI_SET_FLAG (DataOutPacket
, ISCSI_BHS_FLAG_FINAL
);
2517 Send the Data in a sequence of Data Out PDUs one by one.
2519 @param[in] Data The data to carry by Data Out PDUs.
2520 @param[in] Lun The LUN the data will be sent to.
2521 @param[in] Tcb The task control block.
2523 @retval EFI_SUCCESS The data is sent out to the LUN.
2524 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2525 @retval Others Other errors as indicated.
2529 IScsiSendDataOutPduSequence (
2535 LIST_ENTRY
*DataOutPduList
;
2541 // Generate the Data Out PDU sequence.
2543 DataOutPduList
= IScsiGenerateDataOutPduSequence (Data
, Tcb
, Lun
);
2544 if (DataOutPduList
== NULL
) {
2545 return EFI_OUT_OF_RESOURCES
;
2548 Status
= EFI_SUCCESS
;
2551 // Send the Data Out PDU's one by one.
2553 NET_LIST_FOR_EACH (Entry
, DataOutPduList
) {
2554 Pdu
= NET_LIST_USER_STRUCT (Entry
, NET_BUF
, List
);
2556 Status
= TcpIoTransmit (&Tcb
->Conn
->TcpIo
, Pdu
);
2558 if (EFI_ERROR (Status
)) {
2563 IScsiFreeNbufList (DataOutPduList
);
2569 Process the received iSCSI SCSI Data In PDU.
2571 @param[in] Pdu The Data In PDU received.
2572 @param[in] Tcb The task control block.
2573 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2575 @retval EFI_SUCCESS The check on the Data IN PDU is passed and some update
2577 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
2578 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2579 @retval Others Other errors as indicated.
2586 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2589 ISCSI_SCSI_DATA_IN
*DataInHdr
;
2592 DataInHdr
= (ISCSI_SCSI_DATA_IN
*)NetbufGetByte (Pdu
, 0, NULL
);
2593 if (DataInHdr
== NULL
) {
2594 return EFI_PROTOCOL_ERROR
;
2597 DataInHdr
->InitiatorTaskTag
= NTOHL (DataInHdr
->InitiatorTaskTag
);
2598 DataInHdr
->ExpCmdSN
= NTOHL (DataInHdr
->ExpCmdSN
);
2599 DataInHdr
->MaxCmdSN
= NTOHL (DataInHdr
->MaxCmdSN
);
2600 DataInHdr
->DataSN
= NTOHL (DataInHdr
->DataSN
);
2603 // Check the DataSN.
2605 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, DataInHdr
->DataSN
);
2606 if (EFI_ERROR (Status
)) {
2610 if (DataInHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2611 return EFI_PROTOCOL_ERROR
;
2615 // Update the command related sequence numbers.
2617 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, DataInHdr
->MaxCmdSN
, DataInHdr
->ExpCmdSN
);
2619 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID
)) {
2620 if (!ISCSI_FLAG_ON (DataInHdr
, ISCSI_BHS_FLAG_FINAL
)) {
2622 // The S bit is on but the F bit is off.
2624 return EFI_PROTOCOL_ERROR
;
2627 Tcb
->StatusXferd
= TRUE
;
2629 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_OVERFLOW
| SCSI_DATA_IN_PDU_FLAG_UNDERFLOW
)) {
2631 // Underflow and Overflow are mutual flags.
2633 return EFI_PROTOCOL_ERROR
;
2637 // S bit is on, the StatSN is valid.
2639 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NTOHL (DataInHdr
->StatSN
));
2640 if (EFI_ERROR (Status
)) {
2644 Packet
->HostAdapterStatus
= 0;
2645 Packet
->TargetStatus
= DataInHdr
->Status
;
2647 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2648 Packet
->InTransferLength
+= NTOHL (DataInHdr
->ResidualCount
);
2649 Status
= EFI_BAD_BUFFER_SIZE
;
2652 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2653 Packet
->InTransferLength
-= NTOHL (DataInHdr
->ResidualCount
);
2661 Process the received iSCSI R2T PDU.
2663 @param[in] Pdu The R2T PDU received.
2664 @param[in] Tcb The task control block.
2665 @param[in] Lun The Lun.
2666 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2668 @retval EFI_SUCCESS The R2T PDU is valid and the solicited data is sent out.
2669 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
2670 @retval Others Other errors as indicated.
2678 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2681 ISCSI_READY_TO_TRANSFER
*R2THdr
;
2683 ISCSI_XFER_CONTEXT
*XferContext
;
2686 R2THdr
= (ISCSI_READY_TO_TRANSFER
*)NetbufGetByte (Pdu
, 0, NULL
);
2687 if (R2THdr
== NULL
) {
2688 return EFI_PROTOCOL_ERROR
;
2691 R2THdr
->InitiatorTaskTag
= NTOHL (R2THdr
->InitiatorTaskTag
);
2692 R2THdr
->TargetTransferTag
= NTOHL (R2THdr
->TargetTransferTag
);
2693 R2THdr
->StatSN
= NTOHL (R2THdr
->StatSN
);
2694 R2THdr
->R2TSeqNum
= NTOHL (R2THdr
->R2TSeqNum
);
2695 R2THdr
->BufferOffset
= NTOHL (R2THdr
->BufferOffset
);
2696 R2THdr
->DesiredDataTransferLength
= NTOHL (R2THdr
->DesiredDataTransferLength
);
2698 if ((R2THdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) || !ISCSI_SEQ_EQ (R2THdr
->StatSN
, Tcb
->Conn
->ExpStatSN
)) {
2699 return EFI_PROTOCOL_ERROR
;
2703 // Check the sequence number.
2705 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, R2THdr
->R2TSeqNum
);
2706 if (EFI_ERROR (Status
)) {
2710 XferContext
= &Tcb
->XferContext
;
2711 XferContext
->TargetTransferTag
= R2THdr
->TargetTransferTag
;
2712 XferContext
->Offset
= R2THdr
->BufferOffset
;
2713 XferContext
->DesiredLength
= R2THdr
->DesiredDataTransferLength
;
2715 if (((XferContext
->Offset
+ XferContext
->DesiredLength
) > Packet
->OutTransferLength
) ||
2716 (XferContext
->DesiredLength
> Tcb
->Conn
->Session
->MaxBurstLength
)
2719 return EFI_PROTOCOL_ERROR
;
2723 // Send the data solicited by this R2T.
2725 Data
= (UINT8
*)Packet
->OutDataBuffer
+ XferContext
->Offset
;
2726 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2732 Process the received iSCSI SCSI Response PDU.
2734 @param[in] Pdu The Response PDU received.
2735 @param[in] Tcb The task control block.
2736 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2738 @retval EFI_SUCCESS The Response PDU is processed.
2739 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
2740 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2741 @retval Others Other errors as indicated.
2745 IScsiOnScsiRspRcvd (
2748 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2751 SCSI_RESPONSE
*ScsiRspHdr
;
2752 ISCSI_SENSE_DATA
*SenseData
;
2756 ScsiRspHdr
= (SCSI_RESPONSE
*)NetbufGetByte (Pdu
, 0, NULL
);
2757 if (ScsiRspHdr
== NULL
) {
2758 return EFI_PROTOCOL_ERROR
;
2761 ScsiRspHdr
->InitiatorTaskTag
= NTOHL (ScsiRspHdr
->InitiatorTaskTag
);
2762 if (ScsiRspHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2763 return EFI_PROTOCOL_ERROR
;
2766 ScsiRspHdr
->StatSN
= NTOHL (ScsiRspHdr
->StatSN
);
2768 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, ScsiRspHdr
->StatSN
);
2769 if (EFI_ERROR (Status
)) {
2773 ScsiRspHdr
->MaxCmdSN
= NTOHL (ScsiRspHdr
->MaxCmdSN
);
2774 ScsiRspHdr
->ExpCmdSN
= NTOHL (ScsiRspHdr
->ExpCmdSN
);
2775 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, ScsiRspHdr
->MaxCmdSN
, ScsiRspHdr
->ExpCmdSN
);
2777 Tcb
->StatusXferd
= TRUE
;
2779 Packet
->HostAdapterStatus
= ScsiRspHdr
->Response
;
2780 if (Packet
->HostAdapterStatus
!= ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET
) {
2784 Packet
->TargetStatus
= ScsiRspHdr
->Status
;
2786 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
| SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
) ||
2787 ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
| SCSI_RSP_PDU_FLAG_UNDERFLOW
)
2790 return EFI_PROTOCOL_ERROR
;
2793 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
)) {
2794 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2795 Status
= EFI_BAD_BUFFER_SIZE
;
2798 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
)) {
2799 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2802 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2803 if (Packet
->DataDirection
== DataIn
) {
2804 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2806 Packet
->OutTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2809 Status
= EFI_BAD_BUFFER_SIZE
;
2812 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2813 if (Packet
->DataDirection
== DataIn
) {
2814 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2816 Packet
->OutTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2820 DataSegLen
= ISCSI_GET_DATASEG_LEN (ScsiRspHdr
);
2821 if (DataSegLen
!= 0) {
2822 SenseData
= (ISCSI_SENSE_DATA
*)NetbufGetByte (Pdu
, sizeof (SCSI_RESPONSE
), NULL
);
2823 if (SenseData
== NULL
) {
2824 return EFI_PROTOCOL_ERROR
;
2827 SenseData
->Length
= NTOHS (SenseData
->Length
);
2829 Packet
->SenseDataLength
= (UINT8
)MIN (SenseData
->Length
, Packet
->SenseDataLength
);
2830 if (Packet
->SenseDataLength
!= 0) {
2831 CopyMem (Packet
->SenseData
, &SenseData
->Data
[0], Packet
->SenseDataLength
);
2834 Packet
->SenseDataLength
= 0;
2841 Process the received NOP In PDU.
2843 @param[in] Pdu The NOP In PDU received.
2844 @param[in] Tcb The task control block.
2846 @retval EFI_SUCCESS The NOP In PDU is processed and the related sequence
2847 numbers are updated.
2848 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
2857 ISCSI_NOP_IN
*NopInHdr
;
2860 NopInHdr
= (ISCSI_NOP_IN
*)NetbufGetByte (Pdu
, 0, NULL
);
2861 if (NopInHdr
== NULL
) {
2862 return EFI_PROTOCOL_ERROR
;
2865 NopInHdr
->StatSN
= NTOHL (NopInHdr
->StatSN
);
2866 NopInHdr
->ExpCmdSN
= NTOHL (NopInHdr
->ExpCmdSN
);
2867 NopInHdr
->MaxCmdSN
= NTOHL (NopInHdr
->MaxCmdSN
);
2869 if (NopInHdr
->InitiatorTaskTag
== ISCSI_RESERVED_TAG
) {
2870 if (NopInHdr
->StatSN
!= Tcb
->Conn
->ExpStatSN
) {
2871 return EFI_PROTOCOL_ERROR
;
2874 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NopInHdr
->StatSN
);
2875 if (EFI_ERROR (Status
)) {
2880 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, NopInHdr
->MaxCmdSN
, NopInHdr
->ExpCmdSN
);
2886 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
2888 @param[in] PassThru The EXT SCSI PASS THRU protocol.
2889 @param[in] Target The target ID.
2890 @param[in] Lun The LUN.
2891 @param[in, out] Packet The request packet containing IO request, SCSI command
2892 buffer and buffers to read/write.
2894 @retval EFI_SUCCESS The SCSI command is executed and the result is updated to
2896 @retval EFI_DEVICE_ERROR Session state was not as required.
2897 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2898 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
2899 @retval EFI_NOT_READY The target can not accept new commands.
2900 @retval Others Other errors as indicated.
2904 IScsiExecuteScsiCommand (
2905 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
,
2908 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2912 ISCSI_DRIVER_DATA
*Private
;
2913 ISCSI_SESSION
*Session
;
2914 EFI_EVENT TimeoutEvent
;
2915 ISCSI_CONNECTION
*Conn
;
2918 ISCSI_XFER_CONTEXT
*XferContext
;
2920 ISCSI_IN_BUFFER_CONTEXT InBufferContext
;
2924 Private
= ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru
);
2925 Session
= Private
->Session
;
2926 Status
= EFI_SUCCESS
;
2928 TimeoutEvent
= NULL
;
2931 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
2932 Status
= EFI_DEVICE_ERROR
;
2936 Conn
= NET_LIST_USER_STRUCT_S (
2937 Session
->Conns
.ForwardLink
,
2940 ISCSI_CONNECTION_SIGNATURE
2943 if (Packet
->Timeout
!= 0) {
2944 Timeout
= MultU64x32 (Packet
->Timeout
, 4);
2947 Status
= IScsiNewTcb (Conn
, &Tcb
);
2948 if (EFI_ERROR (Status
)) {
2953 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2955 Pdu
= IScsiNewScsiCmdPdu (Packet
, Lun
, Tcb
);
2957 Status
= EFI_OUT_OF_RESOURCES
;
2961 XferContext
= &Tcb
->XferContext
;
2962 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2963 if (PduHdr
== NULL
) {
2964 Status
= EFI_PROTOCOL_ERROR
;
2969 XferContext
->Offset
= ISCSI_GET_DATASEG_LEN (PduHdr
);
2972 // Transmit the SCSI Command PDU.
2974 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
2978 if (EFI_ERROR (Status
)) {
2982 if (!Session
->InitialR2T
&&
2983 (XferContext
->Offset
< Session
->FirstBurstLength
) &&
2984 (XferContext
->Offset
< Packet
->OutTransferLength
)
2988 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2989 // OUT data, and the limit of FirstBurstLength is not reached.
2991 XferContext
->TargetTransferTag
= ISCSI_RESERVED_TAG
;
2992 XferContext
->DesiredLength
= MIN (
2993 Session
->FirstBurstLength
,
2994 Packet
->OutTransferLength
- XferContext
->Offset
2997 Data
= (UINT8
*)Packet
->OutDataBuffer
+ XferContext
->Offset
;
2998 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2999 if (EFI_ERROR (Status
)) {
3004 InBufferContext
.InData
= (UINT8
*)Packet
->InDataBuffer
;
3005 InBufferContext
.InDataLen
= Packet
->InTransferLength
;
3007 while (!Tcb
->StatusXferd
) {
3009 // Start the timeout timer.
3012 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, Timeout
);
3013 if (EFI_ERROR (Status
)) {
3017 TimeoutEvent
= Conn
->TimeoutEvent
;
3021 // Try to receive PDU from target.
3023 Status
= IScsiReceivePdu (Conn
, &Pdu
, &InBufferContext
, FALSE
, FALSE
, TimeoutEvent
);
3024 if (EFI_ERROR (Status
)) {
3028 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
3029 if (PduHdr
== NULL
) {
3030 Status
= EFI_PROTOCOL_ERROR
;
3035 switch (ISCSI_GET_OPCODE (PduHdr
)) {
3036 case ISCSI_OPCODE_SCSI_DATA_IN
:
3037 Status
= IScsiOnDataInRcvd (Pdu
, Tcb
, Packet
);
3040 case ISCSI_OPCODE_R2T
:
3041 Status
= IScsiOnR2TRcvd (Pdu
, Tcb
, Lun
, Packet
);
3044 case ISCSI_OPCODE_SCSI_RSP
:
3045 Status
= IScsiOnScsiRspRcvd (Pdu
, Tcb
, Packet
);
3048 case ISCSI_OPCODE_NOP_IN
:
3049 Status
= IScsiOnNopInRcvd (Pdu
, Tcb
);
3052 case ISCSI_OPCODE_VENDOR_T0
:
3053 case ISCSI_OPCODE_VENDOR_T1
:
3054 case ISCSI_OPCODE_VENDOR_T2
:
3056 // These messages are vendor specific. Skip them.
3061 Status
= EFI_PROTOCOL_ERROR
;
3067 if (EFI_ERROR (Status
)) {
3074 if (TimeoutEvent
!= NULL
) {
3075 gBS
->SetTimer (TimeoutEvent
, TimerCancel
, 0);
3086 Reinstate the session on some error.
3088 @param[in] Session The iSCSI session
3090 @retval EFI_SUCCESS The session is reinstated from some error.
3091 @retval Other Reinstatement failed.
3095 IScsiSessionReinstatement (
3096 IN ISCSI_SESSION
*Session
3101 ASSERT (Session
->State
!= SESSION_STATE_FREE
);
3104 // Abort the session and re-init it.
3106 IScsiSessionAbort (Session
);
3107 IScsiSessionInit (Session
, TRUE
);
3112 Status
= IScsiSessionLogin (Session
);
3118 Initialize some session parameters before login.
3120 @param[in, out] Session The iSCSI session.
3121 @param[in] Recovery Whether the request is from a fresh new start or recovery.
3126 IN OUT ISCSI_SESSION
*Session
,
3131 Session
->Signature
= ISCSI_SESSION_SIGNATURE
;
3132 Session
->State
= SESSION_STATE_FREE
;
3134 InitializeListHead (&Session
->Conns
);
3135 InitializeListHead (&Session
->TcbList
);
3141 Session
->InitiatorTaskTag
= 1;
3142 Session
->NextCid
= 1;
3144 Session
->TargetPortalGroupTag
= 0;
3145 Session
->MaxConnections
= ISCSI_MAX_CONNS_PER_SESSION
;
3146 Session
->InitialR2T
= FALSE
;
3147 Session
->ImmediateData
= TRUE
;
3148 Session
->MaxBurstLength
= 262144;
3149 Session
->FirstBurstLength
= MAX_RECV_DATA_SEG_LEN_IN_FFP
;
3150 Session
->DefaultTime2Wait
= 2;
3151 Session
->DefaultTime2Retain
= 20;
3152 Session
->MaxOutstandingR2T
= DEFAULT_MAX_OUTSTANDING_R2T
;
3153 Session
->DataPDUInOrder
= TRUE
;
3154 Session
->DataSequenceInOrder
= TRUE
;
3155 Session
->ErrorRecoveryLevel
= 0;
3159 Abort the iSCSI session. That is, reset all the connection(s), and free the
3162 @param[in, out] Session The iSCSI session.
3167 IN OUT ISCSI_SESSION
*Session
3170 ISCSI_CONNECTION
*Conn
;
3171 EFI_GUID
*ProtocolGuid
;
3173 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
3177 ASSERT (!IsListEmpty (&Session
->Conns
));
3179 while (!IsListEmpty (&Session
->Conns
)) {
3180 Conn
= NET_LIST_USER_STRUCT_S (
3181 Session
->Conns
.ForwardLink
,
3184 ISCSI_CONNECTION_SIGNATURE
3186 if (!Conn
->Ipv6Flag
) {
3187 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
3189 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
3192 gBS
->CloseProtocol (
3195 Session
->Private
->Image
,
3196 Session
->Private
->ExtScsiPassThruHandle
3199 IScsiConnReset (Conn
);
3201 IScsiDetatchConnection (Conn
);
3202 IScsiDestroyConnection (Conn
);
3205 Session
->State
= SESSION_STATE_FAILED
;