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