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