Home | History | Annotate | Download | only in IScsiDxe
      1 /** @file
      2   Miscellaneous routines for iSCSI driver.
      3 
      4 Copyright (c) 2004 - 2016, 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 GLOBAL_REMOVE_IF_UNREFERENCED CONST CHAR8  IScsiHexString[] = "0123456789ABCDEFabcdef";
     18 
     19 /**
     20   Removes (trims) specified leading and trailing characters from a string.
     21 
     22   @param[in, out] Str   Pointer to the null-terminated string to be trimmed.
     23                         On return, Str will hold the trimmed string.
     24 
     25   @param[in]      CharC Character will be trimmed from str.
     26 
     27 **/
     28 VOID
     29 IScsiStrTrim (
     30   IN OUT CHAR16   *Str,
     31   IN     CHAR16   CharC
     32   )
     33 {
     34   CHAR16  *Pointer1;
     35   CHAR16  *Pointer2;
     36 
     37   if (*Str == 0) {
     38     return ;
     39   }
     40 
     41   //
     42   // Trim off the leading and trailing characters c
     43   //
     44   for (Pointer1 = Str; (*Pointer1 != 0) && (*Pointer1 == CharC); Pointer1++) {
     45     ;
     46   }
     47 
     48   Pointer2 = Str;
     49   if (Pointer2 == Pointer1) {
     50     while (*Pointer1 != 0) {
     51       Pointer2++;
     52       Pointer1++;
     53     }
     54   } else {
     55     while (*Pointer1 != 0) {
     56     *Pointer2 = *Pointer1;
     57     Pointer1++;
     58     Pointer2++;
     59     }
     60     *Pointer2 = 0;
     61   }
     62 
     63 
     64   for (Pointer1 = Str + StrLen(Str) - 1; Pointer1 >= Str && *Pointer1 == CharC; Pointer1--) {
     65     ;
     66   }
     67   if  (Pointer1 !=  Str + StrLen(Str) - 1) {
     68     *(Pointer1 + 1) = 0;
     69   }
     70 }
     71 
     72 /**
     73   Calculate the prefix length of the IPv4 subnet mask.
     74 
     75   @param[in]  SubnetMask The IPv4 subnet mask.
     76 
     77   @return     The prefix length of the subnet mask.
     78   @retval 0   Other errors as indicated.
     79 
     80 **/
     81 UINT8
     82 IScsiGetSubnetMaskPrefixLength (
     83   IN EFI_IPv4_ADDRESS  *SubnetMask
     84   )
     85 {
     86   UINT8   Len;
     87   UINT32  ReverseMask;
     88 
     89   //
     90   // The SubnetMask is in network byte order.
     91   //
     92   ReverseMask = (SubnetMask->Addr[0] << 24) | (SubnetMask->Addr[1] << 16) | (SubnetMask->Addr[2] << 8) | (SubnetMask->Addr[3]);
     93 
     94   //
     95   // Reverse it.
     96   //
     97   ReverseMask = ~ReverseMask;
     98 
     99   if ((ReverseMask & (ReverseMask + 1)) != 0) {
    100     return 0;
    101   }
    102 
    103   Len = 0;
    104 
    105   while (ReverseMask != 0) {
    106     ReverseMask = ReverseMask >> 1;
    107     Len++;
    108   }
    109 
    110   return (UINT8) (32 - Len);
    111 }
    112 
    113 
    114 /**
    115   Convert the hexadecimal encoded LUN string into the 64-bit LUN.
    116 
    117   @param[in]   Str             The hexadecimal encoded LUN string.
    118   @param[out]  Lun             Storage to return the 64-bit LUN.
    119 
    120   @retval EFI_SUCCESS            The 64-bit LUN is stored in Lun.
    121   @retval EFI_INVALID_PARAMETER  The string is malformatted.
    122 
    123 **/
    124 EFI_STATUS
    125 IScsiAsciiStrToLun (
    126   IN  CHAR8  *Str,
    127   OUT UINT8  *Lun
    128   )
    129 {
    130   UINTN   Index, IndexValue, IndexNum, SizeStr;
    131   CHAR8   TemStr[2];
    132   UINT8   TemValue;
    133   UINT16  Value[4];
    134 
    135   ZeroMem (Lun, 8);
    136   ZeroMem (TemStr, 2);
    137   ZeroMem ((UINT8 *) Value, sizeof (Value));
    138   SizeStr    = AsciiStrLen (Str);
    139   IndexValue = 0;
    140   IndexNum   = 0;
    141 
    142   for (Index = 0; Index < SizeStr; Index ++) {
    143     TemStr[0] = Str[Index];
    144     TemValue = (UINT8) AsciiStrHexToUint64 (TemStr);
    145     if (TemValue == 0 && TemStr[0] != '0') {
    146       if ((TemStr[0] != '-') || (IndexNum == 0)) {
    147         //
    148         // Invalid Lun Char.
    149         //
    150         return EFI_INVALID_PARAMETER;
    151       }
    152     }
    153 
    154     if ((TemValue == 0) && (TemStr[0] == '-')) {
    155       //
    156       // Next Lun value.
    157       //
    158       if (++IndexValue >= 4) {
    159         //
    160         // Max 4 Lun value.
    161         //
    162         return EFI_INVALID_PARAMETER;
    163       }
    164       //
    165       // Restart str index for the next lun value.
    166       //
    167       IndexNum = 0;
    168       continue;
    169     }
    170 
    171     if (++IndexNum > 4) {
    172       //
    173       // Each Lun Str can't exceed size 4, because it will be as UINT16 value.
    174       //
    175       return EFI_INVALID_PARAMETER;
    176     }
    177 
    178     //
    179     // Combine UINT16 value.
    180     //
    181     Value[IndexValue] = (UINT16) ((Value[IndexValue] << 4) + TemValue);
    182   }
    183 
    184   for (Index = 0; Index <= IndexValue; Index ++) {
    185     *((UINT16 *) &Lun[Index * 2]) =  HTONS (Value[Index]);
    186   }
    187 
    188   return EFI_SUCCESS;
    189 }
    190 
    191 /**
    192   Convert the 64-bit LUN into the hexadecimal encoded LUN string.
    193 
    194   @param[in]   Lun The 64-bit LUN.
    195   @param[out]  Str The storage to return the hexadecimal encoded LUN string.
    196 
    197 **/
    198 VOID
    199 IScsiLunToUnicodeStr (
    200   IN UINT8    *Lun,
    201   OUT CHAR16  *Str
    202   )
    203 {
    204   UINTN   Index;
    205   CHAR16  *TempStr;
    206 
    207   TempStr = Str;
    208 
    209   for (Index = 0; Index < 4; Index++) {
    210 
    211     if ((Lun[2 * Index] | Lun[2 * Index + 1]) == 0) {
    212       CopyMem (TempStr, L"0-", sizeof (L"0-"));
    213     } else {
    214       TempStr[0]  = (CHAR16) IScsiHexString[Lun[2 * Index] >> 4];
    215       TempStr[1]  = (CHAR16) IScsiHexString[Lun[2 * Index] & 0x0F];
    216       TempStr[2]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] >> 4];
    217       TempStr[3]  = (CHAR16) IScsiHexString[Lun[2 * Index + 1] & 0x0F];
    218       TempStr[4]  = L'-';
    219       TempStr[5]  = 0;
    220 
    221       IScsiStrTrim (TempStr, L'0');
    222     }
    223 
    224     TempStr += StrLen (TempStr);
    225   }
    226   //
    227   // Remove the last '-'
    228   //
    229   ASSERT (StrLen(Str) >= 1);
    230   Str[StrLen (Str) - 1] = 0;
    231 
    232   for (Index = StrLen (Str) - 1; Index > 1; Index = Index - 2) {
    233     if ((Str[Index] == L'0') && (Str[Index - 1] == L'-')) {
    234       Str[Index - 1] = 0;
    235     } else {
    236       break;
    237     }
    238   }
    239 }
    240 
    241 /**
    242   Convert the formatted IP address into the binary IP address.
    243 
    244   @param[in]   Str               The UNICODE string.
    245   @param[in]   IpMode            Indicates whether the IP address is v4 or v6.
    246   @param[out]  Ip                The storage to return the ASCII string.
    247 
    248   @retval EFI_SUCCESS            The binary IP address is returned in Ip.
    249   @retval EFI_INVALID_PARAMETER  The IP string is malformatted or IpMode is
    250                                  invalid.
    251 
    252 **/
    253 EFI_STATUS
    254 IScsiAsciiStrToIp (
    255   IN  CHAR8             *Str,
    256   IN  UINT8             IpMode,
    257   OUT EFI_IP_ADDRESS    *Ip
    258   )
    259 {
    260   EFI_STATUS            Status;
    261 
    262   if (IpMode == IP_MODE_IP4 || IpMode == IP_MODE_AUTOCONFIG_IP4) {
    263     return NetLibAsciiStrToIp4 (Str, &Ip->v4);
    264 
    265   } else if (IpMode == IP_MODE_IP6 || IpMode == IP_MODE_AUTOCONFIG_IP6) {
    266     return NetLibAsciiStrToIp6 (Str, &Ip->v6);
    267 
    268   } else if (IpMode == IP_MODE_AUTOCONFIG) {
    269     Status = NetLibAsciiStrToIp4 (Str, &Ip->v4);
    270     if (!EFI_ERROR (Status)) {
    271       return Status;
    272     }
    273     return NetLibAsciiStrToIp6 (Str, &Ip->v6);
    274 
    275   }
    276 
    277   return EFI_INVALID_PARAMETER;
    278 }
    279 
    280 /**
    281   Convert the mac address into a hexadecimal encoded "-" seperated string.
    282 
    283   @param[in]  Mac     The mac address.
    284   @param[in]  Len     Length in bytes of the mac address.
    285   @param[in]  VlanId  VLAN ID of the network device.
    286   @param[out] Str     The storage to return the mac string.
    287 
    288 **/
    289 VOID
    290 IScsiMacAddrToStr (
    291   IN  EFI_MAC_ADDRESS  *Mac,
    292   IN  UINT32           Len,
    293   IN  UINT16           VlanId,
    294   OUT CHAR16           *Str
    295   )
    296 {
    297   UINT32  Index;
    298   CHAR16  *String;
    299 
    300   for (Index = 0; Index < Len; Index++) {
    301     Str[3 * Index]      = (CHAR16) IScsiHexString[(Mac->Addr[Index] >> 4) & 0x0F];
    302     Str[3 * Index + 1]  = (CHAR16) IScsiHexString[Mac->Addr[Index] & 0x0F];
    303     Str[3 * Index + 2]  = L':';
    304   }
    305 
    306   String = &Str[3 * Index - 1] ;
    307   if (VlanId != 0) {
    308     String += UnicodeSPrint (String, 6 * sizeof (CHAR16), L"\\%04x", (UINTN) VlanId);
    309   }
    310 
    311   *String = L'\0';
    312 }
    313 
    314 /**
    315   Convert the binary encoded buffer into a hexadecimal encoded string.
    316 
    317   @param[in]       BinBuffer   The buffer containing the binary data.
    318   @param[in]       BinLength   Length of the binary buffer.
    319   @param[in, out]  HexStr      Pointer to the string.
    320   @param[in, out]  HexLength   The length of the string.
    321 
    322   @retval EFI_SUCCESS          The binary data is converted to the hexadecimal string
    323                                and the length of the string is updated.
    324   @retval EFI_BUFFER_TOO_SMALL The string is too small.
    325   @retval EFI_INVALID_PARAMETER The IP string is malformatted.
    326 
    327 **/
    328 EFI_STATUS
    329 IScsiBinToHex (
    330   IN     UINT8  *BinBuffer,
    331   IN     UINT32 BinLength,
    332   IN OUT CHAR8  *HexStr,
    333   IN OUT UINT32 *HexLength
    334   )
    335 {
    336   UINTN Index;
    337 
    338   if ((HexStr == NULL) || (BinBuffer == NULL) || (BinLength == 0)) {
    339     return EFI_INVALID_PARAMETER;
    340   }
    341 
    342   if (((*HexLength) - 3) < BinLength * 2) {
    343     *HexLength = BinLength * 2 + 3;
    344     return EFI_BUFFER_TOO_SMALL;
    345   }
    346 
    347   *HexLength = BinLength * 2 + 3;
    348   //
    349   // Prefix for Hex String.
    350   //
    351   HexStr[0] = '0';
    352   HexStr[1] = 'x';
    353 
    354   for (Index = 0; Index < BinLength; Index++) {
    355     HexStr[Index * 2 + 2] = IScsiHexString[BinBuffer[Index] >> 4];
    356     HexStr[Index * 2 + 3] = IScsiHexString[BinBuffer[Index] & 0xf];
    357   }
    358 
    359   HexStr[Index * 2 + 2] = '\0';
    360 
    361   return EFI_SUCCESS;
    362 }
    363 
    364 
    365 /**
    366   Convert the hexadecimal string into a binary encoded buffer.
    367 
    368   @param[in, out]  BinBuffer   The binary buffer.
    369   @param[in, out]  BinLength   Length of the binary buffer.
    370   @param[in]       HexStr      The hexadecimal string.
    371 
    372   @retval EFI_SUCCESS          The hexadecimal string is converted into a binary
    373                                encoded buffer.
    374   @retval EFI_BUFFER_TOO_SMALL The binary buffer is too small to hold the converted data.
    375 
    376 **/
    377 EFI_STATUS
    378 IScsiHexToBin (
    379   IN OUT UINT8  *BinBuffer,
    380   IN OUT UINT32 *BinLength,
    381   IN     CHAR8  *HexStr
    382   )
    383 {
    384   UINTN   Index;
    385   UINTN   Length;
    386   UINT8   Digit;
    387   CHAR8   TemStr[2];
    388 
    389   ZeroMem (TemStr, sizeof (TemStr));
    390 
    391   //
    392   // Find out how many hex characters the string has.
    393   //
    394   if ((HexStr[0] == '0') && ((HexStr[1] == 'x') || (HexStr[1] == 'X'))) {
    395     HexStr += 2;
    396   }
    397 
    398   Length = AsciiStrLen (HexStr);
    399 
    400   for (Index = 0; Index < Length; Index ++) {
    401     TemStr[0] = HexStr[Index];
    402     Digit = (UINT8) AsciiStrHexToUint64 (TemStr);
    403     if (Digit == 0 && TemStr[0] != '0') {
    404       //
    405       // Invalid Lun Char.
    406       //
    407       break;
    408     }
    409     if ((Index & 1) == 0) {
    410       BinBuffer [Index/2] = Digit;
    411     } else {
    412       BinBuffer [Index/2] = (UINT8) ((BinBuffer [Index/2] << 4) + Digit);
    413     }
    414   }
    415 
    416   *BinLength = (UINT32) ((Index + 1)/2);
    417 
    418   return EFI_SUCCESS;
    419 }
    420 
    421 
    422 /**
    423   Convert the decimal-constant string or hex-constant string into a numerical value.
    424 
    425   @param[in] Str                    String in decimal or hex.
    426 
    427   @return The numerical value.
    428 
    429 **/
    430 UINTN
    431 IScsiNetNtoi (
    432   IN     CHAR8  *Str
    433   )
    434 {
    435   if ((Str[0] == '0') && ((Str[1] == 'x') || (Str[1] == 'X'))) {
    436     Str += 2;
    437 
    438     return AsciiStrHexToUintn (Str);
    439   }
    440 
    441   return AsciiStrDecimalToUintn (Str);
    442 }
    443 
    444 
    445 /**
    446   Generate random numbers.
    447 
    448   @param[in, out]  Rand       The buffer to contain random numbers.
    449   @param[in]       RandLength The length of the Rand buffer.
    450 
    451 **/
    452 VOID
    453 IScsiGenRandom (
    454   IN OUT UINT8  *Rand,
    455   IN     UINTN  RandLength
    456   )
    457 {
    458   UINT32  Random;
    459 
    460   while (RandLength > 0) {
    461     Random  = NET_RANDOM (NetRandomInitSeed ());
    462     *Rand++ = (UINT8) (Random);
    463     RandLength--;
    464   }
    465 }
    466 
    467 
    468 /**
    469   Record the NIC info in global structure.
    470 
    471   @param[in]  Controller         The handle of the controller.
    472 
    473   @retval EFI_SUCCESS            The operation is completed.
    474   @retval EFI_OUT_OF_RESOURCES   Do not have sufficient resources to finish this
    475                                  operation.
    476 
    477 **/
    478 EFI_STATUS
    479 IScsiAddNic (
    480   IN EFI_HANDLE  Controller
    481   )
    482 {
    483   EFI_STATUS                  Status;
    484   ISCSI_NIC_INFO              *NicInfo;
    485   LIST_ENTRY                  *Entry;
    486   EFI_MAC_ADDRESS             MacAddr;
    487   UINTN                       HwAddressSize;
    488   UINT16                      VlanId;
    489 
    490   //
    491   // Get MAC address of this network device.
    492   //
    493   Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);
    494   if (EFI_ERROR (Status)) {
    495     return Status;
    496   }
    497 
    498   //
    499   // Get VLAN ID of this network device.
    500   //
    501   VlanId = NetLibGetVlanId (Controller);
    502 
    503   //
    504   // Check whether the NIC info already exists. Return directly if so.
    505   //
    506   NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
    507     NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
    508     if (NicInfo->HwAddressSize == HwAddressSize &&
    509         CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&
    510         NicInfo->VlanId == VlanId) {
    511       mPrivate->CurrentNic = NicInfo->NicIndex;
    512       return EFI_SUCCESS;
    513     }
    514 
    515     if (mPrivate->MaxNic < NicInfo->NicIndex) {
    516       mPrivate->MaxNic = NicInfo->NicIndex;
    517     }
    518   }
    519 
    520   //
    521   // Record the NIC info in private structure.
    522   //
    523   NicInfo = AllocateZeroPool (sizeof (ISCSI_NIC_INFO));
    524   if (NicInfo == NULL) {
    525     return EFI_OUT_OF_RESOURCES;
    526   }
    527 
    528   CopyMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize);
    529   NicInfo->HwAddressSize  = (UINT32) HwAddressSize;
    530   NicInfo->VlanId         = VlanId;
    531   NicInfo->NicIndex       = (UINT8) (mPrivate->MaxNic + 1);
    532   mPrivate->MaxNic        = NicInfo->NicIndex;
    533 
    534   //
    535   // Get the PCI location.
    536   //
    537   IScsiGetNICPciLocation (
    538     Controller,
    539     &NicInfo->BusNumber,
    540     &NicInfo->DeviceNumber,
    541     &NicInfo->FunctionNumber
    542     );
    543 
    544   InsertTailList (&mPrivate->NicInfoList, &NicInfo->Link);
    545   mPrivate->NicCount++;
    546 
    547   mPrivate->CurrentNic = NicInfo->NicIndex;
    548   return EFI_SUCCESS;
    549 }
    550 
    551 
    552 /**
    553   Delete the recorded NIC info from global structure. Also delete corresponding
    554   attempts.
    555 
    556   @param[in]  Controller         The handle of the controller.
    557 
    558   @retval EFI_SUCCESS            The operation is completed.
    559   @retval EFI_NOT_FOUND          The NIC info to be deleted is not recorded.
    560 
    561 **/
    562 EFI_STATUS
    563 IScsiRemoveNic (
    564   IN EFI_HANDLE  Controller
    565   )
    566 {
    567   EFI_STATUS                  Status;
    568   ISCSI_NIC_INFO              *NicInfo;
    569   LIST_ENTRY                  *Entry;
    570   LIST_ENTRY                  *NextEntry;
    571   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
    572   ISCSI_NIC_INFO              *ThisNic;
    573   EFI_MAC_ADDRESS             MacAddr;
    574   UINTN                       HwAddressSize;
    575   UINT16                      VlanId;
    576 
    577   //
    578   // Get MAC address of this network device.
    579   //
    580   Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);
    581   if (EFI_ERROR (Status)) {
    582     return Status;
    583   }
    584 
    585   //
    586   // Get VLAN ID of this network device.
    587   //
    588   VlanId = NetLibGetVlanId (Controller);
    589 
    590   //
    591   // Check whether the NIC information exists.
    592   //
    593   ThisNic = NULL;
    594 
    595   NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
    596     NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
    597     if (NicInfo->HwAddressSize == HwAddressSize &&
    598         CompareMem (&NicInfo->PermanentAddress, MacAddr.Addr, HwAddressSize) == 0 &&
    599         NicInfo->VlanId == VlanId) {
    600 
    601       ThisNic = NicInfo;
    602       break;
    603     }
    604   }
    605 
    606   if (ThisNic == NULL) {
    607     return EFI_NOT_FOUND;
    608   }
    609 
    610   mPrivate->CurrentNic = ThisNic->NicIndex;
    611 
    612   RemoveEntryList (&ThisNic->Link);
    613   FreePool (ThisNic);
    614   mPrivate->NicCount--;
    615 
    616   //
    617   // Remove all attempts related to this NIC.
    618   //
    619   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
    620     AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
    621     if (AttemptConfigData->NicIndex == mPrivate->CurrentNic) {
    622       RemoveEntryList (&AttemptConfigData->Link);
    623       mPrivate->AttemptCount--;
    624 
    625       if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO && mPrivate->MpioCount > 0) {
    626         if (--mPrivate->MpioCount == 0) {
    627           mPrivate->EnableMpio = FALSE;
    628         }
    629 
    630         if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB && mPrivate->Krb5MpioCount > 0) {
    631           mPrivate->Krb5MpioCount--;
    632         }
    633 
    634       } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED && mPrivate->SinglePathCount > 0) {
    635         mPrivate->SinglePathCount--;
    636 
    637         if (mPrivate->ValidSinglePathCount > 0) {
    638           mPrivate->ValidSinglePathCount--;
    639         }
    640       }
    641 
    642       FreePool (AttemptConfigData);
    643     }
    644   }
    645 
    646   //
    647   // Free attempt is created but not saved to system.
    648   //
    649   if (mPrivate->NewAttempt != NULL) {
    650     FreePool (mPrivate->NewAttempt);
    651     mPrivate->NewAttempt = NULL;
    652   }
    653 
    654   return EFI_SUCCESS;
    655 }
    656 
    657 
    658 /**
    659   Get the recorded NIC info from global structure by the Index.
    660 
    661   @param[in]  NicIndex          The index indicates the position of NIC info.
    662 
    663   @return Pointer to the NIC info, or NULL if not found.
    664 
    665 **/
    666 ISCSI_NIC_INFO *
    667 IScsiGetNicInfoByIndex (
    668   IN UINT8      NicIndex
    669   )
    670 {
    671   LIST_ENTRY        *Entry;
    672   ISCSI_NIC_INFO    *NicInfo;
    673 
    674   NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
    675     NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
    676     if (NicInfo->NicIndex == NicIndex) {
    677       return NicInfo;
    678     }
    679   }
    680 
    681   return NULL;
    682 }
    683 
    684 
    685 /**
    686   Get the NIC's PCI location and return it according to the composited
    687   format defined in iSCSI Boot Firmware Table.
    688 
    689   @param[in]   Controller        The handle of the controller.
    690   @param[out]  Bus               The bus number.
    691   @param[out]  Device            The device number.
    692   @param[out]  Function          The function number.
    693 
    694   @return      The composited representation of the NIC PCI location.
    695 
    696 **/
    697 UINT16
    698 IScsiGetNICPciLocation (
    699   IN EFI_HANDLE  Controller,
    700   OUT UINTN      *Bus,
    701   OUT UINTN      *Device,
    702   OUT UINTN      *Function
    703   )
    704 {
    705   EFI_STATUS                Status;
    706   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
    707   EFI_HANDLE                PciIoHandle;
    708   EFI_PCI_IO_PROTOCOL       *PciIo;
    709   UINTN                     Segment;
    710 
    711   Status = gBS->HandleProtocol (
    712                   Controller,
    713                   &gEfiDevicePathProtocolGuid,
    714                   (VOID **) &DevicePath
    715                   );
    716   if (EFI_ERROR (Status)) {
    717     return 0;
    718   }
    719 
    720   Status = gBS->LocateDevicePath (
    721                   &gEfiPciIoProtocolGuid,
    722                   &DevicePath,
    723                   &PciIoHandle
    724                   );
    725   if (EFI_ERROR (Status)) {
    726     return 0;
    727   }
    728 
    729   Status = gBS->HandleProtocol (PciIoHandle, &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
    730   if (EFI_ERROR (Status)) {
    731     return 0;
    732   }
    733 
    734   Status = PciIo->GetLocation (PciIo, &Segment, Bus, Device, Function);
    735   if (EFI_ERROR (Status)) {
    736     return 0;
    737   }
    738 
    739   return (UINT16) ((*Bus << 8) | (*Device << 3) | *Function);
    740 }
    741 
    742 
    743 /**
    744   Read the EFI variable (VendorGuid/Name) and return a dynamically allocated
    745   buffer, and the size of the buffer. If failure, return NULL.
    746 
    747   @param[in]   Name                   String part of EFI variable name.
    748   @param[in]   VendorGuid             GUID part of EFI variable name.
    749   @param[out]  VariableSize           Returns the size of the EFI variable that was read.
    750 
    751   @return Dynamically allocated memory that contains a copy of the EFI variable.
    752   @return Caller is responsible freeing the buffer.
    753   @retval NULL                   Variable was not read.
    754 
    755 **/
    756 VOID *
    757 IScsiGetVariableAndSize (
    758   IN  CHAR16              *Name,
    759   IN  EFI_GUID            *VendorGuid,
    760   OUT UINTN               *VariableSize
    761   )
    762 {
    763   EFI_STATUS  Status;
    764   UINTN       BufferSize;
    765   VOID        *Buffer;
    766 
    767   Buffer = NULL;
    768 
    769   //
    770   // Pass in a zero size buffer to find the required buffer size.
    771   //
    772   BufferSize  = 0;
    773   Status      = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
    774   if (Status == EFI_BUFFER_TOO_SMALL) {
    775     //
    776     // Allocate the buffer to return
    777     //
    778     Buffer = AllocateZeroPool (BufferSize);
    779     if (Buffer == NULL) {
    780       return NULL;
    781     }
    782     //
    783     // Read variable into the allocated buffer.
    784     //
    785     Status = gRT->GetVariable (Name, VendorGuid, NULL, &BufferSize, Buffer);
    786     if (EFI_ERROR (Status)) {
    787       BufferSize = 0;
    788     }
    789   }
    790 
    791   *VariableSize = BufferSize;
    792   return Buffer;
    793 }
    794 
    795 
    796 /**
    797   Create the iSCSI driver data.
    798 
    799   @param[in] Image      The handle of the driver image.
    800   @param[in] Controller The handle of the controller.
    801 
    802   @return The iSCSI driver data created.
    803   @retval NULL Other errors as indicated.
    804 
    805 **/
    806 ISCSI_DRIVER_DATA *
    807 IScsiCreateDriverData (
    808   IN EFI_HANDLE  Image,
    809   IN EFI_HANDLE  Controller
    810   )
    811 {
    812   ISCSI_DRIVER_DATA *Private;
    813   EFI_STATUS        Status;
    814 
    815   Private = AllocateZeroPool (sizeof (ISCSI_DRIVER_DATA));
    816   if (Private == NULL) {
    817     return NULL;
    818   }
    819 
    820   Private->Signature  = ISCSI_DRIVER_DATA_SIGNATURE;
    821   Private->Image      = Image;
    822   Private->Controller = Controller;
    823   Private->Session    = NULL;
    824 
    825   //
    826   // Create an event to be signaled when the BS to RT transition is triggerd so
    827   // as to abort the iSCSI session.
    828   //
    829   Status = gBS->CreateEventEx (
    830                   EVT_NOTIFY_SIGNAL,
    831                   TPL_CALLBACK,
    832                   IScsiOnExitBootService,
    833                   Private,
    834                   &gEfiEventExitBootServicesGuid,
    835                   &Private->ExitBootServiceEvent
    836                   );
    837   if (EFI_ERROR (Status)) {
    838     FreePool (Private);
    839     return NULL;
    840   }
    841 
    842   Private->ExtScsiPassThruHandle = NULL;
    843   CopyMem(&Private->IScsiExtScsiPassThru, &gIScsiExtScsiPassThruProtocolTemplate, sizeof(EFI_EXT_SCSI_PASS_THRU_PROTOCOL));
    844 
    845   //
    846   // 0 is designated to the TargetId, so use another value for the AdapterId.
    847   //
    848   Private->ExtScsiPassThruMode.AdapterId  = 2;
    849   Private->ExtScsiPassThruMode.Attributes = EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_PHYSICAL | EFI_EXT_SCSI_PASS_THRU_ATTRIBUTES_LOGICAL;
    850   Private->ExtScsiPassThruMode.IoAlign    = 4;
    851   Private->IScsiExtScsiPassThru.Mode      = &Private->ExtScsiPassThruMode;
    852 
    853   return Private;
    854 }
    855 
    856 
    857 /**
    858   Clean the iSCSI driver data.
    859 
    860   @param[in]              Private The iSCSI driver data.
    861 
    862   @retval EFI_SUCCESS     The clean operation is successful.
    863   @retval Others          Other errors as indicated.
    864 
    865 **/
    866 EFI_STATUS
    867 IScsiCleanDriverData (
    868   IN ISCSI_DRIVER_DATA  *Private
    869   )
    870 {
    871   EFI_STATUS            Status;
    872 
    873   Status = EFI_SUCCESS;
    874 
    875   if (Private->DevicePath != NULL) {
    876     Status = gBS->UninstallProtocolInterface (
    877                     Private->ExtScsiPassThruHandle,
    878                     &gEfiDevicePathProtocolGuid,
    879                     Private->DevicePath
    880                     );
    881     if (EFI_ERROR (Status)) {
    882       goto EXIT;
    883     }
    884 
    885     FreePool (Private->DevicePath);
    886   }
    887 
    888   if (Private->ExtScsiPassThruHandle != NULL) {
    889     Status = gBS->UninstallProtocolInterface (
    890                     Private->ExtScsiPassThruHandle,
    891                     &gEfiExtScsiPassThruProtocolGuid,
    892                     &Private->IScsiExtScsiPassThru
    893                     );
    894     if (!EFI_ERROR (Status)) {
    895       mPrivate->OneSessionEstablished = FALSE;
    896     }
    897   }
    898 
    899 EXIT:
    900 
    901   gBS->CloseEvent (Private->ExitBootServiceEvent);
    902 
    903   mCallbackInfo->Current = NULL;
    904 
    905   FreePool (Private);
    906   return Status;
    907 }
    908 
    909 /**
    910   Check wheather the Controller handle is configured to use DHCP protocol.
    911 
    912   @param[in]  Controller           The handle of the controller.
    913   @param[in]  IpVersion            IP_VERSION_4 or IP_VERSION_6.
    914 
    915   @retval TRUE                     The handle of the controller need the Dhcp protocol.
    916   @retval FALSE                    The handle of the controller does not need the Dhcp protocol.
    917 
    918 **/
    919 BOOLEAN
    920 IScsiDhcpIsConfigured (
    921   IN EFI_HANDLE  Controller,
    922   IN UINT8       IpVersion
    923   )
    924 {
    925   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp;
    926   UINT8                       *AttemptConfigOrder;
    927   UINTN                       AttemptConfigOrderSize;
    928   UINTN                       Index;
    929   EFI_STATUS                  Status;
    930   EFI_MAC_ADDRESS             MacAddr;
    931   UINTN                       HwAddressSize;
    932   UINT16                      VlanId;
    933   CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];
    934   CHAR16                      AttemptName[ISCSI_NAME_IFR_MAX_SIZE];
    935 
    936   AttemptConfigOrder = IScsiGetVariableAndSize (
    937                          L"AttemptOrder",
    938                          &gIScsiConfigGuid,
    939                          &AttemptConfigOrderSize
    940                          );
    941   if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) {
    942     return FALSE;
    943   }
    944 
    945   //
    946   // Get MAC address of this network device.
    947   //
    948   Status = NetLibGetMacAddress (Controller, &MacAddr, &HwAddressSize);
    949   if(EFI_ERROR (Status)) {
    950     return FALSE;
    951   }
    952   //
    953   // Get VLAN ID of this network device.
    954   //
    955   VlanId = NetLibGetVlanId (Controller);
    956   IScsiMacAddrToStr (&MacAddr, (UINT32) HwAddressSize, VlanId, MacString);
    957 
    958   for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
    959     UnicodeSPrint (
    960       AttemptName,
    961       (UINTN) 128,
    962       L"%s%d",
    963       MacString,
    964       (UINTN) AttemptConfigOrder[Index]
    965       );
    966     Status = GetVariable2 (
    967                AttemptName,
    968                &gEfiIScsiInitiatorNameProtocolGuid,
    969                (VOID**)&AttemptTmp,
    970                NULL
    971                );
    972     if(AttemptTmp == NULL || EFI_ERROR (Status)) {
    973       continue;
    974     }
    975 
    976     ASSERT (AttemptConfigOrder[Index] == AttemptTmp->AttemptConfigIndex);
    977 
    978     if (AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) {
    979       FreePool (AttemptTmp);
    980       continue;
    981     }
    982 
    983     if (AttemptTmp->SessionConfigData.IpMode != IP_MODE_AUTOCONFIG &&
    984         AttemptTmp->SessionConfigData.IpMode != ((IpVersion == IP_VERSION_4) ? IP_MODE_IP4 : IP_MODE_IP6)) {
    985       FreePool (AttemptTmp);
    986       continue;
    987     }
    988 
    989     if(AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG ||
    990        AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp == TRUE ||
    991        AttemptTmp->SessionConfigData.TargetInfoFromDhcp == TRUE) {
    992       FreePool (AttemptTmp);
    993       FreePool (AttemptConfigOrder);
    994       return TRUE;
    995     }
    996 
    997     FreePool (AttemptTmp);
    998   }
    999 
   1000   FreePool (AttemptConfigOrder);
   1001   return FALSE;
   1002 }
   1003 
   1004 /**
   1005   Get the various configuration data.
   1006 
   1007   @param[in]  Private   The iSCSI driver data.
   1008 
   1009   @retval EFI_SUCCESS            The configuration data is retrieved.
   1010   @retval EFI_NOT_FOUND          This iSCSI driver is not configured yet.
   1011   @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
   1012 
   1013 **/
   1014 EFI_STATUS
   1015 IScsiGetConfigData (
   1016   IN ISCSI_DRIVER_DATA  *Private
   1017   )
   1018 {
   1019   EFI_STATUS                  Status;
   1020   CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];
   1021   UINTN                       Index;
   1022   ISCSI_NIC_INFO              *NicInfo;
   1023   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
   1024   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptTmp;
   1025   UINT8                       *AttemptConfigOrder;
   1026   UINTN                       AttemptConfigOrderSize;
   1027   CHAR16                      IScsiMode[64];
   1028   CHAR16                      IpMode[64];
   1029 
   1030   //
   1031   // There should be at least one attempt configured.
   1032   //
   1033   AttemptConfigOrder = IScsiGetVariableAndSize (
   1034                          L"AttemptOrder",
   1035                          &gIScsiConfigGuid,
   1036                          &AttemptConfigOrderSize
   1037                          );
   1038   if (AttemptConfigOrder == NULL || AttemptConfigOrderSize == 0) {
   1039     return EFI_NOT_FOUND;
   1040   }
   1041 
   1042   //
   1043   // Get the iSCSI Initiator Name.
   1044   //
   1045   mPrivate->InitiatorNameLength  = ISCSI_NAME_MAX_SIZE;
   1046   Status = gIScsiInitiatorName.Get (
   1047                                  &gIScsiInitiatorName,
   1048                                  &mPrivate->InitiatorNameLength,
   1049                                  mPrivate->InitiatorName
   1050                                  );
   1051   if (EFI_ERROR (Status)) {
   1052     return Status;
   1053   }
   1054 
   1055   //
   1056   // Get the normal configuration.
   1057   //
   1058   for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
   1059 
   1060     //
   1061     // Check whether the attempt exists in AttemptConfig.
   1062     //
   1063     AttemptTmp = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
   1064     if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled == ISCSI_DISABLED) {
   1065       continue;
   1066     } else if (AttemptTmp != NULL && AttemptTmp->SessionConfigData.Enabled != ISCSI_DISABLED) {
   1067       //
   1068       // Check the autoconfig path to see whether it should be retried.
   1069       //
   1070       if (AttemptTmp->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG &&
   1071           !AttemptTmp->AutoConfigureSuccess) {
   1072         if (mPrivate->Ipv6Flag &&
   1073             AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6) {
   1074           //
   1075           // Autoconfigure for IP6 already attempted but failed. Do not try again.
   1076           //
   1077           continue;
   1078         } else if (!mPrivate->Ipv6Flag &&
   1079                    AttemptTmp->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4) {
   1080           //
   1081           // Autoconfigure for IP4  already attempted but failed. Do not try again.
   1082           //
   1083           continue;
   1084         } else {
   1085           //
   1086           // Try another approach for this autoconfigure path.
   1087           //
   1088           AttemptTmp->AutoConfigureMode =
   1089             (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
   1090           AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
   1091           AttemptTmp->SessionConfigData.TargetInfoFromDhcp    = TRUE;
   1092           AttemptTmp->DhcpSuccess                             = FALSE;
   1093 
   1094           //
   1095           // Get some information from the dhcp server.
   1096           //
   1097           if (!mPrivate->Ipv6Flag) {
   1098             Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
   1099             if (!EFI_ERROR (Status)) {
   1100               AttemptTmp->DhcpSuccess = TRUE;
   1101             }
   1102           } else {
   1103             Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
   1104             if (!EFI_ERROR (Status)) {
   1105               AttemptTmp->DhcpSuccess = TRUE;
   1106             }
   1107           }
   1108 
   1109           //
   1110           // Refresh the state of this attempt to NVR.
   1111           //
   1112           AsciiStrToUnicodeStrS (AttemptTmp->MacString, MacString, ARRAY_SIZE (MacString));
   1113           UnicodeSPrint (
   1114             mPrivate->PortString,
   1115             (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
   1116             L"%s%d",
   1117             MacString,
   1118             (UINTN) AttemptTmp->AttemptConfigIndex
   1119             );
   1120 
   1121           gRT->SetVariable (
   1122                  mPrivate->PortString,
   1123                  &gEfiIScsiInitiatorNameProtocolGuid,
   1124                  ISCSI_CONFIG_VAR_ATTR,
   1125                  sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
   1126                  AttemptTmp
   1127                  );
   1128 
   1129           continue;
   1130         }
   1131       } else if (AttemptTmp->SessionConfigData.InitiatorInfoFromDhcp && !AttemptTmp->ValidPath) {
   1132         //
   1133         // Get DHCP information for already added, but failed, attempt.
   1134         //
   1135         AttemptTmp->DhcpSuccess = FALSE;
   1136         if (!mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP4)) {
   1137           Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptTmp);
   1138           if (!EFI_ERROR (Status)) {
   1139             AttemptTmp->DhcpSuccess = TRUE;
   1140           }
   1141         } else if (mPrivate->Ipv6Flag && (AttemptTmp->SessionConfigData.IpMode == IP_MODE_IP6)) {
   1142           Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptTmp);
   1143           if (!EFI_ERROR (Status)) {
   1144             AttemptTmp->DhcpSuccess = TRUE;
   1145           }
   1146         }
   1147 
   1148         //
   1149         // Refresh the state of this attempt to NVR.
   1150         //
   1151         AsciiStrToUnicodeStrS (AttemptTmp->MacString, MacString, ARRAY_SIZE (MacString));
   1152         UnicodeSPrint (
   1153           mPrivate->PortString,
   1154           (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
   1155           L"%s%d",
   1156           MacString,
   1157           (UINTN) AttemptTmp->AttemptConfigIndex
   1158           );
   1159 
   1160         gRT->SetVariable (
   1161                mPrivate->PortString,
   1162                &gEfiIScsiInitiatorNameProtocolGuid,
   1163                ISCSI_CONFIG_VAR_ATTR,
   1164                sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
   1165                AttemptTmp
   1166                );
   1167 
   1168         continue;
   1169 
   1170       } else {
   1171         continue;
   1172       }
   1173     }
   1174 
   1175     //
   1176     // This attempt does not exist in AttemptConfig. Try to add a new one.
   1177     //
   1178 
   1179     NicInfo = IScsiGetNicInfoByIndex (mPrivate->CurrentNic);
   1180     ASSERT (NicInfo != NULL);
   1181     IScsiMacAddrToStr (&NicInfo->PermanentAddress, NicInfo->HwAddressSize, NicInfo->VlanId, MacString);
   1182     UnicodeSPrint (
   1183       mPrivate->PortString,
   1184       (UINTN) 128,
   1185       L"%s%d",
   1186       MacString,
   1187       (UINTN) AttemptConfigOrder[Index]
   1188       );
   1189 
   1190     GetVariable2 (
   1191       mPrivate->PortString,
   1192       &gEfiIScsiInitiatorNameProtocolGuid,
   1193       (VOID**)&AttemptConfigData,
   1194       NULL
   1195       );
   1196 
   1197     if (AttemptConfigData == NULL) {
   1198       continue;
   1199     }
   1200 
   1201     ASSERT (AttemptConfigOrder[Index] == AttemptConfigData->AttemptConfigIndex);
   1202 
   1203     AttemptConfigData->NicIndex      = NicInfo->NicIndex;
   1204     AttemptConfigData->DhcpSuccess   = FALSE;
   1205     AttemptConfigData->ValidiBFTPath = (BOOLEAN) (mPrivate->EnableMpio ? TRUE : FALSE);
   1206     AttemptConfigData->ValidPath     = FALSE;
   1207 
   1208     if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
   1209       AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp = TRUE;
   1210       AttemptConfigData->SessionConfigData.TargetInfoFromDhcp    = TRUE;
   1211 
   1212       AttemptConfigData->AutoConfigureMode =
   1213         (UINT8) (mPrivate->Ipv6Flag ? IP_MODE_AUTOCONFIG_IP6 : IP_MODE_AUTOCONFIG_IP4);
   1214       AttemptConfigData->AutoConfigureSuccess = FALSE;
   1215     }
   1216 
   1217     //
   1218     // Get some information from dhcp server.
   1219     //
   1220     if (AttemptConfigData->SessionConfigData.Enabled != ISCSI_DISABLED &&
   1221         AttemptConfigData->SessionConfigData.InitiatorInfoFromDhcp) {
   1222 
   1223       if (!mPrivate->Ipv6Flag &&
   1224           (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4 ||
   1225            AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP4)) {
   1226         Status = IScsiDoDhcp (Private->Image, Private->Controller, AttemptConfigData);
   1227         if (!EFI_ERROR (Status)) {
   1228           AttemptConfigData->DhcpSuccess = TRUE;
   1229         }
   1230       } else if (mPrivate->Ipv6Flag &&
   1231                 (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6 ||
   1232                  AttemptConfigData->AutoConfigureMode == IP_MODE_AUTOCONFIG_IP6)) {
   1233         Status = IScsiDoDhcp6 (Private->Image, Private->Controller, AttemptConfigData);
   1234         if (!EFI_ERROR (Status)) {
   1235           AttemptConfigData->DhcpSuccess = TRUE;
   1236         }
   1237       }
   1238 
   1239       //
   1240       // Refresh the state of this attempt to NVR.
   1241       //
   1242       AsciiStrToUnicodeStrS (AttemptConfigData->MacString, MacString, ARRAY_SIZE (MacString));
   1243       UnicodeSPrint (
   1244         mPrivate->PortString,
   1245         (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
   1246         L"%s%d",
   1247         MacString,
   1248         (UINTN) AttemptConfigData->AttemptConfigIndex
   1249         );
   1250 
   1251       gRT->SetVariable (
   1252              mPrivate->PortString,
   1253              &gEfiIScsiInitiatorNameProtocolGuid,
   1254              ISCSI_CONFIG_VAR_ATTR,
   1255              sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
   1256              AttemptConfigData
   1257              );
   1258     }
   1259 
   1260     //
   1261     // Update Attempt Help Info.
   1262     //
   1263 
   1264     if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_DISABLED) {
   1265       UnicodeSPrint (IScsiMode, 64, L"Disabled");
   1266     } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
   1267       UnicodeSPrint (IScsiMode, 64, L"Enabled");
   1268     } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
   1269       UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");
   1270     }
   1271 
   1272     if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP4) {
   1273       UnicodeSPrint (IpMode, 64, L"IP4");
   1274     } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_IP6) {
   1275       UnicodeSPrint (IpMode, 64, L"IP6");
   1276     } else if (AttemptConfigData->SessionConfigData.IpMode == IP_MODE_AUTOCONFIG) {
   1277       UnicodeSPrint (IpMode, 64, L"Autoconfigure");
   1278     }
   1279 
   1280     UnicodeSPrint (
   1281       mPrivate->PortString,
   1282       (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
   1283       L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",
   1284       MacString,
   1285       NicInfo->BusNumber,
   1286       NicInfo->DeviceNumber,
   1287       NicInfo->FunctionNumber,
   1288       IScsiMode,
   1289       IpMode
   1290       );
   1291 
   1292     AttemptConfigData->AttemptTitleHelpToken = HiiSetString (
   1293                                                  mCallbackInfo->RegisteredHandle,
   1294                                                  0,
   1295                                                  mPrivate->PortString,
   1296                                                  NULL
   1297                                                  );
   1298     if (AttemptConfigData->AttemptTitleHelpToken == 0) {
   1299       return EFI_OUT_OF_RESOURCES;
   1300     }
   1301 
   1302     //
   1303     // Record the attempt in global link list.
   1304     //
   1305     InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
   1306     mPrivate->AttemptCount++;
   1307 
   1308     if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
   1309       mPrivate->MpioCount++;
   1310       mPrivate->EnableMpio = TRUE;
   1311 
   1312       if (AttemptConfigData->AuthenticationType == ISCSI_AUTH_TYPE_KRB) {
   1313         mPrivate->Krb5MpioCount++;
   1314       }
   1315     } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
   1316       mPrivate->SinglePathCount++;
   1317     }
   1318   }
   1319 
   1320   //
   1321   // Reorder the AttemptConfig by the configured order.
   1322   //
   1323   for (Index = 0; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
   1324     AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (AttemptConfigOrder[Index]);
   1325     if (AttemptConfigData == NULL) {
   1326       continue;
   1327     }
   1328 
   1329     RemoveEntryList (&AttemptConfigData->Link);
   1330     InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
   1331   }
   1332 
   1333   //
   1334   // Update the Main Form.
   1335   //
   1336   IScsiConfigUpdateAttempt ();
   1337 
   1338   FreePool (AttemptConfigOrder);
   1339 
   1340   //
   1341   //  There should be at least one attempt configuration.
   1342   //
   1343   if (!mPrivate->EnableMpio) {
   1344     if (mPrivate->SinglePathCount == 0) {
   1345       return EFI_NOT_FOUND;
   1346     }
   1347     mPrivate->ValidSinglePathCount = mPrivate->SinglePathCount;
   1348   }
   1349 
   1350   return EFI_SUCCESS;
   1351 }
   1352 
   1353 
   1354 /**
   1355   Get the device path of the iSCSI tcp connection and update it.
   1356 
   1357   @param  Session                The iSCSI session.
   1358 
   1359   @return The updated device path.
   1360   @retval NULL Other errors as indicated.
   1361 
   1362 **/
   1363 EFI_DEVICE_PATH_PROTOCOL *
   1364 IScsiGetTcpConnDevicePath (
   1365   IN ISCSI_SESSION      *Session
   1366   )
   1367 {
   1368   ISCSI_CONNECTION          *Conn;
   1369   EFI_DEVICE_PATH_PROTOCOL  *DevicePath;
   1370   EFI_STATUS                Status;
   1371   EFI_DEV_PATH              *DPathNode;
   1372   UINTN                     PathLen;
   1373 
   1374   if (Session->State != SESSION_STATE_LOGGED_IN) {
   1375     return NULL;
   1376   }
   1377 
   1378   Conn = NET_LIST_USER_STRUCT_S (
   1379            Session->Conns.ForwardLink,
   1380            ISCSI_CONNECTION,
   1381            Link,
   1382            ISCSI_CONNECTION_SIGNATURE
   1383            );
   1384 
   1385   Status = gBS->HandleProtocol (
   1386                   Conn->TcpIo.Handle,
   1387                   &gEfiDevicePathProtocolGuid,
   1388                   (VOID **) &DevicePath
   1389                   );
   1390   if (EFI_ERROR (Status)) {
   1391     return NULL;
   1392   }
   1393   //
   1394   // Duplicate it.
   1395   //
   1396   DevicePath  = DuplicateDevicePath (DevicePath);
   1397   if (DevicePath == NULL) {
   1398     return NULL;
   1399   }
   1400 
   1401   DPathNode   = (EFI_DEV_PATH *) DevicePath;
   1402 
   1403   while (!IsDevicePathEnd (&DPathNode->DevPath)) {
   1404     if (DevicePathType (&DPathNode->DevPath) == MESSAGING_DEVICE_PATH) {
   1405       if (!Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv4_DP) {
   1406         DPathNode->Ipv4.LocalPort       = 0;
   1407 
   1408         DPathNode->Ipv4.StaticIpAddress =
   1409           (BOOLEAN) (!Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp);
   1410 
   1411         //
   1412         //  Add a judgement here to support previous versions of IPv4_DEVICE_PATH.
   1413         //  In previous versions of IPv4_DEVICE_PATH, GatewayIpAddress and SubnetMask
   1414         //  do not exist.
   1415         //  In new version of IPv4_DEVICE_PATH, structcure length is 27.
   1416         //
   1417 
   1418         PathLen = DevicePathNodeLength (&DPathNode->Ipv4);
   1419 
   1420         if (PathLen == IP4_NODE_LEN_NEW_VERSIONS) {
   1421 
   1422           IP4_COPY_ADDRESS (
   1423             &DPathNode->Ipv4.GatewayIpAddress,
   1424             &Session->ConfigData->SessionConfigData.Gateway
   1425             );
   1426 
   1427           IP4_COPY_ADDRESS (
   1428             &DPathNode->Ipv4.SubnetMask,
   1429             &Session->ConfigData->SessionConfigData.SubnetMask
   1430             );
   1431         }
   1432 
   1433         break;
   1434       } else if (Conn->Ipv6Flag && DevicePathSubType (&DPathNode->DevPath) == MSG_IPv6_DP) {
   1435         DPathNode->Ipv6.LocalPort       = 0;
   1436 
   1437         //
   1438         //  Add a judgement here to support previous versions of IPv6_DEVICE_PATH.
   1439         //  In previous versions of IPv6_DEVICE_PATH, IpAddressOrigin, PrefixLength
   1440         //  and GatewayIpAddress do not exist.
   1441         //  In new version of IPv6_DEVICE_PATH, structure length is 60, while in
   1442         //  old versions, the length is 43.
   1443         //
   1444 
   1445         PathLen = DevicePathNodeLength (&DPathNode->Ipv6);
   1446 
   1447         if (PathLen == IP6_NODE_LEN_NEW_VERSIONS ) {
   1448 
   1449           DPathNode->Ipv6.IpAddressOrigin = 0;
   1450           DPathNode->Ipv6.PrefixLength    = IP6_PREFIX_LENGTH;
   1451           ZeroMem (&DPathNode->Ipv6.GatewayIpAddress, sizeof (EFI_IPv6_ADDRESS));
   1452         }
   1453         else if (PathLen == IP6_NODE_LEN_OLD_VERSIONS) {
   1454 
   1455           //
   1456           //  StaticIPAddress is a field in old versions of IPv6_DEVICE_PATH, while ignored in new
   1457           //  version. Set StaticIPAddress through its' offset in old IPv6_DEVICE_PATH.
   1458           //
   1459           *((UINT8 *)(&DPathNode->Ipv6) + IP6_OLD_IPADDRESS_OFFSET) =
   1460             (BOOLEAN) (!Session->ConfigData->SessionConfigData.InitiatorInfoFromDhcp);
   1461         }
   1462 
   1463         break;
   1464       }
   1465     }
   1466 
   1467     DPathNode = (EFI_DEV_PATH *) NextDevicePathNode (&DPathNode->DevPath);
   1468   }
   1469 
   1470   return DevicePath;
   1471 }
   1472 
   1473 
   1474 /**
   1475   Abort the session when the transition from BS to RT is initiated.
   1476 
   1477   @param[in]   Event  The event signaled.
   1478   @param[in]  Context The iSCSI driver data.
   1479 
   1480 **/
   1481 VOID
   1482 EFIAPI
   1483 IScsiOnExitBootService (
   1484   IN EFI_EVENT  Event,
   1485   IN VOID       *Context
   1486   )
   1487 {
   1488   ISCSI_DRIVER_DATA *Private;
   1489 
   1490   Private = (ISCSI_DRIVER_DATA *) Context;
   1491   gBS->CloseEvent (Private->ExitBootServiceEvent);
   1492 
   1493   if (Private->Session != NULL) {
   1494     IScsiSessionAbort (Private->Session);
   1495   }
   1496 }
   1497 
   1498 /**
   1499   Tests whether a controller handle is being managed by IScsi driver.
   1500 
   1501   This function tests whether the driver specified by DriverBindingHandle is
   1502   currently managing the controller specified by ControllerHandle.  This test
   1503   is performed by evaluating if the the protocol specified by ProtocolGuid is
   1504   present on ControllerHandle and is was opened by DriverBindingHandle and Nic
   1505   Device handle with an attribute of EFI_OPEN_PROTOCOL_BY_DRIVER.
   1506   If ProtocolGuid is NULL, then ASSERT().
   1507 
   1508   @param  ControllerHandle     A handle for a controller to test.
   1509   @param  DriverBindingHandle  Specifies the driver binding handle for the
   1510                                driver.
   1511   @param  ProtocolGuid         Specifies the protocol that the driver specified
   1512                                by DriverBindingHandle opens in its Start()
   1513                                function.
   1514 
   1515   @retval EFI_SUCCESS          ControllerHandle is managed by the driver
   1516                                specified by DriverBindingHandle.
   1517   @retval EFI_UNSUPPORTED      ControllerHandle is not managed by the driver
   1518                                specified by DriverBindingHandle.
   1519 
   1520 **/
   1521 EFI_STATUS
   1522 EFIAPI
   1523 IScsiTestManagedDevice (
   1524   IN  EFI_HANDLE       ControllerHandle,
   1525   IN  EFI_HANDLE       DriverBindingHandle,
   1526   IN  EFI_GUID         *ProtocolGuid
   1527   )
   1528 {
   1529   EFI_STATUS     Status;
   1530   VOID           *ManagedInterface;
   1531   EFI_HANDLE     NicControllerHandle;
   1532 
   1533   ASSERT (ProtocolGuid != NULL);
   1534 
   1535   NicControllerHandle = NetLibGetNicHandle (ControllerHandle, ProtocolGuid);
   1536   if (NicControllerHandle == NULL) {
   1537     return EFI_UNSUPPORTED;
   1538   }
   1539 
   1540   Status = gBS->OpenProtocol (
   1541                   ControllerHandle,
   1542                   (EFI_GUID *) ProtocolGuid,
   1543                   &ManagedInterface,
   1544                   DriverBindingHandle,
   1545                   NicControllerHandle,
   1546                   EFI_OPEN_PROTOCOL_BY_DRIVER
   1547                   );
   1548   if (!EFI_ERROR (Status)) {
   1549     gBS->CloseProtocol (
   1550            ControllerHandle,
   1551            (EFI_GUID *) ProtocolGuid,
   1552            DriverBindingHandle,
   1553            NicControllerHandle
   1554            );
   1555     return EFI_UNSUPPORTED;
   1556   }
   1557 
   1558   if (Status != EFI_ALREADY_STARTED) {
   1559     return EFI_UNSUPPORTED;
   1560   }
   1561 
   1562   return EFI_SUCCESS;
   1563 }
   1564