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