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