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