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