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