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