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