VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/NetworkPkg/IScsiDxe/IScsiProto.c@ 70372

最後變更 在這個檔案從70372是 58466,由 vboxsync 提交於 9 年 前

EFI/Firmware: Merged in the svn:eol-style, svn:mime-type and trailing whitespace cleanup that was done after the initial UDK2014.SP1 import: svn merge /vendor/edk2/UDK2014.SP1 /vendor/edk2/current .

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

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