]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiProto.c
Re-execute the failed SCSI command if iSCSI driver could reinstates the session succe...
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiProto.c
1 /** @file
2 The implementation of iSCSI protocol based on RFC3720.
3
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
9
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.
12
13 **/
14
15 #include "IScsiImpl.h"
16
17 UINT32 mDataSegPad = 0;
18
19 /**
20 Attach the iSCSI connection to the iSCSI session.
21
22 @param[in, out] Session The iSCSI session.
23 @param[in, out] Conn The iSCSI connection.
24
25 **/
26 VOID
27 IScsiAttatchConnection (
28 IN OUT ISCSI_SESSION *Session,
29 IN OUT ISCSI_CONNECTION *Conn
30 )
31 {
32 InsertTailList (&Session->Conns, &Conn->Link);
33 Conn->Session = Session;
34 Session->NumConns++;
35 }
36
37 /**
38 Detach the iSCSI connection from the session it belongs to.
39
40 @param[in, out] Conn The iSCSI connection.
41
42 **/
43 VOID
44 IScsiDetatchConnection (
45 IN OUT ISCSI_CONNECTION *Conn
46 )
47 {
48 RemoveEntryList (&Conn->Link);
49 Conn->Session->NumConns--;
50 Conn->Session = NULL;
51 }
52
53
54 /**
55 Check the sequence number according to RFC3720.
56
57 @param[in, out] ExpSN The currently expected sequence number.
58 @param[in] NewSN The sequence number to check.
59
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.
63
64 **/
65 EFI_STATUS
66 IScsiCheckSN (
67 IN OUT UINT32 *ExpSN,
68 IN UINT32 NewSN
69 )
70 {
71 if (!ISCSI_SEQ_EQ (NewSN, *ExpSN)) {
72 if (ISCSI_SEQ_LT (NewSN, *ExpSN)) {
73 //
74 // Duplicate
75 //
76 return EFI_NOT_READY;
77 } else {
78 return EFI_PROTOCOL_ERROR;
79 }
80 } else {
81 //
82 // Advance the ExpSN
83 //
84 (*ExpSN)++;
85 return EFI_SUCCESS;
86 }
87 }
88
89
90 /**
91 Update the sequence numbers for the iSCSI command.
92
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.
96
97 **/
98 VOID
99 IScsiUpdateCmdSN (
100 IN OUT ISCSI_SESSION *Session,
101 IN UINT32 MaxCmdSN,
102 IN UINT32 ExpCmdSN
103 )
104 {
105 if (ISCSI_SEQ_LT (MaxCmdSN, ExpCmdSN - 1)) {
106 return ;
107 }
108
109 if (ISCSI_SEQ_GT (MaxCmdSN, Session->MaxCmdSN)) {
110 Session->MaxCmdSN = MaxCmdSN;
111 }
112
113 if (ISCSI_SEQ_GT (ExpCmdSN, Session->ExpCmdSN)) {
114 Session->ExpCmdSN = ExpCmdSN;
115 }
116 }
117
118
119 /**
120 This function does the iSCSI connection login.
121
122 @param[in, out] Conn The iSCSI connection to login.
123 @param Timeout The timeout value in millisecond.
124
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.
128
129 **/
130 EFI_STATUS
131 IScsiConnLogin (
132 IN OUT ISCSI_CONNECTION *Conn,
133 IN UINT16 Timeout
134 )
135 {
136 EFI_STATUS Status;
137
138 //
139 // Start the timer, and wait Timeout seconds to establish the TCP connection.
140 //
141 Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout * TICKS_PER_MS);
142 if (EFI_ERROR (Status)) {
143 return Status;
144 }
145
146 //
147 // Try to establish the tcp connection.
148 //
149 Status = TcpIoConnect (&Conn->TcpIo, Conn->TimeoutEvent);
150 gBS->SetTimer (Conn->TimeoutEvent, TimerCancel, 0);
151
152 if (EFI_ERROR (Status)) {
153 return Status;
154 }
155
156 Conn->State = CONN_STATE_IN_LOGIN;
157
158 //
159 // Connection is established, start the iSCSI Login.
160 //
161 do {
162 Status = IScsiSendLoginReq (Conn);
163 if (EFI_ERROR (Status)) {
164 break;
165 }
166
167 Status = IScsiReceiveLoginRsp (Conn);
168 if (EFI_ERROR (Status)) {
169 break;
170 }
171 } while (Conn->CurrentStage != ISCSI_FULL_FEATURE_PHASE);
172
173 return Status;
174 }
175
176
177 /**
178 Reset the iSCSI connection.
179
180 @param[in, out] Conn The iSCSI connection to reset.
181
182 **/
183 VOID
184 IScsiConnReset (
185 IN OUT ISCSI_CONNECTION *Conn
186 )
187 {
188 TcpIoReset (&Conn->TcpIo);
189 }
190
191
192 /**
193 Create a TCP connection for the iSCSI session.
194
195 @param[in] Session Points to the iSCSI session.
196
197 @return The newly created iSCSI connection.
198
199 **/
200 ISCSI_CONNECTION *
201 IScsiCreateConnection (
202 IN ISCSI_SESSION *Session
203 )
204 {
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;
211 EFI_STATUS Status;
212
213 Private = Session->Private;
214 NvData = &Session->ConfigData->SessionConfigData;
215
216 Conn = AllocateZeroPool (sizeof (ISCSI_CONNECTION));
217 if (Conn == NULL) {
218 return NULL;
219 }
220
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;
226 Conn->ExpStatSN = 0;
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;
232
233 Status = gBS->CreateEvent (
234 EVT_TIMER,
235 TPL_CALLBACK,
236 NULL,
237 NULL,
238 &Conn->TimeoutEvent
239 );
240 if (EFI_ERROR (Status)) {
241 FreePool (Conn);
242 return NULL;
243 }
244
245 NetbufQueInit (&Conn->RspQue);
246
247 //
248 // Set the default connection-only parameters.
249 //
250 Conn->MaxRecvDataSegmentLength = DEFAULT_MAX_RECV_DATA_SEG_LEN;
251 Conn->HeaderDigest = IScsiDigestNone;
252 Conn->DataDigest = IScsiDigestNone;
253
254 if (!Conn->Ipv6Flag) {
255 Tcp4IoConfig = &TcpIoConfig.Tcp4IoConfigData;
256
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));
261
262 Tcp4IoConfig->RemotePort = NvData->TargetPort;
263 Tcp4IoConfig->ActiveFlag = TRUE;
264 Tcp4IoConfig->StationPort = 0;
265 } else {
266 Tcp6IoConfig = &TcpIoConfig.Tcp6IoConfigData;
267
268 CopyMem (&Tcp6IoConfig->RemoteIp, &NvData->TargetIp, sizeof (EFI_IPv6_ADDRESS));
269 Tcp6IoConfig->RemotePort = NvData->TargetPort;
270 Tcp6IoConfig->ActiveFlag = TRUE;
271 Tcp6IoConfig->StationPort = 0;
272 }
273
274 //
275 // Create the TCP IO for this connection.
276 //
277 Status = TcpIoCreateSocket (
278 Private->Image,
279 Private->Controller,
280 (UINT8) (!Conn->Ipv6Flag ? TCP_VERSION_4: TCP_VERSION_6),
281 &TcpIoConfig,
282 &Conn->TcpIo
283 );
284 if (EFI_ERROR (Status)) {
285 gBS->CloseEvent (Conn->TimeoutEvent);
286 FreePool (Conn);
287 Conn = NULL;
288 }
289
290 return Conn;
291 }
292
293
294 /**
295 Destroy an iSCSI connection.
296
297 @param[in] Conn The connection to destroy.
298
299 **/
300 VOID
301 IScsiDestroyConnection (
302 IN ISCSI_CONNECTION *Conn
303 )
304 {
305 TcpIoDestroySocket (&Conn->TcpIo);
306
307 NetbufQueFlush (&Conn->RspQue);
308 gBS->CloseEvent (Conn->TimeoutEvent);
309 FreePool (Conn);
310 }
311
312 /**
313 Retrieve the IPv6 Address/Prefix/Gateway from the established TCP connection, these informations
314 will be filled in the iSCSI Boot Firmware Table.
315
316 @param[in] Conn The connection used in the iSCSI login phase.
317
318 @retval EFI_SUCCESS Get the NIC information successfully.
319 @retval Others Other errors as indicated.
320
321 **/
322 EFI_STATUS
323 IScsiGetIp6NicInfo (
324 IN ISCSI_CONNECTION *Conn
325 )
326 {
327 ISCSI_SESSION_CONFIG_NVDATA *NvData;
328 EFI_TCP6_PROTOCOL *Tcp6;
329 EFI_IP6_MODE_DATA Ip6ModeData;
330 EFI_STATUS Status;
331 EFI_IPv6_ADDRESS *TargetIp;
332 UINTN Index;
333 UINT8 SubnetPrefixLength;
334 UINTN RouteEntry;
335
336 NvData = &Conn->Session->ConfigData->SessionConfigData;
337 TargetIp = &NvData->TargetIp.v6;
338 Tcp6 = Conn->TcpIo.Tcp.Tcp6;
339
340 ZeroMem (&Ip6ModeData, sizeof (EFI_IP6_MODE_DATA));
341 Status = Tcp6->GetModeData (
342 Tcp6,
343 NULL,
344 NULL,
345 &Ip6ModeData,
346 NULL,
347 NULL
348 );
349 if (EFI_ERROR (Status)) {
350 return Status;
351 }
352
353 if (!Ip6ModeData.IsConfigured) {
354 Status = EFI_ABORTED;
355 goto ON_EXIT;
356 }
357
358 IP6_COPY_ADDRESS (&NvData->LocalIp, &Ip6ModeData.ConfigData.StationAddress);
359
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;
364 break;
365 }
366 }
367
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;
374 RouteEntry = Index;
375 }
376 }
377 }
378 if (RouteEntry != Ip6ModeData.RouteCount) {
379 IP6_COPY_ADDRESS (&NvData->Gateway, &Ip6ModeData.RouteTable[RouteEntry].Gateway);
380 }
381
382 ON_EXIT:
383 if (Ip6ModeData.AddressList != NULL) {
384 FreePool (Ip6ModeData.AddressList);
385 }
386 if (Ip6ModeData.GroupTable!= NULL) {
387 FreePool (Ip6ModeData.GroupTable);
388 }
389 if (Ip6ModeData.RouteTable!= NULL) {
390 FreePool (Ip6ModeData.RouteTable);
391 }
392 if (Ip6ModeData.NeighborCache!= NULL) {
393 FreePool (Ip6ModeData.NeighborCache);
394 }
395 if (Ip6ModeData.PrefixTable!= NULL) {
396 FreePool (Ip6ModeData.PrefixTable);
397 }
398 if (Ip6ModeData.IcmpTypeList!= NULL) {
399 FreePool (Ip6ModeData.IcmpTypeList);
400 }
401
402 return Status;
403 }
404
405 /**
406 Login the iSCSI session.
407
408 @param[in] Session The iSCSI session.
409
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.
414
415 **/
416 EFI_STATUS
417 IScsiSessionLogin (
418 IN ISCSI_SESSION *Session
419 )
420 {
421 EFI_STATUS Status;
422 ISCSI_CONNECTION *Conn;
423 VOID *Tcp;
424 EFI_GUID *ProtocolGuid;
425 UINT8 RetryCount;
426 BOOLEAN MediaPresent;
427
428 //
429 // Check media status before session login.
430 //
431 MediaPresent = TRUE;
432 NetLibDetectMedia (Session->Private->Controller, &MediaPresent);
433 if (!MediaPresent) {
434 return EFI_NO_MEDIA;
435 }
436
437 //
438 // Set session identifier
439 //
440 CopyMem (Session->Isid, Session->ConfigData->SessionConfigData.IsId, 6);
441
442 RetryCount = 0;
443
444 do {
445 //
446 // Create a connection for the session.
447 //
448 Conn = IScsiCreateConnection (Session);
449 if (Conn == NULL) {
450 return EFI_OUT_OF_RESOURCES;
451 }
452
453 IScsiAttatchConnection (Session, Conn);
454
455 //
456 // Login througth the newly created connection.
457 //
458 Status = IScsiConnLogin (Conn, Session->ConfigData->SessionConfigData.ConnectTimeout);
459 if (EFI_ERROR (Status)) {
460 IScsiConnReset (Conn);
461 IScsiDetatchConnection (Conn);
462 IScsiDestroyConnection (Conn);
463 }
464
465 if (Status != EFI_TIMEOUT) {
466 break;
467 }
468
469 RetryCount++;
470 } while (RetryCount <= Session->ConfigData->SessionConfigData.ConnectRetryCount);
471
472 if (!EFI_ERROR (Status)) {
473 Session->State = SESSION_STATE_LOGGED_IN;
474
475 if (!Conn->Ipv6Flag) {
476 ProtocolGuid = &gEfiTcp4ProtocolGuid;
477 } else {
478 ProtocolGuid = &gEfiTcp6ProtocolGuid;
479 }
480
481 Status = gBS->OpenProtocol (
482 Conn->TcpIo.Handle,
483 ProtocolGuid,
484 (VOID **) &Tcp,
485 Session->Private->Image,
486 Session->Private->ExtScsiPassThruHandle,
487 EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
488 );
489
490 ASSERT_EFI_ERROR (Status);
491
492 if (Conn->Ipv6Flag) {
493 Status = IScsiGetIp6NicInfo (Conn);
494 }
495 }
496
497 return Status;
498 }
499
500
501 /**
502 Wait for IPsec negotiation, then try to login the iSCSI session again.
503
504 @param[in] Session The iSCSI session.
505
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.
509
510 **/
511 EFI_STATUS
512 IScsiSessionReLogin (
513 IN ISCSI_SESSION *Session
514 )
515 {
516
517 EFI_STATUS Status;
518 EFI_STATUS TimerStatus;
519 EFI_EVENT Timer;
520
521 Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
522 if (EFI_ERROR (Status)) {
523 return Status;
524 }
525
526 Status = gBS->SetTimer (
527 Timer,
528 TimerRelative,
529 ISCSI_WAIT_IPSEC_TIMEOUT
530 );
531
532 if (EFI_ERROR (Status)) {
533 gBS->CloseEvent (Timer);
534 return Status;
535 }
536
537 do {
538
539 TimerStatus = gBS->CheckEvent (Timer);
540
541 if (!EFI_ERROR (TimerStatus)) {
542 Status = IScsiSessionLogin (Session);
543 }
544
545 } while (TimerStatus == EFI_NOT_READY);
546
547 gBS->CloseEvent (Timer);
548 return Status;
549 }
550
551
552 /**
553 Build and send the iSCSI login request to the iSCSI target according to
554 the current login stage.
555
556 @param[in] Conn The connection in the iSCSI login phase.
557
558 @retval EFI_SUCCESS The iSCSI login request PDU is built and sent on this
559 connection.
560 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
561 @retval EFI_DEVICE_ERROR Some kind of device error occurred.
562
563 **/
564 EFI_STATUS
565 IScsiSendLoginReq (
566 IN ISCSI_CONNECTION *Conn
567 )
568 {
569 NET_BUF *Pdu;
570 EFI_STATUS Status;
571
572 //
573 // Build the Login Request PDU.
574 //
575 Pdu = IScsiPrepareLoginReq (Conn);
576 if (Pdu == NULL) {
577 return EFI_DEVICE_ERROR;
578 }
579 //
580 // Send it to the iSCSI target.
581 //
582 Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
583
584 NetbufFree (Pdu);
585
586 return Status;
587 }
588
589
590 /**
591 Receive and process the iSCSI login response.
592
593 @param[in] Conn The connection in the iSCSI login phase.
594
595 @retval EFI_SUCCESS The iSCSI login response PDU is received and processed.
596 @retval Others Other errors as indicated.
597
598 **/
599 EFI_STATUS
600 IScsiReceiveLoginRsp (
601 IN ISCSI_CONNECTION *Conn
602 )
603 {
604 EFI_STATUS Status;
605 NET_BUF *Pdu;
606
607 //
608 // Receive the iSCSI login response.
609 //
610 Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL);
611 if (EFI_ERROR (Status)) {
612 return Status;
613 }
614 ASSERT (Pdu != NULL);
615
616 //
617 // A Login Response is received; process it.
618 //
619 Status = IScsiProcessLoginRsp (Conn, Pdu);
620
621 NetbufFree (Pdu);
622
623 return Status;
624 }
625
626
627 /**
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
630 updated.
631
632 @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
633 be added to.
634 @param[in] Key The key name string.
635 @param[in] Value The value string.
636
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
640 pair.
641 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
642 **/
643 EFI_STATUS
644 IScsiAddKeyValuePair (
645 IN OUT NET_BUF *Pdu,
646 IN CHAR8 *Key,
647 IN CHAR8 *Value
648 )
649 {
650 UINT32 DataSegLen;
651 UINT32 KeyLen;
652 UINT32 ValueLen;
653 UINT32 TotalLen;
654 ISCSI_LOGIN_REQUEST *LoginReq;
655 CHAR8 *Data;
656
657 LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL);
658 if (LoginReq == NULL) {
659 return EFI_PROTOCOL_ERROR;
660 }
661 DataSegLen = NTOH24 (LoginReq->DataSegmentLength);
662
663 KeyLen = (UINT32) AsciiStrLen (Key);
664 ValueLen = (UINT32) AsciiStrLen (Value);
665
666 //
667 // 1 byte for the key value separator '=' and 1 byte for the null
668 // delimiter after the value.
669 //
670 TotalLen = KeyLen + 1 + ValueLen + 1;
671
672 //
673 // Allocate the space for the key-value pair.
674 //
675 Data = (CHAR8 *) NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL);
676 if (Data == NULL) {
677 return EFI_OUT_OF_RESOURCES;
678 }
679 //
680 // Add the key.
681 //
682 CopyMem (Data, Key, KeyLen);
683 Data += KeyLen;
684
685 *Data = '=';
686 Data++;
687
688 //
689 // Add the value.
690 //
691 CopyMem (Data, Value, ValueLen);
692 Data += ValueLen;
693
694 *Data = '\0';
695
696 //
697 // Update the DataSegmentLength
698 //
699 ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen);
700
701 return EFI_SUCCESS;
702 }
703
704
705 /**
706 Prepare the iSCSI login request to be sent according to the current login status.
707
708 @param[in, out] Conn The connection in the iSCSI login phase.
709
710 @return The pointer to the net buffer containing the iSCSI login request built.
711 @retval NULL Other errors as indicated.
712
713 **/
714 NET_BUF *
715 IScsiPrepareLoginReq (
716 IN OUT ISCSI_CONNECTION *Conn
717 )
718 {
719 ISCSI_SESSION *Session;
720 NET_BUF *Nbuf;
721 ISCSI_LOGIN_REQUEST *LoginReq;
722 EFI_STATUS Status;
723
724 Session = Conn->Session;
725
726 Nbuf = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN);
727 if (Nbuf == NULL) {
728 return NULL;
729 }
730
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));
734
735 //
736 // Init the login request pdu
737 //
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);
746
747 //
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
750 // a connection.
751 // For subsequent Login Requests it is used to acknowledge the Login Responses
752 // with their increasing StatSN values.
753 //
754 LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN);
755 CopyMem (LoginReq->Isid, Session->Isid, sizeof (LoginReq->Isid));
756
757 if (Conn->PartialRspRcvd) {
758 //
759 // A partial response. The initiator must send an empty Login Request.
760 //
761 return Nbuf;
762 }
763
764 Status = EFI_SUCCESS;
765
766 switch (Conn->CurrentStage) {
767 case ISCSI_SECURITY_NEGOTIATION:
768 //
769 // Both none authentication and CHAP authentication share the CHAP path.
770 //
771 //
772 if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
773 Status = IScsiCHAPToSendReq (Conn, Nbuf);
774 }
775
776 break;
777
778 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
779 //
780 // Only negotiate the paramter once.
781 //
782 if (!Conn->ParamNegotiated) {
783 IScsiFillOpParams (Conn, Nbuf);
784 }
785
786 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
787 break;
788
789 default:
790 //
791 // An error occurs...
792 //
793 Status = EFI_DEVICE_ERROR;
794 break;
795 }
796
797 if (EFI_ERROR (Status)) {
798 NetbufFree (Nbuf);
799 Nbuf = NULL;
800 } else {
801 //
802 // Pad the data segment if needed.
803 //
804 IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq));
805 //
806 // Check whether we will issue the stage transition signal?
807 //
808 Conn->TransitInitiated = ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
809 }
810
811 return Nbuf;
812 }
813
814
815 /**
816 Process the iSCSI Login Response.
817
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.
820
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.
825
826 **/
827 EFI_STATUS
828 IScsiProcessLoginRsp (
829 IN OUT ISCSI_CONNECTION *Conn,
830 IN OUT NET_BUF *Pdu
831 )
832 {
833 EFI_STATUS Status;
834 ISCSI_SESSION *Session;
835 ISCSI_LOGIN_RESPONSE *LoginRsp;
836 BOOLEAN Transit;
837 BOOLEAN Continue;
838 UINT8 CurrentStage;
839 UINT8 NextStage;
840 UINT8 *DataSeg;
841 UINT32 DataSegLen;
842
843 Status = EFI_SUCCESS;
844 Session = Conn->Session;
845
846 LoginRsp = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
847 if (LoginRsp == NULL) {
848 return EFI_PROTOCOL_ERROR;
849 }
850 if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) {
851 //
852 // It is not a Login Response.
853 //
854 return EFI_PROTOCOL_ERROR;
855 }
856 //
857 // Get the data segment, if any.
858 //
859 DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp);
860 if (DataSegLen != 0) {
861 DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL);
862 } else {
863 DataSeg = NULL;
864 }
865 //
866 // Check the status class in the login response PDU.
867 //
868 switch (LoginRsp->StatusClass) {
869 case ISCSI_LOGIN_STATUS_SUCCESS:
870 //
871 // Just break here; the response and the data segment will be processed later.
872 //
873 break;
874
875 case ISCSI_LOGIN_STATUS_REDIRECTION:
876 //
877 // The target may be moved to a different address.
878 //
879 if (DataSeg == NULL) {
880 return EFI_PROTOCOL_ERROR;
881 }
882 //
883 // Process the TargetAddress key-value strings in the data segment to update the
884 // target address info.
885 //
886 Status = IScsiUpdateTargetAddress (Session, (CHAR8 *) DataSeg, DataSegLen);
887 if (EFI_ERROR (Status)) {
888 return Status;
889 }
890 //
891 // Session will be restarted on this error status because the Target is
892 // redirected by this Login Response.
893 //
894 return EFI_MEDIA_CHANGED;
895
896 default:
897 //
898 // Initiator Error, Target Error, or any other undefined error code.
899 //
900 return EFI_PROTOCOL_ERROR;
901 }
902 //
903 // The status is success; extract the wanted fields from the header segment.
904 //
905 Transit = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT);
906 Continue = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE);
907
908 CurrentStage = ISCSI_GET_CURRENT_STAGE (LoginRsp);
909 NextStage = ISCSI_GET_NEXT_STAGE (LoginRsp);
910
911 LoginRsp->InitiatorTaskTag = NTOHL (LoginRsp->InitiatorTaskTag);
912
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)
919 ) {
920 //
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.
927 //
928 return EFI_PROTOCOL_ERROR;
929 }
930
931 LoginRsp->StatSN = NTOHL (LoginRsp->StatSN);
932 LoginRsp->ExpCmdSN = NTOHL (LoginRsp->ExpCmdSN);
933 LoginRsp->MaxCmdSN = NTOHL (LoginRsp->MaxCmdSN);
934
935 if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->AuthStep == ISCSI_AUTH_INITIAL)) {
936 //
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.
939 //
940 if ((Session->State == SESSION_STATE_FREE) && (Session->CmdSN != LoginRsp->ExpCmdSN)) {
941 return EFI_PROTOCOL_ERROR;
942 }
943
944 //
945 // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
946 // and ExpCmdSN.
947 //
948 Conn->ExpStatSN = LoginRsp->StatSN + 1;
949 Session->MaxCmdSN = LoginRsp->MaxCmdSN;
950 Session->ExpCmdSN = LoginRsp->ExpCmdSN;
951 } else {
952 //
953 // Check the StatSN of this PDU.
954 //
955 Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN);
956 if (!EFI_ERROR (Status)) {
957 //
958 // Update the MaxCmdSN and ExpCmdSN.
959 //
960 IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN);
961 } else {
962 return Status;
963 }
964 }
965 //
966 // Trim off the header segment.
967 //
968 NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD);
969
970 //
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.
974 //
975 NET_GET_REF (Pdu);
976 NetbufQueAppend (&Conn->RspQue, Pdu);
977
978 Conn->PartialRspRcvd = Continue;
979 if (Continue) {
980 //
981 // It is a partial response; must wait for another or more Request/Response
982 // conversations to get the full response.
983 //
984 return EFI_SUCCESS;
985 }
986
987 switch (CurrentStage) {
988 case ISCSI_SECURITY_NEGOTIATION:
989 //
990 // In security negotiation stage, let CHAP module handle it.
991 //
992 if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
993 Status = IScsiCHAPOnRspReceived (Conn);
994 }
995 break;
996
997 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
998 //
999 // Response received with negotiation response on iSCSI parameters: check them.
1000 //
1001 Status = IScsiCheckOpParams (Conn);
1002 if (!EFI_ERROR (Status)) {
1003 Conn->ParamNegotiated = TRUE;
1004 }
1005
1006 break;
1007
1008 default:
1009 //
1010 // Should never get here.
1011 //
1012 Status = EFI_PROTOCOL_ERROR;
1013 break;
1014 }
1015
1016 if (Transit && (Status == EFI_SUCCESS)) {
1017 //
1018 // Do the state transition.
1019 //
1020 Conn->CurrentStage = Conn->NextStage;
1021
1022 if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) {
1023 Conn->NextStage = ISCSI_FULL_FEATURE_PHASE;
1024 } else {
1025 //
1026 // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
1027 // get the TSIH from the Login Response.
1028 //
1029 Session->Tsih = NTOHS (LoginRsp->Tsih);
1030 }
1031 }
1032 //
1033 // Flush the response(s) received.
1034 //
1035 NetbufQueFlush (&Conn->RspQue);
1036
1037 return Status;
1038 }
1039
1040
1041 /**
1042 Updated the target information according the data received in the iSCSI
1043 login response with an target redirection status.
1044
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.
1049
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.
1054
1055 **/
1056 EFI_STATUS
1057 IScsiUpdateTargetAddress (
1058 IN OUT ISCSI_SESSION *Session,
1059 IN CHAR8 *Data,
1060 IN UINT32 Len
1061 )
1062 {
1063 LIST_ENTRY *KeyValueList;
1064 CHAR8 *TargetAddress;
1065 CHAR8 *IpStr;
1066 EFI_STATUS Status;
1067 UINTN Number;
1068 UINT8 IpMode;
1069
1070 KeyValueList = IScsiBuildKeyValueList (Data, Len);
1071 if (KeyValueList == NULL) {
1072 return EFI_OUT_OF_RESOURCES;
1073 }
1074
1075 Status = EFI_NOT_FOUND;
1076
1077 while (TRUE) {
1078 TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);
1079 if (TargetAddress == NULL) {
1080 break;
1081 }
1082
1083 if (!NET_IS_DIGIT (TargetAddress[0])) {
1084 //
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
1087 // IPv4 address.
1088 //
1089 continue;
1090 }
1091
1092 IpStr = TargetAddress;
1093
1094 while ((*TargetAddress != 0) && (*TargetAddress != ':') && (*TargetAddress != ',')) {
1095 //
1096 // NULL, ':', or ',' ends the IPv4 string.
1097 //
1098 TargetAddress++;
1099 }
1100
1101 if (*TargetAddress == ',') {
1102 //
1103 // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
1104 // as the result of a redirection.
1105 //
1106 continue;
1107 } else if (*TargetAddress == ':') {
1108 *TargetAddress = '\0';
1109
1110 TargetAddress++;
1111
1112 Number = AsciiStrDecimalToUintn (TargetAddress);
1113 if (Number > 0xFFFF) {
1114 continue;
1115 } else {
1116 Session->ConfigData->SessionConfigData.TargetPort = (UINT16) Number;
1117 }
1118 } else {
1119 //
1120 // The string only contains the IPv4 address. Use the well-known port.
1121 //
1122 Session->ConfigData->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
1123 }
1124 //
1125 // Update the target IP address.
1126 //
1127 if (Session->ConfigData->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {
1128 IpMode = Session->ConfigData->SessionConfigData.IpMode;
1129 } else {
1130 IpMode = Session->ConfigData->AutoConfigureMode;
1131 }
1132
1133 Status = IScsiAsciiStrToIp (
1134 IpStr,
1135 IpMode,
1136 &Session->ConfigData->SessionConfigData.TargetIp
1137 );
1138
1139 if (EFI_ERROR (Status)) {
1140 continue;
1141 } else {
1142 break;
1143 }
1144 }
1145
1146 IScsiFreeKeyValueList (KeyValueList);
1147
1148 return Status;
1149 }
1150
1151
1152 /**
1153 The callback function to free the net buffer list.
1154
1155 @param[in] Arg The opaque parameter.
1156
1157 **/
1158 VOID
1159 EFIAPI
1160 IScsiFreeNbufList (
1161 VOID *Arg
1162 )
1163 {
1164 ASSERT (Arg != NULL);
1165
1166 NetbufFreeList ((LIST_ENTRY *) Arg);
1167 FreePool (Arg);
1168 }
1169
1170
1171 /**
1172 The callback function called in NetBufFree; it does nothing.
1173
1174 @param[in] Arg The opaque parameter.
1175
1176 **/
1177 VOID
1178 EFIAPI
1179 IScsiNbufExtFree (
1180 VOID *Arg
1181 )
1182 {
1183 }
1184
1185
1186 /**
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.
1191
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.
1199
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.
1204
1205 **/
1206 EFI_STATUS
1207 IScsiReceivePdu (
1208 IN ISCSI_CONNECTION *Conn,
1209 OUT NET_BUF **Pdu,
1210 IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL
1211 IN BOOLEAN HeaderDigest,
1212 IN BOOLEAN DataDigest,
1213 IN EFI_EVENT TimeoutEvent OPTIONAL
1214 )
1215 {
1216 LIST_ENTRY *NbufList;
1217 UINT32 Len;
1218 NET_BUF *PduHdr;
1219 UINT8 *Header;
1220 EFI_STATUS Status;
1221 UINT32 PadLen;
1222 UINT32 InDataOffset;
1223 NET_FRAGMENT Fragment[2];
1224 UINT32 FragmentCount;
1225 NET_BUF *DataSeg;
1226 UINT32 PadAndCRC32[2];
1227
1228 NbufList = AllocatePool (sizeof (LIST_ENTRY));
1229 if (NbufList == NULL) {
1230 return EFI_OUT_OF_RESOURCES;
1231 }
1232
1233 InitializeListHead (NbufList);
1234
1235 //
1236 // The header digest will be received together with the PDU header, if exists.
1237 //
1238 Len = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0);
1239 PduHdr = NetbufAlloc (Len);
1240 if (PduHdr == NULL) {
1241 Status = EFI_OUT_OF_RESOURCES;
1242 goto ON_EXIT;
1243 }
1244
1245 Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);
1246 ASSERT (Header != NULL);
1247 InsertTailList (NbufList, &PduHdr->List);
1248
1249 //
1250 // First step, receive the BHS of the PDU.
1251 //
1252 Status = TcpIoReceive (&Conn->TcpIo, PduHdr, FALSE, TimeoutEvent);
1253
1254 if (EFI_ERROR (Status)) {
1255 goto ON_EXIT;
1256 }
1257
1258 if (HeaderDigest) {
1259 //
1260 // TODO: check the header-digest.
1261 //
1262 //
1263 // Trim off the digest.
1264 //
1265 NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL);
1266 }
1267
1268 Len = ISCSI_GET_DATASEG_LEN (Header);
1269 if (Len == 0) {
1270 //
1271 // No data segment.
1272 //
1273 goto FORM_PDU;
1274 }
1275 //
1276 // Get the length of the padding bytes of the data segment.
1277 //
1278 PadLen = ISCSI_GET_PAD_LEN (Len);
1279
1280 switch (ISCSI_GET_OPCODE (Header)) {
1281 case ISCSI_OPCODE_SCSI_DATA_IN:
1282 //
1283 // To reduce memory copy overhead, try to use the buffer described by Context
1284 // if the PDU is an iSCSI SCSI data.
1285 //
1286 InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header);
1287 if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) {
1288 Status = EFI_PROTOCOL_ERROR;
1289 goto ON_EXIT;
1290 }
1291
1292 Fragment[0].Len = Len;
1293 Fragment[0].Bulk = Context->InData + InDataOffset;
1294
1295 if (DataDigest || (PadLen != 0)) {
1296 //
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.
1299 //
1300 Fragment[1].Len = PadLen + (DataDigest ? sizeof (UINT32) : 0);
1301 Fragment[1].Bulk = (UINT8 *)PadAndCRC32 + (4 - PadLen);
1302
1303 FragmentCount = 2;
1304 } else {
1305 FragmentCount = 1;
1306 }
1307
1308 DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
1309 if (DataSeg == NULL) {
1310 Status = EFI_OUT_OF_RESOURCES;
1311 goto ON_EXIT;
1312 }
1313
1314 break;
1315
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:
1325 //
1326 // Allocate buffer to receive the data segment.
1327 //
1328 Len += PadLen + (DataDigest ? sizeof (UINT32) : 0);
1329 DataSeg = NetbufAlloc (Len);
1330 if (DataSeg == NULL) {
1331 Status = EFI_OUT_OF_RESOURCES;
1332 goto ON_EXIT;
1333 }
1334
1335 NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);
1336 break;
1337
1338 default:
1339 Status = EFI_PROTOCOL_ERROR;
1340 goto ON_EXIT;
1341 }
1342
1343 InsertTailList (NbufList, &DataSeg->List);
1344
1345 //
1346 // Receive the data segment with the data digest, if any.
1347 //
1348 Status = TcpIoReceive (&Conn->TcpIo, DataSeg, FALSE, TimeoutEvent);
1349
1350 if (EFI_ERROR (Status)) {
1351 goto ON_EXIT;
1352 }
1353
1354 if (DataDigest) {
1355 //
1356 // TODO: Check the data digest.
1357 //
1358 NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL);
1359 }
1360
1361 if (PadLen != 0) {
1362 //
1363 // Trim off the padding bytes in the data segment.
1364 //
1365 NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL);
1366 }
1367
1368 FORM_PDU:
1369 //
1370 // Form the pdu from a list of pdu segments.
1371 //
1372 *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
1373 if (*Pdu == NULL) {
1374 Status = EFI_OUT_OF_RESOURCES;
1375 }
1376
1377 ON_EXIT:
1378
1379 if (EFI_ERROR (Status)) {
1380 //
1381 // Free the Nbufs in this NbufList and the NbufList itself.
1382 //
1383 IScsiFreeNbufList (NbufList);
1384 }
1385
1386 return Status;
1387 }
1388
1389
1390 /**
1391 Check and get the result of the parameter negotiation.
1392
1393 @param[in, out] Conn The connection in iSCSI login.
1394
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.
1398
1399 **/
1400 EFI_STATUS
1401 IScsiCheckOpParams (
1402 IN OUT ISCSI_CONNECTION *Conn
1403 )
1404 {
1405 EFI_STATUS Status;
1406 LIST_ENTRY *KeyValueList;
1407 CHAR8 *Data;
1408 UINT32 Len;
1409 ISCSI_SESSION *Session;
1410 CHAR8 *Value;
1411 UINTN NumericValue;
1412
1413 ASSERT (Conn->RspQue.BufNum != 0);
1414
1415 Session = Conn->Session;
1416
1417 Len = Conn->RspQue.BufSize;
1418 Data = AllocatePool (Len);
1419 if (Data == NULL) {
1420 return EFI_OUT_OF_RESOURCES;
1421 }
1422
1423 NetbufQueCopy (&Conn->RspQue, 0, Len, (UINT8 *) Data);
1424
1425 Status = EFI_PROTOCOL_ERROR;
1426
1427 //
1428 // Extract the Key-Value pairs into a list.
1429 //
1430 KeyValueList = IScsiBuildKeyValueList (Data, Len);
1431 if (KeyValueList == NULL) {
1432 FreePool (Data);
1433 return Status;
1434 }
1435 //
1436 // HeaderDigest
1437 //
1438 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST);
1439 if (Value == NULL) {
1440 goto ON_ERROR;
1441 }
1442
1443 if (AsciiStrCmp (Value, "CRC32") == 0) {
1444 if (Conn->HeaderDigest != IScsiDigestCRC32) {
1445 goto ON_ERROR;
1446 }
1447 } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
1448 Conn->HeaderDigest = IScsiDigestNone;
1449 } else {
1450 goto ON_ERROR;
1451 }
1452 //
1453 // DataDigest
1454 //
1455 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST);
1456 if (Value == NULL) {
1457 goto ON_ERROR;
1458 }
1459
1460 if (AsciiStrCmp (Value, "CRC32") == 0) {
1461 if (Conn->DataDigest != IScsiDigestCRC32) {
1462 goto ON_ERROR;
1463 }
1464 } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
1465 Conn->DataDigest = IScsiDigestNone;
1466 } else {
1467 goto ON_ERROR;
1468 }
1469 //
1470 // ErrorRecoveryLevel: result fuction is Minimum.
1471 //
1472 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL);
1473 if (Value == NULL) {
1474 goto ON_ERROR;
1475 }
1476
1477 NumericValue = IScsiNetNtoi (Value);
1478 if (NumericValue > 2) {
1479 goto ON_ERROR;
1480 }
1481
1482 Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue);
1483
1484 //
1485 // InitialR2T: result function is OR.
1486 //
1487 if (!Session->InitialR2T) {
1488 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
1489 if (Value == NULL) {
1490 goto ON_ERROR;
1491 }
1492
1493 Session->InitialR2T = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
1494 }
1495
1496 //
1497 // ImmediateData: result function is AND.
1498 //
1499 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA);
1500 if (Value == NULL) {
1501 goto ON_ERROR;
1502 }
1503
1504 Session->ImmediateData = (BOOLEAN) (Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0));
1505
1506 //
1507 // MaxRecvDataSegmentLength is declarative.
1508 //
1509 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH);
1510 if (Value != NULL) {
1511 Conn->MaxRecvDataSegmentLength = (UINT32) IScsiNetNtoi (Value);
1512 }
1513 //
1514 // MaxBurstLength: result funtion is Mininum.
1515 //
1516 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH);
1517 if (Value == NULL) {
1518 goto ON_ERROR;
1519 }
1520
1521 NumericValue = IScsiNetNtoi (Value);
1522 Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue);
1523
1524 //
1525 // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
1526 // ImmediateData=No.
1527 //
1528 if (!(Session->InitialR2T && !Session->ImmediateData)) {
1529 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
1530 if (Value == NULL) {
1531 goto ON_ERROR;
1532 }
1533
1534 NumericValue = IScsiNetNtoi (Value);
1535 Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue);
1536 }
1537
1538 //
1539 // MaxConnections: result function is Minimum.
1540 //
1541 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS);
1542 if (Value == NULL) {
1543 goto ON_ERROR;
1544 }
1545
1546 NumericValue = IScsiNetNtoi (Value);
1547 if ((NumericValue == 0) || (NumericValue > 65535)) {
1548 goto ON_ERROR;
1549 }
1550
1551 Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue);
1552
1553 //
1554 // DataPDUInOrder: result function is OR.
1555 //
1556 if (!Session->DataPDUInOrder) {
1557 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
1558 if (Value == NULL) {
1559 goto ON_ERROR;
1560 }
1561
1562 Session->DataPDUInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
1563 }
1564
1565 //
1566 // DataSequenceInorder: result function is OR.
1567 //
1568 if (!Session->DataSequenceInOrder) {
1569 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
1570 if (Value == NULL) {
1571 goto ON_ERROR;
1572 }
1573
1574 Session->DataSequenceInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
1575 }
1576
1577 //
1578 // DefaultTime2Wait: result function is Maximum.
1579 //
1580 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT);
1581 if (Value == NULL) {
1582 goto ON_ERROR;
1583 }
1584
1585 NumericValue = IScsiNetNtoi (Value);
1586 if (NumericValue == 0) {
1587 Session->DefaultTime2Wait = 0;
1588 } else if (NumericValue > 3600) {
1589 goto ON_ERROR;
1590 } else {
1591 Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue);
1592 }
1593 //
1594 // DefaultTime2Retain: result function is Minimum.
1595 //
1596 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN);
1597 if (Value == NULL) {
1598 goto ON_ERROR;
1599 }
1600
1601 NumericValue = IScsiNetNtoi (Value);
1602 if (NumericValue == 0) {
1603 Session->DefaultTime2Retain = 0;
1604 } else if (NumericValue > 3600) {
1605 goto ON_ERROR;
1606 } else {
1607 Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue);
1608 }
1609 //
1610 // MaxOutstandingR2T: result function is Minimum.
1611 //
1612 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T);
1613 if (Value == NULL) {
1614 goto ON_ERROR;
1615 }
1616
1617 NumericValue = IScsiNetNtoi (Value);
1618 if ((NumericValue == 0) || (NumericValue > 65535)) {
1619 goto ON_ERROR;
1620 }
1621
1622 Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue);
1623
1624 //
1625 // Remove declarative key-value pairs, if any.
1626 //
1627 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE);
1628 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS);
1629 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
1630
1631
1632 //
1633 // Remove the key-value that may not needed for result function is OR.
1634 //
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);
1638
1639 //
1640 // Remove irrelevant parameter, if any.
1641 //
1642 if (Session->InitialR2T && !Session->ImmediateData) {
1643 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
1644 }
1645
1646 if (IsListEmpty (KeyValueList)) {
1647 //
1648 // Succeed if no more keys in the list.
1649 //
1650 Status = EFI_SUCCESS;
1651 }
1652
1653 ON_ERROR:
1654
1655 IScsiFreeKeyValueList (KeyValueList);
1656
1657 FreePool (Data);
1658
1659 return Status;
1660 }
1661
1662
1663 /**
1664 Fill the operational parameters.
1665
1666 @param[in] Conn The connection in iSCSI login.
1667 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1668
1669 **/
1670 VOID
1671 IScsiFillOpParams (
1672 IN ISCSI_CONNECTION *Conn,
1673 IN OUT NET_BUF *Pdu
1674 )
1675 {
1676 ISCSI_SESSION *Session;
1677 CHAR8 Value[256];
1678
1679 Session = Conn->Session;
1680
1681 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
1682 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);
1683
1684 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
1685 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);
1686
1687 AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);
1688 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);
1689
1690 AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");
1691 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);
1692
1693 AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");
1694 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);
1695
1696 AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);
1697 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);
1698
1699 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);
1700 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);
1701
1702 AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);
1703 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);
1704
1705 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);
1706 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);
1707
1708 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");
1709 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);
1710
1711 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");
1712 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);
1713
1714 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);
1715 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);
1716
1717 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);
1718 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);
1719
1720 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);
1721 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);
1722 }
1723
1724
1725 /**
1726 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
1727
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.
1730
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
1733 padding bytes.
1734 **/
1735 EFI_STATUS
1736 IScsiPadSegment (
1737 IN OUT NET_BUF *Pdu,
1738 IN UINT32 Len
1739 )
1740 {
1741 UINT32 PadLen;
1742 UINT8 *Data;
1743
1744 PadLen = ISCSI_GET_PAD_LEN (Len);
1745
1746 if (PadLen != 0) {
1747 Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);
1748 if (Data == NULL) {
1749 return EFI_OUT_OF_RESOURCES;
1750 }
1751
1752 ZeroMem (Data, PadLen);
1753 }
1754
1755 return EFI_SUCCESS;
1756 }
1757
1758
1759 /**
1760 Build a key-value list from the data segment.
1761
1762 @param[in] Data The data segment containing the key-value pairs.
1763 @param[in] Len Length of the data segment.
1764
1765 @return The key-value list.
1766 @retval NULL Other errors as indicated.
1767
1768 **/
1769 LIST_ENTRY *
1770 IScsiBuildKeyValueList (
1771 IN CHAR8 *Data,
1772 IN UINT32 Len
1773 )
1774 {
1775 LIST_ENTRY *ListHead;
1776 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1777
1778 ListHead = AllocatePool (sizeof (LIST_ENTRY));
1779 if (ListHead == NULL) {
1780 return NULL;
1781 }
1782
1783 InitializeListHead (ListHead);
1784
1785 while (Len > 0) {
1786 KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));
1787 if (KeyValuePair == NULL) {
1788 goto ON_ERROR;
1789 }
1790
1791 InitializeListHead (&KeyValuePair->List);
1792
1793 KeyValuePair->Key = Data;
1794
1795 while ((Len > 0) && (*Data != '=')) {
1796 Len--;
1797 Data++;
1798 }
1799
1800 if (*Data == '=') {
1801 *Data = '\0';
1802
1803 Data++;
1804 Len--;
1805 } else {
1806 FreePool (KeyValuePair);
1807 goto ON_ERROR;
1808 }
1809
1810 KeyValuePair->Value = Data;
1811
1812 InsertTailList (ListHead, &KeyValuePair->List);;
1813
1814 Data += AsciiStrLen (KeyValuePair->Value) + 1;
1815 Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;
1816 }
1817
1818 return ListHead;
1819
1820 ON_ERROR:
1821
1822 IScsiFreeKeyValueList (ListHead);
1823
1824 return NULL;
1825 }
1826
1827
1828 /**
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.
1831
1832 @param[in, out] KeyValueList The key-value list.
1833 @param[in] Key The key name to find.
1834
1835 @return The value string.
1836 @retval NULL The key value pair cannot be found.
1837
1838 **/
1839 CHAR8 *
1840 IScsiGetValueByKeyFromList (
1841 IN OUT LIST_ENTRY *KeyValueList,
1842 IN CHAR8 *Key
1843 )
1844 {
1845 LIST_ENTRY *Entry;
1846 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1847 CHAR8 *Value;
1848
1849 Value = NULL;
1850
1851 NET_LIST_FOR_EACH (Entry, KeyValueList) {
1852 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
1853
1854 if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {
1855 Value = KeyValuePair->Value;
1856
1857 RemoveEntryList (&KeyValuePair->List);
1858 FreePool (KeyValuePair);
1859 break;
1860 }
1861 }
1862
1863 return Value;
1864 }
1865
1866
1867 /**
1868 Free the key-value list.
1869
1870 @param[in] KeyValueList The key-value list.
1871
1872 **/
1873 VOID
1874 IScsiFreeKeyValueList (
1875 IN LIST_ENTRY *KeyValueList
1876 )
1877 {
1878 LIST_ENTRY *Entry;
1879 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1880
1881 while (!IsListEmpty (KeyValueList)) {
1882 Entry = NetListRemoveHead (KeyValueList);
1883 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
1884
1885 FreePool (KeyValuePair);
1886 }
1887
1888 FreePool (KeyValueList);
1889 }
1890
1891
1892 /**
1893 Normalize the iSCSI name according to RFC.
1894
1895 @param[in, out] Name The iSCSI name.
1896 @param[in] Len Length of the iSCSI name.
1897
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.
1900
1901 **/
1902 EFI_STATUS
1903 IScsiNormalizeName (
1904 IN OUT CHAR8 *Name,
1905 IN UINTN Len
1906 )
1907 {
1908 UINTN Index;
1909
1910 for (Index = 0; Index < Len; Index++) {
1911 if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {
1912 //
1913 // Convert the upper-case characters to lower-case ones.
1914 //
1915 Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');
1916 }
1917
1918 if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&
1919 !NET_IS_DIGIT (Name[Index]) &&
1920 (Name[Index] != '-') &&
1921 (Name[Index] != '.') &&
1922 (Name[Index] != ':')
1923 ) {
1924 //
1925 // ASCII dash, dot, colon lower-case characters and digit characters
1926 // are allowed.
1927 //
1928 return EFI_PROTOCOL_ERROR;
1929 }
1930 }
1931
1932 if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {
1933 //
1934 // Only IQN format is accepted now.
1935 //
1936 return EFI_PROTOCOL_ERROR;
1937 }
1938
1939 return EFI_SUCCESS;
1940 }
1941
1942
1943 /**
1944 Create an iSCSI task control block.
1945
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.
1948
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.
1952
1953 **/
1954 EFI_STATUS
1955 IScsiNewTcb (
1956 IN ISCSI_CONNECTION *Conn,
1957 OUT ISCSI_TCB **Tcb
1958 )
1959 {
1960 ISCSI_SESSION *Session;
1961 ISCSI_TCB *NewTcb;
1962
1963 ASSERT (Tcb != NULL);
1964
1965 Session = Conn->Session;
1966
1967 if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {
1968 return EFI_NOT_READY;
1969 }
1970
1971 NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));
1972 if (NewTcb == NULL) {
1973 return EFI_OUT_OF_RESOURCES;
1974 }
1975
1976 InitializeListHead (&NewTcb->Link);
1977
1978 NewTcb->SoFarInOrder = TRUE;
1979 NewTcb->InitiatorTaskTag = Session->InitiatorTaskTag;
1980 NewTcb->CmdSN = Session->CmdSN;
1981 NewTcb->Conn = Conn;
1982
1983 InsertTailList (&Session->TcbList, &NewTcb->Link);
1984
1985 //
1986 // Advance the initiator task tag.
1987 //
1988 Session->InitiatorTaskTag++;
1989 Session->CmdSN++;
1990
1991 *Tcb = NewTcb;
1992
1993 return EFI_SUCCESS;
1994 }
1995
1996
1997 /**
1998 Delete the tcb from the connection and destroy it.
1999
2000 @param[in] Tcb The tcb to delete.
2001
2002 **/
2003 VOID
2004 IScsiDelTcb (
2005 IN ISCSI_TCB *Tcb
2006 )
2007 {
2008 RemoveEntryList (&Tcb->Link);
2009
2010 FreePool (Tcb);
2011 }
2012
2013
2014 /**
2015 Find the task control block by the initator task tag.
2016
2017 @param[in] TcbList The tcb list.
2018 @param[in] InitiatorTaskTag The initiator task tag.
2019
2020 @return The task control block found.
2021 @retval NULL The task control block cannot be found.
2022
2023 **/
2024 ISCSI_TCB *
2025 IScsiFindTcbByITT (
2026 IN LIST_ENTRY *TcbList,
2027 IN UINT32 InitiatorTaskTag
2028 )
2029 {
2030 ISCSI_TCB *Tcb;
2031 LIST_ENTRY *Entry;
2032
2033 Tcb = NULL;
2034
2035 NET_LIST_FOR_EACH (Entry, TcbList) {
2036 Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);
2037
2038 if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {
2039 break;
2040 }
2041 }
2042
2043 return Tcb;
2044 }
2045
2046
2047 /**
2048 Create a data segment, pad it, and calculate the CRC if needed.
2049
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.
2053
2054 @return The net buffer wrapping the data segment.
2055
2056 **/
2057 NET_BUF *
2058 IScsiNewDataSegment (
2059 IN UINT8 *Data,
2060 IN UINT32 Len,
2061 IN BOOLEAN DataDigest
2062 )
2063 {
2064 NET_FRAGMENT Fragment[2];
2065 UINT32 FragmentCount;
2066 UINT32 PadLen;
2067 NET_BUF *DataSeg;
2068
2069 Fragment[0].Len = Len;
2070 Fragment[0].Bulk = Data;
2071
2072 PadLen = ISCSI_GET_PAD_LEN (Len);
2073 if (PadLen != 0) {
2074 Fragment[1].Len = PadLen;
2075 Fragment[1].Bulk = (UINT8 *) &mDataSegPad;
2076
2077 FragmentCount = 2;
2078 } else {
2079 FragmentCount = 1;
2080 }
2081
2082 DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
2083
2084 return DataSeg;
2085 }
2086
2087
2088 /**
2089 Create a iSCSI SCSI command PDU to encapsulate the command issued
2090 by SCSI through the EXT SCSI PASS THRU Protocol.
2091
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.
2095
2096 @return The created iSCSI SCSI command PDU.
2097 @retval NULL Other errors as indicated.
2098
2099 **/
2100 NET_BUF *
2101 IScsiNewScsiCmdPdu (
2102 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
2103 IN UINT64 Lun,
2104 IN ISCSI_TCB *Tcb
2105 )
2106 {
2107 LIST_ENTRY *NbufList;
2108 NET_BUF *Pdu;
2109 NET_BUF *PduHeader;
2110 NET_BUF *DataSeg;
2111 SCSI_COMMAND *ScsiCmd;
2112 UINT8 AHSLength;
2113 UINT32 Length;
2114 ISCSI_ADDITIONAL_HEADER *Header;
2115 ISCSI_BI_EXP_READ_DATA_LEN_AHS *BiExpReadDataLenAHS;
2116 ISCSI_SESSION *Session;
2117 UINT32 ImmediateDataLen;
2118
2119 AHSLength = 0;
2120
2121 if (Packet->DataDirection == DataBi) {
2122 //
2123 // Bidirectional Read/Write command, the bidirectional expected
2124 // read data length AHS is required.
2125 //
2126 AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);
2127 }
2128
2129 if (Packet->CdbLength > 16) {
2130 //
2131 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2132 //
2133 AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER));
2134 }
2135
2136 Length = sizeof (SCSI_COMMAND) + AHSLength;
2137 PduHeader = NetbufAlloc (Length);
2138 if (PduHeader == NULL) {
2139 return NULL;
2140 }
2141
2142 ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);
2143 if (ScsiCmd == NULL) {
2144 NetbufFree (PduHeader);
2145 return NULL;
2146 }
2147 Header = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);
2148
2149 ZeroMem (ScsiCmd, Length);
2150
2151 ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);
2152 ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);
2153
2154 //
2155 // Set the READ/WRITE flags according to the IO type of this request.
2156 //
2157 switch (Packet->DataDirection) {
2158 case DataIn:
2159 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);
2160 ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);
2161 break;
2162
2163 case DataOut:
2164 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);
2165 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
2166 break;
2167
2168 case DataBi:
2169 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);
2170 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
2171
2172 //
2173 // Fill the bidirectional expected read data length AHS.
2174 //
2175 BiExpReadDataLenAHS = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;
2176 Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);
2177
2178 BiExpReadDataLenAHS->Length = NTOHS (5);
2179 BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;
2180 BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);
2181
2182 break;
2183 }
2184
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);
2190
2191 CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));
2192
2193 if (Packet->CdbLength > 16) {
2194 Header->Length = NTOHS ((UINT16) (Packet->CdbLength - 15));
2195 Header->Type = ISCSI_AHS_TYPE_EXT_CDB;
2196
2197 CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);
2198 }
2199
2200 Pdu = PduHeader;
2201 Session = Tcb->Conn->Session;
2202 ImmediateDataLen = 0;
2203
2204 if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {
2205 //
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.
2209 //
2210 ImmediateDataLen = MIN (Session->FirstBurstLength, Packet->OutTransferLength);
2211 ImmediateDataLen = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);
2212
2213 //
2214 // Update the data segment length in the PDU header.
2215 //
2216 ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);
2217
2218 //
2219 // Create the data segment.
2220 //
2221 DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);
2222 if (DataSeg == NULL) {
2223 NetbufFree (PduHeader);
2224 Pdu = NULL;
2225 goto ON_EXIT;
2226 }
2227
2228 NbufList = AllocatePool (sizeof (LIST_ENTRY));
2229 if (NbufList == NULL) {
2230 NetbufFree (PduHeader);
2231 NetbufFree (DataSeg);
2232
2233 Pdu = NULL;
2234 goto ON_EXIT;
2235 }
2236
2237 InitializeListHead (NbufList);
2238 InsertTailList (NbufList, &PduHeader->List);
2239 InsertTailList (NbufList, &DataSeg->List);
2240
2241 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
2242 if (Pdu == NULL) {
2243 IScsiFreeNbufList (NbufList);
2244 }
2245 }
2246
2247 if (Session->InitialR2T ||
2248 (ImmediateDataLen == Session->FirstBurstLength) ||
2249 (ImmediateDataLen == Packet->OutTransferLength)
2250 ) {
2251 //
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
2256 // PDU.
2257 //
2258 ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);
2259 }
2260
2261 ON_EXIT:
2262
2263 return Pdu;
2264 }
2265
2266
2267 /**
2268 Create a new iSCSI SCSI Data Out PDU.
2269
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.
2275
2276 @return The net buffer wrapping the Data Out PDU.
2277 @retval NULL Other errors as indicated.
2278
2279 **/
2280 NET_BUF *
2281 IScsiNewDataOutPdu (
2282 IN UINT8 *Data,
2283 IN UINT32 Len,
2284 IN UINT32 DataSN,
2285 IN ISCSI_TCB *Tcb,
2286 IN UINT64 Lun
2287 )
2288 {
2289 LIST_ENTRY *NbufList;
2290 NET_BUF *PduHdr;
2291 NET_BUF *DataSeg;
2292 NET_BUF *Pdu;
2293 ISCSI_SCSI_DATA_OUT *DataOutHdr;
2294 ISCSI_XFER_CONTEXT *XferContext;
2295
2296 NbufList = AllocatePool (sizeof (LIST_ENTRY));
2297 if (NbufList == NULL) {
2298 return NULL;
2299 }
2300
2301 InitializeListHead (NbufList);
2302
2303 //
2304 // Allocate memory for the BHS.
2305 //
2306 PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));
2307 if (PduHdr == NULL) {
2308 FreePool (NbufList);
2309 return NULL;
2310 }
2311 //
2312 // Insert the BHS into the buffer list.
2313 //
2314 InsertTailList (NbufList, &PduHdr->List);
2315
2316 DataOutHdr = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);
2317 ASSERT (DataOutHdr != NULL);
2318 XferContext = &Tcb->XferContext;
2319
2320 ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));
2321
2322 //
2323 // Set the flags and fields of the Data Out PDU BHS.
2324 //
2325 ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);
2326 ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);
2327
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);
2333
2334 if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {
2335 CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));
2336 }
2337 //
2338 // Build the data segment for this Data Out PDU.
2339 //
2340 DataSeg = IScsiNewDataSegment (Data, Len, FALSE);
2341 if (DataSeg == NULL) {
2342 IScsiFreeNbufList (NbufList);
2343 return NULL;
2344 }
2345 //
2346 // Put the data segment into the buffer list and combine it with the BHS
2347 // into a full Data Out PDU.
2348 //
2349 InsertTailList (NbufList, &DataSeg->List);
2350 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
2351 if (Pdu == NULL) {
2352 IScsiFreeNbufList (NbufList);
2353 }
2354
2355 return Pdu;
2356 }
2357
2358
2359 /**
2360 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
2361
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.
2365
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.
2368
2369 **/
2370 LIST_ENTRY *
2371 IScsiGenerateDataOutPduSequence (
2372 IN UINT8 *Data,
2373 IN ISCSI_TCB *Tcb,
2374 IN UINT64 Lun
2375 )
2376 {
2377 LIST_ENTRY *PduList;
2378 UINT32 DataSN;
2379 UINT32 DataLen;
2380 NET_BUF *DataOutPdu;
2381 ISCSI_CONNECTION *Conn;
2382 ISCSI_XFER_CONTEXT *XferContext;
2383 UINT8 *DataOutPacket;
2384
2385 PduList = AllocatePool (sizeof (LIST_ENTRY));
2386 if (PduList == NULL) {
2387 return NULL;
2388 }
2389
2390 InitializeListHead (PduList);
2391
2392 DataSN = 0;
2393 Conn = Tcb->Conn;
2394 DataOutPdu = NULL;
2395 XferContext = &Tcb->XferContext;
2396
2397 while (XferContext->DesiredLength > 0) {
2398 //
2399 // Determine the length of data this Data Out PDU can carry.
2400 //
2401 DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);
2402
2403 //
2404 // Create a Data Out PDU.
2405 //
2406 DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);
2407 if (DataOutPdu == NULL) {
2408 IScsiFreeNbufList (PduList);
2409 PduList = NULL;
2410
2411 goto ON_EXIT;
2412 }
2413
2414 InsertTailList (PduList, &DataOutPdu->List);
2415
2416 //
2417 // Update the context and DataSN.
2418 //
2419 Data += DataLen;
2420 XferContext->Offset += DataLen;
2421 XferContext->DesiredLength -= DataLen;
2422 DataSN++;
2423 }
2424 //
2425 // Set the F bit for the last data out PDU in this sequence.
2426 //
2427 DataOutPacket = NetbufGetByte (DataOutPdu, 0, NULL);
2428 if (DataOutPacket == NULL) {
2429 IScsiFreeNbufList (PduList);
2430 PduList = NULL;
2431 goto ON_EXIT;
2432 }
2433
2434 ISCSI_SET_FLAG (DataOutPacket, ISCSI_BHS_FLAG_FINAL);
2435
2436 ON_EXIT:
2437
2438 return PduList;
2439 }
2440
2441 /**
2442 Send the Data in a sequence of Data Out PDUs one by one.
2443
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.
2447
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.
2451
2452 **/
2453 EFI_STATUS
2454 IScsiSendDataOutPduSequence (
2455 IN UINT8 *Data,
2456 IN UINT64 Lun,
2457 IN ISCSI_TCB *Tcb
2458 )
2459 {
2460 LIST_ENTRY *DataOutPduList;
2461 LIST_ENTRY *Entry;
2462 NET_BUF *Pdu;
2463 EFI_STATUS Status;
2464
2465 //
2466 // Generate the Data Out PDU sequence.
2467 //
2468 DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);
2469 if (DataOutPduList == NULL) {
2470 return EFI_OUT_OF_RESOURCES;
2471 }
2472
2473 Status = EFI_SUCCESS;
2474
2475 //
2476 // Send the Data Out PDU's one by one.
2477 //
2478 NET_LIST_FOR_EACH (Entry, DataOutPduList) {
2479 Pdu = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
2480
2481 Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu);
2482
2483 if (EFI_ERROR (Status)) {
2484 break;
2485 }
2486 }
2487
2488 IScsiFreeNbufList (DataOutPduList);
2489
2490 return Status;
2491 }
2492
2493
2494 /**
2495 Process the received iSCSI SCSI Data In PDU.
2496
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.
2500
2501 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
2502 actions are taken.
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.
2506
2507 **/
2508 EFI_STATUS
2509 IScsiOnDataInRcvd (
2510 IN NET_BUF *Pdu,
2511 IN ISCSI_TCB *Tcb,
2512 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2513 )
2514 {
2515 ISCSI_SCSI_DATA_IN *DataInHdr;
2516 EFI_STATUS Status;
2517
2518 DataInHdr = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);
2519 if (DataInHdr == NULL) {
2520 return EFI_PROTOCOL_ERROR;
2521 }
2522
2523 DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);
2524 DataInHdr->ExpCmdSN = NTOHL (DataInHdr->ExpCmdSN);
2525 DataInHdr->MaxCmdSN = NTOHL (DataInHdr->MaxCmdSN);
2526 DataInHdr->DataSN = NTOHL (DataInHdr->DataSN);
2527
2528 //
2529 // Check the DataSN.
2530 //
2531 Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);
2532 if (EFI_ERROR (Status)) {
2533 return Status;
2534 }
2535
2536 if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
2537 return EFI_PROTOCOL_ERROR;
2538 }
2539 //
2540 // Update the command related sequence numbers.
2541 //
2542 IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);
2543
2544 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {
2545 if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {
2546 //
2547 // The S bit is on but the F bit is off.
2548 //
2549 return EFI_PROTOCOL_ERROR;
2550 }
2551
2552 Tcb->StatusXferd = TRUE;
2553
2554 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {
2555 //
2556 // Underflow and Overflow are mutual flags.
2557 //
2558 return EFI_PROTOCOL_ERROR;
2559 }
2560 //
2561 // S bit is on, the StatSN is valid.
2562 //
2563 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));
2564 if (EFI_ERROR (Status)) {
2565 return Status;
2566 }
2567
2568 Packet->HostAdapterStatus = 0;
2569 Packet->TargetStatus = DataInHdr->Status;
2570
2571 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
2572 Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);
2573 Status = EFI_BAD_BUFFER_SIZE;
2574 }
2575
2576 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
2577 Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);
2578 }
2579 }
2580
2581 return Status;
2582 }
2583
2584
2585 /**
2586 Process the received iSCSI R2T PDU.
2587
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.
2592
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.
2596
2597 **/
2598 EFI_STATUS
2599 IScsiOnR2TRcvd (
2600 IN NET_BUF *Pdu,
2601 IN ISCSI_TCB *Tcb,
2602 IN UINT64 Lun,
2603 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2604 )
2605 {
2606 ISCSI_READY_TO_TRANSFER *R2THdr;
2607 EFI_STATUS Status;
2608 ISCSI_XFER_CONTEXT *XferContext;
2609 UINT8 *Data;
2610
2611 R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);
2612 if (R2THdr == NULL) {
2613 return EFI_PROTOCOL_ERROR;
2614 }
2615
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);
2622
2623 if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {
2624 return EFI_PROTOCOL_ERROR;;
2625 }
2626 //
2627 // Check the sequence number.
2628 //
2629 Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);
2630 if (EFI_ERROR (Status)) {
2631 return Status;
2632 }
2633
2634 XferContext = &Tcb->XferContext;
2635 XferContext->TargetTransferTag = R2THdr->TargetTransferTag;
2636 XferContext->Offset = R2THdr->BufferOffset;
2637 XferContext->DesiredLength = R2THdr->DesiredDataTransferLength;
2638
2639 if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||
2640 (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)
2641 ) {
2642 return EFI_PROTOCOL_ERROR;
2643 }
2644 //
2645 // Send the data solicited by this R2T.
2646 //
2647 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
2648 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
2649
2650 return Status;
2651 }
2652
2653
2654 /**
2655 Process the received iSCSI SCSI Response PDU.
2656
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.
2660
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.
2665
2666 **/
2667 EFI_STATUS
2668 IScsiOnScsiRspRcvd (
2669 IN NET_BUF *Pdu,
2670 IN ISCSI_TCB *Tcb,
2671 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2672 )
2673 {
2674 SCSI_RESPONSE *ScsiRspHdr;
2675 ISCSI_SENSE_DATA *SenseData;
2676 EFI_STATUS Status;
2677 UINT32 DataSegLen;
2678
2679 ScsiRspHdr = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
2680 if (ScsiRspHdr == NULL) {
2681 return EFI_PROTOCOL_ERROR;
2682 }
2683
2684 ScsiRspHdr->InitiatorTaskTag = NTOHL (ScsiRspHdr->InitiatorTaskTag);
2685 if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
2686 return EFI_PROTOCOL_ERROR;
2687 }
2688
2689 ScsiRspHdr->StatSN = NTOHL (ScsiRspHdr->StatSN);
2690
2691 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);
2692 if (EFI_ERROR (Status)) {
2693 return Status;
2694 }
2695
2696 ScsiRspHdr->MaxCmdSN = NTOHL (ScsiRspHdr->MaxCmdSN);
2697 ScsiRspHdr->ExpCmdSN = NTOHL (ScsiRspHdr->ExpCmdSN);
2698 IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);
2699
2700 Tcb->StatusXferd = TRUE;
2701
2702 Packet->HostAdapterStatus = ScsiRspHdr->Response;
2703 if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {
2704 return EFI_SUCCESS;
2705 }
2706
2707 Packet->TargetStatus = ScsiRspHdr->Status;
2708
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)
2711 ) {
2712 return EFI_PROTOCOL_ERROR;
2713 }
2714
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;
2718 }
2719
2720 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {
2721 Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);
2722 }
2723
2724 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
2725 if (Packet->DataDirection == DataIn) {
2726 Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
2727 } else {
2728 Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
2729 }
2730
2731 Status = EFI_BAD_BUFFER_SIZE;
2732 }
2733
2734 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
2735 if (Packet->DataDirection == DataIn) {
2736 Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
2737 } else {
2738 Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
2739 }
2740 }
2741
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;
2747 }
2748
2749 SenseData->Length = NTOHS (SenseData->Length);
2750
2751 Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);
2752 if (Packet->SenseDataLength != 0) {
2753 CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);
2754 }
2755 } else {
2756 Packet->SenseDataLength = 0;
2757 }
2758
2759 return Status;
2760 }
2761
2762
2763 /**
2764 Process the received NOP In PDU.
2765
2766 @param[in] Pdu The NOP In PDU received.
2767 @param[in] Tcb The task control block.
2768
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.
2772
2773 **/
2774 EFI_STATUS
2775 IScsiOnNopInRcvd (
2776 IN NET_BUF *Pdu,
2777 IN ISCSI_TCB *Tcb
2778 )
2779 {
2780 ISCSI_NOP_IN *NopInHdr;
2781 EFI_STATUS Status;
2782
2783 NopInHdr = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);
2784 if (NopInHdr == NULL) {
2785 return EFI_PROTOCOL_ERROR;
2786 }
2787
2788 NopInHdr->StatSN = NTOHL (NopInHdr->StatSN);
2789 NopInHdr->ExpCmdSN = NTOHL (NopInHdr->ExpCmdSN);
2790 NopInHdr->MaxCmdSN = NTOHL (NopInHdr->MaxCmdSN);
2791
2792 if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {
2793 if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {
2794 return EFI_PROTOCOL_ERROR;
2795 }
2796 } else {
2797 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);
2798 if (EFI_ERROR (Status)) {
2799 return Status;
2800 }
2801 }
2802
2803 IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);
2804
2805 return EFI_SUCCESS;
2806 }
2807
2808
2809 /**
2810 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
2811
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.
2817
2818 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
2819 the Packet.
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.
2825
2826 **/
2827 EFI_STATUS
2828 IScsiExecuteScsiCommand (
2829 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,
2830 IN UINT8 *Target,
2831 IN UINT64 Lun,
2832 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2833 )
2834 {
2835 EFI_STATUS Status;
2836 ISCSI_DRIVER_DATA *Private;
2837 ISCSI_SESSION *Session;
2838 EFI_EVENT TimeoutEvent;
2839 ISCSI_CONNECTION *Conn;
2840 ISCSI_TCB *Tcb;
2841 NET_BUF *Pdu;
2842 ISCSI_XFER_CONTEXT *XferContext;
2843 UINT8 *Data;
2844 ISCSI_IN_BUFFER_CONTEXT InBufferContext;
2845 UINT64 Timeout;
2846 UINT8 *PduHdr;
2847
2848 Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
2849 Session = Private->Session;
2850 Status = EFI_SUCCESS;
2851 Tcb = NULL;
2852 TimeoutEvent = NULL;
2853 Timeout = 0;
2854
2855 if (Session->State != SESSION_STATE_LOGGED_IN) {
2856 Status = EFI_DEVICE_ERROR;
2857 goto ON_EXIT;
2858 }
2859
2860 Conn = NET_LIST_USER_STRUCT_S (
2861 Session->Conns.ForwardLink,
2862 ISCSI_CONNECTION,
2863 Link,
2864 ISCSI_CONNECTION_SIGNATURE
2865 );
2866
2867 if (Packet->Timeout != 0) {
2868 Timeout = MultU64x32 (Packet->Timeout, 4);
2869 }
2870
2871 Status = IScsiNewTcb (Conn, &Tcb);
2872 if (EFI_ERROR (Status)) {
2873 goto ON_EXIT;
2874 }
2875 //
2876 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2877 //
2878 Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);
2879 if (Pdu == NULL) {
2880 Status = EFI_OUT_OF_RESOURCES;
2881 goto ON_EXIT;
2882 }
2883
2884 XferContext = &Tcb->XferContext;
2885 PduHdr = NetbufGetByte (Pdu, 0, NULL);
2886 if (PduHdr == NULL) {
2887 Status = EFI_PROTOCOL_ERROR;
2888 NetbufFree (Pdu);
2889 goto ON_EXIT;
2890 }
2891 XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);
2892
2893 //
2894 // Transmit the SCSI Command PDU.
2895 //
2896 Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
2897
2898 NetbufFree (Pdu);
2899
2900 if (EFI_ERROR (Status)) {
2901 goto ON_EXIT;
2902 }
2903
2904 if (!Session->InitialR2T &&
2905 (XferContext->Offset < Session->FirstBurstLength) &&
2906 (XferContext->Offset < Packet->OutTransferLength)
2907 ) {
2908 //
2909 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2910 // OUT data, and the limit of FirstBurstLength is not reached.
2911 //
2912 XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;
2913 XferContext->DesiredLength = MIN (
2914 Session->FirstBurstLength,
2915 Packet->OutTransferLength - XferContext->Offset
2916 );
2917
2918 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
2919 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
2920 if (EFI_ERROR (Status)) {
2921 goto ON_EXIT;
2922 }
2923 }
2924
2925 InBufferContext.InData = (UINT8 *) Packet->InDataBuffer;
2926 InBufferContext.InDataLen = Packet->InTransferLength;
2927
2928 while (!Tcb->StatusXferd) {
2929 //
2930 // Start the timeout timer.
2931 //
2932 if (Timeout != 0) {
2933 Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);
2934 if (EFI_ERROR (Status)) {
2935 goto ON_EXIT;
2936 }
2937
2938 TimeoutEvent = Conn->TimeoutEvent;
2939 }
2940
2941 //
2942 // Try to receive PDU from target.
2943 //
2944 Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);
2945 if (EFI_ERROR (Status)) {
2946 goto ON_EXIT;
2947 }
2948
2949 PduHdr = NetbufGetByte (Pdu, 0, NULL);
2950 if (PduHdr == NULL) {
2951 Status = EFI_PROTOCOL_ERROR;
2952 NetbufFree (Pdu);
2953 goto ON_EXIT;
2954 }
2955 switch (ISCSI_GET_OPCODE (PduHdr)) {
2956 case ISCSI_OPCODE_SCSI_DATA_IN:
2957 Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);
2958 break;
2959
2960 case ISCSI_OPCODE_R2T:
2961 Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);
2962 break;
2963
2964 case ISCSI_OPCODE_SCSI_RSP:
2965 Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);
2966 break;
2967
2968 case ISCSI_OPCODE_NOP_IN:
2969 Status = IScsiOnNopInRcvd (Pdu, Tcb);
2970 break;
2971
2972 case ISCSI_OPCODE_VENDOR_T0:
2973 case ISCSI_OPCODE_VENDOR_T1:
2974 case ISCSI_OPCODE_VENDOR_T2:
2975 //
2976 // These messages are vendor specific. Skip them.
2977 //
2978 break;
2979
2980 default:
2981 Status = EFI_PROTOCOL_ERROR;
2982 break;
2983 }
2984
2985 NetbufFree (Pdu);
2986
2987 if (EFI_ERROR (Status)) {
2988 break;
2989 }
2990 }
2991
2992 ON_EXIT:
2993
2994 if (TimeoutEvent != NULL) {
2995 gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
2996 }
2997
2998 if (Tcb != NULL) {
2999 IScsiDelTcb (Tcb);
3000 }
3001
3002 return Status;
3003 }
3004
3005
3006 /**
3007 Reinstate the session on some error.
3008
3009 @param[in] Session The iSCSI session
3010
3011 @retval EFI_SUCCESS The session is reinstated from some error.
3012 @retval Other Reinstatement failed.
3013
3014 **/
3015 EFI_STATUS
3016 IScsiSessionReinstatement (
3017 IN ISCSI_SESSION *Session
3018 )
3019 {
3020 EFI_STATUS Status;
3021
3022 ASSERT (Session->State != SESSION_STATE_FREE);
3023
3024 //
3025 // Abort the session and re-init it.
3026 //
3027 IScsiSessionAbort (Session);
3028 IScsiSessionInit (Session, TRUE);
3029
3030 //
3031 // Login again.
3032 //
3033 Status = IScsiSessionLogin (Session);
3034
3035 return Status;
3036 }
3037
3038
3039 /**
3040 Initialize some session parameters before login.
3041
3042 @param[in, out] Session The iSCSI session.
3043 @param[in] Recovery Whether the request is from a fresh new start or recovery.
3044
3045 **/
3046 VOID
3047 IScsiSessionInit (
3048 IN OUT ISCSI_SESSION *Session,
3049 IN BOOLEAN Recovery
3050 )
3051 {
3052 if (!Recovery) {
3053 Session->Signature = ISCSI_SESSION_SIGNATURE;
3054 Session->State = SESSION_STATE_FREE;
3055
3056 InitializeListHead (&Session->Conns);
3057 InitializeListHead (&Session->TcbList);
3058 }
3059
3060 Session->Tsih = 0;
3061
3062 Session->CmdSN = 1;
3063 Session->InitiatorTaskTag = 1;
3064 Session->NextCid = 1;
3065
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;
3078 }
3079
3080
3081 /**
3082 Abort the iSCSI session. That is, reset all the connection(s), and free the
3083 resources.
3084
3085 @param[in, out] Session The iSCSI session.
3086
3087 **/
3088 VOID
3089 IScsiSessionAbort (
3090 IN OUT ISCSI_SESSION *Session
3091 )
3092 {
3093 ISCSI_CONNECTION *Conn;
3094 EFI_GUID *ProtocolGuid;
3095
3096 if (Session->State != SESSION_STATE_LOGGED_IN) {
3097 return ;
3098 }
3099
3100 ASSERT (!IsListEmpty (&Session->Conns));
3101
3102 while (!IsListEmpty (&Session->Conns)) {
3103 Conn = NET_LIST_USER_STRUCT_S (
3104 Session->Conns.ForwardLink,
3105 ISCSI_CONNECTION,
3106 Link,
3107 ISCSI_CONNECTION_SIGNATURE
3108 );
3109 if (!Conn->Ipv6Flag) {
3110 ProtocolGuid = &gEfiTcp4ProtocolGuid;
3111 } else {
3112 ProtocolGuid = &gEfiTcp6ProtocolGuid;
3113 }
3114
3115 gBS->CloseProtocol (
3116 Conn->TcpIo.Handle,
3117 ProtocolGuid,
3118 Session->Private->Image,
3119 Session->Private->ExtScsiPassThruHandle
3120 );
3121
3122 IScsiConnReset (Conn);
3123
3124 IScsiDetatchConnection (Conn);
3125 IScsiDestroyConnection (Conn);
3126 }
3127
3128 Session->State = SESSION_STATE_FAILED;
3129
3130 return ;
3131 }