VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/IScsiDxe/IScsiProto.c@ 85718

Last change on this file since 85718 was 85718, checked in by vboxsync, 4 years ago

Devices/EFI: Merge edk-stable202005 and make it build, bugref:4643

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette