]> git.proxmox.com Git - mirror_edk2.git/blob - NetworkPkg/IScsiDxe/IScsiProto.c
MdePkg CryptoPkg EdkCompatibilityPkg: retire NO_BUILTIN_VA_FUNCS define
[mirror_edk2.git] / NetworkPkg / IScsiDxe / IScsiProto.c
1 /** @file
2 The implementation of iSCSI protocol based on RFC3720.
3
4 Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
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 Pdu = NULL;
608
609 //
610 // Receive the iSCSI login response.
611 //
612 Status = IScsiReceivePdu (Conn, &Pdu, NULL, FALSE, FALSE, NULL);
613 if (EFI_ERROR (Status)) {
614 return Status;
615 }
616 ASSERT (Pdu != NULL);
617
618 //
619 // A Login Response is received; process it.
620 //
621 Status = IScsiProcessLoginRsp (Conn, Pdu);
622
623 NetbufFree (Pdu);
624
625 return Status;
626 }
627
628
629 /**
630 Add an iSCSI key-value pair as a string into the data segment of the Login Request PDU.
631 The DataSegmentLength and the actual size of the net buffer containing this PDU will be
632 updated.
633
634 @param[in, out] Pdu The iSCSI PDU whose data segment the key-value pair will
635 be added to.
636 @param[in] Key The key name string.
637 @param[in] Value The value string.
638
639 @retval EFI_SUCCESS The key-value pair is added to the PDU's data segment and
640 the correspondence length fields are updated.
641 @retval EFI_OUT_OF_RESOURCES There is not enough space in the PDU to add the key-value
642 pair.
643 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
644 **/
645 EFI_STATUS
646 IScsiAddKeyValuePair (
647 IN OUT NET_BUF *Pdu,
648 IN CHAR8 *Key,
649 IN CHAR8 *Value
650 )
651 {
652 UINT32 DataSegLen;
653 UINT32 KeyLen;
654 UINT32 ValueLen;
655 UINT32 TotalLen;
656 ISCSI_LOGIN_REQUEST *LoginReq;
657 CHAR8 *Data;
658
659 LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufGetByte (Pdu, 0, NULL);
660 if (LoginReq == NULL) {
661 return EFI_PROTOCOL_ERROR;
662 }
663 DataSegLen = NTOH24 (LoginReq->DataSegmentLength);
664
665 KeyLen = (UINT32) AsciiStrLen (Key);
666 ValueLen = (UINT32) AsciiStrLen (Value);
667
668 //
669 // 1 byte for the key value separator '=' and 1 byte for the null
670 // delimiter after the value.
671 //
672 TotalLen = KeyLen + 1 + ValueLen + 1;
673
674 //
675 // Allocate the space for the key-value pair.
676 //
677 Data = (CHAR8 *) NetbufAllocSpace (Pdu, TotalLen, NET_BUF_TAIL);
678 if (Data == NULL) {
679 return EFI_OUT_OF_RESOURCES;
680 }
681 //
682 // Add the key.
683 //
684 CopyMem (Data, Key, KeyLen);
685 Data += KeyLen;
686
687 *Data = '=';
688 Data++;
689
690 //
691 // Add the value.
692 //
693 CopyMem (Data, Value, ValueLen);
694 Data += ValueLen;
695
696 *Data = '\0';
697
698 //
699 // Update the DataSegmentLength
700 //
701 ISCSI_SET_DATASEG_LEN (LoginReq, DataSegLen + TotalLen);
702
703 return EFI_SUCCESS;
704 }
705
706
707 /**
708 Prepare the iSCSI login request to be sent according to the current login status.
709
710 @param[in, out] Conn The connection in the iSCSI login phase.
711
712 @return The pointer to the net buffer containing the iSCSI login request built.
713 @retval NULL Other errors as indicated.
714
715 **/
716 NET_BUF *
717 IScsiPrepareLoginReq (
718 IN OUT ISCSI_CONNECTION *Conn
719 )
720 {
721 ISCSI_SESSION *Session;
722 NET_BUF *Nbuf;
723 ISCSI_LOGIN_REQUEST *LoginReq;
724 EFI_STATUS Status;
725
726 Session = Conn->Session;
727
728 Nbuf = NetbufAlloc (sizeof (ISCSI_LOGIN_REQUEST) + DEFAULT_MAX_RECV_DATA_SEG_LEN);
729 if (Nbuf == NULL) {
730 return NULL;
731 }
732
733 LoginReq = (ISCSI_LOGIN_REQUEST *) NetbufAllocSpace (Nbuf, sizeof (ISCSI_LOGIN_REQUEST), NET_BUF_TAIL);
734 if (LoginReq == NULL) {
735 NetbufFree (Nbuf);
736 return NULL;
737 }
738 ZeroMem (LoginReq, sizeof (ISCSI_LOGIN_REQUEST));
739
740 //
741 // Init the login request pdu
742 //
743 ISCSI_SET_OPCODE (LoginReq, ISCSI_OPCODE_LOGIN_REQ, ISCSI_REQ_IMMEDIATE);
744 ISCSI_SET_STAGES (LoginReq, Conn->CurrentStage, Conn->NextStage);
745 LoginReq->VersionMax = ISCSI_VERSION_MAX;
746 LoginReq->VersionMin = ISCSI_VERSION_MIN;
747 LoginReq->Tsih = HTONS (Session->Tsih);
748 LoginReq->InitiatorTaskTag = HTONL (Session->InitiatorTaskTag);
749 LoginReq->Cid = HTONS (Conn->Cid);
750 LoginReq->CmdSN = HTONL (Session->CmdSN);
751
752 //
753 // For the first Login Request on a coonection this is ExpStatSN for the
754 // old connection, and this field is only valid if the Login Request restarts
755 // a connection.
756 // For subsequent Login Requests it is used to acknowledge the Login Responses
757 // with their increasing StatSN values.
758 //
759 LoginReq->ExpStatSN = HTONL (Conn->ExpStatSN);
760 CopyMem (LoginReq->Isid, Session->Isid, sizeof (LoginReq->Isid));
761
762 if (Conn->PartialRspRcvd) {
763 //
764 // A partial response. The initiator must send an empty Login Request.
765 //
766 return Nbuf;
767 }
768
769 Status = EFI_SUCCESS;
770
771 switch (Conn->CurrentStage) {
772 case ISCSI_SECURITY_NEGOTIATION:
773 //
774 // Both none authentication and CHAP authentication share the CHAP path.
775 //
776 //
777 if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
778 Status = IScsiCHAPToSendReq (Conn, Nbuf);
779 }
780
781 break;
782
783 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
784 //
785 // Only negotiate the paramter once.
786 //
787 if (!Conn->ParamNegotiated) {
788 IScsiFillOpParams (Conn, Nbuf);
789 }
790
791 ISCSI_SET_FLAG (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
792 break;
793
794 default:
795 //
796 // An error occurs...
797 //
798 Status = EFI_DEVICE_ERROR;
799 break;
800 }
801
802 if (EFI_ERROR (Status)) {
803 NetbufFree (Nbuf);
804 Nbuf = NULL;
805 } else {
806 //
807 // Pad the data segment if needed.
808 //
809 IScsiPadSegment (Nbuf, ISCSI_GET_DATASEG_LEN (LoginReq));
810 //
811 // Check whether we will issue the stage transition signal?
812 //
813 Conn->TransitInitiated = ISCSI_FLAG_ON (LoginReq, ISCSI_LOGIN_REQ_PDU_FLAG_TRANSIT);
814 }
815
816 return Nbuf;
817 }
818
819
820 /**
821 Process the iSCSI Login Response.
822
823 @param[in, out] Conn The connection on which the iSCSI login response is received.
824 @param[in, out] Pdu The iSCSI login response PDU.
825
826 @retval EFI_SUCCESS The iSCSI login response PDU is processed, and all checks are passed.
827 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
828 @retval EFI_MEDIA_CHANGED Target is redirected.
829 @retval Others Other errors as indicated.
830
831 **/
832 EFI_STATUS
833 IScsiProcessLoginRsp (
834 IN OUT ISCSI_CONNECTION *Conn,
835 IN OUT NET_BUF *Pdu
836 )
837 {
838 EFI_STATUS Status;
839 ISCSI_SESSION *Session;
840 ISCSI_LOGIN_RESPONSE *LoginRsp;
841 BOOLEAN Transit;
842 BOOLEAN Continue;
843 UINT8 CurrentStage;
844 UINT8 NextStage;
845 UINT8 *DataSeg;
846 UINT32 DataSegLen;
847
848 Status = EFI_SUCCESS;
849 Session = Conn->Session;
850
851 LoginRsp = (ISCSI_LOGIN_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
852 if (LoginRsp == NULL) {
853 return EFI_PROTOCOL_ERROR;
854 }
855 if (!ISCSI_CHECK_OPCODE (LoginRsp, ISCSI_OPCODE_LOGIN_RSP)) {
856 //
857 // It is not a Login Response.
858 //
859 return EFI_PROTOCOL_ERROR;
860 }
861 //
862 // Get the data segment, if any.
863 //
864 DataSegLen = ISCSI_GET_DATASEG_LEN (LoginRsp);
865 if (DataSegLen != 0) {
866 DataSeg = NetbufGetByte (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NULL);
867 } else {
868 DataSeg = NULL;
869 }
870 //
871 // Check the status class in the login response PDU.
872 //
873 switch (LoginRsp->StatusClass) {
874 case ISCSI_LOGIN_STATUS_SUCCESS:
875 //
876 // Just break here; the response and the data segment will be processed later.
877 //
878 break;
879
880 case ISCSI_LOGIN_STATUS_REDIRECTION:
881 //
882 // The target may be moved to a different address.
883 //
884 if (DataSeg == NULL) {
885 return EFI_PROTOCOL_ERROR;
886 }
887 //
888 // Process the TargetAddress key-value strings in the data segment to update the
889 // target address info.
890 //
891 Status = IScsiUpdateTargetAddress (Session, (CHAR8 *) DataSeg, DataSegLen);
892 if (EFI_ERROR (Status)) {
893 return Status;
894 }
895 //
896 // Session will be restarted on this error status because the Target is
897 // redirected by this Login Response.
898 //
899 return EFI_MEDIA_CHANGED;
900
901 default:
902 //
903 // Initiator Error, Target Error, or any other undefined error code.
904 //
905 return EFI_PROTOCOL_ERROR;
906 }
907 //
908 // The status is success; extract the wanted fields from the header segment.
909 //
910 Transit = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_TRANSIT);
911 Continue = ISCSI_FLAG_ON (LoginRsp, ISCSI_LOGIN_RSP_PDU_FLAG_CONTINUE);
912
913 CurrentStage = ISCSI_GET_CURRENT_STAGE (LoginRsp);
914 NextStage = ISCSI_GET_NEXT_STAGE (LoginRsp);
915
916 LoginRsp->InitiatorTaskTag = NTOHL (LoginRsp->InitiatorTaskTag);
917
918 if ((Transit && Continue) ||
919 (CurrentStage != Conn->CurrentStage) ||
920 (!Conn->TransitInitiated && Transit) ||
921 (Transit && (NextStage != Conn->NextStage)) ||
922 (CompareMem (Session->Isid, LoginRsp->Isid, sizeof (LoginRsp->Isid)) != 0) ||
923 (LoginRsp->InitiatorTaskTag != Session->InitiatorTaskTag)
924 ) {
925 //
926 // A Login Response with the C bit set to 1 MUST have the T bit set to 0.
927 // The CSG in the Login Response MUST be the same with the I-end of this connection.
928 // The T bit can't be 1 if the last Login Response sent by the initiator doesn't
929 // initiate the transistion.
930 // The NSG MUST be the same with the I-end of this connection if Transit is required.
931 // The ISID in the Login Response MUST be the same with this session.
932 //
933 return EFI_PROTOCOL_ERROR;
934 }
935
936 LoginRsp->StatSN = NTOHL (LoginRsp->StatSN);
937 LoginRsp->ExpCmdSN = NTOHL (LoginRsp->ExpCmdSN);
938 LoginRsp->MaxCmdSN = NTOHL (LoginRsp->MaxCmdSN);
939
940 if ((Conn->CurrentStage == ISCSI_SECURITY_NEGOTIATION) && (Conn->AuthStep == ISCSI_AUTH_INITIAL)) {
941 //
942 // If the Login Request is a leading Login Request, the target MUST use
943 // the value presented in CmdSN as the target value for ExpCmdSN.
944 //
945 if ((Session->State == SESSION_STATE_FREE) && (Session->CmdSN != LoginRsp->ExpCmdSN)) {
946 return EFI_PROTOCOL_ERROR;
947 }
948
949 //
950 // It's the initial Login Response, initialize the local ExpStatSN, MaxCmdSN
951 // and ExpCmdSN.
952 //
953 Conn->ExpStatSN = LoginRsp->StatSN + 1;
954 Session->MaxCmdSN = LoginRsp->MaxCmdSN;
955 Session->ExpCmdSN = LoginRsp->ExpCmdSN;
956 } else {
957 //
958 // Check the StatSN of this PDU.
959 //
960 Status = IScsiCheckSN (&Conn->ExpStatSN, LoginRsp->StatSN);
961 if (!EFI_ERROR (Status)) {
962 //
963 // Update the MaxCmdSN and ExpCmdSN.
964 //
965 IScsiUpdateCmdSN (Session, LoginRsp->MaxCmdSN, LoginRsp->ExpCmdSN);
966 } else {
967 return Status;
968 }
969 }
970 //
971 // Trim off the header segment.
972 //
973 NetbufTrim (Pdu, sizeof (ISCSI_LOGIN_RESPONSE), NET_BUF_HEAD);
974
975 //
976 // Queue this login response first in case it's a partial response so that
977 // later when the full response list is received we can combine these scattered
978 // responses' data segment and then process it.
979 //
980 NET_GET_REF (Pdu);
981 NetbufQueAppend (&Conn->RspQue, Pdu);
982
983 Conn->PartialRspRcvd = Continue;
984 if (Continue) {
985 //
986 // It is a partial response; must wait for another or more Request/Response
987 // conversations to get the full response.
988 //
989 return EFI_SUCCESS;
990 }
991
992 switch (CurrentStage) {
993 case ISCSI_SECURITY_NEGOTIATION:
994 //
995 // In security negotiation stage, let CHAP module handle it.
996 //
997 if (Session->AuthType != ISCSI_AUTH_TYPE_KRB) {
998 Status = IScsiCHAPOnRspReceived (Conn);
999 }
1000 break;
1001
1002 case ISCSI_LOGIN_OPERATIONAL_NEGOTIATION:
1003 //
1004 // Response received with negotiation response on iSCSI parameters: check them.
1005 //
1006 Status = IScsiCheckOpParams (Conn);
1007 if (!EFI_ERROR (Status)) {
1008 Conn->ParamNegotiated = TRUE;
1009 }
1010
1011 break;
1012
1013 default:
1014 //
1015 // Should never get here.
1016 //
1017 Status = EFI_PROTOCOL_ERROR;
1018 break;
1019 }
1020
1021 if (Transit && (Status == EFI_SUCCESS)) {
1022 //
1023 // Do the state transition.
1024 //
1025 Conn->CurrentStage = Conn->NextStage;
1026
1027 if (Conn->CurrentStage == ISCSI_LOGIN_OPERATIONAL_NEGOTIATION) {
1028 Conn->NextStage = ISCSI_FULL_FEATURE_PHASE;
1029 } else {
1030 //
1031 // CurrentStage is iSCSI Full Feature. It is the Login-Final Response;
1032 // get the TSIH from the Login Response.
1033 //
1034 Session->Tsih = NTOHS (LoginRsp->Tsih);
1035 }
1036 }
1037 //
1038 // Flush the response(s) received.
1039 //
1040 NetbufQueFlush (&Conn->RspQue);
1041
1042 return Status;
1043 }
1044
1045
1046 /**
1047 Updated the target information according the data received in the iSCSI
1048 login response with an target redirection status.
1049
1050 @param[in, out] Session The iSCSI session.
1051 @param[in] Data The data segment that should contain the
1052 TargetAddress key-value list.
1053 @param[in] Len Length of the data.
1054
1055 @retval EFI_SUCCESS The target address is updated.
1056 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1057 @retval EFI_NOT_FOUND The TargetAddress key is not found.
1058 @retval Others Other errors as indicated.
1059
1060 **/
1061 EFI_STATUS
1062 IScsiUpdateTargetAddress (
1063 IN OUT ISCSI_SESSION *Session,
1064 IN CHAR8 *Data,
1065 IN UINT32 Len
1066 )
1067 {
1068 LIST_ENTRY *KeyValueList;
1069 CHAR8 *TargetAddress;
1070 CHAR8 *IpStr;
1071 EFI_STATUS Status;
1072 UINTN Number;
1073 UINT8 IpMode;
1074
1075 KeyValueList = IScsiBuildKeyValueList (Data, Len);
1076 if (KeyValueList == NULL) {
1077 return EFI_OUT_OF_RESOURCES;
1078 }
1079
1080 Status = EFI_NOT_FOUND;
1081
1082 while (TRUE) {
1083 TargetAddress = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ADDRESS);
1084 if (TargetAddress == NULL) {
1085 break;
1086 }
1087
1088 if (!NET_IS_DIGIT (TargetAddress[0])) {
1089 //
1090 // The domainname of the target may be presented in three formats: a DNS host name,
1091 // a dotted-decimal IPv4 address, or a bracketed IPv6 address. Only accept dotted
1092 // IPv4 address.
1093 //
1094 continue;
1095 }
1096
1097 IpStr = TargetAddress;
1098
1099 while ((*TargetAddress != 0) && (*TargetAddress != ':') && (*TargetAddress != ',')) {
1100 //
1101 // NULL, ':', or ',' ends the IPv4 string.
1102 //
1103 TargetAddress++;
1104 }
1105
1106 if (*TargetAddress == ',') {
1107 //
1108 // Comma and the portal group tag MUST be ommitted if the TargetAddress is sent
1109 // as the result of a redirection.
1110 //
1111 continue;
1112 } else if (*TargetAddress == ':') {
1113 *TargetAddress = '\0';
1114
1115 TargetAddress++;
1116
1117 Number = AsciiStrDecimalToUintn (TargetAddress);
1118 if (Number > 0xFFFF) {
1119 continue;
1120 } else {
1121 Session->ConfigData->SessionConfigData.TargetPort = (UINT16) Number;
1122 }
1123 } else {
1124 //
1125 // The string only contains the IPv4 address. Use the well-known port.
1126 //
1127 Session->ConfigData->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
1128 }
1129 //
1130 // Update the target IP address.
1131 //
1132 if (Session->ConfigData->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {
1133 IpMode = Session->ConfigData->SessionConfigData.IpMode;
1134 } else {
1135 IpMode = Session->ConfigData->AutoConfigureMode;
1136 }
1137
1138 Status = IScsiAsciiStrToIp (
1139 IpStr,
1140 IpMode,
1141 &Session->ConfigData->SessionConfigData.TargetIp
1142 );
1143
1144 if (EFI_ERROR (Status)) {
1145 continue;
1146 } else {
1147 break;
1148 }
1149 }
1150
1151 IScsiFreeKeyValueList (KeyValueList);
1152
1153 return Status;
1154 }
1155
1156
1157 /**
1158 The callback function to free the net buffer list.
1159
1160 @param[in] Arg The opaque parameter.
1161
1162 **/
1163 VOID
1164 EFIAPI
1165 IScsiFreeNbufList (
1166 VOID *Arg
1167 )
1168 {
1169 ASSERT (Arg != NULL);
1170
1171 NetbufFreeList ((LIST_ENTRY *) Arg);
1172 FreePool (Arg);
1173 }
1174
1175
1176 /**
1177 The callback function called in NetBufFree; it does nothing.
1178
1179 @param[in] Arg The opaque parameter.
1180
1181 **/
1182 VOID
1183 EFIAPI
1184 IScsiNbufExtFree (
1185 VOID *Arg
1186 )
1187 {
1188 }
1189
1190
1191 /**
1192 Receive an iSCSI response PDU. An iSCSI response PDU contains an iSCSI PDU header and
1193 an optional data segment. The two parts will be put into two blocks of buffers in the
1194 net buffer. The digest check will be conducted in this function if needed and the digests
1195 will be trimmed from the PDU buffer.
1196
1197 @param[in] Conn The iSCSI connection to receive data from.
1198 @param[out] Pdu The received iSCSI pdu.
1199 @param[in] Context The context used to describe information on the caller provided
1200 buffer to receive data segment of the iSCSI pdu. It is optional.
1201 @param[in] HeaderDigest Whether there will be header digest received.
1202 @param[in] DataDigest Whether there will be data digest.
1203 @param[in] TimeoutEvent The timeout event. It is optional.
1204
1205 @retval EFI_SUCCESS An iSCSI pdu is received.
1206 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1207 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1208 @retval Others Other errors as indicated.
1209
1210 **/
1211 EFI_STATUS
1212 IScsiReceivePdu (
1213 IN ISCSI_CONNECTION *Conn,
1214 OUT NET_BUF **Pdu,
1215 IN ISCSI_IN_BUFFER_CONTEXT *Context, OPTIONAL
1216 IN BOOLEAN HeaderDigest,
1217 IN BOOLEAN DataDigest,
1218 IN EFI_EVENT TimeoutEvent OPTIONAL
1219 )
1220 {
1221 LIST_ENTRY *NbufList;
1222 UINT32 Len;
1223 NET_BUF *PduHdr;
1224 UINT8 *Header;
1225 EFI_STATUS Status;
1226 UINT32 PadLen;
1227 UINT32 InDataOffset;
1228 NET_FRAGMENT Fragment[2];
1229 UINT32 FragmentCount;
1230 NET_BUF *DataSeg;
1231 UINT32 PadAndCRC32[2];
1232
1233 NbufList = AllocatePool (sizeof (LIST_ENTRY));
1234 if (NbufList == NULL) {
1235 return EFI_OUT_OF_RESOURCES;
1236 }
1237
1238 InitializeListHead (NbufList);
1239
1240 //
1241 // The header digest will be received together with the PDU header, if exists.
1242 //
1243 Len = sizeof (ISCSI_BASIC_HEADER) + (HeaderDigest ? sizeof (UINT32) : 0);
1244 PduHdr = NetbufAlloc (Len);
1245 if (PduHdr == NULL) {
1246 Status = EFI_OUT_OF_RESOURCES;
1247 goto ON_EXIT;
1248 }
1249
1250 Header = NetbufAllocSpace (PduHdr, Len, NET_BUF_TAIL);
1251 if (Header == NULL) {
1252 Status = EFI_OUT_OF_RESOURCES;
1253 goto ON_EXIT;
1254 }
1255 InsertTailList (NbufList, &PduHdr->List);
1256
1257 //
1258 // First step, receive the BHS of the PDU.
1259 //
1260 Status = TcpIoReceive (&Conn->TcpIo, PduHdr, FALSE, TimeoutEvent);
1261
1262 if (EFI_ERROR (Status)) {
1263 goto ON_EXIT;
1264 }
1265
1266 if (HeaderDigest) {
1267 //
1268 // TODO: check the header-digest.
1269 //
1270 //
1271 // Trim off the digest.
1272 //
1273 NetbufTrim (PduHdr, sizeof (UINT32), NET_BUF_TAIL);
1274 }
1275
1276 Len = ISCSI_GET_DATASEG_LEN (Header);
1277 if (Len == 0) {
1278 //
1279 // No data segment.
1280 //
1281 goto FORM_PDU;
1282 }
1283 //
1284 // Get the length of the padding bytes of the data segment.
1285 //
1286 PadLen = ISCSI_GET_PAD_LEN (Len);
1287
1288 switch (ISCSI_GET_OPCODE (Header)) {
1289 case ISCSI_OPCODE_SCSI_DATA_IN:
1290 //
1291 // To reduce memory copy overhead, try to use the buffer described by Context
1292 // if the PDU is an iSCSI SCSI data.
1293 //
1294 InDataOffset = ISCSI_GET_BUFFER_OFFSET (Header);
1295 if ((Context == NULL) || ((InDataOffset + Len) > Context->InDataLen)) {
1296 Status = EFI_PROTOCOL_ERROR;
1297 goto ON_EXIT;
1298 }
1299
1300 Fragment[0].Len = Len;
1301 Fragment[0].Bulk = Context->InData + InDataOffset;
1302
1303 if (DataDigest || (PadLen != 0)) {
1304 //
1305 // The data segment is padded. Use two fragments to receive it:
1306 // the first to receive the useful data; the second to receive the padding.
1307 //
1308 Fragment[1].Len = PadLen + (DataDigest ? sizeof (UINT32) : 0);
1309 Fragment[1].Bulk = (UINT8 *)PadAndCRC32 + (4 - PadLen);
1310
1311 FragmentCount = 2;
1312 } else {
1313 FragmentCount = 1;
1314 }
1315
1316 DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
1317 if (DataSeg == NULL) {
1318 Status = EFI_OUT_OF_RESOURCES;
1319 goto ON_EXIT;
1320 }
1321
1322 break;
1323
1324 case ISCSI_OPCODE_SCSI_RSP:
1325 case ISCSI_OPCODE_NOP_IN:
1326 case ISCSI_OPCODE_LOGIN_RSP:
1327 case ISCSI_OPCODE_TEXT_RSP:
1328 case ISCSI_OPCODE_ASYNC_MSG:
1329 case ISCSI_OPCODE_REJECT:
1330 case ISCSI_OPCODE_VENDOR_T0:
1331 case ISCSI_OPCODE_VENDOR_T1:
1332 case ISCSI_OPCODE_VENDOR_T2:
1333 //
1334 // Allocate buffer to receive the data segment.
1335 //
1336 Len += PadLen + (DataDigest ? sizeof (UINT32) : 0);
1337 DataSeg = NetbufAlloc (Len);
1338 if (DataSeg == NULL) {
1339 Status = EFI_OUT_OF_RESOURCES;
1340 goto ON_EXIT;
1341 }
1342
1343 NetbufAllocSpace (DataSeg, Len, NET_BUF_TAIL);
1344 break;
1345
1346 default:
1347 Status = EFI_PROTOCOL_ERROR;
1348 goto ON_EXIT;
1349 }
1350
1351 InsertTailList (NbufList, &DataSeg->List);
1352
1353 //
1354 // Receive the data segment with the data digest, if any.
1355 //
1356 Status = TcpIoReceive (&Conn->TcpIo, DataSeg, FALSE, TimeoutEvent);
1357
1358 if (EFI_ERROR (Status)) {
1359 goto ON_EXIT;
1360 }
1361
1362 if (DataDigest) {
1363 //
1364 // TODO: Check the data digest.
1365 //
1366 NetbufTrim (DataSeg, sizeof (UINT32), NET_BUF_TAIL);
1367 }
1368
1369 if (PadLen != 0) {
1370 //
1371 // Trim off the padding bytes in the data segment.
1372 //
1373 NetbufTrim (DataSeg, PadLen, NET_BUF_TAIL);
1374 }
1375
1376 FORM_PDU:
1377 //
1378 // Form the pdu from a list of pdu segments.
1379 //
1380 *Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
1381 if (*Pdu == NULL) {
1382 Status = EFI_OUT_OF_RESOURCES;
1383 }
1384
1385 ON_EXIT:
1386
1387 if (EFI_ERROR (Status)) {
1388 //
1389 // Free the Nbufs in this NbufList and the NbufList itself.
1390 //
1391 IScsiFreeNbufList (NbufList);
1392 }
1393
1394 return Status;
1395 }
1396
1397
1398 /**
1399 Check and get the result of the parameter negotiation.
1400
1401 @param[in, out] Conn The connection in iSCSI login.
1402
1403 @retval EFI_SUCCESS The parmeter check is passed and negotiation is finished.
1404 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol error occurred.
1405 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1406
1407 **/
1408 EFI_STATUS
1409 IScsiCheckOpParams (
1410 IN OUT ISCSI_CONNECTION *Conn
1411 )
1412 {
1413 EFI_STATUS Status;
1414 LIST_ENTRY *KeyValueList;
1415 CHAR8 *Data;
1416 UINT32 Len;
1417 ISCSI_SESSION *Session;
1418 CHAR8 *Value;
1419 UINTN NumericValue;
1420
1421 ASSERT (Conn->RspQue.BufNum != 0);
1422
1423 Session = Conn->Session;
1424
1425 Len = Conn->RspQue.BufSize;
1426 Data = AllocatePool (Len);
1427 if (Data == NULL) {
1428 return EFI_OUT_OF_RESOURCES;
1429 }
1430
1431 NetbufQueCopy (&Conn->RspQue, 0, Len, (UINT8 *) Data);
1432
1433 Status = EFI_PROTOCOL_ERROR;
1434
1435 //
1436 // Extract the Key-Value pairs into a list.
1437 //
1438 KeyValueList = IScsiBuildKeyValueList (Data, Len);
1439 if (KeyValueList == NULL) {
1440 FreePool (Data);
1441 return Status;
1442 }
1443 //
1444 // HeaderDigest
1445 //
1446 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_HEADER_DIGEST);
1447 if (Value == NULL) {
1448 goto ON_ERROR;
1449 }
1450
1451 if (AsciiStrCmp (Value, "CRC32") == 0) {
1452 if (Conn->HeaderDigest != IScsiDigestCRC32) {
1453 goto ON_ERROR;
1454 }
1455 } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
1456 Conn->HeaderDigest = IScsiDigestNone;
1457 } else {
1458 goto ON_ERROR;
1459 }
1460 //
1461 // DataDigest
1462 //
1463 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_DIGEST);
1464 if (Value == NULL) {
1465 goto ON_ERROR;
1466 }
1467
1468 if (AsciiStrCmp (Value, "CRC32") == 0) {
1469 if (Conn->DataDigest != IScsiDigestCRC32) {
1470 goto ON_ERROR;
1471 }
1472 } else if (AsciiStrCmp (Value, ISCSI_KEY_VALUE_NONE) == 0) {
1473 Conn->DataDigest = IScsiDigestNone;
1474 } else {
1475 goto ON_ERROR;
1476 }
1477 //
1478 // ErrorRecoveryLevel: result fuction is Minimum.
1479 //
1480 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_ERROR_RECOVERY_LEVEL);
1481 if (Value == NULL) {
1482 goto ON_ERROR;
1483 }
1484
1485 NumericValue = IScsiNetNtoi (Value);
1486 if (NumericValue > 2) {
1487 goto ON_ERROR;
1488 }
1489
1490 Session->ErrorRecoveryLevel = (UINT8) MIN (Session->ErrorRecoveryLevel, NumericValue);
1491
1492 //
1493 // InitialR2T: result function is OR.
1494 //
1495 if (!Session->InitialR2T) {
1496 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
1497 if (Value == NULL) {
1498 goto ON_ERROR;
1499 }
1500
1501 Session->InitialR2T = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
1502 }
1503
1504 //
1505 // ImmediateData: result function is AND.
1506 //
1507 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_IMMEDIATE_DATA);
1508 if (Value == NULL) {
1509 goto ON_ERROR;
1510 }
1511
1512 Session->ImmediateData = (BOOLEAN) (Session->ImmediateData && (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0));
1513
1514 //
1515 // MaxRecvDataSegmentLength is declarative.
1516 //
1517 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH);
1518 if (Value != NULL) {
1519 Conn->MaxRecvDataSegmentLength = (UINT32) IScsiNetNtoi (Value);
1520 }
1521 //
1522 // MaxBurstLength: result funtion is Mininum.
1523 //
1524 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_BURST_LENGTH);
1525 if (Value == NULL) {
1526 goto ON_ERROR;
1527 }
1528
1529 NumericValue = IScsiNetNtoi (Value);
1530 Session->MaxBurstLength = (UINT32) MIN (Session->MaxBurstLength, NumericValue);
1531
1532 //
1533 // FirstBurstLength: result function is Minimum. Irrelevant when InitialR2T=Yes and
1534 // ImmediateData=No.
1535 //
1536 if (!(Session->InitialR2T && !Session->ImmediateData)) {
1537 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
1538 if (Value == NULL) {
1539 goto ON_ERROR;
1540 }
1541
1542 NumericValue = IScsiNetNtoi (Value);
1543 Session->FirstBurstLength = (UINT32) MIN (Session->FirstBurstLength, NumericValue);
1544 }
1545
1546 //
1547 // MaxConnections: result function is Minimum.
1548 //
1549 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_CONNECTIONS);
1550 if (Value == NULL) {
1551 goto ON_ERROR;
1552 }
1553
1554 NumericValue = IScsiNetNtoi (Value);
1555 if ((NumericValue == 0) || (NumericValue > 65535)) {
1556 goto ON_ERROR;
1557 }
1558
1559 Session->MaxConnections = (UINT32) MIN (Session->MaxConnections, NumericValue);
1560
1561 //
1562 // DataPDUInOrder: result function is OR.
1563 //
1564 if (!Session->DataPDUInOrder) {
1565 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
1566 if (Value == NULL) {
1567 goto ON_ERROR;
1568 }
1569
1570 Session->DataPDUInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
1571 }
1572
1573 //
1574 // DataSequenceInorder: result function is OR.
1575 //
1576 if (!Session->DataSequenceInOrder) {
1577 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
1578 if (Value == NULL) {
1579 goto ON_ERROR;
1580 }
1581
1582 Session->DataSequenceInOrder = (BOOLEAN) (AsciiStrCmp (Value, "Yes") == 0);
1583 }
1584
1585 //
1586 // DefaultTime2Wait: result function is Maximum.
1587 //
1588 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2WAIT);
1589 if (Value == NULL) {
1590 goto ON_ERROR;
1591 }
1592
1593 NumericValue = IScsiNetNtoi (Value);
1594 if (NumericValue == 0) {
1595 Session->DefaultTime2Wait = 0;
1596 } else if (NumericValue > 3600) {
1597 goto ON_ERROR;
1598 } else {
1599 Session->DefaultTime2Wait = (UINT32) MAX (Session->DefaultTime2Wait, NumericValue);
1600 }
1601 //
1602 // DefaultTime2Retain: result function is Minimum.
1603 //
1604 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DEFAULT_TIME2RETAIN);
1605 if (Value == NULL) {
1606 goto ON_ERROR;
1607 }
1608
1609 NumericValue = IScsiNetNtoi (Value);
1610 if (NumericValue == 0) {
1611 Session->DefaultTime2Retain = 0;
1612 } else if (NumericValue > 3600) {
1613 goto ON_ERROR;
1614 } else {
1615 Session->DefaultTime2Retain = (UINT32) MIN (Session->DefaultTime2Retain, NumericValue);
1616 }
1617 //
1618 // MaxOutstandingR2T: result function is Minimum.
1619 //
1620 Value = IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_MAX_OUTSTANDING_R2T);
1621 if (Value == NULL) {
1622 goto ON_ERROR;
1623 }
1624
1625 NumericValue = IScsiNetNtoi (Value);
1626 if ((NumericValue == 0) || (NumericValue > 65535)) {
1627 goto ON_ERROR;
1628 }
1629
1630 Session->MaxOutstandingR2T = (UINT16) MIN (Session->MaxOutstandingR2T, NumericValue);
1631
1632 //
1633 // Remove declarative key-value pairs, if any.
1634 //
1635 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_SESSION_TYPE);
1636 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_ALIAS);
1637 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_TARGET_PORTAL_GROUP_TAG);
1638
1639
1640 //
1641 // Remove the key-value that may not needed for result function is OR.
1642 //
1643 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_INITIAL_R2T);
1644 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_PDU_IN_ORDER);
1645 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER);
1646
1647 //
1648 // Remove irrelevant parameter, if any.
1649 //
1650 if (Session->InitialR2T && !Session->ImmediateData) {
1651 IScsiGetValueByKeyFromList (KeyValueList, ISCSI_KEY_FIRST_BURST_LENGTH);
1652 }
1653
1654 if (IsListEmpty (KeyValueList)) {
1655 //
1656 // Succeed if no more keys in the list.
1657 //
1658 Status = EFI_SUCCESS;
1659 }
1660
1661 ON_ERROR:
1662
1663 IScsiFreeKeyValueList (KeyValueList);
1664
1665 FreePool (Data);
1666
1667 return Status;
1668 }
1669
1670
1671 /**
1672 Fill the operational parameters.
1673
1674 @param[in] Conn The connection in iSCSI login.
1675 @param[in, out] Pdu The iSCSI login request PDU to fill the parameters.
1676
1677 **/
1678 VOID
1679 IScsiFillOpParams (
1680 IN ISCSI_CONNECTION *Conn,
1681 IN OUT NET_BUF *Pdu
1682 )
1683 {
1684 ISCSI_SESSION *Session;
1685 CHAR8 Value[256];
1686
1687 Session = Conn->Session;
1688
1689 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->HeaderDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
1690 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_HEADER_DIGEST, Value);
1691
1692 AsciiSPrint (Value, sizeof (Value), "%a", (Conn->DataDigest == IScsiDigestCRC32) ? "None,CRC32" : "None");
1693 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_DIGEST, Value);
1694
1695 AsciiSPrint (Value, sizeof (Value), "%d", Session->ErrorRecoveryLevel);
1696 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_ERROR_RECOVERY_LEVEL, Value);
1697
1698 AsciiSPrint (Value, sizeof (Value), "%a", Session->InitialR2T ? "Yes" : "No");
1699 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_INITIAL_R2T, Value);
1700
1701 AsciiSPrint (Value, sizeof (Value), "%a", Session->ImmediateData ? "Yes" : "No");
1702 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_IMMEDIATE_DATA, Value);
1703
1704 AsciiSPrint (Value, sizeof (Value), "%d", MAX_RECV_DATA_SEG_LEN_IN_FFP);
1705 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_RECV_DATA_SEGMENT_LENGTH, Value);
1706
1707 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxBurstLength);
1708 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_BURST_LENGTH, Value);
1709
1710 AsciiSPrint (Value, sizeof (Value), "%d", Session->FirstBurstLength);
1711 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_FIRST_BURST_LENGTH, Value);
1712
1713 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxConnections);
1714 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_CONNECTIONS, Value);
1715
1716 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataPDUInOrder ? "Yes" : "No");
1717 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_PDU_IN_ORDER, Value);
1718
1719 AsciiSPrint (Value, sizeof (Value), "%a", Session->DataSequenceInOrder ? "Yes" : "No");
1720 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DATA_SEQUENCE_IN_ORDER, Value);
1721
1722 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Wait);
1723 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2WAIT, Value);
1724
1725 AsciiSPrint (Value, sizeof (Value), "%d", Session->DefaultTime2Retain);
1726 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_DEFAULT_TIME2RETAIN, Value);
1727
1728 AsciiSPrint (Value, sizeof (Value), "%d", Session->MaxOutstandingR2T);
1729 IScsiAddKeyValuePair (Pdu, ISCSI_KEY_MAX_OUTSTANDING_R2T, Value);
1730 }
1731
1732
1733 /**
1734 Pad the iSCSI AHS or data segment to an integer number of 4 byte words.
1735
1736 @param[in, out] Pdu The iSCSI pdu which contains segments to pad.
1737 @param[in] Len The length of the last segment in the PDU.
1738
1739 @retval EFI_SUCCESS The segment is padded or there is no need to pad it.
1740 @retval EFI_OUT_OF_RESOURCES There is not enough remaining free space to add the
1741 padding bytes.
1742 **/
1743 EFI_STATUS
1744 IScsiPadSegment (
1745 IN OUT NET_BUF *Pdu,
1746 IN UINT32 Len
1747 )
1748 {
1749 UINT32 PadLen;
1750 UINT8 *Data;
1751
1752 PadLen = ISCSI_GET_PAD_LEN (Len);
1753
1754 if (PadLen != 0) {
1755 Data = NetbufAllocSpace (Pdu, PadLen, NET_BUF_TAIL);
1756 if (Data == NULL) {
1757 return EFI_OUT_OF_RESOURCES;
1758 }
1759
1760 ZeroMem (Data, PadLen);
1761 }
1762
1763 return EFI_SUCCESS;
1764 }
1765
1766
1767 /**
1768 Build a key-value list from the data segment.
1769
1770 @param[in] Data The data segment containing the key-value pairs.
1771 @param[in] Len Length of the data segment.
1772
1773 @return The key-value list.
1774 @retval NULL Other errors as indicated.
1775
1776 **/
1777 LIST_ENTRY *
1778 IScsiBuildKeyValueList (
1779 IN CHAR8 *Data,
1780 IN UINT32 Len
1781 )
1782 {
1783 LIST_ENTRY *ListHead;
1784 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1785
1786 ListHead = AllocatePool (sizeof (LIST_ENTRY));
1787 if (ListHead == NULL) {
1788 return NULL;
1789 }
1790
1791 InitializeListHead (ListHead);
1792
1793 while (Len > 0) {
1794 KeyValuePair = AllocatePool (sizeof (ISCSI_KEY_VALUE_PAIR));
1795 if (KeyValuePair == NULL) {
1796 goto ON_ERROR;
1797 }
1798
1799 InitializeListHead (&KeyValuePair->List);
1800
1801 KeyValuePair->Key = Data;
1802
1803 while ((Len > 0) && (*Data != '=')) {
1804 Len--;
1805 Data++;
1806 }
1807
1808 if (*Data == '=') {
1809 *Data = '\0';
1810
1811 Data++;
1812 Len--;
1813 } else {
1814 FreePool (KeyValuePair);
1815 goto ON_ERROR;
1816 }
1817
1818 KeyValuePair->Value = Data;
1819
1820 InsertTailList (ListHead, &KeyValuePair->List);;
1821
1822 Data += AsciiStrLen (KeyValuePair->Value) + 1;
1823 Len -= (UINT32) AsciiStrLen (KeyValuePair->Value) + 1;
1824 }
1825
1826 return ListHead;
1827
1828 ON_ERROR:
1829
1830 IScsiFreeKeyValueList (ListHead);
1831
1832 return NULL;
1833 }
1834
1835
1836 /**
1837 Get the value string by the key name from the key-value list. If found,
1838 the key-value entry will be removed from the list.
1839
1840 @param[in, out] KeyValueList The key-value list.
1841 @param[in] Key The key name to find.
1842
1843 @return The value string.
1844 @retval NULL The key value pair cannot be found.
1845
1846 **/
1847 CHAR8 *
1848 IScsiGetValueByKeyFromList (
1849 IN OUT LIST_ENTRY *KeyValueList,
1850 IN CHAR8 *Key
1851 )
1852 {
1853 LIST_ENTRY *Entry;
1854 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1855 CHAR8 *Value;
1856
1857 Value = NULL;
1858
1859 NET_LIST_FOR_EACH (Entry, KeyValueList) {
1860 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
1861
1862 if (AsciiStrCmp (KeyValuePair->Key, Key) == 0) {
1863 Value = KeyValuePair->Value;
1864
1865 RemoveEntryList (&KeyValuePair->List);
1866 FreePool (KeyValuePair);
1867 break;
1868 }
1869 }
1870
1871 return Value;
1872 }
1873
1874
1875 /**
1876 Free the key-value list.
1877
1878 @param[in] KeyValueList The key-value list.
1879
1880 **/
1881 VOID
1882 IScsiFreeKeyValueList (
1883 IN LIST_ENTRY *KeyValueList
1884 )
1885 {
1886 LIST_ENTRY *Entry;
1887 ISCSI_KEY_VALUE_PAIR *KeyValuePair;
1888
1889 while (!IsListEmpty (KeyValueList)) {
1890 Entry = NetListRemoveHead (KeyValueList);
1891 KeyValuePair = NET_LIST_USER_STRUCT (Entry, ISCSI_KEY_VALUE_PAIR, List);
1892
1893 FreePool (KeyValuePair);
1894 }
1895
1896 FreePool (KeyValueList);
1897 }
1898
1899
1900 /**
1901 Normalize the iSCSI name according to RFC.
1902
1903 @param[in, out] Name The iSCSI name.
1904 @param[in] Len Length of the iSCSI name.
1905
1906 @retval EFI_SUCCESS The iSCSI name is valid and normalized.
1907 @retval EFI_PROTOCOL_ERROR The iSCSI name is malformatted or not in the IQN format.
1908
1909 **/
1910 EFI_STATUS
1911 IScsiNormalizeName (
1912 IN OUT CHAR8 *Name,
1913 IN UINTN Len
1914 )
1915 {
1916 UINTN Index;
1917
1918 for (Index = 0; Index < Len; Index++) {
1919 if (NET_IS_UPPER_CASE_CHAR (Name[Index])) {
1920 //
1921 // Convert the upper-case characters to lower-case ones.
1922 //
1923 Name[Index] = (CHAR8) (Name[Index] - 'A' + 'a');
1924 }
1925
1926 if (!NET_IS_LOWER_CASE_CHAR (Name[Index]) &&
1927 !NET_IS_DIGIT (Name[Index]) &&
1928 (Name[Index] != '-') &&
1929 (Name[Index] != '.') &&
1930 (Name[Index] != ':')
1931 ) {
1932 //
1933 // ASCII dash, dot, colon lower-case characters and digit characters
1934 // are allowed.
1935 //
1936 return EFI_PROTOCOL_ERROR;
1937 }
1938 }
1939
1940 if ((Len < 4) || (CompareMem (Name, "iqn.", 4) != 0)) {
1941 //
1942 // Only IQN format is accepted now.
1943 //
1944 return EFI_PROTOCOL_ERROR;
1945 }
1946
1947 return EFI_SUCCESS;
1948 }
1949
1950
1951 /**
1952 Create an iSCSI task control block.
1953
1954 @param[in] Conn The connection on which the task control block will be created.
1955 @param[out] Tcb The newly created task control block.
1956
1957 @retval EFI_SUCCESS The task control block is created.
1958 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
1959 @retval EFI_NOT_READY The target cannot accept new commands.
1960
1961 **/
1962 EFI_STATUS
1963 IScsiNewTcb (
1964 IN ISCSI_CONNECTION *Conn,
1965 OUT ISCSI_TCB **Tcb
1966 )
1967 {
1968 ISCSI_SESSION *Session;
1969 ISCSI_TCB *NewTcb;
1970
1971 ASSERT (Tcb != NULL);
1972
1973 Session = Conn->Session;
1974
1975 if (ISCSI_SEQ_GT (Session->CmdSN, Session->MaxCmdSN)) {
1976 return EFI_NOT_READY;
1977 }
1978
1979 NewTcb = AllocateZeroPool (sizeof (ISCSI_TCB));
1980 if (NewTcb == NULL) {
1981 return EFI_OUT_OF_RESOURCES;
1982 }
1983
1984 InitializeListHead (&NewTcb->Link);
1985
1986 NewTcb->SoFarInOrder = TRUE;
1987 NewTcb->InitiatorTaskTag = Session->InitiatorTaskTag;
1988 NewTcb->CmdSN = Session->CmdSN;
1989 NewTcb->Conn = Conn;
1990
1991 InsertTailList (&Session->TcbList, &NewTcb->Link);
1992
1993 //
1994 // Advance the initiator task tag.
1995 //
1996 Session->InitiatorTaskTag++;
1997 Session->CmdSN++;
1998
1999 *Tcb = NewTcb;
2000
2001 return EFI_SUCCESS;
2002 }
2003
2004
2005 /**
2006 Delete the tcb from the connection and destroy it.
2007
2008 @param[in] Tcb The tcb to delete.
2009
2010 **/
2011 VOID
2012 IScsiDelTcb (
2013 IN ISCSI_TCB *Tcb
2014 )
2015 {
2016 RemoveEntryList (&Tcb->Link);
2017
2018 FreePool (Tcb);
2019 }
2020
2021
2022 /**
2023 Find the task control block by the initator task tag.
2024
2025 @param[in] TcbList The tcb list.
2026 @param[in] InitiatorTaskTag The initiator task tag.
2027
2028 @return The task control block found.
2029 @retval NULL The task control block cannot be found.
2030
2031 **/
2032 ISCSI_TCB *
2033 IScsiFindTcbByITT (
2034 IN LIST_ENTRY *TcbList,
2035 IN UINT32 InitiatorTaskTag
2036 )
2037 {
2038 ISCSI_TCB *Tcb;
2039 LIST_ENTRY *Entry;
2040
2041 Tcb = NULL;
2042
2043 NET_LIST_FOR_EACH (Entry, TcbList) {
2044 Tcb = NET_LIST_USER_STRUCT (Entry, ISCSI_TCB, Link);
2045
2046 if (Tcb->InitiatorTaskTag == InitiatorTaskTag) {
2047 break;
2048 }
2049 }
2050
2051 return Tcb;
2052 }
2053
2054
2055 /**
2056 Create a data segment, pad it, and calculate the CRC if needed.
2057
2058 @param[in] Data The data to fill into the data segment.
2059 @param[in] Len Length of the data.
2060 @param[in] DataDigest Whether to calculate CRC for this data segment.
2061
2062 @return The net buffer wrapping the data segment.
2063
2064 **/
2065 NET_BUF *
2066 IScsiNewDataSegment (
2067 IN UINT8 *Data,
2068 IN UINT32 Len,
2069 IN BOOLEAN DataDigest
2070 )
2071 {
2072 NET_FRAGMENT Fragment[2];
2073 UINT32 FragmentCount;
2074 UINT32 PadLen;
2075 NET_BUF *DataSeg;
2076
2077 Fragment[0].Len = Len;
2078 Fragment[0].Bulk = Data;
2079
2080 PadLen = ISCSI_GET_PAD_LEN (Len);
2081 if (PadLen != 0) {
2082 Fragment[1].Len = PadLen;
2083 Fragment[1].Bulk = (UINT8 *) &mDataSegPad;
2084
2085 FragmentCount = 2;
2086 } else {
2087 FragmentCount = 1;
2088 }
2089
2090 DataSeg = NetbufFromExt (&Fragment[0], FragmentCount, 0, 0, IScsiNbufExtFree, NULL);
2091
2092 return DataSeg;
2093 }
2094
2095
2096 /**
2097 Create a iSCSI SCSI command PDU to encapsulate the command issued
2098 by SCSI through the EXT SCSI PASS THRU Protocol.
2099
2100 @param[in] Packet The EXT SCSI PASS THRU request packet containing the SCSI command.
2101 @param[in] Lun The LUN.
2102 @param[in] Tcb The tcb assocated with this SCSI command.
2103
2104 @return The created iSCSI SCSI command PDU.
2105 @retval NULL Other errors as indicated.
2106
2107 **/
2108 NET_BUF *
2109 IScsiNewScsiCmdPdu (
2110 IN EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet,
2111 IN UINT64 Lun,
2112 IN ISCSI_TCB *Tcb
2113 )
2114 {
2115 LIST_ENTRY *NbufList;
2116 NET_BUF *Pdu;
2117 NET_BUF *PduHeader;
2118 NET_BUF *DataSeg;
2119 SCSI_COMMAND *ScsiCmd;
2120 UINT8 AHSLength;
2121 UINT32 Length;
2122 ISCSI_ADDITIONAL_HEADER *Header;
2123 ISCSI_BI_EXP_READ_DATA_LEN_AHS *BiExpReadDataLenAHS;
2124 ISCSI_SESSION *Session;
2125 UINT32 ImmediateDataLen;
2126
2127 AHSLength = 0;
2128
2129 if (Packet->DataDirection == DataBi) {
2130 //
2131 // Bidirectional Read/Write command, the bidirectional expected
2132 // read data length AHS is required.
2133 //
2134 AHSLength += sizeof (ISCSI_BI_EXP_READ_DATA_LEN_AHS);
2135 }
2136
2137 if (Packet->CdbLength > 16) {
2138 //
2139 // The CDB exceeds 16 bytes. An extended CDB AHS is required.
2140 //
2141 AHSLength = (UINT8) (AHSLength + ISCSI_ROUNDUP (Packet->CdbLength - 16) + sizeof (ISCSI_ADDITIONAL_HEADER));
2142 }
2143
2144 Length = sizeof (SCSI_COMMAND) + AHSLength;
2145 PduHeader = NetbufAlloc (Length);
2146 if (PduHeader == NULL) {
2147 return NULL;
2148 }
2149
2150 ScsiCmd = (SCSI_COMMAND *) NetbufAllocSpace (PduHeader, Length, NET_BUF_TAIL);
2151 if (ScsiCmd == NULL) {
2152 NetbufFree (PduHeader);
2153 return NULL;
2154 }
2155 Header = (ISCSI_ADDITIONAL_HEADER *) (ScsiCmd + 1);
2156
2157 ZeroMem (ScsiCmd, Length);
2158
2159 ISCSI_SET_OPCODE (ScsiCmd, ISCSI_OPCODE_SCSI_CMD, 0);
2160 ISCSI_SET_FLAG (ScsiCmd, ISCSI_TASK_ATTR_SIMPLE);
2161
2162 //
2163 // Set the READ/WRITE flags according to the IO type of this request.
2164 //
2165 switch (Packet->DataDirection) {
2166 case DataIn:
2167 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ);
2168 ScsiCmd->ExpDataXferLength = NTOHL (Packet->InTransferLength);
2169 break;
2170
2171 case DataOut:
2172 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_WRITE);
2173 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
2174 break;
2175
2176 case DataBi:
2177 ISCSI_SET_FLAG (ScsiCmd, SCSI_CMD_PDU_FLAG_READ | SCSI_CMD_PDU_FLAG_WRITE);
2178 ScsiCmd->ExpDataXferLength = NTOHL (Packet->OutTransferLength);
2179
2180 //
2181 // Fill the bidirectional expected read data length AHS.
2182 //
2183 BiExpReadDataLenAHS = (ISCSI_BI_EXP_READ_DATA_LEN_AHS *) Header;
2184 Header = (ISCSI_ADDITIONAL_HEADER *) (BiExpReadDataLenAHS + 1);
2185
2186 BiExpReadDataLenAHS->Length = NTOHS (5);
2187 BiExpReadDataLenAHS->Type = ISCSI_AHS_TYPE_BI_EXP_READ_DATA_LEN;
2188 BiExpReadDataLenAHS->ExpReadDataLength = NTOHL (Packet->InTransferLength);
2189
2190 break;
2191 }
2192
2193 ScsiCmd->TotalAHSLength = AHSLength;
2194 CopyMem (ScsiCmd->Lun, &Lun, sizeof (ScsiCmd->Lun));
2195 ScsiCmd->InitiatorTaskTag = NTOHL (Tcb->InitiatorTaskTag);
2196 ScsiCmd->CmdSN = NTOHL (Tcb->CmdSN);
2197 ScsiCmd->ExpStatSN = NTOHL (Tcb->Conn->ExpStatSN);
2198
2199 CopyMem (ScsiCmd->Cdb, Packet->Cdb, sizeof (ScsiCmd->Cdb));
2200
2201 if (Packet->CdbLength > 16) {
2202 Header->Length = NTOHS ((UINT16) (Packet->CdbLength - 15));
2203 Header->Type = ISCSI_AHS_TYPE_EXT_CDB;
2204
2205 CopyMem (Header + 1, (UINT8 *) Packet->Cdb + 16, Packet->CdbLength - 16);
2206 }
2207
2208 Pdu = PduHeader;
2209 Session = Tcb->Conn->Session;
2210 ImmediateDataLen = 0;
2211
2212 if (Session->ImmediateData && (Packet->OutTransferLength != 0)) {
2213 //
2214 // Send immediate data in this SCSI Command PDU. The length of the immeidate
2215 // data is the minimum of FirstBurstLength, the data length to be xfered, and
2216 // the MaxRecvdataSegmentLength on this connection.
2217 //
2218 ImmediateDataLen = MIN (Session->FirstBurstLength, Packet->OutTransferLength);
2219 ImmediateDataLen = MIN (ImmediateDataLen, Tcb->Conn->MaxRecvDataSegmentLength);
2220
2221 //
2222 // Update the data segment length in the PDU header.
2223 //
2224 ISCSI_SET_DATASEG_LEN (ScsiCmd, ImmediateDataLen);
2225
2226 //
2227 // Create the data segment.
2228 //
2229 DataSeg = IScsiNewDataSegment ((UINT8 *) Packet->OutDataBuffer, ImmediateDataLen, FALSE);
2230 if (DataSeg == NULL) {
2231 NetbufFree (PduHeader);
2232 Pdu = NULL;
2233 goto ON_EXIT;
2234 }
2235
2236 NbufList = AllocatePool (sizeof (LIST_ENTRY));
2237 if (NbufList == NULL) {
2238 NetbufFree (PduHeader);
2239 NetbufFree (DataSeg);
2240
2241 Pdu = NULL;
2242 goto ON_EXIT;
2243 }
2244
2245 InitializeListHead (NbufList);
2246 InsertTailList (NbufList, &PduHeader->List);
2247 InsertTailList (NbufList, &DataSeg->List);
2248
2249 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
2250 if (Pdu == NULL) {
2251 IScsiFreeNbufList (NbufList);
2252 }
2253 }
2254
2255 if (Session->InitialR2T ||
2256 (ImmediateDataLen == Session->FirstBurstLength) ||
2257 (ImmediateDataLen == Packet->OutTransferLength)
2258 ) {
2259 //
2260 // Unsolicited data out sequence is not allowed,
2261 // or FirstBustLength data is already sent out by immediate data,
2262 // or all the OUT data accompany this SCSI packet are sent as
2263 // immediate data. The final flag should be set on this SCSI Command
2264 // PDU.
2265 //
2266 ISCSI_SET_FLAG (ScsiCmd, ISCSI_BHS_FLAG_FINAL);
2267 }
2268
2269 ON_EXIT:
2270
2271 return Pdu;
2272 }
2273
2274
2275 /**
2276 Create a new iSCSI SCSI Data Out PDU.
2277
2278 @param[in] Data The data to put into the Data Out PDU.
2279 @param[in] Len Length of the data.
2280 @param[in] DataSN The DataSN of the Data Out PDU.
2281 @param[in] Tcb The task control block of this Data Out PDU.
2282 @param[in] Lun The LUN.
2283
2284 @return The net buffer wrapping the Data Out PDU.
2285 @retval NULL Other errors as indicated.
2286
2287 **/
2288 NET_BUF *
2289 IScsiNewDataOutPdu (
2290 IN UINT8 *Data,
2291 IN UINT32 Len,
2292 IN UINT32 DataSN,
2293 IN ISCSI_TCB *Tcb,
2294 IN UINT64 Lun
2295 )
2296 {
2297 LIST_ENTRY *NbufList;
2298 NET_BUF *PduHdr;
2299 NET_BUF *DataSeg;
2300 NET_BUF *Pdu;
2301 ISCSI_SCSI_DATA_OUT *DataOutHdr;
2302 ISCSI_XFER_CONTEXT *XferContext;
2303
2304 NbufList = AllocatePool (sizeof (LIST_ENTRY));
2305 if (NbufList == NULL) {
2306 return NULL;
2307 }
2308
2309 InitializeListHead (NbufList);
2310
2311 //
2312 // Allocate memory for the BHS.
2313 //
2314 PduHdr = NetbufAlloc (sizeof (ISCSI_SCSI_DATA_OUT));
2315 if (PduHdr == NULL) {
2316 FreePool (NbufList);
2317 return NULL;
2318 }
2319 //
2320 // Insert the BHS into the buffer list.
2321 //
2322 InsertTailList (NbufList, &PduHdr->List);
2323
2324 DataOutHdr = (ISCSI_SCSI_DATA_OUT *) NetbufAllocSpace (PduHdr, sizeof (ISCSI_SCSI_DATA_OUT), NET_BUF_TAIL);
2325 if (DataOutHdr == NULL) {
2326 IScsiFreeNbufList (NbufList);
2327 return NULL;
2328 }
2329 XferContext = &Tcb->XferContext;
2330
2331 ZeroMem (DataOutHdr, sizeof (ISCSI_SCSI_DATA_OUT));
2332
2333 //
2334 // Set the flags and fields of the Data Out PDU BHS.
2335 //
2336 ISCSI_SET_OPCODE (DataOutHdr, ISCSI_OPCODE_SCSI_DATA_OUT, 0);
2337 ISCSI_SET_DATASEG_LEN (DataOutHdr, Len);
2338
2339 DataOutHdr->InitiatorTaskTag = HTONL (Tcb->InitiatorTaskTag);
2340 DataOutHdr->TargetTransferTag = HTONL (XferContext->TargetTransferTag);
2341 DataOutHdr->ExpStatSN = HTONL (Tcb->Conn->ExpStatSN);
2342 DataOutHdr->DataSN = HTONL (DataSN);
2343 DataOutHdr->BufferOffset = HTONL (XferContext->Offset);
2344
2345 if (XferContext->TargetTransferTag != ISCSI_RESERVED_TAG) {
2346 CopyMem (&DataOutHdr->Lun, &Lun, sizeof (DataOutHdr->Lun));
2347 }
2348 //
2349 // Build the data segment for this Data Out PDU.
2350 //
2351 DataSeg = IScsiNewDataSegment (Data, Len, FALSE);
2352 if (DataSeg == NULL) {
2353 IScsiFreeNbufList (NbufList);
2354 return NULL;
2355 }
2356 //
2357 // Put the data segment into the buffer list and combine it with the BHS
2358 // into a full Data Out PDU.
2359 //
2360 InsertTailList (NbufList, &DataSeg->List);
2361 Pdu = NetbufFromBufList (NbufList, 0, 0, IScsiFreeNbufList, NbufList);
2362 if (Pdu == NULL) {
2363 IScsiFreeNbufList (NbufList);
2364 }
2365
2366 return Pdu;
2367 }
2368
2369
2370 /**
2371 Generate a consecutive sequence of iSCSI SCSI Data Out PDUs.
2372
2373 @param[in] Data The data which will be carried by the sequence of iSCSI SCSI Data Out PDUs.
2374 @param[in] Tcb The task control block of the data to send out.
2375 @param[in] Lun The LUN the data will be sent to.
2376
2377 @return A list of net buffers with each of them wrapping an iSCSI SCSI Data Out PDU.
2378 @retval NULL Other errors as indicated.
2379
2380 **/
2381 LIST_ENTRY *
2382 IScsiGenerateDataOutPduSequence (
2383 IN UINT8 *Data,
2384 IN ISCSI_TCB *Tcb,
2385 IN UINT64 Lun
2386 )
2387 {
2388 LIST_ENTRY *PduList;
2389 UINT32 DataSN;
2390 UINT32 DataLen;
2391 NET_BUF *DataOutPdu;
2392 ISCSI_CONNECTION *Conn;
2393 ISCSI_XFER_CONTEXT *XferContext;
2394 UINT8 *DataOutPacket;
2395
2396 PduList = AllocatePool (sizeof (LIST_ENTRY));
2397 if (PduList == NULL) {
2398 return NULL;
2399 }
2400
2401 InitializeListHead (PduList);
2402
2403 DataSN = 0;
2404 Conn = Tcb->Conn;
2405 DataOutPdu = NULL;
2406 XferContext = &Tcb->XferContext;
2407
2408 while (XferContext->DesiredLength > 0) {
2409 //
2410 // Determine the length of data this Data Out PDU can carry.
2411 //
2412 DataLen = MIN (XferContext->DesiredLength, Conn->MaxRecvDataSegmentLength);
2413
2414 //
2415 // Create a Data Out PDU.
2416 //
2417 DataOutPdu = IScsiNewDataOutPdu (Data, DataLen, DataSN, Tcb, Lun);
2418 if (DataOutPdu == NULL) {
2419 IScsiFreeNbufList (PduList);
2420 PduList = NULL;
2421
2422 goto ON_EXIT;
2423 }
2424
2425 InsertTailList (PduList, &DataOutPdu->List);
2426
2427 //
2428 // Update the context and DataSN.
2429 //
2430 Data += DataLen;
2431 XferContext->Offset += DataLen;
2432 XferContext->DesiredLength -= DataLen;
2433 DataSN++;
2434 }
2435 //
2436 // Set the F bit for the last data out PDU in this sequence.
2437 //
2438 DataOutPacket = NetbufGetByte (DataOutPdu, 0, NULL);
2439 if (DataOutPacket == NULL) {
2440 IScsiFreeNbufList (PduList);
2441 PduList = NULL;
2442 goto ON_EXIT;
2443 }
2444
2445 ISCSI_SET_FLAG (DataOutPacket, ISCSI_BHS_FLAG_FINAL);
2446
2447 ON_EXIT:
2448
2449 return PduList;
2450 }
2451
2452 /**
2453 Send the Data in a sequence of Data Out PDUs one by one.
2454
2455 @param[in] Data The data to carry by Data Out PDUs.
2456 @param[in] Lun The LUN the data will be sent to.
2457 @param[in] Tcb The task control block.
2458
2459 @retval EFI_SUCCES The data is sent out to the LUN.
2460 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2461 @retval Others Other errors as indicated.
2462
2463 **/
2464 EFI_STATUS
2465 IScsiSendDataOutPduSequence (
2466 IN UINT8 *Data,
2467 IN UINT64 Lun,
2468 IN ISCSI_TCB *Tcb
2469 )
2470 {
2471 LIST_ENTRY *DataOutPduList;
2472 LIST_ENTRY *Entry;
2473 NET_BUF *Pdu;
2474 EFI_STATUS Status;
2475
2476 //
2477 // Generate the Data Out PDU sequence.
2478 //
2479 DataOutPduList = IScsiGenerateDataOutPduSequence (Data, Tcb, Lun);
2480 if (DataOutPduList == NULL) {
2481 return EFI_OUT_OF_RESOURCES;
2482 }
2483
2484 Status = EFI_SUCCESS;
2485
2486 //
2487 // Send the Data Out PDU's one by one.
2488 //
2489 NET_LIST_FOR_EACH (Entry, DataOutPduList) {
2490 Pdu = NET_LIST_USER_STRUCT (Entry, NET_BUF, List);
2491
2492 Status = TcpIoTransmit (&Tcb->Conn->TcpIo, Pdu);
2493
2494 if (EFI_ERROR (Status)) {
2495 break;
2496 }
2497 }
2498
2499 IScsiFreeNbufList (DataOutPduList);
2500
2501 return Status;
2502 }
2503
2504
2505 /**
2506 Process the received iSCSI SCSI Data In PDU.
2507
2508 @param[in] Pdu The Data In PDU received.
2509 @param[in] Tcb The task control block.
2510 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2511
2512 @retval EFI_SUCCES The check on the Data IN PDU is passed and some update
2513 actions are taken.
2514 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2515 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2516 @retval Others Other errors as indicated.
2517
2518 **/
2519 EFI_STATUS
2520 IScsiOnDataInRcvd (
2521 IN NET_BUF *Pdu,
2522 IN ISCSI_TCB *Tcb,
2523 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2524 )
2525 {
2526 ISCSI_SCSI_DATA_IN *DataInHdr;
2527 EFI_STATUS Status;
2528
2529 DataInHdr = (ISCSI_SCSI_DATA_IN *) NetbufGetByte (Pdu, 0, NULL);
2530 if (DataInHdr == NULL) {
2531 return EFI_PROTOCOL_ERROR;
2532 }
2533
2534 DataInHdr->InitiatorTaskTag = NTOHL (DataInHdr->InitiatorTaskTag);
2535 DataInHdr->ExpCmdSN = NTOHL (DataInHdr->ExpCmdSN);
2536 DataInHdr->MaxCmdSN = NTOHL (DataInHdr->MaxCmdSN);
2537 DataInHdr->DataSN = NTOHL (DataInHdr->DataSN);
2538
2539 //
2540 // Check the DataSN.
2541 //
2542 Status = IScsiCheckSN (&Tcb->ExpDataSN, DataInHdr->DataSN);
2543 if (EFI_ERROR (Status)) {
2544 return Status;
2545 }
2546
2547 if (DataInHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
2548 return EFI_PROTOCOL_ERROR;
2549 }
2550 //
2551 // Update the command related sequence numbers.
2552 //
2553 IScsiUpdateCmdSN (Tcb->Conn->Session, DataInHdr->MaxCmdSN, DataInHdr->ExpCmdSN);
2554
2555 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_STATUS_VALID)) {
2556 if (!ISCSI_FLAG_ON (DataInHdr, ISCSI_BHS_FLAG_FINAL)) {
2557 //
2558 // The S bit is on but the F bit is off.
2559 //
2560 return EFI_PROTOCOL_ERROR;
2561 }
2562
2563 Tcb->StatusXferd = TRUE;
2564
2565 if (ISCSI_FLAG_ON (DataInHdr, SCSI_DATA_IN_PDU_FLAG_OVERFLOW | SCSI_DATA_IN_PDU_FLAG_UNDERFLOW)) {
2566 //
2567 // Underflow and Overflow are mutual flags.
2568 //
2569 return EFI_PROTOCOL_ERROR;
2570 }
2571 //
2572 // S bit is on, the StatSN is valid.
2573 //
2574 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NTOHL (DataInHdr->StatSN));
2575 if (EFI_ERROR (Status)) {
2576 return Status;
2577 }
2578
2579 Packet->HostAdapterStatus = 0;
2580 Packet->TargetStatus = DataInHdr->Status;
2581
2582 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
2583 Packet->InTransferLength += NTOHL (DataInHdr->ResidualCount);
2584 Status = EFI_BAD_BUFFER_SIZE;
2585 }
2586
2587 if (ISCSI_FLAG_ON (DataInHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
2588 Packet->InTransferLength -= NTOHL (DataInHdr->ResidualCount);
2589 }
2590 }
2591
2592 return Status;
2593 }
2594
2595
2596 /**
2597 Process the received iSCSI R2T PDU.
2598
2599 @param[in] Pdu The R2T PDU received.
2600 @param[in] Tcb The task control block.
2601 @param[in] Lun The Lun.
2602 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2603
2604 @retval EFI_SUCCES The R2T PDU is valid and the solicited data is sent out.
2605 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2606 @retval Others Other errors as indicated.
2607
2608 **/
2609 EFI_STATUS
2610 IScsiOnR2TRcvd (
2611 IN NET_BUF *Pdu,
2612 IN ISCSI_TCB *Tcb,
2613 IN UINT64 Lun,
2614 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2615 )
2616 {
2617 ISCSI_READY_TO_TRANSFER *R2THdr;
2618 EFI_STATUS Status;
2619 ISCSI_XFER_CONTEXT *XferContext;
2620 UINT8 *Data;
2621
2622 R2THdr = (ISCSI_READY_TO_TRANSFER *) NetbufGetByte (Pdu, 0, NULL);
2623 if (R2THdr == NULL) {
2624 return EFI_PROTOCOL_ERROR;
2625 }
2626
2627 R2THdr->InitiatorTaskTag = NTOHL (R2THdr->InitiatorTaskTag);
2628 R2THdr->TargetTransferTag = NTOHL (R2THdr->TargetTransferTag);
2629 R2THdr->StatSN = NTOHL (R2THdr->StatSN);
2630 R2THdr->R2TSeqNum = NTOHL (R2THdr->R2TSeqNum);
2631 R2THdr->BufferOffset = NTOHL (R2THdr->BufferOffset);
2632 R2THdr->DesiredDataTransferLength = NTOHL (R2THdr->DesiredDataTransferLength);
2633
2634 if ((R2THdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) || !ISCSI_SEQ_EQ (R2THdr->StatSN, Tcb->Conn->ExpStatSN)) {
2635 return EFI_PROTOCOL_ERROR;;
2636 }
2637 //
2638 // Check the sequence number.
2639 //
2640 Status = IScsiCheckSN (&Tcb->ExpDataSN, R2THdr->R2TSeqNum);
2641 if (EFI_ERROR (Status)) {
2642 return Status;
2643 }
2644
2645 XferContext = &Tcb->XferContext;
2646 XferContext->TargetTransferTag = R2THdr->TargetTransferTag;
2647 XferContext->Offset = R2THdr->BufferOffset;
2648 XferContext->DesiredLength = R2THdr->DesiredDataTransferLength;
2649
2650 if (((XferContext->Offset + XferContext->DesiredLength) > Packet->OutTransferLength) ||
2651 (XferContext->DesiredLength > Tcb->Conn->Session->MaxBurstLength)
2652 ) {
2653 return EFI_PROTOCOL_ERROR;
2654 }
2655 //
2656 // Send the data solicited by this R2T.
2657 //
2658 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
2659 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
2660
2661 return Status;
2662 }
2663
2664
2665 /**
2666 Process the received iSCSI SCSI Response PDU.
2667
2668 @param[in] Pdu The Response PDU received.
2669 @param[in] Tcb The task control block.
2670 @param[in, out] Packet The EXT SCSI PASS THRU request packet.
2671
2672 @retval EFI_SUCCES The Response PDU is processed.
2673 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2674 @retval EFI_BAD_BUFFER_SIZEE The buffer was not the proper size for the request.
2675 @retval Others Other errors as indicated.
2676
2677 **/
2678 EFI_STATUS
2679 IScsiOnScsiRspRcvd (
2680 IN NET_BUF *Pdu,
2681 IN ISCSI_TCB *Tcb,
2682 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2683 )
2684 {
2685 SCSI_RESPONSE *ScsiRspHdr;
2686 ISCSI_SENSE_DATA *SenseData;
2687 EFI_STATUS Status;
2688 UINT32 DataSegLen;
2689
2690 ScsiRspHdr = (SCSI_RESPONSE *) NetbufGetByte (Pdu, 0, NULL);
2691 if (ScsiRspHdr == NULL) {
2692 return EFI_PROTOCOL_ERROR;
2693 }
2694
2695 ScsiRspHdr->InitiatorTaskTag = NTOHL (ScsiRspHdr->InitiatorTaskTag);
2696 if (ScsiRspHdr->InitiatorTaskTag != Tcb->InitiatorTaskTag) {
2697 return EFI_PROTOCOL_ERROR;
2698 }
2699
2700 ScsiRspHdr->StatSN = NTOHL (ScsiRspHdr->StatSN);
2701
2702 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, ScsiRspHdr->StatSN);
2703 if (EFI_ERROR (Status)) {
2704 return Status;
2705 }
2706
2707 ScsiRspHdr->MaxCmdSN = NTOHL (ScsiRspHdr->MaxCmdSN);
2708 ScsiRspHdr->ExpCmdSN = NTOHL (ScsiRspHdr->ExpCmdSN);
2709 IScsiUpdateCmdSN (Tcb->Conn->Session, ScsiRspHdr->MaxCmdSN, ScsiRspHdr->ExpCmdSN);
2710
2711 Tcb->StatusXferd = TRUE;
2712
2713 Packet->HostAdapterStatus = ScsiRspHdr->Response;
2714 if (Packet->HostAdapterStatus != ISCSI_SERVICE_RSP_COMMAND_COMPLETE_AT_TARGET) {
2715 return EFI_SUCCESS;
2716 }
2717
2718 Packet->TargetStatus = ScsiRspHdr->Status;
2719
2720 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW | SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW) ||
2721 ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW | SCSI_RSP_PDU_FLAG_UNDERFLOW)
2722 ) {
2723 return EFI_PROTOCOL_ERROR;
2724 }
2725
2726 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_OVERFLOW)) {
2727 Packet->InTransferLength += NTOHL (ScsiRspHdr->BiReadResidualCount);
2728 Status = EFI_BAD_BUFFER_SIZE;
2729 }
2730
2731 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_BI_READ_UNDERFLOW)) {
2732 Packet->InTransferLength -= NTOHL (ScsiRspHdr->BiReadResidualCount);
2733 }
2734
2735 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_OVERFLOW)) {
2736 if (Packet->DataDirection == DataIn) {
2737 Packet->InTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
2738 } else {
2739 Packet->OutTransferLength += NTOHL (ScsiRspHdr->ResidualCount);
2740 }
2741
2742 Status = EFI_BAD_BUFFER_SIZE;
2743 }
2744
2745 if (ISCSI_FLAG_ON (ScsiRspHdr, SCSI_RSP_PDU_FLAG_UNDERFLOW)) {
2746 if (Packet->DataDirection == DataIn) {
2747 Packet->InTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
2748 } else {
2749 Packet->OutTransferLength -= NTOHL (ScsiRspHdr->ResidualCount);
2750 }
2751 }
2752
2753 DataSegLen = ISCSI_GET_DATASEG_LEN (ScsiRspHdr);
2754 if (DataSegLen != 0) {
2755 SenseData = (ISCSI_SENSE_DATA *) NetbufGetByte (Pdu, sizeof (SCSI_RESPONSE), NULL);
2756 if (SenseData == NULL) {
2757 return EFI_PROTOCOL_ERROR;
2758 }
2759
2760 SenseData->Length = NTOHS (SenseData->Length);
2761
2762 Packet->SenseDataLength = (UINT8) MIN (SenseData->Length, Packet->SenseDataLength);
2763 if (Packet->SenseDataLength != 0) {
2764 CopyMem (Packet->SenseData, &SenseData->Data[0], Packet->SenseDataLength);
2765 }
2766 } else {
2767 Packet->SenseDataLength = 0;
2768 }
2769
2770 return Status;
2771 }
2772
2773
2774 /**
2775 Process the received NOP In PDU.
2776
2777 @param[in] Pdu The NOP In PDU received.
2778 @param[in] Tcb The task control block.
2779
2780 @retval EFI_SUCCES The NOP In PDU is processed and the related sequence
2781 numbers are updated.
2782 @retval EFI_PROTOCOL_ERROR Some kind of iSCSI protocol errror occurred.
2783
2784 **/
2785 EFI_STATUS
2786 IScsiOnNopInRcvd (
2787 IN NET_BUF *Pdu,
2788 IN ISCSI_TCB *Tcb
2789 )
2790 {
2791 ISCSI_NOP_IN *NopInHdr;
2792 EFI_STATUS Status;
2793
2794 NopInHdr = (ISCSI_NOP_IN *) NetbufGetByte (Pdu, 0, NULL);
2795 if (NopInHdr == NULL) {
2796 return EFI_PROTOCOL_ERROR;
2797 }
2798
2799 NopInHdr->StatSN = NTOHL (NopInHdr->StatSN);
2800 NopInHdr->ExpCmdSN = NTOHL (NopInHdr->ExpCmdSN);
2801 NopInHdr->MaxCmdSN = NTOHL (NopInHdr->MaxCmdSN);
2802
2803 if (NopInHdr->InitiatorTaskTag == ISCSI_RESERVED_TAG) {
2804 if (NopInHdr->StatSN != Tcb->Conn->ExpStatSN) {
2805 return EFI_PROTOCOL_ERROR;
2806 }
2807 } else {
2808 Status = IScsiCheckSN (&Tcb->Conn->ExpStatSN, NopInHdr->StatSN);
2809 if (EFI_ERROR (Status)) {
2810 return Status;
2811 }
2812 }
2813
2814 IScsiUpdateCmdSN (Tcb->Conn->Session, NopInHdr->MaxCmdSN, NopInHdr->ExpCmdSN);
2815
2816 return EFI_SUCCESS;
2817 }
2818
2819
2820 /**
2821 Execute the SCSI command issued through the EXT SCSI PASS THRU protocol.
2822
2823 @param[in] PassThru The EXT SCSI PASS THRU protocol.
2824 @param[in] Target The target ID.
2825 @param[in] Lun The LUN.
2826 @param[in, out] Packet The request packet containing IO request, SCSI command
2827 buffer and buffers to read/write.
2828
2829 @retval EFI_SUCCES The SCSI command is executed and the result is updated to
2830 the Packet.
2831 @retval EFI_DEVICE_ERROR Session state was not as required.
2832 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
2833 @retval EFI_PROTOCOL_ERROR There is no such data in the net buffer.
2834 @retval EFI_NOT_READY The target can not accept new commands.
2835 @retval Others Other errors as indicated.
2836
2837 **/
2838 EFI_STATUS
2839 IScsiExecuteScsiCommand (
2840 IN EFI_EXT_SCSI_PASS_THRU_PROTOCOL *PassThru,
2841 IN UINT8 *Target,
2842 IN UINT64 Lun,
2843 IN OUT EFI_EXT_SCSI_PASS_THRU_SCSI_REQUEST_PACKET *Packet
2844 )
2845 {
2846 EFI_STATUS Status;
2847 ISCSI_DRIVER_DATA *Private;
2848 ISCSI_SESSION *Session;
2849 EFI_EVENT TimeoutEvent;
2850 ISCSI_CONNECTION *Conn;
2851 ISCSI_TCB *Tcb;
2852 NET_BUF *Pdu;
2853 ISCSI_XFER_CONTEXT *XferContext;
2854 UINT8 *Data;
2855 ISCSI_IN_BUFFER_CONTEXT InBufferContext;
2856 UINT64 Timeout;
2857 UINT8 *PduHdr;
2858
2859 Private = ISCSI_DRIVER_DATA_FROM_EXT_SCSI_PASS_THRU (PassThru);
2860 Session = Private->Session;
2861 Status = EFI_SUCCESS;
2862 Tcb = NULL;
2863 TimeoutEvent = NULL;
2864 Timeout = 0;
2865
2866 if (Session->State != SESSION_STATE_LOGGED_IN) {
2867 Status = EFI_DEVICE_ERROR;
2868 goto ON_EXIT;
2869 }
2870
2871 Conn = NET_LIST_USER_STRUCT_S (
2872 Session->Conns.ForwardLink,
2873 ISCSI_CONNECTION,
2874 Link,
2875 ISCSI_CONNECTION_SIGNATURE
2876 );
2877
2878 if (Packet->Timeout != 0) {
2879 Timeout = MultU64x32 (Packet->Timeout, 4);
2880 }
2881
2882 Status = IScsiNewTcb (Conn, &Tcb);
2883 if (EFI_ERROR (Status)) {
2884 goto ON_EXIT;
2885 }
2886 //
2887 // Encapsulate the SCSI request packet into an iSCSI SCSI Command PDU.
2888 //
2889 Pdu = IScsiNewScsiCmdPdu (Packet, Lun, Tcb);
2890 if (Pdu == NULL) {
2891 Status = EFI_OUT_OF_RESOURCES;
2892 goto ON_EXIT;
2893 }
2894
2895 XferContext = &Tcb->XferContext;
2896 PduHdr = NetbufGetByte (Pdu, 0, NULL);
2897 if (PduHdr == NULL) {
2898 Status = EFI_PROTOCOL_ERROR;
2899 NetbufFree (Pdu);
2900 goto ON_EXIT;
2901 }
2902 XferContext->Offset = ISCSI_GET_DATASEG_LEN (PduHdr);
2903
2904 //
2905 // Transmit the SCSI Command PDU.
2906 //
2907 Status = TcpIoTransmit (&Conn->TcpIo, Pdu);
2908
2909 NetbufFree (Pdu);
2910
2911 if (EFI_ERROR (Status)) {
2912 goto ON_EXIT;
2913 }
2914
2915 if (!Session->InitialR2T &&
2916 (XferContext->Offset < Session->FirstBurstLength) &&
2917 (XferContext->Offset < Packet->OutTransferLength)
2918 ) {
2919 //
2920 // Unsolicited Data-Out sequence is allowed. There is remaining SCSI
2921 // OUT data, and the limit of FirstBurstLength is not reached.
2922 //
2923 XferContext->TargetTransferTag = ISCSI_RESERVED_TAG;
2924 XferContext->DesiredLength = MIN (
2925 Session->FirstBurstLength,
2926 Packet->OutTransferLength - XferContext->Offset
2927 );
2928
2929 Data = (UINT8 *) Packet->OutDataBuffer + XferContext->Offset;
2930 Status = IScsiSendDataOutPduSequence (Data, Lun, Tcb);
2931 if (EFI_ERROR (Status)) {
2932 goto ON_EXIT;
2933 }
2934 }
2935
2936 InBufferContext.InData = (UINT8 *) Packet->InDataBuffer;
2937 InBufferContext.InDataLen = Packet->InTransferLength;
2938
2939 while (!Tcb->StatusXferd) {
2940 //
2941 // Start the timeout timer.
2942 //
2943 if (Timeout != 0) {
2944 Status = gBS->SetTimer (Conn->TimeoutEvent, TimerRelative, Timeout);
2945 if (EFI_ERROR (Status)) {
2946 goto ON_EXIT;
2947 }
2948
2949 TimeoutEvent = Conn->TimeoutEvent;
2950 }
2951
2952 //
2953 // Try to receive PDU from target.
2954 //
2955 Status = IScsiReceivePdu (Conn, &Pdu, &InBufferContext, FALSE, FALSE, TimeoutEvent);
2956 if (EFI_ERROR (Status)) {
2957 goto ON_EXIT;
2958 }
2959
2960 PduHdr = NetbufGetByte (Pdu, 0, NULL);
2961 if (PduHdr == NULL) {
2962 Status = EFI_PROTOCOL_ERROR;
2963 NetbufFree (Pdu);
2964 goto ON_EXIT;
2965 }
2966 switch (ISCSI_GET_OPCODE (PduHdr)) {
2967 case ISCSI_OPCODE_SCSI_DATA_IN:
2968 Status = IScsiOnDataInRcvd (Pdu, Tcb, Packet);
2969 break;
2970
2971 case ISCSI_OPCODE_R2T:
2972 Status = IScsiOnR2TRcvd (Pdu, Tcb, Lun, Packet);
2973 break;
2974
2975 case ISCSI_OPCODE_SCSI_RSP:
2976 Status = IScsiOnScsiRspRcvd (Pdu, Tcb, Packet);
2977 break;
2978
2979 case ISCSI_OPCODE_NOP_IN:
2980 Status = IScsiOnNopInRcvd (Pdu, Tcb);
2981 break;
2982
2983 case ISCSI_OPCODE_VENDOR_T0:
2984 case ISCSI_OPCODE_VENDOR_T1:
2985 case ISCSI_OPCODE_VENDOR_T2:
2986 //
2987 // These messages are vendor specific. Skip them.
2988 //
2989 break;
2990
2991 default:
2992 Status = EFI_PROTOCOL_ERROR;
2993 break;
2994 }
2995
2996 NetbufFree (Pdu);
2997
2998 if (EFI_ERROR (Status)) {
2999 break;
3000 }
3001 }
3002
3003 ON_EXIT:
3004
3005 if (TimeoutEvent != NULL) {
3006 gBS->SetTimer (TimeoutEvent, TimerCancel, 0);
3007 }
3008
3009 if (Tcb != NULL) {
3010 IScsiDelTcb (Tcb);
3011 }
3012
3013 return Status;
3014 }
3015
3016
3017 /**
3018 Reinstate the session on some error.
3019
3020 @param[in] Session The iSCSI session
3021
3022 @retval EFI_SUCCESS The session is reinstated from some error.
3023 @retval Other Reinstatement failed.
3024
3025 **/
3026 EFI_STATUS
3027 IScsiSessionReinstatement (
3028 IN ISCSI_SESSION *Session
3029 )
3030 {
3031 EFI_STATUS Status;
3032
3033 ASSERT (Session->State != SESSION_STATE_FREE);
3034
3035 //
3036 // Abort the session and re-init it.
3037 //
3038 IScsiSessionAbort (Session);
3039 IScsiSessionInit (Session, TRUE);
3040
3041 //
3042 // Login again.
3043 //
3044 Status = IScsiSessionLogin (Session);
3045
3046 return Status;
3047 }
3048
3049
3050 /**
3051 Initialize some session parameters before login.
3052
3053 @param[in, out] Session The iSCSI session.
3054 @param[in] Recovery Whether the request is from a fresh new start or recovery.
3055
3056 **/
3057 VOID
3058 IScsiSessionInit (
3059 IN OUT ISCSI_SESSION *Session,
3060 IN BOOLEAN Recovery
3061 )
3062 {
3063 if (!Recovery) {
3064 Session->Signature = ISCSI_SESSION_SIGNATURE;
3065 Session->State = SESSION_STATE_FREE;
3066
3067 InitializeListHead (&Session->Conns);
3068 InitializeListHead (&Session->TcbList);
3069 }
3070
3071 Session->Tsih = 0;
3072
3073 Session->CmdSN = 1;
3074 Session->InitiatorTaskTag = 1;
3075 Session->NextCid = 1;
3076
3077 Session->TargetPortalGroupTag = 0;
3078 Session->MaxConnections = ISCSI_MAX_CONNS_PER_SESSION;
3079 Session->InitialR2T = FALSE;
3080 Session->ImmediateData = TRUE;
3081 Session->MaxBurstLength = 262144;
3082 Session->FirstBurstLength = MAX_RECV_DATA_SEG_LEN_IN_FFP;
3083 Session->DefaultTime2Wait = 2;
3084 Session->DefaultTime2Retain = 20;
3085 Session->MaxOutstandingR2T = DEFAULT_MAX_OUTSTANDING_R2T;
3086 Session->DataPDUInOrder = TRUE;
3087 Session->DataSequenceInOrder = TRUE;
3088 Session->ErrorRecoveryLevel = 0;
3089 }
3090
3091
3092 /**
3093 Abort the iSCSI session. That is, reset all the connection(s), and free the
3094 resources.
3095
3096 @param[in, out] Session The iSCSI session.
3097
3098 **/
3099 VOID
3100 IScsiSessionAbort (
3101 IN OUT ISCSI_SESSION *Session
3102 )
3103 {
3104 ISCSI_CONNECTION *Conn;
3105 EFI_GUID *ProtocolGuid;
3106
3107 if (Session->State != SESSION_STATE_LOGGED_IN) {
3108 return ;
3109 }
3110
3111 ASSERT (!IsListEmpty (&Session->Conns));
3112
3113 while (!IsListEmpty (&Session->Conns)) {
3114 Conn = NET_LIST_USER_STRUCT_S (
3115 Session->Conns.ForwardLink,
3116 ISCSI_CONNECTION,
3117 Link,
3118 ISCSI_CONNECTION_SIGNATURE
3119 );
3120 if (!Conn->Ipv6Flag) {
3121 ProtocolGuid = &gEfiTcp4ProtocolGuid;
3122 } else {
3123 ProtocolGuid = &gEfiTcp6ProtocolGuid;
3124 }
3125
3126 gBS->CloseProtocol (
3127 Conn->TcpIo.Handle,
3128 ProtocolGuid,
3129 Session->Private->Image,
3130 Session->Private->ExtScsiPassThruHandle
3131 );
3132
3133 IScsiConnReset (Conn);
3134
3135 IScsiDetatchConnection (Conn);
3136 IScsiDestroyConnection (Conn);
3137 }
3138
3139 Session->State = SESSION_STATE_FAILED;
3140
3141 return ;
3142 }