2 The implementation of iSCSI protocol based on RFC3720.
4 Copyright (c) 2004 - 2014, 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
608 // Receive the iSCSI login response.
610 Status
= IScsiReceivePdu (Conn
, &Pdu
, NULL
, FALSE
, FALSE
, NULL
);
611 if (EFI_ERROR (Status
)) {
614 ASSERT (Pdu
!= NULL
);
617 // A Login Response is received; process it.
619 Status
= IScsiProcessLoginRsp (Conn
, Pdu
);
628 Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
629 The DataSegmentLength and the actual size of the net buffer containing this PDU will be
632 @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
634 @param[in] Key The key name string.
635 @param[in] Value The value string.
637 @retval EFI_SUCCESS The key-value pair is added to the PDU's data segment and
638 the correspondence length fields are updated.
639 @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
641 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
644 IScsiAddKeyValuePair (
654 ISCSI_LOGIN_REQUEST
*LoginReq
;
657 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufGetByte (Pdu
, 0, NULL
);
658 if (LoginReq
== NULL
) {
659 return EFI_PROTOCOL_ERROR
;
661 DataSegLen
= NTOH24 (LoginReq
->DataSegmentLength
);
663 KeyLen
= (UINT32
) AsciiStrLen (Key
);
664 ValueLen
= (UINT32
) AsciiStrLen (Value
);
667 // 1 byte for the key value separator '=' and 1 byte for the null
668 // delimiter after the value.
670 TotalLen
= KeyLen
+ 1 + ValueLen
+ 1;
673 // Allocate the space for the key-value pair.
675 Data
= (CHAR8
*) NetbufAllocSpace (Pdu
, TotalLen
, NET_BUF_TAIL
);
677 return EFI_OUT_OF_RESOURCES
;
682 CopyMem (Data
, Key
, KeyLen
);
691 CopyMem (Data
, Value
, ValueLen
);
697 // Update the DataSegmentLength
699 ISCSI_SET_DATASEG_LEN (LoginReq
, DataSegLen
+ TotalLen
);
706 Prepare the iSCSI login request to be sent according to the current login status.
708 @param[in, out] Conn The connection in the iSCSI login phase.
710 @return The pointer to the net buffer containing the iSCSI login request built.
711 @retval NULL Other errors as indicated.
715 IScsiPrepareLoginReq (
716 IN OUT ISCSI_CONNECTION
*Conn
719 ISCSI_SESSION
*Session
;
721 ISCSI_LOGIN_REQUEST
*LoginReq
;
724 Session
= Conn
->Session
;
726 Nbuf
= NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST
) + DEFAULT_MAX_RECV_DATA_SEG_LEN
);
731 LoginReq
= (ISCSI_LOGIN_REQUEST
*) NetbufAllocSpace (Nbuf
, sizeof (ISCSI_LOGIN_REQUEST
), NET_BUF_TAIL
);
732 ASSERT (LoginReq
!= NULL
);
733 ZeroMem (LoginReq
, sizeof (ISCSI_LOGIN_REQUEST
));
736 // Init the login request pdu
738 ISCSI_SET_OPCODE (LoginReq
, ISCSI_OPCODE_LOGIN_REQ
, ISCSI_REQ_IMMEDIATE
);
739 ISCSI_SET_STAGES (LoginReq
, Conn
->CurrentStage
, Conn
->NextStage
);
740 LoginReq
->VersionMax
= ISCSI_VERSION_MAX
;
741 LoginReq
->VersionMin
= ISCSI_VERSION_MIN
;
742 LoginReq
->Tsih
= HTONS (Session
->Tsih
);
743 LoginReq
->InitiatorTaskTag
= HTONL (Session
->InitiatorTaskTag
);
744 LoginReq
->Cid
= HTONS (Conn
->Cid
);
745 LoginReq
->CmdSN
= HTONL (Session
->CmdSN
);
748 // For the first Login Request on a coonection this is ExpStatSN for the
749 // old connection, and this field is only valid if the Login Request restarts
751 // For subsequent Login Requests it is used to acknowledge the Login Responses
752 // with their increasing StatSN values.
754 LoginReq
->ExpStatSN
= HTONL (Conn
->ExpStatSN
);
755 CopyMem (LoginReq
->Isid
, Session
->Isid
, sizeof (LoginReq
->Isid
));
757 if (Conn
->PartialRspRcvd
) {
759 // A partial response. The initiator must send an empty Login Request.
764 Status
= EFI_SUCCESS
;
766 switch (Conn
->CurrentStage
) {
767 case ISCSI_SECURITY_NEGOTIATION
:
769 // Both none authentication and CHAP authentication share the CHAP path.
772 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
773 Status
= IScsiCHAPToSendReq (Conn
, Nbuf
);
778 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
780 // Only negotiate the paramter once.
782 if (!Conn
->ParamNegotiated
) {
783 IScsiFillOpParams (Conn
, Nbuf
);
786 ISCSI_SET_FLAG (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
791 // An error occurs...
793 Status
= EFI_DEVICE_ERROR
;
797 if (EFI_ERROR (Status
)) {
802 // Pad the data segment if needed.
804 IScsiPadSegment (Nbuf
, ISCSI_GET_DATASEG_LEN (LoginReq
));
806 // Check whether we will issue the stage transition signal?
808 Conn
->TransitInitiated
= ISCSI_FLAG_ON (LoginReq
, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT
);
816 Process the iSCSI Login Response.
818 @param[in, out] Conn The connection on which the iSCSI login response is received.
819 @param[in, out] Pdu The iSCSI login response PDU.
821 @retval EFI_SUCCESS The iSCSI login response PDU is processed, and all checks are passed.
822 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
823 @retval EFI_MEDIA_CHANGED Target is redirected.
824 @retval Others Other errors as indicated.
828 IScsiProcessLoginRsp (
829 IN OUT ISCSI_CONNECTION
*Conn
,
834 ISCSI_SESSION
*Session
;
835 ISCSI_LOGIN_RESPONSE
*LoginRsp
;
843 Status
= EFI_SUCCESS
;
844 Session
= Conn
->Session
;
846 LoginRsp
= (ISCSI_LOGIN_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
847 if (LoginRsp
== NULL
) {
848 return EFI_PROTOCOL_ERROR
;
850 if (!ISCSI_CHECK_OPCODE (LoginRsp
, ISCSI_OPCODE_LOGIN_RSP
)) {
852 // It is not a Login Response.
854 return EFI_PROTOCOL_ERROR
;
857 // Get the data segment, if any.
859 DataSegLen
= ISCSI_GET_DATASEG_LEN (LoginRsp
);
860 if (DataSegLen
!= 0) {
861 DataSeg
= NetbufGetByte (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NULL
);
866 // Check the status class in the login response PDU.
868 switch (LoginRsp
->StatusClass
) {
869 case ISCSI_LOGIN_STATUS_SUCCESS
:
871 // Just break here; the response and the data segment will be processed later.
875 case ISCSI_LOGIN_STATUS_REDIRECTION
:
877 // The target may be moved to a different address.
879 if (DataSeg
== NULL
) {
880 return EFI_PROTOCOL_ERROR
;
883 // Process the TargetAddress key-value strings in the data segment to update the
884 // target address info.
886 Status
= IScsiUpdateTargetAddress (Session
, (CHAR8
*) DataSeg
, DataSegLen
);
887 if (EFI_ERROR (Status
)) {
891 // Session will be restarted on this error status because the Target is
892 // redirected by this Login Response.
894 return EFI_MEDIA_CHANGED
;
898 // Initiator Error, Target Error, or any other undefined error code.
900 return EFI_PROTOCOL_ERROR
;
903 // The status is success; extract the wanted fields from the header segment.
905 Transit
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT
);
906 Continue
= ISCSI_FLAG_ON (LoginRsp
, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE
);
908 CurrentStage
= ISCSI_GET_CURRENT_STAGE (LoginRsp
);
909 NextStage
= ISCSI_GET_NEXT_STAGE (LoginRsp
);
911 LoginRsp
->InitiatorTaskTag
= NTOHL (LoginRsp
->InitiatorTaskTag
);
913 if ((Transit
&& Continue
) ||
914 (CurrentStage
!= Conn
->CurrentStage
) ||
915 (!Conn
->TransitInitiated
&& Transit
) ||
916 (Transit
&& (NextStage
!= Conn
->NextStage
)) ||
917 (CompareMem (Session
->Isid
, LoginRsp
->Isid
, sizeof (LoginRsp
->Isid
)) != 0) ||
918 (LoginRsp
->InitiatorTaskTag
!= Session
->InitiatorTaskTag
)
921 // A Login Response with the C bit set to 1 MUST have the T bit set to 0.
922 // The CSG in the Login Response MUST be the same with the I-end of this connection.
923 // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
924 // initiate the transistion.
925 // The NSG MUST be the same with the I-end of this connection if Transit is required.
926 // The ISID in the Login Response MUST be the same with this session.
928 return EFI_PROTOCOL_ERROR
;
931 LoginRsp
->StatSN
= NTOHL (LoginRsp
->StatSN
);
932 LoginRsp
->ExpCmdSN
= NTOHL (LoginRsp
->ExpCmdSN
);
933 LoginRsp
->MaxCmdSN
= NTOHL (LoginRsp
->MaxCmdSN
);
935 if ((Conn
->CurrentStage
== ISCSI_SECURITY_NEGOTIATION
) && (Conn
->AuthStep
== ISCSI_AUTH_INITIAL
)) {
937 // If the Login Request is a leading Login Request, the target MUST use
938 // the value presented in CmdSN as the target value for ExpCmdSN.
940 if ((Session
->State
== SESSION_STATE_FREE
) && (Session
->CmdSN
!= LoginRsp
->ExpCmdSN
)) {
941 return EFI_PROTOCOL_ERROR
;
945 // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
948 Conn
->ExpStatSN
= LoginRsp
->StatSN
+ 1;
949 Session
->MaxCmdSN
= LoginRsp
->MaxCmdSN
;
950 Session
->ExpCmdSN
= LoginRsp
->ExpCmdSN
;
953 // Check the StatSN of this PDU.
955 Status
= IScsiCheckSN (&Conn
->ExpStatSN
, LoginRsp
->StatSN
);
956 if (!EFI_ERROR (Status
)) {
958 // Update the MaxCmdSN and ExpCmdSN.
960 IScsiUpdateCmdSN (Session
, LoginRsp
->MaxCmdSN
, LoginRsp
->ExpCmdSN
);
966 // Trim off the header segment.
968 NetbufTrim (Pdu
, sizeof (ISCSI_LOGIN_RESPONSE
), NET_BUF_HEAD
);
971 // Queue this login response first in case it's a partial response so that
972 // later when the full response list is received we can combine these scattered
973 // responses' data segment and then process it.
976 NetbufQueAppend (&Conn
->RspQue
, Pdu
);
978 Conn
->PartialRspRcvd
= Continue
;
981 // It is a partial response; must wait for another or more Request/Response
982 // conversations to get the full response.
987 switch (CurrentStage
) {
988 case ISCSI_SECURITY_NEGOTIATION
:
990 // In security negotiation stage, let CHAP module handle it.
992 if (Session
->AuthType
!= ISCSI_AUTH_TYPE_KRB
) {
993 Status
= IScsiCHAPOnRspReceived (Conn
);
997 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
:
999 // Response received with negotiation response on iSCSI parameters: check them.
1001 Status
= IScsiCheckOpParams (Conn
);
1002 if (!EFI_ERROR (Status
)) {
1003 Conn
->ParamNegotiated
= TRUE
;
1010 // Should never get here.
1012 Status
= EFI_PROTOCOL_ERROR
;
1016 if (Transit
&& (Status
== EFI_SUCCESS
)) {
1018 // Do the state transition.
1020 Conn
->CurrentStage
= Conn
->NextStage
;
1022 if (Conn
->CurrentStage
== ISCSI_LOGIN_OPERATIONAL_NEGOTIATION
) {
1023 Conn
->NextStage
= ISCSI_FULL_FEATURE_PHASE
;
1026 // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
1027 // get the TSIH from the Login Response.
1029 Session
->Tsih
= NTOHS (LoginRsp
->Tsih
);
1033 // Flush the response(s) received.
1035 NetbufQueFlush (&Conn
->RspQue
);
1042 Updated the target information according the data received in the iSCSI
1043 login response with an target redirection status.
1045 @param[in, out] Session The iSCSI session.
1046 @param[in] Data The data segment that should contain the
1047 TargetAddress key-value list.
1048 @param[in] Len Length of the data.
1050 @retval EFI_SUCCESS The target address is updated.
1051 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1052 @retval EFI_NOT_FOUND The TargetAddress key is not found.
1053 @retval Others Other errors as indicated.
1057 IScsiUpdateTargetAddress (
1058 IN OUT ISCSI_SESSION
*Session
,
1063 LIST_ENTRY
*KeyValueList
;
1064 CHAR8
*TargetAddress
;
1070 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1071 if (KeyValueList
== NULL
) {
1072 return EFI_OUT_OF_RESOURCES
;
1075 Status
= EFI_NOT_FOUND
;
1078 TargetAddress
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ADDRESS
);
1079 if (TargetAddress
== NULL
) {
1083 if (!NET_IS_DIGIT (TargetAddress
[0])) {
1085 // The domainname of the target may be presented in three formats: a DNS host name,
1086 // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted
1092 IpStr
= TargetAddress
;
1094 while ((*TargetAddress
!= 0) && (*TargetAddress
!= ':') && (*TargetAddress
!= ',')) {
1096 // NULL, ':', or ',' ends the IPv4 string.
1101 if (*TargetAddress
== ',') {
1103 // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
1104 // as the result of a redirection.
1107 } else if (*TargetAddress
== ':') {
1108 *TargetAddress
= '\0';
1112 Number
= AsciiStrDecimalToUintn (TargetAddress
);
1113 if (Number
> 0xFFFF) {
1116 Session
->ConfigData
->SessionConfigData
.TargetPort
= (UINT16
) Number
;
1120 // The string only contains the IPv4 address. Use the well-known port.
1122 Session
->ConfigData
->SessionConfigData
.TargetPort
= ISCSI_WELL_KNOWN_PORT
;
1125 // Update the target IP address.
1127 if (Session
->ConfigData
->SessionConfigData
.IpMode
< IP_MODE_AUTOCONFIG
) {
1128 IpMode
= Session
->ConfigData
->SessionConfigData
.IpMode
;
1130 IpMode
= Session
->ConfigData
->AutoConfigureMode
;
1133 Status
= IScsiAsciiStrToIp (
1136 &Session
->ConfigData
->SessionConfigData
.TargetIp
1139 if (EFI_ERROR (Status
)) {
1146 IScsiFreeKeyValueList (KeyValueList
);
1153 The callback function to free the net buffer list.
1155 @param[in] Arg The opaque parameter.
1164 ASSERT (Arg
!= NULL
);
1166 NetbufFreeList ((LIST_ENTRY
*) Arg
);
1172 The callback function called in NetBufFree; it does nothing.
1174 @param[in] Arg The opaque parameter.
1187 Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
1188 an optional data segment. The two parts will be put into two blocks of buffers in the
1189 net buffer. The digest check will be conducted in this function if needed and the digests
1190 will be trimmed from the PDU buffer.
1192 @param[in] Conn The iSCSI connection to receive data from.
1193 @param[out] Pdu The received iSCSI pdu.
1194 @param[in] Context The context used to describe information on the caller provided
1195 buffer to receive data segment of the iSCSI pdu. It is optional.
1196 @param[in] HeaderDigest Whether there will be header digest received.
1197 @param[in] DataDigest Whether there will be data digest.
1198 @param[in] TimeoutEvent The timeout event. It is optional.
1200 @retval EFI_SUCCESS An iSCSI pdu is received.
1201 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1202 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1203 @retval Others Other errors as indicated.
1208 IN ISCSI_CONNECTION
*Conn
,
1210 IN ISCSI_IN_BUFFER_CONTEXT
*Context
, OPTIONAL
1211 IN BOOLEAN HeaderDigest
,
1212 IN BOOLEAN DataDigest
,
1213 IN EFI_EVENT TimeoutEvent OPTIONAL
1216 LIST_ENTRY
*NbufList
;
1222 UINT32 InDataOffset
;
1223 NET_FRAGMENT Fragment
[2];
1224 UINT32 FragmentCount
;
1226 UINT32 PadAndCRC32
[2];
1228 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
1229 if (NbufList
== NULL
) {
1230 return EFI_OUT_OF_RESOURCES
;
1233 InitializeListHead (NbufList
);
1236 // The header digest will be received together with the PDU header, if exists.
1238 Len
= sizeof (ISCSI_BASIC_HEADER
) + (HeaderDigest
? sizeof (UINT32
) : 0);
1239 PduHdr
= NetbufAlloc (Len
);
1240 if (PduHdr
== NULL
) {
1241 Status
= EFI_OUT_OF_RESOURCES
;
1245 Header
= NetbufAllocSpace (PduHdr
, Len
, NET_BUF_TAIL
);
1246 ASSERT (Header
!= NULL
);
1247 InsertTailList (NbufList
, &PduHdr
->List
);
1250 // First step, receive the BHS of the PDU.
1252 Status
= TcpIoReceive (&Conn
->TcpIo
, PduHdr
, FALSE
, TimeoutEvent
);
1254 if (EFI_ERROR (Status
)) {
1260 // TODO: check the header-digest.
1263 // Trim off the digest.
1265 NetbufTrim (PduHdr
, sizeof (UINT32
), NET_BUF_TAIL
);
1268 Len
= ISCSI_GET_DATASEG_LEN (Header
);
1276 // Get the length of the padding bytes of the data segment.
1278 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1280 switch (ISCSI_GET_OPCODE (Header
)) {
1281 case ISCSI_OPCODE_SCSI_DATA_IN
:
1283 // To reduce memory copy overhead, try to use the buffer described by Context
1284 // if the PDU is an iSCSI SCSI data.
1286 InDataOffset
= ISCSI_GET_BUFFER_OFFSET (Header
);
1287 if ((Context
== NULL
) || ((InDataOffset
+ Len
) > Context
->InDataLen
)) {
1288 Status
= EFI_PROTOCOL_ERROR
;
1292 Fragment
[0].Len
= Len
;
1293 Fragment
[0].Bulk
= Context
->InData
+ InDataOffset
;
1295 if (DataDigest
|| (PadLen
!= 0)) {
1297 // The data segment is padded. Use two fragments to receive it:
1298 // the first to receive the useful data; the second to receive the padding.
1300 Fragment
[1].Len
= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1301 Fragment
[1].Bulk
= (UINT8
*)PadAndCRC32
+ (4 - PadLen
);
1308 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
1309 if (DataSeg
== NULL
) {
1310 Status
= EFI_OUT_OF_RESOURCES
;
1316 case ISCSI_OPCODE_SCSI_RSP
:
1317 case ISCSI_OPCODE_NOP_IN
:
1318 case ISCSI_OPCODE_LOGIN_RSP
:
1319 case ISCSI_OPCODE_TEXT_RSP
:
1320 case ISCSI_OPCODE_ASYNC_MSG
:
1321 case ISCSI_OPCODE_REJECT
:
1322 case ISCSI_OPCODE_VENDOR_T0
:
1323 case ISCSI_OPCODE_VENDOR_T1
:
1324 case ISCSI_OPCODE_VENDOR_T2
:
1326 // Allocate buffer to receive the data segment.
1328 Len
+= PadLen
+ (DataDigest
? sizeof (UINT32
) : 0);
1329 DataSeg
= NetbufAlloc (Len
);
1330 if (DataSeg
== NULL
) {
1331 Status
= EFI_OUT_OF_RESOURCES
;
1335 NetbufAllocSpace (DataSeg
, Len
, NET_BUF_TAIL
);
1339 Status
= EFI_PROTOCOL_ERROR
;
1343 InsertTailList (NbufList
, &DataSeg
->List
);
1346 // Receive the data segment with the data digest, if any.
1348 Status
= TcpIoReceive (&Conn
->TcpIo
, DataSeg
, FALSE
, TimeoutEvent
);
1350 if (EFI_ERROR (Status
)) {
1356 // TODO: Check the data digest.
1358 NetbufTrim (DataSeg
, sizeof (UINT32
), NET_BUF_TAIL
);
1363 // Trim off the padding bytes in the data segment.
1365 NetbufTrim (DataSeg
, PadLen
, NET_BUF_TAIL
);
1370 // Form the pdu from a list of pdu segments.
1372 *Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
1374 Status
= EFI_OUT_OF_RESOURCES
;
1379 if (EFI_ERROR (Status
)) {
1381 // Free the Nbufs in this NbufList and the NbufList itself.
1383 IScsiFreeNbufList (NbufList
);
1391 Check and get the result of the parameter negotiation.
1393 @param[in, out] Conn The connection in iSCSI login.
1395 @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
1396 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1397 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1401 IScsiCheckOpParams (
1402 IN OUT ISCSI_CONNECTION
*Conn
1406 LIST_ENTRY
*KeyValueList
;
1409 ISCSI_SESSION
*Session
;
1413 ASSERT (Conn
->RspQue
.BufNum
!= 0);
1415 Session
= Conn
->Session
;
1417 Len
= Conn
->RspQue
.BufSize
;
1418 Data
= AllocatePool (Len
);
1420 return EFI_OUT_OF_RESOURCES
;
1423 NetbufQueCopy (&Conn
->RspQue
, 0, Len
, (UINT8
*) Data
);
1425 Status
= EFI_PROTOCOL_ERROR
;
1428 // Extract the Key-Value pairs into a list.
1430 KeyValueList
= IScsiBuildKeyValueList (Data
, Len
);
1431 if (KeyValueList
== NULL
) {
1438 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_HEADER_DIGEST
);
1439 if (Value
== NULL
) {
1443 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1444 if (Conn
->HeaderDigest
!= IScsiDigestCRC32
) {
1447 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1448 Conn
->HeaderDigest
= IScsiDigestNone
;
1455 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_DIGEST
);
1456 if (Value
== NULL
) {
1460 if (AsciiStrCmp (Value
, "CRC32") == 0) {
1461 if (Conn
->DataDigest
!= IScsiDigestCRC32
) {
1464 } else if (AsciiStrCmp (Value
, ISCSI_KEY_VALUE_NONE
) == 0) {
1465 Conn
->DataDigest
= IScsiDigestNone
;
1470 // ErrorRecoveryLevel: result fuction is Minimum.
1472 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
);
1473 if (Value
== NULL
) {
1477 NumericValue
= IScsiNetNtoi (Value
);
1478 if (NumericValue
> 2) {
1482 Session
->ErrorRecoveryLevel
= (UINT8
) MIN (Session
->ErrorRecoveryLevel
, NumericValue
);
1485 // InitialR2T: result function is OR.
1487 if (!Session
->InitialR2T
) {
1488 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1489 if (Value
== NULL
) {
1493 Session
->InitialR2T
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1497 // ImmediateData: result function is AND.
1499 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_IMMEDIATE_DATA
);
1500 if (Value
== NULL
) {
1504 Session
->ImmediateData
= (BOOLEAN
) (Session
->ImmediateData
&& (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0));
1507 // MaxRecvDataSegmentLength is declarative.
1509 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
);
1510 if (Value
!= NULL
) {
1511 Conn
->MaxRecvDataSegmentLength
= (UINT32
) IScsiNetNtoi (Value
);
1514 // MaxBurstLength: result funtion is Mininum.
1516 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_BURST_LENGTH
);
1517 if (Value
== NULL
) {
1521 NumericValue
= IScsiNetNtoi (Value
);
1522 Session
->MaxBurstLength
= (UINT32
) MIN (Session
->MaxBurstLength
, NumericValue
);
1525 // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
1526 // ImmediateData=No.
1528 if (!(Session
->InitialR2T
&& !Session
->ImmediateData
)) {
1529 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1530 if (Value
== NULL
) {
1534 NumericValue
= IScsiNetNtoi (Value
);
1535 Session
->FirstBurstLength
= (UINT32
) MIN (Session
->FirstBurstLength
, NumericValue
);
1539 // MaxConnections: result function is Minimum.
1541 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_CONNECTIONS
);
1542 if (Value
== NULL
) {
1546 NumericValue
= IScsiNetNtoi (Value
);
1547 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1551 Session
->MaxConnections
= (UINT32
) MIN (Session
->MaxConnections
, NumericValue
);
1554 // DataPDUInOrder: result function is OR.
1556 if (!Session
->DataPDUInOrder
) {
1557 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1558 if (Value
== NULL
) {
1562 Session
->DataPDUInOrder
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1566 // DataSequenceInorder: result function is OR.
1568 if (!Session
->DataSequenceInOrder
) {
1569 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1570 if (Value
== NULL
) {
1574 Session
->DataSequenceInOrder
= (BOOLEAN
) (AsciiStrCmp (Value
, "Yes") == 0);
1578 // DefaultTime2Wait: result function is Maximum.
1580 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2WAIT
);
1581 if (Value
== NULL
) {
1585 NumericValue
= IScsiNetNtoi (Value
);
1586 if (NumericValue
== 0) {
1587 Session
->DefaultTime2Wait
= 0;
1588 } else if (NumericValue
> 3600) {
1591 Session
->DefaultTime2Wait
= (UINT32
) MAX (Session
->DefaultTime2Wait
, NumericValue
);
1594 // DefaultTime2Retain: result function is Minimum.
1596 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DEFAULT_TIME2RETAIN
);
1597 if (Value
== NULL
) {
1601 NumericValue
= IScsiNetNtoi (Value
);
1602 if (NumericValue
== 0) {
1603 Session
->DefaultTime2Retain
= 0;
1604 } else if (NumericValue
> 3600) {
1607 Session
->DefaultTime2Retain
= (UINT32
) MIN (Session
->DefaultTime2Retain
, NumericValue
);
1610 // MaxOutstandingR2T: result function is Minimum.
1612 Value
= IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_MAX_OUTSTANDING_R2T
);
1613 if (Value
== NULL
) {
1617 NumericValue
= IScsiNetNtoi (Value
);
1618 if ((NumericValue
== 0) || (NumericValue
> 65535)) {
1622 Session
->MaxOutstandingR2T
= (UINT16
) MIN (Session
->MaxOutstandingR2T
, NumericValue
);
1625 // Remove declarative key-value pairs, if any.
1627 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_SESSION_TYPE
);
1628 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_ALIAS
);
1629 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG
);
1633 // Remove the key-value that may not needed for result function is OR.
1635 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_INITIAL_R2T
);
1636 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_PDU_IN_ORDER
);
1637 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
);
1640 // Remove irrelevant parameter, if any.
1642 if (Session
->InitialR2T
&& !Session
->ImmediateData
) {
1643 IScsiGetValueByKeyFromList (KeyValueList
, ISCSI_KEY_FIRST_BURST_LENGTH
);
1646 if (IsListEmpty (KeyValueList
)) {
1648 // Succeed if no more keys in the list.
1650 Status
= EFI_SUCCESS
;
1655 IScsiFreeKeyValueList (KeyValueList
);
1664 Fill the operational parameters.
1666 @param[in] Conn The connection in iSCSI login.
1667 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1672 IN ISCSI_CONNECTION
*Conn
,
1676 ISCSI_SESSION
*Session
;
1679 Session
= Conn
->Session
;
1681 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->HeaderDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1682 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_HEADER_DIGEST
, Value
);
1684 AsciiSPrint (Value
, sizeof (Value
), "%a", (Conn
->DataDigest
== IScsiDigestCRC32
) ? "None,CRC32" : "None");
1685 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_DIGEST
, Value
);
1687 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->ErrorRecoveryLevel
);
1688 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_ERROR_RECOVERY_LEVEL
, Value
);
1690 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->InitialR2T
? "Yes" : "No");
1691 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_INITIAL_R2T
, Value
);
1693 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->ImmediateData
? "Yes" : "No");
1694 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_IMMEDIATE_DATA
, Value
);
1696 AsciiSPrint (Value
, sizeof (Value
), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP
);
1697 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH
, Value
);
1699 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxBurstLength
);
1700 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_BURST_LENGTH
, Value
);
1702 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->FirstBurstLength
);
1703 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_FIRST_BURST_LENGTH
, Value
);
1705 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxConnections
);
1706 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_CONNECTIONS
, Value
);
1708 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataPDUInOrder
? "Yes" : "No");
1709 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_PDU_IN_ORDER
, Value
);
1711 AsciiSPrint (Value
, sizeof (Value
), "%a", Session
->DataSequenceInOrder
? "Yes" : "No");
1712 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER
, Value
);
1714 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Wait
);
1715 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2WAIT
, Value
);
1717 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->DefaultTime2Retain
);
1718 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_DEFAULT_TIME2RETAIN
, Value
);
1720 AsciiSPrint (Value
, sizeof (Value
), "%d", Session
->MaxOutstandingR2T
);
1721 IScsiAddKeyValuePair (Pdu
, ISCSI_KEY_MAX_OUTSTANDING_R2T
, Value
);
1726 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
1728 @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
1729 @param[in] Len The length of the last segment in the PDU.
1731 @retval EFI_SUCCESS The segment is padded or there is no need to pad it.
1732 @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
1737 IN OUT NET_BUF
*Pdu
,
1744 PadLen
= ISCSI_GET_PAD_LEN (Len
);
1747 Data
= NetbufAllocSpace (Pdu
, PadLen
, NET_BUF_TAIL
);
1749 return EFI_OUT_OF_RESOURCES
;
1752 ZeroMem (Data
, PadLen
);
1760 Build a key-value list from the data segment.
1762 @param[in] Data The data segment containing the key-value pairs.
1763 @param[in] Len Length of the data segment.
1765 @return The key-value list.
1766 @retval NULL Other errors as indicated.
1770 IScsiBuildKeyValueList (
1775 LIST_ENTRY
*ListHead
;
1776 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1778 ListHead
= AllocatePool (sizeof (LIST_ENTRY
));
1779 if (ListHead
== NULL
) {
1783 InitializeListHead (ListHead
);
1786 KeyValuePair
= AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR
));
1787 if (KeyValuePair
== NULL
) {
1791 InitializeListHead (&KeyValuePair
->List
);
1793 KeyValuePair
->Key
= Data
;
1795 while ((Len
> 0) && (*Data
!= '=')) {
1806 FreePool (KeyValuePair
);
1810 KeyValuePair
->Value
= Data
;
1812 InsertTailList (ListHead
, &KeyValuePair
->List
);;
1814 Data
+= AsciiStrLen (KeyValuePair
->Value
) + 1;
1815 Len
-= (UINT32
) AsciiStrLen (KeyValuePair
->Value
) + 1;
1822 IScsiFreeKeyValueList (ListHead
);
1829 Get the value string by the key name from the key-value list. If found,
1830 the key-value entry will be removed from the list.
1832 @param[in, out] KeyValueList The key-value list.
1833 @param[in] Key The key name to find.
1835 @return The value string.
1836 @retval NULL The key value pair cannot be found.
1840 IScsiGetValueByKeyFromList (
1841 IN OUT LIST_ENTRY
*KeyValueList
,
1846 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1851 NET_LIST_FOR_EACH (Entry
, KeyValueList
) {
1852 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1854 if (AsciiStrCmp (KeyValuePair
->Key
, Key
) == 0) {
1855 Value
= KeyValuePair
->Value
;
1857 RemoveEntryList (&KeyValuePair
->List
);
1858 FreePool (KeyValuePair
);
1868 Free the key-value list.
1870 @param[in] KeyValueList The key-value list.
1874 IScsiFreeKeyValueList (
1875 IN LIST_ENTRY
*KeyValueList
1879 ISCSI_KEY_VALUE_PAIR
*KeyValuePair
;
1881 while (!IsListEmpty (KeyValueList
)) {
1882 Entry
= NetListRemoveHead (KeyValueList
);
1883 KeyValuePair
= NET_LIST_USER_STRUCT (Entry
, ISCSI_KEY_VALUE_PAIR
, List
);
1885 FreePool (KeyValuePair
);
1888 FreePool (KeyValueList
);
1893 Normalize the iSCSI name according to RFC.
1895 @param[in, out] Name The iSCSI name.
1896 @param[in] Len Length of the iSCSI name.
1898 @retval EFI_SUCCESS The iSCSI name is valid and normalized.
1899 @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.
1903 IScsiNormalizeName (
1910 for (Index
= 0; Index
< Len
; Index
++) {
1911 if (NET_IS_UPPER_CASE_CHAR (Name
[Index
])) {
1913 // Convert the upper-case characters to lower-case ones.
1915 Name
[Index
] = (CHAR8
) (Name
[Index
] - 'A' + 'a');
1918 if (!NET_IS_LOWER_CASE_CHAR (Name
[Index
]) &&
1919 !NET_IS_DIGIT (Name
[Index
]) &&
1920 (Name
[Index
] != '-') &&
1921 (Name
[Index
] != '.') &&
1922 (Name
[Index
] != ':')
1925 // ASCII dash, dot, colon lower-case characters and digit characters
1928 return EFI_PROTOCOL_ERROR
;
1932 if ((Len
< 4) || (CompareMem (Name
, "iqn.", 4) != 0)) {
1934 // Only IQN format is accepted now.
1936 return EFI_PROTOCOL_ERROR
;
1944 Create an iSCSI task control block.
1946 @param[in] Conn The connection on which the task control block will be created.
1947 @param[out] Tcb The newly created task control block.
1949 @retval EFI_SUCCESS The task control block is created.
1950 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1951 @retval EFI_NOT_READY The target cannot accept new commands.
1956 IN ISCSI_CONNECTION
*Conn
,
1960 ISCSI_SESSION
*Session
;
1963 ASSERT (Tcb
!= NULL
);
1965 Session
= Conn
->Session
;
1967 if (ISCSI_SEQ_GT (Session
->CmdSN
, Session
->MaxCmdSN
)) {
1968 return EFI_NOT_READY
;
1971 NewTcb
= AllocateZeroPool (sizeof (ISCSI_TCB
));
1972 if (NewTcb
== NULL
) {
1973 return EFI_OUT_OF_RESOURCES
;
1976 InitializeListHead (&NewTcb
->Link
);
1978 NewTcb
->SoFarInOrder
= TRUE
;
1979 NewTcb
->InitiatorTaskTag
= Session
->InitiatorTaskTag
;
1980 NewTcb
->CmdSN
= Session
->CmdSN
;
1981 NewTcb
->Conn
= Conn
;
1983 InsertTailList (&Session
->TcbList
, &NewTcb
->Link
);
1986 // Advance the initiator task tag.
1988 Session
->InitiatorTaskTag
++;
1998 Delete the tcb from the connection and destroy it.
2000 @param[in] Tcb The tcb to delete.
2008 RemoveEntryList (&Tcb
->Link
);
2015 Find the task control block by the initator task tag.
2017 @param[in] TcbList The tcb list.
2018 @param[in] InitiatorTaskTag The initiator task tag.
2020 @return The task control block found.
2021 @retval NULL The task control block cannot be found.
2026 IN LIST_ENTRY
*TcbList
,
2027 IN UINT32 InitiatorTaskTag
2035 NET_LIST_FOR_EACH (Entry
, TcbList
) {
2036 Tcb
= NET_LIST_USER_STRUCT (Entry
, ISCSI_TCB
, Link
);
2038 if (Tcb
->InitiatorTaskTag
== InitiatorTaskTag
) {
2048 Create a data segment, pad it, and calculate the CRC if needed.
2050 @param[in] Data The data to fill into the data segment.
2051 @param[in] Len Length of the data.
2052 @param[in] DataDigest Whether to calculate CRC for this data segment.
2054 @return The net buffer wrapping the data segment.
2058 IScsiNewDataSegment (
2061 IN BOOLEAN DataDigest
2064 NET_FRAGMENT Fragment
[2];
2065 UINT32 FragmentCount
;
2069 Fragment
[0].Len
= Len
;
2070 Fragment
[0].Bulk
= Data
;
2072 PadLen
= ISCSI_GET_PAD_LEN (Len
);
2074 Fragment
[1].Len
= PadLen
;
2075 Fragment
[1].Bulk
= (UINT8
*) &mDataSegPad
;
2082 DataSeg
= NetbufFromExt (&Fragment
[0], FragmentCount
, 0, 0, IScsiNbufExtFree
, NULL
);
2089 Create a iSCSI SCSI command PDU to encapsulate the command issued
2090 by SCSI through the EXT SCSI PASS THRU Protocol.
2092 @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
2093 @param[in] Lun The LUN.
2094 @param[in] Tcb The tcb assocated with this SCSI command.
2096 @return The created iSCSI SCSI command PDU.
2097 @retval NULL Other errors as indicated.
2101 IScsiNewScsiCmdPdu (
2102 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
,
2107 LIST_ENTRY
*NbufList
;
2111 SCSI_COMMAND
*ScsiCmd
;
2114 ISCSI_ADDITIONAL_HEADER
*Header
;
2115 ISCSI_BI_EXP_READ_DATA_LEN_AHS
*BiExpReadDataLenAHS
;
2116 ISCSI_SESSION
*Session
;
2117 UINT32 ImmediateDataLen
;
2121 if (Packet
->DataDirection
== DataBi
) {
2123 // Bidirectional Read/Write command, the bidirectional expected
2124 // read data length AHS is required.
2126 AHSLength
+= sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS
);
2129 if (Packet
->CdbLength
> 16) {
2131 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2133 AHSLength
= (UINT8
) (AHSLength
+ ISCSI_ROUNDUP (Packet
->CdbLength
- 16) + sizeof (ISCSI_ADDITIONAL_HEADER
));
2136 Length
= sizeof (SCSI_COMMAND
) + AHSLength
;
2137 PduHeader
= NetbufAlloc (Length
);
2138 if (PduHeader
== NULL
) {
2142 ScsiCmd
= (SCSI_COMMAND
*) NetbufAllocSpace (PduHeader
, Length
, NET_BUF_TAIL
);
2143 if (ScsiCmd
== NULL
) {
2144 NetbufFree (PduHeader
);
2147 Header
= (ISCSI_ADDITIONAL_HEADER
*) (ScsiCmd
+ 1);
2149 ZeroMem (ScsiCmd
, Length
);
2151 ISCSI_SET_OPCODE (ScsiCmd
, ISCSI_OPCODE_SCSI_CMD
, 0);
2152 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_TASK_ATTR_SIMPLE
);
2155 // Set the READ/WRITE flags according to the IO type of this request.
2157 switch (Packet
->DataDirection
) {
2159 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
);
2160 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->InTransferLength
);
2164 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_WRITE
);
2165 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2169 ISCSI_SET_FLAG (ScsiCmd
, SCSI_CMD_PDU_FLAG_READ
| SCSI_CMD_PDU_FLAG_WRITE
);
2170 ScsiCmd
->ExpDataXferLength
= NTOHL (Packet
->OutTransferLength
);
2173 // Fill the bidirectional expected read data length AHS.
2175 BiExpReadDataLenAHS
= (ISCSI_BI_EXP_READ_DATA_LEN_AHS
*) Header
;
2176 Header
= (ISCSI_ADDITIONAL_HEADER
*) (BiExpReadDataLenAHS
+ 1);
2178 BiExpReadDataLenAHS
->Length
= NTOHS (5);
2179 BiExpReadDataLenAHS
->Type
= ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN
;
2180 BiExpReadDataLenAHS
->ExpReadDataLength
= NTOHL (Packet
->InTransferLength
);
2185 ScsiCmd
->TotalAHSLength
= AHSLength
;
2186 CopyMem (ScsiCmd
->Lun
, &Lun
, sizeof (ScsiCmd
->Lun
));
2187 ScsiCmd
->InitiatorTaskTag
= NTOHL (Tcb
->InitiatorTaskTag
);
2188 ScsiCmd
->CmdSN
= NTOHL (Tcb
->CmdSN
);
2189 ScsiCmd
->ExpStatSN
= NTOHL (Tcb
->Conn
->ExpStatSN
);
2191 CopyMem (ScsiCmd
->Cdb
, Packet
->Cdb
, sizeof (ScsiCmd
->Cdb
));
2193 if (Packet
->CdbLength
> 16) {
2194 Header
->Length
= NTOHS ((UINT16
) (Packet
->CdbLength
- 15));
2195 Header
->Type
= ISCSI_AHS_TYPE_EXT_CDB
;
2197 CopyMem (Header
+ 1, (UINT8
*) Packet
->Cdb
+ 16, Packet
->CdbLength
- 16);
2201 Session
= Tcb
->Conn
->Session
;
2202 ImmediateDataLen
= 0;
2204 if (Session
->ImmediateData
&& (Packet
->OutTransferLength
!= 0)) {
2206 // Send immediate data in this SCSI Command PDU. The length of the immeidate
2207 // data is the minimum of FirstBurstLength, the data length to be xfered, and
2208 // the MaxRecvdataSegmentLength on this connection.
2210 ImmediateDataLen
= MIN (Session
->FirstBurstLength
, Packet
->OutTransferLength
);
2211 ImmediateDataLen
= MIN (ImmediateDataLen
, Tcb
->Conn
->MaxRecvDataSegmentLength
);
2214 // Update the data segment length in the PDU header.
2216 ISCSI_SET_DATASEG_LEN (ScsiCmd
, ImmediateDataLen
);
2219 // Create the data segment.
2221 DataSeg
= IScsiNewDataSegment ((UINT8
*) Packet
->OutDataBuffer
, ImmediateDataLen
, FALSE
);
2222 if (DataSeg
== NULL
) {
2223 NetbufFree (PduHeader
);
2228 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2229 if (NbufList
== NULL
) {
2230 NetbufFree (PduHeader
);
2231 NetbufFree (DataSeg
);
2237 InitializeListHead (NbufList
);
2238 InsertTailList (NbufList
, &PduHeader
->List
);
2239 InsertTailList (NbufList
, &DataSeg
->List
);
2241 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2243 IScsiFreeNbufList (NbufList
);
2247 if (Session
->InitialR2T
||
2248 (ImmediateDataLen
== Session
->FirstBurstLength
) ||
2249 (ImmediateDataLen
== Packet
->OutTransferLength
)
2252 // Unsolicited data out sequence is not allowed,
2253 // or FirstBustLength data is already sent out by immediate data,
2254 // or all the OUT data accompany this SCSI packet are sent as
2255 // immediate data. The final flag should be set on this SCSI Command
2258 ISCSI_SET_FLAG (ScsiCmd
, ISCSI_BHS_FLAG_FINAL
);
2268 Create a new iSCSI SCSI Data Out PDU.
2270 @param[in] Data The data to put into the Data Out PDU.
2271 @param[in] Len Length of the data.
2272 @param[in] DataSN The DataSN of the Data Out PDU.
2273 @param[in] Tcb The task control block of this Data Out PDU.
2274 @param[in] Lun The LUN.
2276 @return The net buffer wrapping the Data Out PDU.
2277 @retval NULL Other errors as indicated.
2281 IScsiNewDataOutPdu (
2289 LIST_ENTRY
*NbufList
;
2293 ISCSI_SCSI_DATA_OUT
*DataOutHdr
;
2294 ISCSI_XFER_CONTEXT
*XferContext
;
2296 NbufList
= AllocatePool (sizeof (LIST_ENTRY
));
2297 if (NbufList
== NULL
) {
2301 InitializeListHead (NbufList
);
2304 // Allocate memory for the BHS.
2306 PduHdr
= NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT
));
2307 if (PduHdr
== NULL
) {
2308 FreePool (NbufList
);
2312 // Insert the BHS into the buffer list.
2314 InsertTailList (NbufList
, &PduHdr
->List
);
2316 DataOutHdr
= (ISCSI_SCSI_DATA_OUT
*) NetbufAllocSpace (PduHdr
, sizeof (ISCSI_SCSI_DATA_OUT
), NET_BUF_TAIL
);
2317 ASSERT (DataOutHdr
!= NULL
);
2318 XferContext
= &Tcb
->XferContext
;
2320 ZeroMem (DataOutHdr
, sizeof (ISCSI_SCSI_DATA_OUT
));
2323 // Set the flags and fields of the Data Out PDU BHS.
2325 ISCSI_SET_OPCODE (DataOutHdr
, ISCSI_OPCODE_SCSI_DATA_OUT
, 0);
2326 ISCSI_SET_DATASEG_LEN (DataOutHdr
, Len
);
2328 DataOutHdr
->InitiatorTaskTag
= HTONL (Tcb
->InitiatorTaskTag
);
2329 DataOutHdr
->TargetTransferTag
= HTONL (XferContext
->TargetTransferTag
);
2330 DataOutHdr
->ExpStatSN
= HTONL (Tcb
->Conn
->ExpStatSN
);
2331 DataOutHdr
->DataSN
= HTONL (DataSN
);
2332 DataOutHdr
->BufferOffset
= HTONL (XferContext
->Offset
);
2334 if (XferContext
->TargetTransferTag
!= ISCSI_RESERVED_TAG
) {
2335 CopyMem (&DataOutHdr
->Lun
, &Lun
, sizeof (DataOutHdr
->Lun
));
2338 // Build the data segment for this Data Out PDU.
2340 DataSeg
= IScsiNewDataSegment (Data
, Len
, FALSE
);
2341 if (DataSeg
== NULL
) {
2342 IScsiFreeNbufList (NbufList
);
2346 // Put the data segment into the buffer list and combine it with the BHS
2347 // into a full Data Out PDU.
2349 InsertTailList (NbufList
, &DataSeg
->List
);
2350 Pdu
= NetbufFromBufList (NbufList
, 0, 0, IScsiFreeNbufList
, NbufList
);
2352 IScsiFreeNbufList (NbufList
);
2360 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
2362 @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
2363 @param[in] Tcb The task control block of the data to send out.
2364 @param[in] Lun The LUN the data will be sent to.
2366 @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.
2367 @retval NULL Other errors as indicated.
2371 IScsiGenerateDataOutPduSequence (
2377 LIST_ENTRY
*PduList
;
2380 NET_BUF
*DataOutPdu
;
2381 ISCSI_CONNECTION
*Conn
;
2382 ISCSI_XFER_CONTEXT
*XferContext
;
2383 UINT8
*DataOutPacket
;
2385 PduList
= AllocatePool (sizeof (LIST_ENTRY
));
2386 if (PduList
== NULL
) {
2390 InitializeListHead (PduList
);
2395 XferContext
= &Tcb
->XferContext
;
2397 while (XferContext
->DesiredLength
> 0) {
2399 // Determine the length of data this Data Out PDU can carry.
2401 DataLen
= MIN (XferContext
->DesiredLength
, Conn
->MaxRecvDataSegmentLength
);
2404 // Create a Data Out PDU.
2406 DataOutPdu
= IScsiNewDataOutPdu (Data
, DataLen
, DataSN
, Tcb
, Lun
);
2407 if (DataOutPdu
== NULL
) {
2408 IScsiFreeNbufList (PduList
);
2414 InsertTailList (PduList
, &DataOutPdu
->List
);
2417 // Update the context and DataSN.
2420 XferContext
->Offset
+= DataLen
;
2421 XferContext
->DesiredLength
-= DataLen
;
2425 // Set the F bit for the last data out PDU in this sequence.
2427 DataOutPacket
= NetbufGetByte (DataOutPdu
, 0, NULL
);
2428 if (DataOutPacket
== NULL
) {
2429 IScsiFreeNbufList (PduList
);
2434 ISCSI_SET_FLAG (DataOutPacket
, ISCSI_BHS_FLAG_FINAL
);
2442 Send the Data in a sequence of Data Out PDUs one by one.
2444 @param[in] Data The data to carry by Data Out PDUs.
2445 @param[in] Lun The LUN the data will be sent to.
2446 @param[in] Tcb The task control block.
2448 @retval EFI_SUCCES The data is sent out to the LUN.
2449 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2450 @retval Others Other errors as indicated.
2454 IScsiSendDataOutPduSequence (
2460 LIST_ENTRY
*DataOutPduList
;
2466 // Generate the Data Out PDU sequence.
2468 DataOutPduList
= IScsiGenerateDataOutPduSequence (Data
, Tcb
, Lun
);
2469 if (DataOutPduList
== NULL
) {
2470 return EFI_OUT_OF_RESOURCES
;
2473 Status
= EFI_SUCCESS
;
2476 // Send the Data Out PDU's one by one.
2478 NET_LIST_FOR_EACH (Entry
, DataOutPduList
) {
2479 Pdu
= NET_LIST_USER_STRUCT (Entry
, NET_BUF
, List
);
2481 Status
= TcpIoTransmit (&Tcb
->Conn
->TcpIo
, Pdu
);
2483 if (EFI_ERROR (Status
)) {
2488 IScsiFreeNbufList (DataOutPduList
);
2495 Process the received iSCSI SCSI Data In PDU.
2497 @param[in] Pdu The Data In PDU received.
2498 @param[in] Tcb The task control block.
2499 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2501 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
2503 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2504 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2505 @retval Others Other errors as indicated.
2512 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2515 ISCSI_SCSI_DATA_IN
*DataInHdr
;
2518 DataInHdr
= (ISCSI_SCSI_DATA_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2519 if (DataInHdr
== NULL
) {
2520 return EFI_PROTOCOL_ERROR
;
2523 DataInHdr
->InitiatorTaskTag
= NTOHL (DataInHdr
->InitiatorTaskTag
);
2524 DataInHdr
->ExpCmdSN
= NTOHL (DataInHdr
->ExpCmdSN
);
2525 DataInHdr
->MaxCmdSN
= NTOHL (DataInHdr
->MaxCmdSN
);
2526 DataInHdr
->DataSN
= NTOHL (DataInHdr
->DataSN
);
2529 // Check the DataSN.
2531 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, DataInHdr
->DataSN
);
2532 if (EFI_ERROR (Status
)) {
2536 if (DataInHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2537 return EFI_PROTOCOL_ERROR
;
2540 // Update the command related sequence numbers.
2542 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, DataInHdr
->MaxCmdSN
, DataInHdr
->ExpCmdSN
);
2544 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID
)) {
2545 if (!ISCSI_FLAG_ON (DataInHdr
, ISCSI_BHS_FLAG_FINAL
)) {
2547 // The S bit is on but the F bit is off.
2549 return EFI_PROTOCOL_ERROR
;
2552 Tcb
->StatusXferd
= TRUE
;
2554 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_DATA_IN_PDU_FLAG_OVERFLOW
| SCSI_DATA_IN_PDU_FLAG_UNDERFLOW
)) {
2556 // Underflow and Overflow are mutual flags.
2558 return EFI_PROTOCOL_ERROR
;
2561 // S bit is on, the StatSN is valid.
2563 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NTOHL (DataInHdr
->StatSN
));
2564 if (EFI_ERROR (Status
)) {
2568 Packet
->HostAdapterStatus
= 0;
2569 Packet
->TargetStatus
= DataInHdr
->Status
;
2571 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2572 Packet
->InTransferLength
+= NTOHL (DataInHdr
->ResidualCount
);
2573 Status
= EFI_BAD_BUFFER_SIZE
;
2576 if (ISCSI_FLAG_ON (DataInHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2577 Packet
->InTransferLength
-= NTOHL (DataInHdr
->ResidualCount
);
2586 Process the received iSCSI R2T PDU.
2588 @param[in] Pdu The R2T PDU received.
2589 @param[in] Tcb The task control block.
2590 @param[in] Lun The Lun.
2591 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2593 @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.
2594 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2595 @retval Others Other errors as indicated.
2603 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2606 ISCSI_READY_TO_TRANSFER
*R2THdr
;
2608 ISCSI_XFER_CONTEXT
*XferContext
;
2611 R2THdr
= (ISCSI_READY_TO_TRANSFER
*) NetbufGetByte (Pdu
, 0, NULL
);
2612 if (R2THdr
== NULL
) {
2613 return EFI_PROTOCOL_ERROR
;
2616 R2THdr
->InitiatorTaskTag
= NTOHL (R2THdr
->InitiatorTaskTag
);
2617 R2THdr
->TargetTransferTag
= NTOHL (R2THdr
->TargetTransferTag
);
2618 R2THdr
->StatSN
= NTOHL (R2THdr
->StatSN
);
2619 R2THdr
->R2TSeqNum
= NTOHL (R2THdr
->R2TSeqNum
);
2620 R2THdr
->BufferOffset
= NTOHL (R2THdr
->BufferOffset
);
2621 R2THdr
->DesiredDataTransferLength
= NTOHL (R2THdr
->DesiredDataTransferLength
);
2623 if ((R2THdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) || !ISCSI_SEQ_EQ (R2THdr
->StatSN
, Tcb
->Conn
->ExpStatSN
)) {
2624 return EFI_PROTOCOL_ERROR
;;
2627 // Check the sequence number.
2629 Status
= IScsiCheckSN (&Tcb
->ExpDataSN
, R2THdr
->R2TSeqNum
);
2630 if (EFI_ERROR (Status
)) {
2634 XferContext
= &Tcb
->XferContext
;
2635 XferContext
->TargetTransferTag
= R2THdr
->TargetTransferTag
;
2636 XferContext
->Offset
= R2THdr
->BufferOffset
;
2637 XferContext
->DesiredLength
= R2THdr
->DesiredDataTransferLength
;
2639 if (((XferContext
->Offset
+ XferContext
->DesiredLength
) > Packet
->OutTransferLength
) ||
2640 (XferContext
->DesiredLength
> Tcb
->Conn
->Session
->MaxBurstLength
)
2642 return EFI_PROTOCOL_ERROR
;
2645 // Send the data solicited by this R2T.
2647 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2648 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2655 Process the received iSCSI SCSI Response PDU.
2657 @param[in] Pdu The Response PDU received.
2658 @param[in] Tcb The task control block.
2659 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2661 @retval EFI_SUCCES The Response PDU is processed.
2662 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2663 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2664 @retval Others Other errors as indicated.
2668 IScsiOnScsiRspRcvd (
2671 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2674 SCSI_RESPONSE
*ScsiRspHdr
;
2675 ISCSI_SENSE_DATA
*SenseData
;
2679 ScsiRspHdr
= (SCSI_RESPONSE
*) NetbufGetByte (Pdu
, 0, NULL
);
2680 if (ScsiRspHdr
== NULL
) {
2681 return EFI_PROTOCOL_ERROR
;
2684 ScsiRspHdr
->InitiatorTaskTag
= NTOHL (ScsiRspHdr
->InitiatorTaskTag
);
2685 if (ScsiRspHdr
->InitiatorTaskTag
!= Tcb
->InitiatorTaskTag
) {
2686 return EFI_PROTOCOL_ERROR
;
2689 ScsiRspHdr
->StatSN
= NTOHL (ScsiRspHdr
->StatSN
);
2691 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, ScsiRspHdr
->StatSN
);
2692 if (EFI_ERROR (Status
)) {
2696 ScsiRspHdr
->MaxCmdSN
= NTOHL (ScsiRspHdr
->MaxCmdSN
);
2697 ScsiRspHdr
->ExpCmdSN
= NTOHL (ScsiRspHdr
->ExpCmdSN
);
2698 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, ScsiRspHdr
->MaxCmdSN
, ScsiRspHdr
->ExpCmdSN
);
2700 Tcb
->StatusXferd
= TRUE
;
2702 Packet
->HostAdapterStatus
= ScsiRspHdr
->Response
;
2703 if (Packet
->HostAdapterStatus
!= ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET
) {
2707 Packet
->TargetStatus
= ScsiRspHdr
->Status
;
2709 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
| SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
) ||
2710 ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
| SCSI_RSP_PDU_FLAG_UNDERFLOW
)
2712 return EFI_PROTOCOL_ERROR
;
2715 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW
)) {
2716 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2717 Status
= EFI_BAD_BUFFER_SIZE
;
2720 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW
)) {
2721 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->BiReadResidualCount
);
2724 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_OVERFLOW
)) {
2725 if (Packet
->DataDirection
== DataIn
) {
2726 Packet
->InTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2728 Packet
->OutTransferLength
+= NTOHL (ScsiRspHdr
->ResidualCount
);
2731 Status
= EFI_BAD_BUFFER_SIZE
;
2734 if (ISCSI_FLAG_ON (ScsiRspHdr
, SCSI_RSP_PDU_FLAG_UNDERFLOW
)) {
2735 if (Packet
->DataDirection
== DataIn
) {
2736 Packet
->InTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2738 Packet
->OutTransferLength
-= NTOHL (ScsiRspHdr
->ResidualCount
);
2742 DataSegLen
= ISCSI_GET_DATASEG_LEN (ScsiRspHdr
);
2743 if (DataSegLen
!= 0) {
2744 SenseData
= (ISCSI_SENSE_DATA
*) NetbufGetByte (Pdu
, sizeof (SCSI_RESPONSE
), NULL
);
2745 if (SenseData
== NULL
) {
2746 return EFI_PROTOCOL_ERROR
;
2749 SenseData
->Length
= NTOHS (SenseData
->Length
);
2751 Packet
->SenseDataLength
= (UINT8
) MIN (SenseData
->Length
, Packet
->SenseDataLength
);
2752 if (Packet
->SenseDataLength
!= 0) {
2753 CopyMem (Packet
->SenseData
, &SenseData
->Data
[0], Packet
->SenseDataLength
);
2756 Packet
->SenseDataLength
= 0;
2764 Process the received NOP In PDU.
2766 @param[in] Pdu The NOP In PDU received.
2767 @param[in] Tcb The task control block.
2769 @retval EFI_SUCCES The NOP In PDU is processed and the related sequence
2770 numbers are updated.
2771 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2780 ISCSI_NOP_IN
*NopInHdr
;
2783 NopInHdr
= (ISCSI_NOP_IN
*) NetbufGetByte (Pdu
, 0, NULL
);
2784 if (NopInHdr
== NULL
) {
2785 return EFI_PROTOCOL_ERROR
;
2788 NopInHdr
->StatSN
= NTOHL (NopInHdr
->StatSN
);
2789 NopInHdr
->ExpCmdSN
= NTOHL (NopInHdr
->ExpCmdSN
);
2790 NopInHdr
->MaxCmdSN
= NTOHL (NopInHdr
->MaxCmdSN
);
2792 if (NopInHdr
->InitiatorTaskTag
== ISCSI_RESERVED_TAG
) {
2793 if (NopInHdr
->StatSN
!= Tcb
->Conn
->ExpStatSN
) {
2794 return EFI_PROTOCOL_ERROR
;
2797 Status
= IScsiCheckSN (&Tcb
->Conn
->ExpStatSN
, NopInHdr
->StatSN
);
2798 if (EFI_ERROR (Status
)) {
2803 IScsiUpdateCmdSN (Tcb
->Conn
->Session
, NopInHdr
->MaxCmdSN
, NopInHdr
->ExpCmdSN
);
2810 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
2812 @param[in] PassThru The EXT SCSI PASS THRU protocol.
2813 @param[in] Target The target ID.
2814 @param[in] Lun The LUN.
2815 @param[in, out] Packet The request packet containing IO request, SCSI command
2816 buffer and buffers to read/write.
2818 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
2820 @retval EFI_DEVICE_ERROR Session state was not as required.
2821 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2822 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
2823 @retval EFI_NOT_READY The target can not accept new commands.
2824 @retval Others Other errors as indicated.
2828 IScsiExecuteScsiCommand (
2829 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL
*PassThru
,
2832 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET
*Packet
2836 ISCSI_DRIVER_DATA
*Private
;
2837 ISCSI_SESSION
*Session
;
2838 EFI_EVENT TimeoutEvent
;
2839 ISCSI_CONNECTION
*Conn
;
2842 ISCSI_XFER_CONTEXT
*XferContext
;
2844 ISCSI_IN_BUFFER_CONTEXT InBufferContext
;
2848 Private
= ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru
);
2849 Session
= Private
->Session
;
2850 Status
= EFI_SUCCESS
;
2852 TimeoutEvent
= NULL
;
2855 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
2856 Status
= EFI_DEVICE_ERROR
;
2860 Conn
= NET_LIST_USER_STRUCT_S (
2861 Session
->Conns
.ForwardLink
,
2864 ISCSI_CONNECTION_SIGNATURE
2867 if (Packet
->Timeout
!= 0) {
2868 Timeout
= MultU64x32 (Packet
->Timeout
, 4);
2871 Status
= IScsiNewTcb (Conn
, &Tcb
);
2872 if (EFI_ERROR (Status
)) {
2876 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2878 Pdu
= IScsiNewScsiCmdPdu (Packet
, Lun
, Tcb
);
2880 Status
= EFI_OUT_OF_RESOURCES
;
2884 XferContext
= &Tcb
->XferContext
;
2885 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2886 if (PduHdr
== NULL
) {
2887 Status
= EFI_PROTOCOL_ERROR
;
2891 XferContext
->Offset
= ISCSI_GET_DATASEG_LEN (PduHdr
);
2894 // Transmit the SCSI Command PDU.
2896 Status
= TcpIoTransmit (&Conn
->TcpIo
, Pdu
);
2900 if (EFI_ERROR (Status
)) {
2904 if (!Session
->InitialR2T
&&
2905 (XferContext
->Offset
< Session
->FirstBurstLength
) &&
2906 (XferContext
->Offset
< Packet
->OutTransferLength
)
2909 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2910 // OUT data, and the limit of FirstBurstLength is not reached.
2912 XferContext
->TargetTransferTag
= ISCSI_RESERVED_TAG
;
2913 XferContext
->DesiredLength
= MIN (
2914 Session
->FirstBurstLength
,
2915 Packet
->OutTransferLength
- XferContext
->Offset
2918 Data
= (UINT8
*) Packet
->OutDataBuffer
+ XferContext
->Offset
;
2919 Status
= IScsiSendDataOutPduSequence (Data
, Lun
, Tcb
);
2920 if (EFI_ERROR (Status
)) {
2925 InBufferContext
.InData
= (UINT8
*) Packet
->InDataBuffer
;
2926 InBufferContext
.InDataLen
= Packet
->InTransferLength
;
2928 while (!Tcb
->StatusXferd
) {
2930 // Start the timeout timer.
2933 Status
= gBS
->SetTimer (Conn
->TimeoutEvent
, TimerRelative
, Timeout
);
2934 if (EFI_ERROR (Status
)) {
2938 TimeoutEvent
= Conn
->TimeoutEvent
;
2942 // Try to receive PDU from target.
2944 Status
= IScsiReceivePdu (Conn
, &Pdu
, &InBufferContext
, FALSE
, FALSE
, TimeoutEvent
);
2945 if (EFI_ERROR (Status
)) {
2949 PduHdr
= NetbufGetByte (Pdu
, 0, NULL
);
2950 if (PduHdr
== NULL
) {
2951 Status
= EFI_PROTOCOL_ERROR
;
2955 switch (ISCSI_GET_OPCODE (PduHdr
)) {
2956 case ISCSI_OPCODE_SCSI_DATA_IN
:
2957 Status
= IScsiOnDataInRcvd (Pdu
, Tcb
, Packet
);
2960 case ISCSI_OPCODE_R2T
:
2961 Status
= IScsiOnR2TRcvd (Pdu
, Tcb
, Lun
, Packet
);
2964 case ISCSI_OPCODE_SCSI_RSP
:
2965 Status
= IScsiOnScsiRspRcvd (Pdu
, Tcb
, Packet
);
2968 case ISCSI_OPCODE_NOP_IN
:
2969 Status
= IScsiOnNopInRcvd (Pdu
, Tcb
);
2972 case ISCSI_OPCODE_VENDOR_T0
:
2973 case ISCSI_OPCODE_VENDOR_T1
:
2974 case ISCSI_OPCODE_VENDOR_T2
:
2976 // These messages are vendor specific. Skip them.
2981 Status
= EFI_PROTOCOL_ERROR
;
2987 if (EFI_ERROR (Status
)) {
2994 if (TimeoutEvent
!= NULL
) {
2995 gBS
->SetTimer (TimeoutEvent
, TimerCancel
, 0);
3007 Reinstate the session on some error.
3009 @param[in] Session The iSCSI session
3011 @retval EFI_SUCCESS The session is reinstated from some error.
3012 @retval Other Reinstatement failed.
3016 IScsiSessionReinstatement (
3017 IN ISCSI_SESSION
*Session
3022 ASSERT (Session
->State
!= SESSION_STATE_FREE
);
3025 // Abort the session and re-init it.
3027 IScsiSessionAbort (Session
);
3028 IScsiSessionInit (Session
, TRUE
);
3033 Status
= IScsiSessionLogin (Session
);
3040 Initialize some session parameters before login.
3042 @param[in, out] Session The iSCSI session.
3043 @param[in] Recovery Whether the request is from a fresh new start or recovery.
3048 IN OUT ISCSI_SESSION
*Session
,
3053 Session
->Signature
= ISCSI_SESSION_SIGNATURE
;
3054 Session
->State
= SESSION_STATE_FREE
;
3056 InitializeListHead (&Session
->Conns
);
3057 InitializeListHead (&Session
->TcbList
);
3063 Session
->InitiatorTaskTag
= 1;
3064 Session
->NextCid
= 1;
3066 Session
->TargetPortalGroupTag
= 0;
3067 Session
->MaxConnections
= ISCSI_MAX_CONNS_PER_SESSION
;
3068 Session
->InitialR2T
= FALSE
;
3069 Session
->ImmediateData
= TRUE
;
3070 Session
->MaxBurstLength
= 262144;
3071 Session
->FirstBurstLength
= MAX_RECV_DATA_SEG_LEN_IN_FFP
;
3072 Session
->DefaultTime2Wait
= 2;
3073 Session
->DefaultTime2Retain
= 20;
3074 Session
->MaxOutstandingR2T
= DEFAULT_MAX_OUTSTANDING_R2T
;
3075 Session
->DataPDUInOrder
= TRUE
;
3076 Session
->DataSequenceInOrder
= TRUE
;
3077 Session
->ErrorRecoveryLevel
= 0;
3082 Abort the iSCSI session. That is, reset all the connection(s), and free the
3085 @param[in, out] Session The iSCSI session.
3090 IN OUT ISCSI_SESSION
*Session
3093 ISCSI_CONNECTION
*Conn
;
3094 EFI_GUID
*ProtocolGuid
;
3096 if (Session
->State
!= SESSION_STATE_LOGGED_IN
) {
3100 ASSERT (!IsListEmpty (&Session
->Conns
));
3102 while (!IsListEmpty (&Session
->Conns
)) {
3103 Conn
= NET_LIST_USER_STRUCT_S (
3104 Session
->Conns
.ForwardLink
,
3107 ISCSI_CONNECTION_SIGNATURE
3109 if (!Conn
->Ipv6Flag
) {
3110 ProtocolGuid
= &gEfiTcp4ProtocolGuid
;
3112 ProtocolGuid
= &gEfiTcp6ProtocolGuid
;
3115 gBS
->CloseProtocol (
3118 Session
->Private
->Image
,
3119 Session
->Private
->ExtScsiPassThruHandle
3122 IScsiConnReset (Conn
);
3124 IScsiDetatchConnection (Conn
);
3125 IScsiDestroyConnection (Conn
);
3128 Session
->State
= SESSION_STATE_FAILED
;