Home | History | Annotate | Download | only in IScsiDxe
      1 /** @file
      2   Helper functions for configuring or getting the parameters relating to iSCSI.
      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 CHAR16          mVendorStorageName[]     = L"ISCSI_CONFIG_IFR_NVDATA";
     18 BOOLEAN         mIScsiDeviceListUpdated  = FALSE;
     19 UINTN           mNumberOfIScsiDevices    = 0;
     20 ISCSI_FORM_CALLBACK_INFO  *mCallbackInfo = NULL;
     21 
     22 HII_VENDOR_DEVICE_PATH  mIScsiHiiVendorDevicePath = {
     23   {
     24     {
     25       HARDWARE_DEVICE_PATH,
     26       HW_VENDOR_DP,
     27       {
     28         (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
     29         (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
     30       }
     31     },
     32     ISCSI_CONFIG_GUID
     33   },
     34   {
     35     END_DEVICE_PATH_TYPE,
     36     END_ENTIRE_DEVICE_PATH_SUBTYPE,
     37     {
     38       (UINT8) (END_DEVICE_PATH_LENGTH),
     39       (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
     40     }
     41   }
     42 };
     43 
     44 
     45 /**
     46   Convert the IP address into a dotted string.
     47 
     48   @param[in]  Ip        The IP address.
     49   @param[in]  Ipv6Flag  Indicates whether the IP address is version 4 or version 6.
     50   @param[out] Str       The formatted IP string.
     51 
     52 **/
     53 VOID
     54 IScsiIpToStr (
     55   IN  EFI_IP_ADDRESS    *Ip,
     56   IN  BOOLEAN           Ipv6Flag,
     57   OUT CHAR16            *Str
     58   )
     59 {
     60   EFI_IPv4_ADDRESS      *Ip4;
     61   EFI_IPv6_ADDRESS      *Ip6;
     62   UINTN                 Index;
     63   BOOLEAN               Short;
     64   UINTN                 Number;
     65   CHAR16                FormatString[8];
     66 
     67   if (!Ipv6Flag) {
     68     Ip4 = &Ip->v4;
     69 
     70     UnicodeSPrint (
     71       Str,
     72       (UINTN) 2 * IP4_STR_MAX_SIZE,
     73       L"%d.%d.%d.%d",
     74       (UINTN) Ip4->Addr[0],
     75       (UINTN) Ip4->Addr[1],
     76       (UINTN) Ip4->Addr[2],
     77       (UINTN) Ip4->Addr[3]
     78       );
     79 
     80     return ;
     81   }
     82 
     83   Ip6   = &Ip->v6;
     84   Short = FALSE;
     85 
     86   for (Index = 0; Index < 15; Index = Index + 2) {
     87     if (!Short &&
     88         Index % 2 == 0 &&
     89         Ip6->Addr[Index] == 0 &&
     90         Ip6->Addr[Index + 1] == 0
     91         ) {
     92       //
     93       // Deal with the case of ::.
     94       //
     95       if (Index == 0) {
     96         *Str       = L':';
     97         *(Str + 1) = L':';
     98         Str        = Str + 2;
     99       } else {
    100         *Str       = L':';
    101         Str        = Str + 1;
    102       }
    103 
    104       while ((Index < 15) && (Ip6->Addr[Index] == 0) && (Ip6->Addr[Index + 1] == 0)) {
    105         Index = Index + 2;
    106       }
    107 
    108       Short = TRUE;
    109 
    110       if (Index == 16) {
    111         //
    112         // :: is at the end of the address.
    113         //
    114         *Str = L'\0';
    115         break;
    116       }
    117     }
    118 
    119     ASSERT (Index < 15);
    120 
    121     if (Ip6->Addr[Index] == 0) {
    122       Number = UnicodeSPrint (Str, 2 * IP_STR_MAX_SIZE, L"%x:", (UINTN) Ip6->Addr[Index + 1]);
    123     } else {
    124       if (Ip6->Addr[Index + 1] < 0x10) {
    125         CopyMem (FormatString, L"%x0%x:", StrSize (L"%x0%x:"));
    126       } else {
    127         CopyMem (FormatString, L"%x%x:", StrSize (L"%x%x:"));
    128       }
    129 
    130       Number = UnicodeSPrint (
    131                  Str,
    132                  2 * IP_STR_MAX_SIZE,
    133                  (CONST CHAR16 *) FormatString,
    134                  (UINTN) Ip6->Addr[Index],
    135                  (UINTN) Ip6->Addr[Index + 1]
    136                  );
    137     }
    138 
    139     Str = Str + Number;
    140 
    141     if (Index + 2 == 16) {
    142       *Str = L'\0';
    143       if (*(Str - 1) == L':') {
    144         *(Str - 1) = L'\0';
    145       }
    146     }
    147   }
    148 }
    149 
    150 /**
    151   Check whether the input IP address is valid.
    152 
    153   @param[in]  Ip        The IP address.
    154   @param[in]  IpMode    Indicates iSCSI running on IP4 or IP6 stack.
    155 
    156   @retval     TRUE      The input IP address is valid.
    157   @retval     FALSE     Otherwise
    158 
    159 **/
    160 BOOLEAN
    161 IpIsUnicast (
    162   IN EFI_IP_ADDRESS *Ip,
    163   IN  UINT8          IpMode
    164   )
    165 {
    166   if (IpMode == IP_MODE_IP4) {
    167     if (IP4_IS_UNSPECIFIED (NTOHL (Ip->Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (Ip->Addr[0])))   {
    168       return FALSE;
    169     }
    170     return TRUE;
    171   } else if (IpMode == IP_MODE_IP6) {
    172     return NetIp6IsValidUnicast (&Ip->v6);
    173   } else {
    174     DEBUG ((DEBUG_ERROR, "IpMode %d is invalid when configuring the iSCSI target IP!\n", IpMode));
    175     return FALSE;
    176   }
    177 }
    178 
    179 /**
    180   Parse IsId in string format and convert it to binary.
    181 
    182   @param[in]        String  The buffer of the string to be parsed.
    183   @param[in, out]   IsId    The buffer to store IsId.
    184 
    185   @retval EFI_SUCCESS              The operation finished successfully.
    186   @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.
    187 
    188 **/
    189 EFI_STATUS
    190 IScsiParseIsIdFromString (
    191   IN CONST CHAR16                    *String,
    192   IN OUT   UINT8                     *IsId
    193   )
    194 {
    195   UINT8                          Index;
    196   CHAR16                         *IsIdStr;
    197   CHAR16                         TempStr[3];
    198   UINTN                          NodeVal;
    199   CHAR16                         PortString[ISCSI_NAME_IFR_MAX_SIZE];
    200   EFI_INPUT_KEY                  Key;
    201 
    202   if ((String == NULL) || (IsId == NULL)) {
    203     return EFI_INVALID_PARAMETER;
    204   }
    205 
    206   IsIdStr = (CHAR16 *) String;
    207 
    208   if (StrLen (IsIdStr) != 6) {
    209     UnicodeSPrint (
    210       PortString,
    211       (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
    212       L"Error! Input is incorrect, please input 6 hex numbers!\n"
    213       );
    214 
    215     CreatePopUp (
    216       EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    217       &Key,
    218       PortString,
    219       NULL
    220       );
    221 
    222     return EFI_INVALID_PARAMETER;
    223   }
    224 
    225   for (Index = 3; Index < 6; Index++) {
    226     CopyMem (TempStr, IsIdStr, sizeof (TempStr));
    227     TempStr[2] = L'\0';
    228 
    229     //
    230     // Convert the string to IsId. StrHexToUintn stops at the first character
    231     // that is not a valid hex character, '\0' here.
    232     //
    233     NodeVal = StrHexToUintn (TempStr);
    234 
    235     IsId[Index] = (UINT8) NodeVal;
    236 
    237     IsIdStr = IsIdStr + 2;
    238   }
    239 
    240   return EFI_SUCCESS;
    241 }
    242 
    243 /**
    244   Convert IsId from binary to string format.
    245 
    246   @param[out]      String  The buffer to store the converted string.
    247   @param[in]       IsId    The buffer to store IsId.
    248 
    249   @retval EFI_SUCCESS              The string converted successfully.
    250   @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.
    251 
    252 **/
    253 EFI_STATUS
    254 IScsiConvertIsIdToString (
    255   OUT CHAR16                         *String,
    256   IN  UINT8                          *IsId
    257   )
    258 {
    259   UINT8                          Index;
    260   UINTN                          Number;
    261 
    262   if ((String == NULL) || (IsId == NULL)) {
    263     return EFI_INVALID_PARAMETER;
    264   }
    265 
    266   for (Index = 0; Index < 6; Index++) {
    267     if (IsId[Index] <= 0xF) {
    268       Number = UnicodeSPrint (
    269                  String,
    270                  2 * ISID_CONFIGURABLE_STORAGE,
    271                  L"0%X",
    272                  (UINTN) IsId[Index]
    273                  );
    274     } else {
    275       Number = UnicodeSPrint (
    276                  String,
    277                  2 * ISID_CONFIGURABLE_STORAGE,
    278                  L"%X",
    279                  (UINTN) IsId[Index]
    280                  );
    281 
    282     }
    283 
    284     String = String + Number;
    285   }
    286 
    287   *String = L'\0';
    288 
    289   return EFI_SUCCESS;
    290 }
    291 
    292 /**
    293   Get the attempt config data from global structure by the ConfigIndex.
    294 
    295   @param[in]  AttemptConfigIndex     The unique index indicates the attempt.
    296 
    297   @return       Pointer to the attempt config data.
    298   @retval NULL  The attempt configuration data cannot be found.
    299 
    300 **/
    301 ISCSI_ATTEMPT_CONFIG_NVDATA *
    302 IScsiConfigGetAttemptByConfigIndex (
    303   IN UINT8                     AttemptConfigIndex
    304   )
    305 {
    306   LIST_ENTRY                   *Entry;
    307   ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt;
    308 
    309   NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
    310     Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
    311     if (Attempt->AttemptConfigIndex == AttemptConfigIndex) {
    312       return Attempt;
    313     }
    314   }
    315 
    316   return NULL;
    317 }
    318 
    319 
    320 /**
    321   Get the existing attempt config data from global structure by the NicIndex.
    322 
    323   @param[in]  NewAttempt         The created new attempt
    324   @param[in]  IScsiMode          The IScsi Mode of the new attempt, Enabled or
    325                                  Enabled for MPIO.
    326 
    327   @return                        Pointer to the existing attempt config data which
    328                                  has the same NICIndex as the new created attempt.
    329   @retval     NULL               The attempt with NicIndex does not exist.
    330 
    331 **/
    332 ISCSI_ATTEMPT_CONFIG_NVDATA *
    333 IScsiConfigGetAttemptByNic (
    334   IN ISCSI_ATTEMPT_CONFIG_NVDATA *NewAttempt,
    335   IN UINT8                       IScsiMode
    336   )
    337 {
    338   LIST_ENTRY                   *Entry;
    339   ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt;
    340 
    341   NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
    342     Attempt = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
    343     if (Attempt != NewAttempt && Attempt->NicIndex == NewAttempt->NicIndex &&
    344         Attempt->SessionConfigData.Enabled == IScsiMode) {
    345       return Attempt;
    346     }
    347   }
    348 
    349   return NULL;
    350 }
    351 
    352 
    353 /**
    354   Convert the iSCSI configuration data into the IFR data.
    355 
    356   @param[in]       Attempt                The iSCSI attempt config data.
    357   @param[in, out]  IfrNvData              The IFR nv data.
    358 
    359 **/
    360 VOID
    361 IScsiConvertAttemptConfigDataToIfrNvData (
    362   IN ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt,
    363   IN OUT ISCSI_CONFIG_IFR_NVDATA  *IfrNvData
    364   )
    365 {
    366   ISCSI_SESSION_CONFIG_NVDATA   *SessionConfigData;
    367   ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;
    368   EFI_IP_ADDRESS                Ip;
    369 
    370   //
    371   // Normal session configuration parameters.
    372   //
    373   SessionConfigData                 = &Attempt->SessionConfigData;
    374   IfrNvData->Enabled                = SessionConfigData->Enabled;
    375   IfrNvData->IpMode                 = SessionConfigData->IpMode;
    376 
    377   IfrNvData->InitiatorInfoFromDhcp  = SessionConfigData->InitiatorInfoFromDhcp;
    378   IfrNvData->TargetInfoFromDhcp     = SessionConfigData->TargetInfoFromDhcp;
    379   IfrNvData->TargetPort             = SessionConfigData->TargetPort;
    380 
    381   if (IfrNvData->IpMode == IP_MODE_IP4) {
    382     CopyMem (&Ip.v4, &SessionConfigData->LocalIp, sizeof (EFI_IPv4_ADDRESS));
    383     IScsiIpToStr (&Ip, FALSE, IfrNvData->LocalIp);
    384     CopyMem (&Ip.v4, &SessionConfigData->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
    385     IScsiIpToStr (&Ip, FALSE, IfrNvData->SubnetMask);
    386     CopyMem (&Ip.v4, &SessionConfigData->Gateway, sizeof (EFI_IPv4_ADDRESS));
    387     IScsiIpToStr (&Ip, FALSE, IfrNvData->Gateway);
    388     CopyMem (&Ip.v4, &SessionConfigData->TargetIp, sizeof (EFI_IPv4_ADDRESS));
    389     IScsiIpToStr (&Ip, FALSE, IfrNvData->TargetIp);
    390   } else if (IfrNvData->IpMode == IP_MODE_IP6) {
    391     ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));
    392     IP6_COPY_ADDRESS (&Ip.v6, &SessionConfigData->TargetIp);
    393     IScsiIpToStr (&Ip, TRUE, IfrNvData->TargetIp);
    394   }
    395 
    396   AsciiStrToUnicodeStrS (
    397     SessionConfigData->TargetName,
    398     IfrNvData->TargetName,
    399     sizeof (IfrNvData->TargetName) / sizeof (IfrNvData->TargetName[0])
    400     );
    401   IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun);
    402   IScsiConvertIsIdToString (IfrNvData->IsId, SessionConfigData->IsId);
    403 
    404   IfrNvData->ConnectRetryCount = SessionConfigData->ConnectRetryCount;
    405   IfrNvData->ConnectTimeout    = SessionConfigData->ConnectTimeout;
    406 
    407   //
    408   // Authentication parameters.
    409   //
    410   IfrNvData->AuthenticationType = Attempt->AuthenticationType;
    411 
    412   if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
    413     AuthConfigData      = &Attempt->AuthConfigData.CHAP;
    414     IfrNvData->CHAPType = AuthConfigData->CHAPType;
    415     AsciiStrToUnicodeStrS (
    416       AuthConfigData->CHAPName,
    417       IfrNvData->CHAPName,
    418       sizeof (IfrNvData->CHAPName) / sizeof (IfrNvData->CHAPName[0])
    419       );
    420     AsciiStrToUnicodeStrS (
    421       AuthConfigData->CHAPSecret,
    422       IfrNvData->CHAPSecret,
    423       sizeof (IfrNvData->CHAPSecret) / sizeof (IfrNvData->CHAPSecret[0])
    424       );
    425     AsciiStrToUnicodeStrS (
    426       AuthConfigData->ReverseCHAPName,
    427       IfrNvData->ReverseCHAPName,
    428       sizeof (IfrNvData->ReverseCHAPName) / sizeof (IfrNvData->ReverseCHAPName[0])
    429       );
    430     AsciiStrToUnicodeStrS (
    431       AuthConfigData->ReverseCHAPSecret,
    432       IfrNvData->ReverseCHAPSecret,
    433       sizeof (IfrNvData->ReverseCHAPSecret) / sizeof (IfrNvData->ReverseCHAPSecret[0])
    434       );
    435   }
    436 
    437   //
    438   // Other parameters.
    439   //
    440   AsciiStrToUnicodeStrS (
    441     Attempt->AttemptName,
    442     IfrNvData->AttemptName,
    443     sizeof (IfrNvData->AttemptName) / sizeof (IfrNvData->AttemptName[0])
    444     );
    445 }
    446 
    447 /**
    448   Convert the IFR data to iSCSI configuration data.
    449 
    450   @param[in]       IfrNvData              Point to ISCSI_CONFIG_IFR_NVDATA.
    451   @param[in, out]  Attempt                The iSCSI attempt config data.
    452 
    453   @retval EFI_INVALID_PARAMETER  Any input or configured parameter is invalid.
    454   @retval EFI_NOT_FOUND          Cannot find the corresponding variable.
    455   @retval EFI_OUT_OF_RESOURCES   The operation is failed due to lack of resources.
    456   @retval EFI_ABORTED            The operation is aborted.
    457   @retval EFI_SUCCESS            The operation is completed successfully.
    458 
    459 **/
    460 EFI_STATUS
    461 IScsiConvertIfrNvDataToAttemptConfigData (
    462   IN ISCSI_CONFIG_IFR_NVDATA          *IfrNvData,
    463   IN OUT ISCSI_ATTEMPT_CONFIG_NVDATA  *Attempt
    464   )
    465 {
    466   EFI_IP_ADDRESS              HostIp;
    467   EFI_IP_ADDRESS              SubnetMask;
    468   EFI_IP_ADDRESS              Gateway;
    469   CHAR16                      *MacString;
    470   CHAR16                      *AttemptName1;
    471   CHAR16                      *AttemptName2;
    472   ISCSI_ATTEMPT_CONFIG_NVDATA *ExistAttempt;
    473   ISCSI_ATTEMPT_CONFIG_NVDATA *SameNicAttempt;
    474   CHAR16                      IScsiMode[64];
    475   CHAR16                      IpMode[64];
    476   ISCSI_NIC_INFO              *NicInfo;
    477   EFI_INPUT_KEY               Key;
    478   UINT8                       *AttemptConfigOrder;
    479   UINTN                       AttemptConfigOrderSize;
    480   UINT8                       *AttemptOrderTmp;
    481   UINTN                       TotalNumber;
    482   EFI_STATUS                  Status;
    483 
    484   if (IfrNvData == NULL || Attempt == NULL) {
    485     return EFI_INVALID_PARAMETER;
    486   }
    487 
    488   //
    489   // Update those fields which don't have INTERACTIVE attribute.
    490   //
    491   Attempt->SessionConfigData.ConnectRetryCount     = IfrNvData->ConnectRetryCount;
    492   Attempt->SessionConfigData.ConnectTimeout        = IfrNvData->ConnectTimeout;
    493   Attempt->SessionConfigData.IpMode                = IfrNvData->IpMode;
    494 
    495   if (IfrNvData->IpMode < IP_MODE_AUTOCONFIG) {
    496     Attempt->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;
    497     Attempt->SessionConfigData.TargetPort            = IfrNvData->TargetPort;
    498 
    499     if (Attempt->SessionConfigData.TargetPort == 0) {
    500       Attempt->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
    501     }
    502 
    503     Attempt->SessionConfigData.TargetInfoFromDhcp = IfrNvData->TargetInfoFromDhcp;
    504   }
    505 
    506   Attempt->AuthenticationType = IfrNvData->AuthenticationType;
    507 
    508   if (Attempt->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
    509     Attempt->AuthConfigData.CHAP.CHAPType = IfrNvData->CHAPType;
    510   }
    511 
    512   //
    513   // Only do full parameter validation if iSCSI is enabled on this device.
    514   //
    515   if (IfrNvData->Enabled != ISCSI_DISABLED) {
    516     if (Attempt->SessionConfigData.ConnectTimeout < CONNECT_MIN_TIMEOUT) {
    517       CreatePopUp (
    518         EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    519         &Key,
    520         L"Connection Establishing Timeout is less than minimum value 100ms.",
    521         NULL
    522         );
    523 
    524       return EFI_INVALID_PARAMETER;
    525     }
    526 
    527     //
    528     // Validate the address configuration of the Initiator if DHCP isn't
    529     // deployed.
    530     //
    531     if (!Attempt->SessionConfigData.InitiatorInfoFromDhcp) {
    532       CopyMem (&HostIp.v4, &Attempt->SessionConfigData.LocalIp, sizeof (HostIp.v4));
    533       CopyMem (&SubnetMask.v4, &Attempt->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));
    534       CopyMem (&Gateway.v4, &Attempt->SessionConfigData.Gateway, sizeof (Gateway.v4));
    535 
    536       if ((Gateway.Addr[0] != 0)) {
    537         if (SubnetMask.Addr[0] == 0) {
    538           CreatePopUp (
    539             EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    540             &Key,
    541             L"Gateway address is set but subnet mask is zero.",
    542             NULL
    543             );
    544 
    545           return EFI_INVALID_PARAMETER;
    546         } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) {
    547           CreatePopUp (
    548             EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    549             &Key,
    550             L"Local IP and Gateway are not in the same subnet.",
    551             NULL
    552             );
    553 
    554           return EFI_INVALID_PARAMETER;
    555         }
    556       }
    557     }
    558     //
    559     // Validate target configuration if DHCP isn't deployed.
    560     //
    561     if (!Attempt->SessionConfigData.TargetInfoFromDhcp && Attempt->SessionConfigData.IpMode < IP_MODE_AUTOCONFIG) {
    562       if (!IpIsUnicast (&Attempt->SessionConfigData.TargetIp, IfrNvData->IpMode)) {
    563         CreatePopUp (
    564           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    565           &Key,
    566           L"Target IP is invalid!",
    567           NULL
    568           );
    569         return EFI_INVALID_PARAMETER;
    570       }
    571 
    572       //
    573       // Validate iSCSI target name configuration again:
    574       // The format of iSCSI target name is already verified in IScsiFormCallback() when
    575       // user input the name; here we only check the case user does not input the name.
    576       //
    577       if (Attempt->SessionConfigData.TargetName[0] == '\0') {
    578         CreatePopUp (
    579           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    580           &Key,
    581           L"iSCSI target name is NULL!",
    582           NULL
    583           );
    584         return EFI_INVALID_PARAMETER;
    585       }
    586     }
    587 
    588 
    589     //
    590     // Validate the authentication info.
    591     //
    592     if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
    593       if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) {
    594         CreatePopUp (
    595           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    596           &Key,
    597           L"CHAP Name or CHAP Secret is invalid!",
    598           NULL
    599           );
    600 
    601         return EFI_INVALID_PARAMETER;
    602       }
    603 
    604       if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) &&
    605           ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0'))
    606           ) {
    607         CreatePopUp (
    608           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    609           &Key,
    610           L"Reverse CHAP Name or Reverse CHAP Secret is invalid!",
    611           NULL
    612           );
    613         return EFI_INVALID_PARAMETER;
    614       }
    615     }
    616 
    617     //
    618     // Check whether this attempt uses NIC which is already used by existing attempt.
    619     //
    620     SameNicAttempt = IScsiConfigGetAttemptByNic (Attempt, IfrNvData->Enabled);
    621     if (SameNicAttempt != NULL) {
    622       AttemptName1 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16));
    623       if (AttemptName1 == NULL) {
    624         return EFI_OUT_OF_RESOURCES;
    625       }
    626 
    627       AttemptName2 = (CHAR16 *) AllocateZeroPool (ATTEMPT_NAME_MAX_SIZE * sizeof (CHAR16));
    628       if (AttemptName2 == NULL) {
    629         FreePool (AttemptName1);
    630         return EFI_OUT_OF_RESOURCES;
    631       }
    632 
    633       AsciiStrToUnicodeStrS (Attempt->AttemptName, AttemptName1, ATTEMPT_NAME_MAX_SIZE);
    634       if (StrLen (AttemptName1) > ATTEMPT_NAME_SIZE) {
    635         CopyMem (&AttemptName1[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));
    636       }
    637 
    638       AsciiStrToUnicodeStrS (SameNicAttempt->AttemptName, AttemptName2, ATTEMPT_NAME_MAX_SIZE);
    639       if (StrLen (AttemptName2) > ATTEMPT_NAME_SIZE) {
    640         CopyMem (&AttemptName2[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));
    641       }
    642 
    643       UnicodeSPrint (
    644         mPrivate->PortString,
    645         (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
    646         L"Warning! Attempt \"%s\" uses same NIC as Attempt \"%s\".",
    647         AttemptName1,
    648         AttemptName2
    649         );
    650 
    651       CreatePopUp (
    652         EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    653         &Key,
    654         mPrivate->PortString,
    655         NULL
    656         );
    657 
    658       FreePool (AttemptName1);
    659       FreePool (AttemptName2);
    660     }
    661   }
    662 
    663   //
    664   // Update the iSCSI Mode data and record it in attempt help info.
    665   //
    666   Attempt->SessionConfigData.Enabled = IfrNvData->Enabled;
    667   if (IfrNvData->Enabled == ISCSI_DISABLED) {
    668     UnicodeSPrint (IScsiMode, 64, L"Disabled");
    669   } else if (IfrNvData->Enabled == ISCSI_ENABLED) {
    670     UnicodeSPrint (IScsiMode, 64, L"Enabled");
    671   } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {
    672     UnicodeSPrint (IScsiMode, 64, L"Enabled for MPIO");
    673   }
    674 
    675   if (IfrNvData->IpMode == IP_MODE_IP4) {
    676     UnicodeSPrint (IpMode, 64, L"IP4");
    677   } else if (IfrNvData->IpMode == IP_MODE_IP6) {
    678     UnicodeSPrint (IpMode, 64, L"IP6");
    679   } else if (IfrNvData->IpMode == IP_MODE_AUTOCONFIG) {
    680     UnicodeSPrint (IpMode, 64, L"Autoconfigure");
    681   }
    682 
    683   NicInfo = IScsiGetNicInfoByIndex (Attempt->NicIndex);
    684   if (NicInfo == NULL) {
    685     return EFI_NOT_FOUND;
    686   }
    687 
    688   MacString = (CHAR16 *) AllocateZeroPool (ISCSI_MAX_MAC_STRING_LEN * sizeof (CHAR16));
    689   if (MacString == NULL) {
    690     return EFI_OUT_OF_RESOURCES;
    691   }
    692 
    693   AsciiStrToUnicodeStrS (Attempt->MacString, MacString, ISCSI_MAX_MAC_STRING_LEN);
    694 
    695   UnicodeSPrint (
    696     mPrivate->PortString,
    697     (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
    698     L"MAC: %s, PFA: Bus %d | Dev %d | Func %d, iSCSI mode: %s, IP version: %s",
    699     MacString,
    700     NicInfo->BusNumber,
    701     NicInfo->DeviceNumber,
    702     NicInfo->FunctionNumber,
    703     IScsiMode,
    704     IpMode
    705     );
    706 
    707   Attempt->AttemptTitleHelpToken = HiiSetString (
    708                                      mCallbackInfo->RegisteredHandle,
    709                                      Attempt->AttemptTitleHelpToken,
    710                                      mPrivate->PortString,
    711                                      NULL
    712                                      );
    713   if (Attempt->AttemptTitleHelpToken == 0) {
    714     FreePool (MacString);
    715     return EFI_OUT_OF_RESOURCES;
    716   }
    717 
    718   //
    719   // Check whether this attempt is an existing one.
    720   //
    721   ExistAttempt = IScsiConfigGetAttemptByConfigIndex (Attempt->AttemptConfigIndex);
    722   if (ExistAttempt != NULL) {
    723     ASSERT (ExistAttempt == Attempt);
    724 
    725     if (IfrNvData->Enabled == ISCSI_DISABLED &&
    726         Attempt->SessionConfigData.Enabled != ISCSI_DISABLED) {
    727 
    728       //
    729       // User updates the Attempt from "Enabled"/"Enabled for MPIO" to "Disabled".
    730       //
    731       if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
    732         if (mPrivate->MpioCount < 1) {
    733           return EFI_ABORTED;
    734         }
    735 
    736         if (--mPrivate->MpioCount == 0) {
    737           mPrivate->EnableMpio = FALSE;
    738         }
    739       } else if (Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) {
    740         if (mPrivate->SinglePathCount < 1) {
    741           return EFI_ABORTED;
    742         }
    743         mPrivate->SinglePathCount--;
    744       }
    745 
    746     } else if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO &&
    747                Attempt->SessionConfigData.Enabled == ISCSI_ENABLED) {
    748       //
    749       // User updates the Attempt from "Enabled" to "Enabled for MPIO".
    750       //
    751       if (mPrivate->SinglePathCount < 1) {
    752         return EFI_ABORTED;
    753       }
    754 
    755       mPrivate->EnableMpio = TRUE;
    756       mPrivate->MpioCount++;
    757       mPrivate->SinglePathCount--;
    758 
    759     } else if (IfrNvData->Enabled == ISCSI_ENABLED &&
    760                Attempt->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
    761       //
    762       // User updates the Attempt from "Enabled for MPIO" to "Enabled".
    763       //
    764       if (mPrivate->MpioCount < 1) {
    765         return EFI_ABORTED;
    766       }
    767 
    768       if (--mPrivate->MpioCount == 0) {
    769         mPrivate->EnableMpio = FALSE;
    770       }
    771       mPrivate->SinglePathCount++;
    772 
    773     } else if (IfrNvData->Enabled != ISCSI_DISABLED &&
    774                Attempt->SessionConfigData.Enabled == ISCSI_DISABLED) {
    775       //
    776       // User updates the Attempt from "Disabled" to "Enabled"/"Enabled for MPIO".
    777       //
    778       if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {
    779         mPrivate->EnableMpio = TRUE;
    780         mPrivate->MpioCount++;
    781 
    782       } else if (IfrNvData->Enabled == ISCSI_ENABLED) {
    783         mPrivate->SinglePathCount++;
    784       }
    785     }
    786 
    787   } else if (ExistAttempt == NULL) {
    788     //
    789     // When a new attempt is created, pointer of the attempt is saved to
    790     // mPrivate->NewAttempt, and also saved to mCallbackInfo->Current in
    791     // IScsiConfigProcessDefault. If input Attempt does not match any existing
    792     // attempt, it should be a new created attempt. Save it to system now.
    793     //
    794     ASSERT (Attempt == mPrivate->NewAttempt);
    795 
    796     //
    797     // Save current order number for this attempt.
    798     //
    799     AttemptConfigOrder = IScsiGetVariableAndSize (
    800                            L"AttemptOrder",
    801                            &gIScsiConfigGuid,
    802                            &AttemptConfigOrderSize
    803                            );
    804 
    805     TotalNumber = AttemptConfigOrderSize / sizeof (UINT8);
    806     TotalNumber++;
    807 
    808     //
    809     // Append the new created attempt order to the end.
    810     //
    811     AttemptOrderTmp = AllocateZeroPool (TotalNumber * sizeof (UINT8));
    812     if (AttemptOrderTmp == NULL) {
    813       if (AttemptConfigOrder != NULL) {
    814         FreePool (AttemptConfigOrder);
    815       }
    816       return EFI_OUT_OF_RESOURCES;
    817     }
    818 
    819     if (AttemptConfigOrder != NULL) {
    820       CopyMem (AttemptOrderTmp, AttemptConfigOrder, AttemptConfigOrderSize);
    821       FreePool (AttemptConfigOrder);
    822     }
    823 
    824     AttemptOrderTmp[TotalNumber - 1] = Attempt->AttemptConfigIndex;
    825     AttemptConfigOrder               = AttemptOrderTmp;
    826     AttemptConfigOrderSize           = TotalNumber * sizeof (UINT8);
    827 
    828     Status = gRT->SetVariable (
    829                     L"AttemptOrder",
    830                     &gIScsiConfigGuid,
    831                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
    832                     AttemptConfigOrderSize,
    833                     AttemptConfigOrder
    834                     );
    835     FreePool (AttemptConfigOrder);
    836     if (EFI_ERROR (Status)) {
    837       return Status;
    838     }
    839 
    840     //
    841     // Insert new created attempt to array.
    842     //
    843     InsertTailList (&mPrivate->AttemptConfigs, &Attempt->Link);
    844     mPrivate->AttemptCount++;
    845     //
    846     // Reset mPrivate->NewAttempt to NULL, which indicates none attempt is created
    847     // but not saved now.
    848     //
    849     mPrivate->NewAttempt = NULL;
    850 
    851     if (IfrNvData->Enabled == ISCSI_ENABLED_FOR_MPIO) {
    852       //
    853       // This new Attempt is enabled for MPIO; enable the multipath mode.
    854       //
    855       mPrivate->EnableMpio = TRUE;
    856       mPrivate->MpioCount++;
    857     } else if (IfrNvData->Enabled == ISCSI_ENABLED) {
    858       mPrivate->SinglePathCount++;
    859     }
    860 
    861     IScsiConfigUpdateAttempt ();
    862   }
    863 
    864   //
    865   // Record the user configuration information in NVR.
    866   //
    867   UnicodeSPrint (
    868     mPrivate->PortString,
    869     (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
    870     L"%s%d",
    871     MacString,
    872     (UINTN) Attempt->AttemptConfigIndex
    873     );
    874 
    875   FreePool (MacString);
    876 
    877   return gRT->SetVariable (
    878                 mPrivate->PortString,
    879                 &gEfiIScsiInitiatorNameProtocolGuid,
    880                 ISCSI_CONFIG_VAR_ATTR,
    881                 sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA),
    882                 Attempt
    883                 );
    884 }
    885 
    886 /**
    887   Create Hii Extend Label OpCode as the start opcode and end opcode. It is
    888   a help function.
    889 
    890   @param[in]  StartLabelNumber   The number of start label.
    891   @param[out] StartOpCodeHandle  Points to the start opcode handle.
    892   @param[out] StartLabel         Points to the created start opcode.
    893   @param[out] EndOpCodeHandle    Points to the end opcode handle.
    894   @param[out] EndLabel           Points to the created end opcode.
    895 
    896   @retval EFI_OUT_OF_RESOURCES   Do not have sufficient resource to finish this
    897                                  operation.
    898   @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.
    899   @retval EFI_SUCCESS            The operation is completed successfully.
    900 
    901 **/
    902 EFI_STATUS
    903 IScsiCreateOpCode (
    904   IN  UINT16                        StartLabelNumber,
    905   OUT VOID                          **StartOpCodeHandle,
    906   OUT EFI_IFR_GUID_LABEL            **StartLabel,
    907   OUT VOID                          **EndOpCodeHandle,
    908   OUT EFI_IFR_GUID_LABEL            **EndLabel
    909   )
    910 {
    911   EFI_STATUS                        Status;
    912   EFI_IFR_GUID_LABEL                *InternalStartLabel;
    913   EFI_IFR_GUID_LABEL                *InternalEndLabel;
    914 
    915   if (StartOpCodeHandle == NULL || StartLabel == NULL || EndOpCodeHandle == NULL || EndLabel == NULL) {
    916     return EFI_INVALID_PARAMETER;
    917   }
    918 
    919   *StartOpCodeHandle = NULL;
    920   *EndOpCodeHandle   = NULL;
    921   Status             = EFI_OUT_OF_RESOURCES;
    922 
    923   //
    924   // Initialize the container for dynamic opcodes.
    925   //
    926   *StartOpCodeHandle = HiiAllocateOpCodeHandle ();
    927   if (*StartOpCodeHandle == NULL) {
    928     return Status;
    929   }
    930 
    931   *EndOpCodeHandle = HiiAllocateOpCodeHandle ();
    932   if (*EndOpCodeHandle == NULL) {
    933     goto Exit;
    934   }
    935 
    936   //
    937   // Create Hii Extend Label OpCode as the start opcode.
    938   //
    939   InternalStartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
    940                                                 *StartOpCodeHandle,
    941                                                 &gEfiIfrTianoGuid,
    942                                                 NULL,
    943                                                 sizeof (EFI_IFR_GUID_LABEL)
    944                                                 );
    945   if (InternalStartLabel == NULL) {
    946     goto Exit;
    947   }
    948 
    949   InternalStartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
    950   InternalStartLabel->Number       = StartLabelNumber;
    951 
    952   //
    953   // Create Hii Extend Label OpCode as the end opcode.
    954   //
    955   InternalEndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (
    956                                               *EndOpCodeHandle,
    957                                               &gEfiIfrTianoGuid,
    958                                               NULL,
    959                                               sizeof (EFI_IFR_GUID_LABEL)
    960                                               );
    961   if (InternalEndLabel == NULL) {
    962     goto Exit;
    963   }
    964 
    965   InternalEndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
    966   InternalEndLabel->Number       = LABEL_END;
    967 
    968   *StartLabel = InternalStartLabel;
    969   *EndLabel   = InternalEndLabel;
    970 
    971   return EFI_SUCCESS;
    972 
    973 Exit:
    974 
    975   if (*StartOpCodeHandle != NULL) {
    976     HiiFreeOpCodeHandle (*StartOpCodeHandle);
    977   }
    978 
    979   if (*EndOpCodeHandle != NULL) {
    980     HiiFreeOpCodeHandle (*EndOpCodeHandle);
    981   }
    982 
    983   return Status;
    984 }
    985 
    986 /**
    987   Callback function when user presses "Add an Attempt".
    988 
    989   @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this
    990                                  operation.
    991   @retval EFI_SUCCESS            The operation is completed successfully.
    992 
    993 **/
    994 EFI_STATUS
    995 IScsiConfigAddAttempt (
    996   VOID
    997   )
    998 {
    999   LIST_ENTRY                    *Entry;
   1000   ISCSI_NIC_INFO                *NicInfo;
   1001   EFI_STRING_ID                 PortTitleToken;
   1002   EFI_STRING_ID                 PortTitleHelpToken;
   1003   CHAR16                        MacString[ISCSI_MAX_MAC_STRING_LEN];
   1004   EFI_STATUS                    Status;
   1005   VOID                          *StartOpCodeHandle;
   1006   EFI_IFR_GUID_LABEL            *StartLabel;
   1007   VOID                          *EndOpCodeHandle;
   1008   EFI_IFR_GUID_LABEL            *EndLabel;
   1009 
   1010   Status = IScsiCreateOpCode (
   1011              MAC_ENTRY_LABEL,
   1012              &StartOpCodeHandle,
   1013              &StartLabel,
   1014              &EndOpCodeHandle,
   1015              &EndLabel
   1016              );
   1017   if (EFI_ERROR (Status)) {
   1018     return Status;
   1019   }
   1020 
   1021   //
   1022   // Ask user to select a MAC for this attempt.
   1023   //
   1024   NET_LIST_FOR_EACH (Entry, &mPrivate->NicInfoList) {
   1025     NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
   1026     IScsiMacAddrToStr (
   1027       &NicInfo->PermanentAddress,
   1028       NicInfo->HwAddressSize,
   1029       NicInfo->VlanId,
   1030       MacString
   1031       );
   1032 
   1033     UnicodeSPrint (mPrivate->PortString, (UINTN) ISCSI_NAME_IFR_MAX_SIZE, L"MAC %s", MacString);
   1034     PortTitleToken = HiiSetString (
   1035                        mCallbackInfo->RegisteredHandle,
   1036                        0,
   1037                        mPrivate->PortString,
   1038                        NULL
   1039                        );
   1040     if (PortTitleToken == 0) {
   1041       Status = EFI_INVALID_PARAMETER;
   1042       goto Exit;
   1043     }
   1044 
   1045     UnicodeSPrint (
   1046       mPrivate->PortString,
   1047       (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
   1048       L"PFA: Bus %d | Dev %d | Func %d",
   1049       NicInfo->BusNumber,
   1050       NicInfo->DeviceNumber,
   1051       NicInfo->FunctionNumber
   1052       );
   1053     PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, mPrivate->PortString, NULL);
   1054     if (PortTitleHelpToken == 0) {
   1055       Status = EFI_INVALID_PARAMETER;
   1056       goto Exit;
   1057     }
   1058 
   1059     HiiCreateGotoOpCode (
   1060       StartOpCodeHandle,                      // Container for dynamic created opcodes
   1061       FORMID_ATTEMPT_FORM,
   1062       PortTitleToken,
   1063       PortTitleHelpToken,
   1064       EFI_IFR_FLAG_CALLBACK,                  // Question flag
   1065       (UINT16) (KEY_MAC_ENTRY_BASE + NicInfo->NicIndex)
   1066       );
   1067   }
   1068 
   1069   Status = HiiUpdateForm (
   1070              mCallbackInfo->RegisteredHandle, // HII handle
   1071              &gIScsiConfigGuid,               // Formset GUID
   1072              FORMID_MAC_FORM,                 // Form ID
   1073              StartOpCodeHandle,               // Label for where to insert opcodes
   1074              EndOpCodeHandle                  // Replace data
   1075              );
   1076 
   1077 Exit:
   1078   HiiFreeOpCodeHandle (StartOpCodeHandle);
   1079   HiiFreeOpCodeHandle (EndOpCodeHandle);
   1080 
   1081   return Status;
   1082 }
   1083 
   1084 
   1085 /**
   1086   Update the MAIN form to display the configured attempts.
   1087 
   1088 **/
   1089 VOID
   1090 IScsiConfigUpdateAttempt (
   1091   VOID
   1092   )
   1093 {
   1094   CHAR16                        AttemptName[ATTEMPT_NAME_MAX_SIZE];
   1095   LIST_ENTRY                    *Entry;
   1096   ISCSI_ATTEMPT_CONFIG_NVDATA   *AttemptConfigData;
   1097   VOID                          *StartOpCodeHandle;
   1098   EFI_IFR_GUID_LABEL            *StartLabel;
   1099   VOID                          *EndOpCodeHandle;
   1100   EFI_IFR_GUID_LABEL            *EndLabel;
   1101   EFI_STATUS                    Status;
   1102 
   1103   Status = IScsiCreateOpCode (
   1104              ATTEMPT_ENTRY_LABEL,
   1105              &StartOpCodeHandle,
   1106              &StartLabel,
   1107              &EndOpCodeHandle,
   1108              &EndLabel
   1109              );
   1110   if (EFI_ERROR (Status)) {
   1111     return ;
   1112   }
   1113 
   1114   NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
   1115     AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
   1116 
   1117     AsciiStrToUnicodeStrS (AttemptConfigData->AttemptName, AttemptName, ARRAY_SIZE (AttemptName));
   1118     UnicodeSPrint (mPrivate->PortString, (UINTN) 128, L"Attempt %s", AttemptName);
   1119     AttemptConfigData->AttemptTitleToken = HiiSetString (
   1120                                              mCallbackInfo->RegisteredHandle,
   1121                                              0,
   1122                                              mPrivate->PortString,
   1123                                              NULL
   1124                                              );
   1125     if (AttemptConfigData->AttemptTitleToken == 0) {
   1126       return ;
   1127     }
   1128 
   1129     HiiCreateGotoOpCode (
   1130       StartOpCodeHandle,                         // Container for dynamic created opcodes
   1131       FORMID_ATTEMPT_FORM,                       // Form ID
   1132       AttemptConfigData->AttemptTitleToken,      // Prompt text
   1133       AttemptConfigData->AttemptTitleHelpToken,  // Help text
   1134       EFI_IFR_FLAG_CALLBACK,                     // Question flag
   1135       (UINT16) (KEY_ATTEMPT_ENTRY_BASE + AttemptConfigData->AttemptConfigIndex)   // Question ID
   1136       );
   1137   }
   1138 
   1139   HiiUpdateForm (
   1140     mCallbackInfo->RegisteredHandle, // HII handle
   1141     &gIScsiConfigGuid,               // Formset GUID
   1142     FORMID_MAIN_FORM,                // Form ID
   1143     StartOpCodeHandle,               // Label for where to insert opcodes
   1144     EndOpCodeHandle                  // Replace data
   1145   );
   1146 
   1147   HiiFreeOpCodeHandle (StartOpCodeHandle);
   1148   HiiFreeOpCodeHandle (EndOpCodeHandle);
   1149 }
   1150 
   1151 
   1152 /**
   1153   Callback function when user presses "Commit Changes and Exit" in Delete Attempts.
   1154 
   1155   @param[in]  IfrNvData          The IFR NV data.
   1156 
   1157   @retval EFI_NOT_FOUND          Cannot find the corresponding variable.
   1158   @retval EFI_SUCCESS            The operation is completed successfully.
   1159   @retval EFI_ABOTRED            This operation is aborted cause of error
   1160                                  configuration.
   1161   @retval EFI_OUT_OF_RESOURCES   Fail to finish the operation due to lack of
   1162                                  resources.
   1163 
   1164 **/
   1165 EFI_STATUS
   1166 IScsiConfigDeleteAttempts (
   1167   IN ISCSI_CONFIG_IFR_NVDATA  *IfrNvData
   1168   )
   1169 {
   1170   EFI_STATUS                  Status;
   1171   UINTN                       Index;
   1172   UINTN                       NewIndex;
   1173   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
   1174   UINT8                       *AttemptConfigOrder;
   1175   UINTN                       AttemptConfigOrderSize;
   1176   UINT8                       *AttemptNewOrder;
   1177   UINT32                      Attribute;
   1178   UINTN                       Total;
   1179   UINTN                       NewTotal;
   1180   LIST_ENTRY                  *Entry;
   1181   LIST_ENTRY                  *NextEntry;
   1182   CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];
   1183 
   1184   AttemptConfigOrder = IScsiGetVariableAndSize (
   1185                          L"AttemptOrder",
   1186                          &gIScsiConfigGuid,
   1187                          &AttemptConfigOrderSize
   1188                          );
   1189   if ((AttemptConfigOrder == NULL) || (AttemptConfigOrderSize == 0)) {
   1190     return EFI_NOT_FOUND;
   1191   }
   1192 
   1193   AttemptNewOrder = AllocateZeroPool (AttemptConfigOrderSize);
   1194   if (AttemptNewOrder == NULL) {
   1195     Status = EFI_OUT_OF_RESOURCES;
   1196     goto Error;
   1197   }
   1198 
   1199   Total    = AttemptConfigOrderSize / sizeof (UINT8);
   1200   NewTotal = Total;
   1201   Index    = 0;
   1202 
   1203   NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &mPrivate->AttemptConfigs) {
   1204     if (IfrNvData->DeleteAttemptList[Index] == 0) {
   1205       Index++;
   1206       continue;
   1207     }
   1208 
   1209     //
   1210     // Delete the attempt.
   1211     //
   1212 
   1213     AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
   1214     if (AttemptConfigData == NULL) {
   1215       Status = EFI_NOT_FOUND;
   1216       goto Error;
   1217     }
   1218 
   1219     //
   1220     // Remove this attempt from UI configured attempt list.
   1221     //
   1222     RemoveEntryList (&AttemptConfigData->Link);
   1223     mPrivate->AttemptCount--;
   1224 
   1225     if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED_FOR_MPIO) {
   1226       if (mPrivate->MpioCount < 1) {
   1227         Status = EFI_ABORTED;
   1228         goto Error;
   1229       }
   1230 
   1231       //
   1232       // No more attempt is enabled for MPIO. Transit the iSCSI mode to single path.
   1233       //
   1234       if (--mPrivate->MpioCount == 0) {
   1235         mPrivate->EnableMpio = FALSE;
   1236       }
   1237     } else if (AttemptConfigData->SessionConfigData.Enabled == ISCSI_ENABLED) {
   1238       if (mPrivate->SinglePathCount < 1) {
   1239         Status = EFI_ABORTED;
   1240         goto Error;
   1241       }
   1242 
   1243       mPrivate->SinglePathCount--;
   1244     }
   1245 
   1246     AsciiStrToUnicodeStrS (AttemptConfigData->MacString, MacString, ARRAY_SIZE (MacString));
   1247 
   1248     UnicodeSPrint (
   1249       mPrivate->PortString,
   1250       (UINTN) 128,
   1251       L"%s%d",
   1252       MacString,
   1253       (UINTN) AttemptConfigData->AttemptConfigIndex
   1254       );
   1255 
   1256     gRT->SetVariable (
   1257            mPrivate->PortString,
   1258            &gEfiIScsiInitiatorNameProtocolGuid,
   1259            0,
   1260            0,
   1261            NULL
   1262            );
   1263 
   1264     //
   1265     // Mark the attempt order in NVR to be deleted - 0.
   1266     //
   1267     for (NewIndex = 0; NewIndex < Total; NewIndex++) {
   1268       if (AttemptConfigOrder[NewIndex] == AttemptConfigData->AttemptConfigIndex) {
   1269         AttemptConfigOrder[NewIndex] = 0;
   1270         break;
   1271       }
   1272     }
   1273 
   1274     NewTotal--;
   1275     FreePool (AttemptConfigData);
   1276 
   1277     //
   1278     // Check next Attempt.
   1279     //
   1280     Index++;
   1281   }
   1282 
   1283   //
   1284   // Construct AttemptNewOrder.
   1285   //
   1286   for (Index = 0, NewIndex = 0; Index < Total; Index++) {
   1287     if (AttemptConfigOrder[Index] != 0) {
   1288       AttemptNewOrder[NewIndex] = AttemptConfigOrder[Index];
   1289       NewIndex++;
   1290     }
   1291   }
   1292 
   1293   Attribute = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE;
   1294 
   1295   //
   1296   // Update AttemptOrder in NVR.
   1297   //
   1298   Status = gRT->SetVariable (
   1299                   L"AttemptOrder",
   1300                   &gIScsiConfigGuid,
   1301                   Attribute,
   1302                   NewTotal * sizeof (UINT8),
   1303                   AttemptNewOrder
   1304                   );
   1305 
   1306 Error:
   1307   if (AttemptConfigOrder != NULL) {
   1308     FreePool (AttemptConfigOrder);
   1309   }
   1310 
   1311   if (AttemptNewOrder != NULL) {
   1312     FreePool (AttemptNewOrder);
   1313   }
   1314 
   1315   return Status;
   1316 }
   1317 
   1318 
   1319 /**
   1320   Callback function when user presses "Delete Attempts".
   1321 
   1322   @param[in]  IfrNvData          The IFR nv data.
   1323 
   1324   @retval EFI_INVALID_PARAMETER  Any parameter is invalid.
   1325   @retval EFI_BUFFER_TOO_SMALL   The buffer in UpdateData is too small.
   1326   @retval EFI_SUCCESS            The operation is completed successfully.
   1327 
   1328 **/
   1329 EFI_STATUS
   1330 IScsiConfigDisplayDeleteAttempts (
   1331   IN ISCSI_CONFIG_IFR_NVDATA  *IfrNvData
   1332   )
   1333 {
   1334 
   1335   UINT8                       *AttemptConfigOrder;
   1336   UINTN                       AttemptConfigOrderSize;
   1337   LIST_ENTRY                  *Entry;
   1338   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
   1339   UINT8                       Index;
   1340   VOID                        *StartOpCodeHandle;
   1341   EFI_IFR_GUID_LABEL          *StartLabel;
   1342   VOID                        *EndOpCodeHandle;
   1343   EFI_IFR_GUID_LABEL          *EndLabel;
   1344   EFI_STATUS                  Status;
   1345 
   1346   Status = IScsiCreateOpCode (
   1347              DELETE_ENTRY_LABEL,
   1348              &StartOpCodeHandle,
   1349              &StartLabel,
   1350              &EndOpCodeHandle,
   1351              &EndLabel
   1352              );
   1353   if (EFI_ERROR (Status)) {
   1354     return Status;
   1355   }
   1356 
   1357   AttemptConfigOrder = IScsiGetVariableAndSize (
   1358                          L"AttemptOrder",
   1359                          &gIScsiConfigGuid,
   1360                          &AttemptConfigOrderSize
   1361                          );
   1362   if (AttemptConfigOrder != NULL) {
   1363     //
   1364     // Create the check box opcode to be deleted.
   1365     //
   1366     Index = 0;
   1367 
   1368     NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
   1369       AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
   1370       IfrNvData->DeleteAttemptList[Index] = 0x00;
   1371 
   1372       HiiCreateCheckBoxOpCode(
   1373         StartOpCodeHandle,
   1374         (EFI_QUESTION_ID) (ATTEMPT_DEL_QUESTION_ID + Index),
   1375         CONFIGURATION_VARSTORE_ID,
   1376         (UINT16) (ATTEMPT_DEL_VAR_OFFSET + Index),
   1377         AttemptConfigData->AttemptTitleToken,
   1378         AttemptConfigData->AttemptTitleHelpToken,
   1379         0,
   1380         0,
   1381         NULL
   1382         );
   1383 
   1384       Index++;
   1385 
   1386       if (Index == ISCSI_MAX_ATTEMPTS_NUM) {
   1387         break;
   1388       }
   1389     }
   1390 
   1391     FreePool (AttemptConfigOrder);
   1392   }
   1393 
   1394   Status = HiiUpdateForm (
   1395              mCallbackInfo->RegisteredHandle, // HII handle
   1396              &gIScsiConfigGuid,               // Formset GUID
   1397              FORMID_DELETE_FORM,              // Form ID
   1398              StartOpCodeHandle,               // Label for where to insert opcodes
   1399              EndOpCodeHandle                  // Replace data
   1400              );
   1401 
   1402   HiiFreeOpCodeHandle (StartOpCodeHandle);
   1403   HiiFreeOpCodeHandle (EndOpCodeHandle);
   1404 
   1405   return Status;
   1406 }
   1407 
   1408 
   1409 /**
   1410   Callback function when user presses "Change Attempt Order".
   1411 
   1412   @retval EFI_INVALID_PARAMETER  Any parameter is invalid.
   1413   @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this
   1414                                  operation.
   1415   @retval EFI_SUCCESS            The operation is completed successfully.
   1416 
   1417 **/
   1418 EFI_STATUS
   1419 IScsiConfigDisplayOrderAttempts (
   1420   VOID
   1421   )
   1422 {
   1423   EFI_STATUS                  Status;
   1424   UINT8                       Index;
   1425   LIST_ENTRY                  *Entry;
   1426   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
   1427   VOID                        *StartOpCodeHandle;
   1428   EFI_IFR_GUID_LABEL          *StartLabel;
   1429   VOID                        *EndOpCodeHandle;
   1430   EFI_IFR_GUID_LABEL          *EndLabel;
   1431   VOID                        *OptionsOpCodeHandle;
   1432 
   1433   Status = IScsiCreateOpCode (
   1434              ORDER_ENTRY_LABEL,
   1435              &StartOpCodeHandle,
   1436              &StartLabel,
   1437              &EndOpCodeHandle,
   1438              &EndLabel
   1439              );
   1440   if (EFI_ERROR (Status)) {
   1441     return Status;
   1442   }
   1443   ASSERT (StartOpCodeHandle != NULL);
   1444 
   1445   OptionsOpCodeHandle = NULL;
   1446 
   1447   //
   1448   // If no attempt to be ordered, update the original form and exit.
   1449   //
   1450   if (mPrivate->AttemptCount == 0) {
   1451     goto Exit;
   1452   }
   1453 
   1454   //
   1455   // Create Option OpCode.
   1456   //
   1457   OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();
   1458   if (OptionsOpCodeHandle == NULL) {
   1459     Status = EFI_OUT_OF_RESOURCES;
   1460     goto Error;
   1461   }
   1462 
   1463   Index = 0;
   1464 
   1465   NET_LIST_FOR_EACH (Entry, &mPrivate->AttemptConfigs) {
   1466     AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
   1467     HiiCreateOneOfOptionOpCode (
   1468       OptionsOpCodeHandle,
   1469       AttemptConfigData->AttemptTitleToken,
   1470       0,
   1471       EFI_IFR_NUMERIC_SIZE_1,
   1472       AttemptConfigData->AttemptConfigIndex
   1473       );
   1474     Index++;
   1475   }
   1476 
   1477   ASSERT (Index == mPrivate->AttemptCount);
   1478 
   1479   HiiCreateOrderedListOpCode (
   1480     StartOpCodeHandle,                          // Container for dynamic created opcodes
   1481     DYNAMIC_ORDERED_LIST_QUESTION_ID,           // Question ID
   1482     CONFIGURATION_VARSTORE_ID,                  // VarStore ID
   1483     DYNAMIC_ORDERED_LIST_VAR_OFFSET,            // Offset in Buffer Storage
   1484     STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),     // Question prompt text
   1485     STRING_TOKEN (STR_ORDER_ATTEMPT_ENTRY),     // Question help text
   1486     0,                                          // Question flag
   1487     EFI_IFR_UNIQUE_SET,                         // Ordered list flag, e.g. EFI_IFR_UNIQUE_SET
   1488     EFI_IFR_NUMERIC_SIZE_1,                     // Data type of Question value
   1489     ISCSI_MAX_ATTEMPTS_NUM,                     // Maximum container
   1490     OptionsOpCodeHandle,                        // Option Opcode list
   1491     NULL                                        // Default Opcode is NULL
   1492     );
   1493 
   1494 Exit:
   1495   Status = HiiUpdateForm (
   1496              mCallbackInfo->RegisteredHandle, // HII handle
   1497              &gIScsiConfigGuid,               // Formset GUID
   1498              FORMID_ORDER_FORM,               // Form ID
   1499              StartOpCodeHandle,               // Label for where to insert opcodes
   1500              EndOpCodeHandle                  // Replace data
   1501              );
   1502 
   1503 Error:
   1504   HiiFreeOpCodeHandle (StartOpCodeHandle);
   1505   HiiFreeOpCodeHandle (EndOpCodeHandle);
   1506   if (OptionsOpCodeHandle != NULL) {
   1507     HiiFreeOpCodeHandle (OptionsOpCodeHandle);
   1508   }
   1509 
   1510   return Status;
   1511 }
   1512 
   1513 
   1514 /**
   1515   Callback function when user presses "Commit Changes and Exit" in Change Attempt Order.
   1516 
   1517   @param[in]  IfrNvData          The IFR nv data.
   1518 
   1519   @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this
   1520                                  operation.
   1521   @retval EFI_NOT_FOUND          Cannot find the corresponding variable.
   1522   @retval EFI_SUCCESS            The operation is completed successfully.
   1523 
   1524 **/
   1525 EFI_STATUS
   1526 IScsiConfigOrderAttempts (
   1527   IN ISCSI_CONFIG_IFR_NVDATA  *IfrNvData
   1528   )
   1529 {
   1530   EFI_STATUS                  Status;
   1531   UINTN                       Index;
   1532   UINTN                       Indexj;
   1533   UINT8                       AttemptConfigIndex;
   1534   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
   1535   UINT8                       *AttemptConfigOrder;
   1536   UINT8                       *AttemptConfigOrderTmp;
   1537   UINTN                       AttemptConfigOrderSize;
   1538 
   1539   AttemptConfigOrder = IScsiGetVariableAndSize (
   1540                          L"AttemptOrder",
   1541                          &gIScsiConfigGuid,
   1542                          &AttemptConfigOrderSize
   1543                          );
   1544   if (AttemptConfigOrder == NULL) {
   1545     return EFI_NOT_FOUND;
   1546   }
   1547 
   1548   AttemptConfigOrderTmp = AllocateZeroPool (AttemptConfigOrderSize);
   1549   if (AttemptConfigOrderTmp == NULL) {
   1550     Status = EFI_OUT_OF_RESOURCES;
   1551     goto Exit;
   1552   }
   1553 
   1554   for (Index = 0; Index < ISCSI_MAX_ATTEMPTS_NUM; Index++) {
   1555     //
   1556     // The real content ends with 0.
   1557     //
   1558     if (IfrNvData->DynamicOrderedList[Index] == 0) {
   1559       break;
   1560     }
   1561 
   1562     AttemptConfigIndex = IfrNvData->DynamicOrderedList[Index];
   1563     AttemptConfigData  = IScsiConfigGetAttemptByConfigIndex (AttemptConfigIndex);
   1564     if (AttemptConfigData == NULL) {
   1565       Status = EFI_NOT_FOUND;
   1566       goto Exit;
   1567     }
   1568 
   1569     //
   1570     // Reorder the Attempt List.
   1571     //
   1572     RemoveEntryList (&AttemptConfigData->Link);
   1573     InsertTailList (&mPrivate->AttemptConfigs, &AttemptConfigData->Link);
   1574 
   1575     AttemptConfigOrderTmp[Index] = AttemptConfigIndex;
   1576 
   1577     //
   1578     // Mark it to be deleted - 0.
   1579     //
   1580     for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) {
   1581       if (AttemptConfigOrder[Indexj] == AttemptConfigIndex) {
   1582         AttemptConfigOrder[Indexj] = 0;
   1583         break;
   1584       }
   1585     }
   1586   }
   1587 
   1588   //
   1589   // Adjust the attempt order in NVR.
   1590   //
   1591   for (; Index < AttemptConfigOrderSize / sizeof (UINT8); Index++) {
   1592     for (Indexj = 0; Indexj < AttemptConfigOrderSize / sizeof (UINT8); Indexj++) {
   1593       if (AttemptConfigOrder[Indexj] != 0) {
   1594         AttemptConfigOrderTmp[Index] = AttemptConfigOrder[Indexj];
   1595         AttemptConfigOrder[Indexj]   = 0;
   1596         continue;
   1597       }
   1598     }
   1599   }
   1600 
   1601   Status = gRT->SetVariable (
   1602                   L"AttemptOrder",
   1603                   &gIScsiConfigGuid,
   1604                   EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
   1605                   AttemptConfigOrderSize,
   1606                   AttemptConfigOrderTmp
   1607                   );
   1608 
   1609 Exit:
   1610   if (AttemptConfigOrderTmp != NULL) {
   1611     FreePool (AttemptConfigOrderTmp);
   1612   }
   1613 
   1614   FreePool (AttemptConfigOrder);
   1615   return Status;
   1616 }
   1617 
   1618 
   1619 /**
   1620   Callback function when a user presses "Attempt *" or when a user selects a NIC to
   1621   create the new attempt.
   1622 
   1623   @param[in]  KeyValue           A unique value which is sent to the original
   1624                                  exporting driver so that it can identify the type
   1625                                  of data to expect.
   1626   @param[in]  IfrNvData          The IFR nv data.
   1627 
   1628   @retval EFI_OUT_OF_RESOURCES   Does not have sufficient resources to finish this
   1629                                  operation.
   1630   @retval EFI_NOT_FOUND          Cannot find the corresponding variable.
   1631   @retval EFI_SUCCESS            The operation is completed successfully.
   1632 
   1633 **/
   1634 EFI_STATUS
   1635 IScsiConfigProcessDefault (
   1636   IN  EFI_QUESTION_ID              KeyValue,
   1637   IN  ISCSI_CONFIG_IFR_NVDATA      *IfrNvData
   1638   )
   1639 {
   1640   BOOLEAN                     NewAttempt;
   1641   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
   1642   ISCSI_SESSION_CONFIG_NVDATA *ConfigData;
   1643   UINT8                       CurrentAttemptConfigIndex;
   1644   ISCSI_NIC_INFO              *NicInfo;
   1645   UINT8                       NicIndex;
   1646   CHAR16                      MacString[ISCSI_MAX_MAC_STRING_LEN];
   1647   UINT8                       *AttemptConfigOrder;
   1648   UINTN                       AttemptConfigOrderSize;
   1649   UINTN                       TotalNumber;
   1650   UINTN                       Index;
   1651 
   1652   //
   1653   // Is User creating a new attempt?
   1654   //
   1655   NewAttempt = FALSE;
   1656 
   1657   if ((KeyValue >= KEY_MAC_ENTRY_BASE) &&
   1658       (KeyValue <= (UINT16) (mPrivate->MaxNic + KEY_MAC_ENTRY_BASE))) {
   1659     //
   1660     // User has pressed "Add an Attempt" and then selects a NIC.
   1661     //
   1662     NewAttempt = TRUE;
   1663   } else if ((KeyValue >= KEY_ATTEMPT_ENTRY_BASE) &&
   1664              (KeyValue < (ISCSI_MAX_ATTEMPTS_NUM + KEY_ATTEMPT_ENTRY_BASE))) {
   1665 
   1666     //
   1667     // User has pressed "Attempt *".
   1668     //
   1669     NewAttempt = FALSE;
   1670   } else {
   1671     //
   1672     // Don't process anything.
   1673     //
   1674     return EFI_SUCCESS;
   1675   }
   1676 
   1677   //
   1678   // Free any attempt that is previously created but not saved to system.
   1679   //
   1680   if (mPrivate->NewAttempt != NULL) {
   1681     FreePool (mPrivate->NewAttempt);
   1682     mPrivate->NewAttempt = NULL;
   1683   }
   1684 
   1685   if (NewAttempt) {
   1686     //
   1687     // Determine which NIC user has selected for the new created attempt.
   1688     //
   1689     NicIndex = (UINT8) (KeyValue - KEY_MAC_ENTRY_BASE);
   1690     NicInfo = IScsiGetNicInfoByIndex (NicIndex);
   1691     if (NicInfo == NULL) {
   1692       return EFI_NOT_FOUND;
   1693     }
   1694 
   1695     //
   1696     // Create new attempt.
   1697     //
   1698 
   1699     AttemptConfigData = AllocateZeroPool (sizeof (ISCSI_ATTEMPT_CONFIG_NVDATA));
   1700     if (AttemptConfigData == NULL) {
   1701       return EFI_OUT_OF_RESOURCES;
   1702     }
   1703 
   1704     ConfigData                    = &AttemptConfigData->SessionConfigData;
   1705     ConfigData->TargetPort        = ISCSI_WELL_KNOWN_PORT;
   1706     ConfigData->ConnectTimeout    = CONNECT_DEFAULT_TIMEOUT;
   1707     ConfigData->ConnectRetryCount = CONNECT_MIN_RETRY;
   1708 
   1709     AttemptConfigData->AuthenticationType           = ISCSI_AUTH_TYPE_CHAP;
   1710     AttemptConfigData->AuthConfigData.CHAP.CHAPType = ISCSI_CHAP_UNI;
   1711 
   1712     //
   1713     // Get current order number for this attempt.
   1714     //
   1715     AttemptConfigOrder = IScsiGetVariableAndSize (
   1716                            L"AttemptOrder",
   1717                            &gIScsiConfigGuid,
   1718                            &AttemptConfigOrderSize
   1719                            );
   1720 
   1721     TotalNumber = AttemptConfigOrderSize / sizeof (UINT8);
   1722 
   1723     if (AttemptConfigOrder == NULL) {
   1724       CurrentAttemptConfigIndex = 1;
   1725     } else {
   1726       //
   1727       // Get the max attempt config index.
   1728       //
   1729       CurrentAttemptConfigIndex = AttemptConfigOrder[0];
   1730       for (Index = 1; Index < TotalNumber; Index++) {
   1731         if (CurrentAttemptConfigIndex < AttemptConfigOrder[Index]) {
   1732           CurrentAttemptConfigIndex = AttemptConfigOrder[Index];
   1733         }
   1734       }
   1735 
   1736       CurrentAttemptConfigIndex++;
   1737     }
   1738 
   1739     TotalNumber++;
   1740 
   1741     //
   1742     // Record the mapping between attempt order and attempt's configdata.
   1743     //
   1744     AttemptConfigData->AttemptConfigIndex  = CurrentAttemptConfigIndex;
   1745 
   1746     if (AttemptConfigOrder != NULL) {
   1747       FreePool (AttemptConfigOrder);
   1748     }
   1749 
   1750     //
   1751     // Record the MAC info in Config Data.
   1752     //
   1753     IScsiMacAddrToStr (
   1754       &NicInfo->PermanentAddress,
   1755       NicInfo->HwAddressSize,
   1756       NicInfo->VlanId,
   1757       MacString
   1758       );
   1759 
   1760     UnicodeStrToAsciiStrS (MacString, AttemptConfigData->MacString, sizeof (AttemptConfigData->MacString));
   1761     AttemptConfigData->NicIndex = NicIndex;
   1762 
   1763     //
   1764     // Generate OUI-format ISID based on MAC address.
   1765     //
   1766     CopyMem (AttemptConfigData->SessionConfigData.IsId, &NicInfo->PermanentAddress, 6);
   1767     AttemptConfigData->SessionConfigData.IsId[0] =
   1768       (UINT8) (AttemptConfigData->SessionConfigData.IsId[0] & 0x3F);
   1769 
   1770     //
   1771     // Add the help info for the new attempt.
   1772     //
   1773     UnicodeSPrint (
   1774       mPrivate->PortString,
   1775       (UINTN) ISCSI_NAME_IFR_MAX_SIZE,
   1776       L"MAC: %s, PFA: Bus %d | Dev %d | Func %d",
   1777       MacString,
   1778       NicInfo->BusNumber,
   1779       NicInfo->DeviceNumber,
   1780       NicInfo->FunctionNumber
   1781       );
   1782 
   1783     AttemptConfigData->AttemptTitleHelpToken  = HiiSetString (
   1784                                                   mCallbackInfo->RegisteredHandle,
   1785                                                   0,
   1786                                                   mPrivate->PortString,
   1787                                                   NULL
   1788                                                   );
   1789     if (AttemptConfigData->AttemptTitleHelpToken == 0) {
   1790       FreePool (AttemptConfigData);
   1791       return EFI_INVALID_PARAMETER;
   1792     }
   1793 
   1794     //
   1795     // Set the attempt name to default.
   1796     //
   1797     UnicodeSPrint (
   1798       mPrivate->PortString,
   1799       (UINTN) 128,
   1800       L"%d",
   1801       (UINTN) AttemptConfigData->AttemptConfigIndex
   1802       );
   1803     UnicodeStrToAsciiStrS (mPrivate->PortString, AttemptConfigData->AttemptName, sizeof (AttemptConfigData->AttemptName));
   1804 
   1805     //
   1806     // Save the created Attempt temporarily. If user does not save the attempt
   1807     // by press 'KEY_SAVE_ATTEMPT_CONFIG' later, iSCSI driver would know that
   1808     // and free resources.
   1809     //
   1810     mPrivate->NewAttempt = (VOID *) AttemptConfigData;
   1811 
   1812   } else {
   1813     //
   1814     // Determine which Attempt user has selected to configure.
   1815     // Get the attempt configuration data.
   1816     //
   1817     CurrentAttemptConfigIndex = (UINT8) (KeyValue - KEY_ATTEMPT_ENTRY_BASE);
   1818 
   1819     AttemptConfigData = IScsiConfigGetAttemptByConfigIndex (CurrentAttemptConfigIndex);
   1820     if (AttemptConfigData == NULL) {
   1821       DEBUG ((DEBUG_ERROR, "Corresponding configuration data can not be retrieved!\n"));
   1822       return EFI_NOT_FOUND;
   1823     }
   1824   }
   1825 
   1826   //
   1827   // Clear the old IFR data to avoid sharing it with other attempts.
   1828   //
   1829   if (IfrNvData->AuthenticationType == ISCSI_AUTH_TYPE_CHAP) {
   1830     ZeroMem (IfrNvData->CHAPName, sizeof (IfrNvData->CHAPName));
   1831     ZeroMem (IfrNvData->CHAPSecret, sizeof (IfrNvData->CHAPSecret));
   1832     ZeroMem (IfrNvData->ReverseCHAPName, sizeof (IfrNvData->ReverseCHAPName));
   1833     ZeroMem (IfrNvData->ReverseCHAPSecret, sizeof (IfrNvData->ReverseCHAPSecret));
   1834   }
   1835 
   1836   IScsiConvertAttemptConfigDataToIfrNvData (AttemptConfigData, IfrNvData);
   1837 
   1838   //
   1839   // Update current attempt to be a new created attempt or an existing attempt.
   1840   //
   1841   mCallbackInfo->Current = AttemptConfigData;
   1842 
   1843   return EFI_SUCCESS;
   1844 }
   1845 
   1846 
   1847 /**
   1848 
   1849   This function allows the caller to request the current
   1850   configuration for one or more named elements. The resulting
   1851   string is in <ConfigAltResp> format. Also, any and all alternative
   1852   configuration strings shall be appended to the end of the
   1853   current configuration string. If they are, they must appear
   1854   after the current configuration. They must contain the same
   1855   routing (GUID, NAME, PATH) as the current configuration string.
   1856   They must have an additional description indicating the type of
   1857   alternative configuration the string represents,
   1858   "ALTCFG=<StringToken>". That <StringToken> (when
   1859   converted from Hex UNICODE to binary) is a reference to a
   1860   string in the associated string pack.
   1861 
   1862   @param[in]  This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
   1863 
   1864   @param[in]  Request    A null-terminated Unicode string in
   1865                          <ConfigRequest> format. Note that this
   1866                          includes the routing information as well as
   1867                          the configurable name / value pairs. It is
   1868                          invalid for this string to be in
   1869                          <MultiConfigRequest> format.
   1870 
   1871   @param[out] Progress   On return, points to a character in the
   1872                          Request string. Points to the string's null
   1873                          terminator if request was successful. Points
   1874                          to the most recent "&" before the first
   1875                          failing name / value pair (or the beginning
   1876                          of the string if the failure is in the first
   1877                          name / value pair) if the request was not successful.
   1878 
   1879   @param[out] Results    A null-terminated Unicode string in
   1880                          <ConfigAltResp> format which has all values
   1881                          filled in for the names in the Request string.
   1882                          String to be allocated by the called function.
   1883 
   1884   @retval EFI_SUCCESS             The Results string is filled with the
   1885                                   values corresponding to all requested
   1886                                   names.
   1887 
   1888   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the
   1889                                   parts of the results that must be
   1890                                   stored awaiting possible future
   1891                                   protocols.
   1892 
   1893   @retval EFI_INVALID_PARAMETER   For example, passing in a NULL
   1894                                   for the Request parameter
   1895                                   would result in this type of
   1896                                   error. In this case, the
   1897                                   Progress parameter would be
   1898                                   set to NULL.
   1899 
   1900   @retval EFI_NOT_FOUND           Routing data doesn't match any
   1901                                   known driver. Progress set to the
   1902                                   first character in the routing header.
   1903                                   Note: There is no requirement that the
   1904                                   driver validate the routing data. It
   1905                                   must skip the <ConfigHdr> in order to
   1906                                   process the names.
   1907 
   1908   @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set
   1909                                   to most recent "&" before the
   1910                                   error or the beginning of the
   1911                                   string.
   1912 
   1913   @retval EFI_INVALID_PARAMETER   Unknown name. Progress points
   1914                                   to the & before the name in
   1915                                   question.
   1916 
   1917 **/
   1918 EFI_STATUS
   1919 EFIAPI
   1920 IScsiFormExtractConfig (
   1921   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
   1922   IN  CONST EFI_STRING                       Request,
   1923   OUT EFI_STRING                             *Progress,
   1924   OUT EFI_STRING                             *Results
   1925   )
   1926 {
   1927   EFI_STATUS                       Status;
   1928   CHAR8                            *InitiatorName;
   1929   UINTN                            BufferSize;
   1930   ISCSI_CONFIG_IFR_NVDATA          *IfrNvData;
   1931   ISCSI_FORM_CALLBACK_INFO         *Private;
   1932   EFI_STRING                       ConfigRequestHdr;
   1933   EFI_STRING                       ConfigRequest;
   1934   BOOLEAN                          AllocatedRequest;
   1935   UINTN                            Size;
   1936 
   1937   if (This == NULL || Progress == NULL || Results == NULL) {
   1938     return EFI_INVALID_PARAMETER;
   1939   }
   1940 
   1941   *Progress = Request;
   1942   if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gIScsiConfigGuid, mVendorStorageName)) {
   1943     return EFI_NOT_FOUND;
   1944   }
   1945 
   1946   ConfigRequestHdr = NULL;
   1947   ConfigRequest    = NULL;
   1948   AllocatedRequest = FALSE;
   1949   Size             = 0;
   1950 
   1951   Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
   1952   IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));
   1953   if (IfrNvData == NULL) {
   1954     return EFI_OUT_OF_RESOURCES;
   1955   }
   1956 
   1957   if (Private->Current != NULL) {
   1958     IScsiConvertAttemptConfigDataToIfrNvData (Private->Current, IfrNvData);
   1959   }
   1960 
   1961   BufferSize    = ISCSI_NAME_MAX_SIZE;
   1962   InitiatorName = (CHAR8 *) AllocateZeroPool (BufferSize);
   1963   if (InitiatorName == NULL) {
   1964     FreePool (IfrNvData);
   1965     return EFI_OUT_OF_RESOURCES;
   1966   }
   1967 
   1968   Status = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);
   1969   if (EFI_ERROR (Status)) {
   1970     IfrNvData->InitiatorName[0] = L'\0';
   1971   } else {
   1972     AsciiStrToUnicodeStrS (
   1973       InitiatorName,
   1974       IfrNvData->InitiatorName,
   1975       sizeof (IfrNvData->InitiatorName) / sizeof (IfrNvData->InitiatorName[0])
   1976       );
   1977   }
   1978 
   1979   //
   1980   // Convert buffer data to <ConfigResp> by helper function BlockToConfig().
   1981   //
   1982   BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
   1983   ConfigRequest = Request;
   1984   if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
   1985     //
   1986     // Request has no request element, construct full request string.
   1987     // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
   1988     // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
   1989     //
   1990     ConfigRequestHdr = HiiConstructConfigHdr (&gIScsiConfigGuid, mVendorStorageName, Private->DriverHandle);
   1991     Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
   1992     ConfigRequest = AllocateZeroPool (Size);
   1993     if (ConfigRequest == NULL) {
   1994       FreePool (IfrNvData);
   1995       FreePool (InitiatorName);
   1996       return EFI_OUT_OF_RESOURCES;
   1997     }
   1998     AllocatedRequest = TRUE;
   1999     UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
   2000     FreePool (ConfigRequestHdr);
   2001   }
   2002 
   2003   Status = gHiiConfigRouting->BlockToConfig (
   2004                                 gHiiConfigRouting,
   2005                                 ConfigRequest,
   2006                                 (UINT8 *) IfrNvData,
   2007                                 BufferSize,
   2008                                 Results,
   2009                                 Progress
   2010                                 );
   2011   FreePool (IfrNvData);
   2012   FreePool (InitiatorName);
   2013 
   2014   //
   2015   // Free the allocated config request string.
   2016   //
   2017   if (AllocatedRequest) {
   2018     FreePool (ConfigRequest);
   2019     ConfigRequest = NULL;
   2020   }
   2021   //
   2022   // Set Progress string to the original request string.
   2023   //
   2024   if (Request == NULL) {
   2025     *Progress = NULL;
   2026   } else if (StrStr (Request, L"OFFSET") == NULL) {
   2027     *Progress = Request + StrLen (Request);
   2028   }
   2029 
   2030   return Status;
   2031 }
   2032 
   2033 
   2034 /**
   2035 
   2036   This function applies changes in a driver's configuration.
   2037   Input is a Configuration, which has the routing data for this
   2038   driver followed by name / value configuration pairs. The driver
   2039   must apply those pairs to its configurable storage. If the
   2040   driver's configuration is stored in a linear block of data
   2041   and the driver's name / value pairs are in <BlockConfig>
   2042   format, it may use the ConfigToBlock helper function (above) to
   2043   simplify the job.
   2044 
   2045   @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
   2046 
   2047   @param[in]  Configuration  A null-terminated Unicode string in
   2048                              <ConfigString> format.
   2049 
   2050   @param[out] Progress       A pointer to a string filled in with the
   2051                              offset of the most recent '&' before the
   2052                              first failing name / value pair (or the
   2053                              beginning of the string if the failure
   2054                              is in the first name / value pair) or
   2055                              the terminating NULL if all was
   2056                              successful.
   2057 
   2058   @retval EFI_SUCCESS             The results have been distributed or are
   2059                                   awaiting distribution.
   2060 
   2061   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the
   2062                                   parts of the results that must be
   2063                                   stored awaiting possible future
   2064                                   protocols.
   2065 
   2066   @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the
   2067                                   Results parameter would result
   2068                                   in this type of error.
   2069 
   2070   @retval EFI_NOT_FOUND           Target for the specified routing data
   2071                                   was not found.
   2072 
   2073 **/
   2074 EFI_STATUS
   2075 EFIAPI
   2076 IScsiFormRouteConfig (
   2077   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
   2078   IN  CONST EFI_STRING                       Configuration,
   2079   OUT EFI_STRING                             *Progress
   2080   )
   2081 {
   2082   if (This == NULL || Configuration == NULL || Progress == NULL) {
   2083     return EFI_INVALID_PARAMETER;
   2084   }
   2085 
   2086   //
   2087   // Check routing data in <ConfigHdr>.
   2088   // Note: if only one Storage is used, then this checking could be skipped.
   2089   //
   2090   if (!HiiIsConfigHdrMatch (Configuration, &gIScsiConfigGuid, mVendorStorageName)) {
   2091     *Progress = Configuration;
   2092     return EFI_NOT_FOUND;
   2093   }
   2094 
   2095   *Progress = Configuration + StrLen (Configuration);
   2096   return EFI_SUCCESS;
   2097 }
   2098 
   2099 
   2100 /**
   2101 
   2102   This function is called to provide results data to the driver.
   2103   This data consists of a unique key that is used to identify
   2104   which data is either being passed back or being asked for.
   2105 
   2106   @param[in]       This          Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
   2107   @param[in]       Action        Specifies the type of action taken by the browser.
   2108   @param[in]       QuestionId    A unique value which is sent to the original
   2109                                  exporting driver so that it can identify the type
   2110                                  of data to expect. The format of the data tends to
   2111                                  vary based on the opcode that generated the callback.
   2112   @param[in]       Type          The type of value for the question.
   2113   @param[in, out]  Value         A pointer to the data being sent to the original
   2114                                  exporting driver.
   2115   @param[out]      ActionRequest On return, points to the action requested by the
   2116                                  callback function.
   2117 
   2118   @retval EFI_SUCCESS            The callback successfully handled the action.
   2119   @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the
   2120                                  variable and its data.
   2121   @retval EFI_DEVICE_ERROR       The variable could not be saved.
   2122   @retval EFI_UNSUPPORTED        The specified Action is not supported by the
   2123                                  callback.
   2124 **/
   2125 EFI_STATUS
   2126 EFIAPI
   2127 IScsiFormCallback (
   2128   IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
   2129   IN        EFI_BROWSER_ACTION               Action,
   2130   IN        EFI_QUESTION_ID                  QuestionId,
   2131   IN        UINT8                            Type,
   2132   IN OUT    EFI_IFR_TYPE_VALUE               *Value,
   2133   OUT       EFI_BROWSER_ACTION_REQUEST       *ActionRequest
   2134   )
   2135 {
   2136   ISCSI_FORM_CALLBACK_INFO    *Private;
   2137   UINTN                       BufferSize;
   2138   CHAR8                       *IScsiName;
   2139   CHAR8                       IpString[IP_STR_MAX_SIZE];
   2140   CHAR8                       LunString[ISCSI_LUN_STR_MAX_LEN];
   2141   UINT64                      Lun;
   2142   EFI_IP_ADDRESS              HostIp;
   2143   EFI_IP_ADDRESS              SubnetMask;
   2144   EFI_IP_ADDRESS              Gateway;
   2145   ISCSI_CONFIG_IFR_NVDATA     *IfrNvData;
   2146   ISCSI_CONFIG_IFR_NVDATA     OldIfrNvData;
   2147   EFI_STATUS                  Status;
   2148   CHAR16                      AttemptName[ATTEMPT_NAME_SIZE + 4];
   2149   EFI_INPUT_KEY               Key;
   2150 
   2151   if ((Action == EFI_BROWSER_ACTION_FORM_OPEN) || (Action == EFI_BROWSER_ACTION_FORM_CLOSE)) {
   2152     //
   2153     // Do nothing for UEFI OPEN/CLOSE Action
   2154     //
   2155     return EFI_SUCCESS;
   2156   }
   2157 
   2158   if ((Action != EFI_BROWSER_ACTION_CHANGING) && (Action != EFI_BROWSER_ACTION_CHANGED)) {
   2159     //
   2160     // All other type return unsupported.
   2161     //
   2162     return EFI_UNSUPPORTED;
   2163   }
   2164 
   2165   if ((Value == NULL) || (ActionRequest == NULL)) {
   2166     return EFI_INVALID_PARAMETER;
   2167   }
   2168 
   2169   Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
   2170 
   2171   //
   2172   // Retrieve uncommitted data from Browser
   2173   //
   2174 
   2175   BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
   2176   IfrNvData = AllocateZeroPool (BufferSize);
   2177   if (IfrNvData == NULL) {
   2178     return EFI_OUT_OF_RESOURCES;
   2179   }
   2180 
   2181   IScsiName = (CHAR8 *) AllocateZeroPool (ISCSI_NAME_MAX_SIZE);
   2182   if (IScsiName == NULL) {
   2183     FreePool (IfrNvData);
   2184     return EFI_OUT_OF_RESOURCES;
   2185   }
   2186 
   2187   Status = EFI_SUCCESS;
   2188 
   2189   ZeroMem (&OldIfrNvData, BufferSize);
   2190 
   2191   HiiGetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData);
   2192 
   2193   CopyMem (&OldIfrNvData, IfrNvData, BufferSize);
   2194 
   2195   if (Action == EFI_BROWSER_ACTION_CHANGING) {
   2196     switch (QuestionId) {
   2197     case KEY_ADD_ATTEMPT:
   2198       //
   2199       // Check whether iSCSI initiator name is configured already.
   2200       //
   2201       mPrivate->InitiatorNameLength = ISCSI_NAME_MAX_SIZE;
   2202       Status = gIScsiInitiatorName.Get (
   2203                                      &gIScsiInitiatorName,
   2204                                      &mPrivate->InitiatorNameLength,
   2205                                      mPrivate->InitiatorName
   2206                                      );
   2207       if (EFI_ERROR (Status)) {
   2208         CreatePopUp (
   2209           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2210           &Key,
   2211           L"Error: please configure iSCSI initiator name first!",
   2212           NULL
   2213           );
   2214         break;
   2215       }
   2216 
   2217       Status = IScsiConfigAddAttempt ();
   2218       break;
   2219 
   2220     case KEY_DELETE_ATTEMPT:
   2221       CopyMem (
   2222         OldIfrNvData.DeleteAttemptList,
   2223         IfrNvData->DeleteAttemptList,
   2224         sizeof (IfrNvData->DeleteAttemptList)
   2225         );
   2226       Status = IScsiConfigDisplayDeleteAttempts (IfrNvData);
   2227       break;
   2228 
   2229     case KEY_ORDER_ATTEMPT_CONFIG:
   2230       //
   2231       // Order the attempt according to user input.
   2232       //
   2233       CopyMem (
   2234         OldIfrNvData.DynamicOrderedList,
   2235         IfrNvData->DynamicOrderedList,
   2236         sizeof (IfrNvData->DynamicOrderedList)
   2237         );
   2238       IScsiConfigDisplayOrderAttempts ();
   2239       break;
   2240 
   2241     default:
   2242       Status = IScsiConfigProcessDefault (QuestionId, IfrNvData);
   2243       break;
   2244     }
   2245   } else if (Action == EFI_BROWSER_ACTION_CHANGED) {
   2246     switch (QuestionId) {
   2247     case KEY_INITIATOR_NAME:
   2248       UnicodeStrToAsciiStrS (IfrNvData->InitiatorName, IScsiName, ISCSI_NAME_MAX_SIZE);
   2249       BufferSize  = AsciiStrSize (IScsiName);
   2250 
   2251       Status      = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName);
   2252       if (EFI_ERROR (Status)) {
   2253         CreatePopUp (
   2254           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2255           &Key,
   2256           L"Invalid iSCSI Name!",
   2257           NULL
   2258           );
   2259       }
   2260 
   2261       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
   2262       break;
   2263     case KEY_ATTEMPT_NAME:
   2264       if (StrLen (IfrNvData->AttemptName) > ATTEMPT_NAME_SIZE) {
   2265         CopyMem (AttemptName, IfrNvData->AttemptName, ATTEMPT_NAME_SIZE * sizeof (CHAR16));
   2266         CopyMem (&AttemptName[ATTEMPT_NAME_SIZE], L"...", 4 * sizeof (CHAR16));
   2267       } else {
   2268         CopyMem (
   2269           AttemptName,
   2270           IfrNvData->AttemptName,
   2271           (StrLen (IfrNvData->AttemptName) + 1) * sizeof (CHAR16)
   2272           );
   2273       }
   2274 
   2275       UnicodeStrToAsciiStrS (IfrNvData->AttemptName, Private->Current->AttemptName, sizeof (Private->Current->AttemptName));
   2276 
   2277       IScsiConfigUpdateAttempt ();
   2278 
   2279       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
   2280       break;
   2281 
   2282     case KEY_SAVE_ATTEMPT_CONFIG:
   2283       Status = IScsiConvertIfrNvDataToAttemptConfigData (IfrNvData, Private->Current);
   2284       if (EFI_ERROR (Status)) {
   2285         break;
   2286       }
   2287 
   2288       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
   2289       break;
   2290 
   2291     case KEY_SAVE_ORDER_CHANGES:
   2292       //
   2293       // Sync the Attempt Order to NVR.
   2294       //
   2295       Status = IScsiConfigOrderAttempts (IfrNvData);
   2296       if (EFI_ERROR (Status)) {
   2297         break;
   2298       }
   2299 
   2300       IScsiConfigUpdateAttempt ();
   2301       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
   2302       break;
   2303 
   2304     case KEY_IGNORE_ORDER_CHANGES:
   2305       CopyMem (
   2306         IfrNvData->DynamicOrderedList,
   2307         OldIfrNvData.DynamicOrderedList,
   2308         sizeof (IfrNvData->DynamicOrderedList)
   2309         );
   2310       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;
   2311       break;
   2312 
   2313     case KEY_SAVE_DELETE_ATTEMPT:
   2314       //
   2315       // Delete the Attempt Order from NVR
   2316       //
   2317       Status = IScsiConfigDeleteAttempts (IfrNvData);
   2318       if (EFI_ERROR (Status)) {
   2319         break;
   2320       }
   2321 
   2322       IScsiConfigUpdateAttempt ();
   2323       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_SUBMIT_EXIT;
   2324       break;
   2325 
   2326     case KEY_IGNORE_DELETE_ATTEMPT:
   2327       CopyMem (
   2328         IfrNvData->DeleteAttemptList,
   2329         OldIfrNvData.DeleteAttemptList,
   2330         sizeof (IfrNvData->DeleteAttemptList)
   2331         );
   2332       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_DISCARD_EXIT;
   2333       break;
   2334 
   2335     case KEY_IP_MODE:
   2336       switch (Value->u8) {
   2337       case IP_MODE_IP6:
   2338         ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));
   2339         IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, TRUE, IfrNvData->TargetIp);
   2340         Private->Current->AutoConfigureMode = 0;
   2341         break;
   2342 
   2343       case IP_MODE_IP4:
   2344         ZeroMem (IfrNvData->TargetIp, sizeof (IfrNvData->TargetIp));
   2345         IScsiIpToStr (&Private->Current->SessionConfigData.TargetIp, FALSE, IfrNvData->TargetIp);
   2346         Private->Current->AutoConfigureMode = 0;
   2347 
   2348         break;
   2349       }
   2350 
   2351       break;
   2352 
   2353     case KEY_LOCAL_IP:
   2354       Status = NetLibStrToIp4 (IfrNvData->LocalIp, &HostIp.v4);
   2355       if (EFI_ERROR (Status) ||
   2356           ((Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
   2357            !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
   2358         CreatePopUp (
   2359           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2360           &Key,
   2361           L"Invalid IP address!",
   2362           NULL
   2363           );
   2364 
   2365         Status = EFI_INVALID_PARAMETER;
   2366       } else {
   2367         CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));
   2368       }
   2369 
   2370       break;
   2371 
   2372     case KEY_SUBNET_MASK:
   2373       Status = NetLibStrToIp4 (IfrNvData->SubnetMask, &SubnetMask.v4);
   2374       if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {
   2375         CreatePopUp (
   2376           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2377           &Key,
   2378           L"Invalid Subnet Mask!",
   2379           NULL
   2380           );
   2381 
   2382         Status = EFI_INVALID_PARAMETER;
   2383       } else {
   2384         CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));
   2385       }
   2386 
   2387       break;
   2388 
   2389     case KEY_GATE_WAY:
   2390       Status = NetLibStrToIp4 (IfrNvData->Gateway, &Gateway.v4);
   2391       if (EFI_ERROR (Status) ||
   2392           ((Gateway.Addr[0] != 0) &&
   2393            (Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
   2394            !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
   2395         CreatePopUp (
   2396           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2397           &Key,
   2398           L"Invalid Gateway!",
   2399           NULL
   2400           );
   2401         Status = EFI_INVALID_PARAMETER;
   2402       } else {
   2403         CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));
   2404       }
   2405 
   2406       break;
   2407 
   2408     case KEY_TARGET_IP:
   2409       UnicodeStrToAsciiStrS (IfrNvData->TargetIp, IpString, sizeof (IpString));
   2410       Status = IScsiAsciiStrToIp (IpString, IfrNvData->IpMode, &HostIp);
   2411       if (EFI_ERROR (Status) || IP4_IS_LOCAL_BROADCAST (EFI_NTOHL(HostIp.v4)) || IP4_IS_UNSPECIFIED (EFI_NTOHL(HostIp.v4))) {
   2412         CreatePopUp (
   2413           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2414           &Key,
   2415           L"Invalid IP address!",
   2416           NULL
   2417           );
   2418         Status = EFI_INVALID_PARAMETER;
   2419       } else {
   2420         CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp, sizeof (HostIp));
   2421       }
   2422 
   2423       break;
   2424 
   2425     case KEY_TARGET_NAME:
   2426       UnicodeStrToAsciiStrS (IfrNvData->TargetName, IScsiName, ISCSI_NAME_MAX_SIZE);
   2427       Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName));
   2428       if (EFI_ERROR (Status)) {
   2429         CreatePopUp (
   2430           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2431           &Key,
   2432           L"Invalid iSCSI Name!",
   2433           NULL
   2434           );
   2435       } else {
   2436         AsciiStrCpyS (Private->Current->SessionConfigData.TargetName, ISCSI_NAME_MAX_SIZE, IScsiName);
   2437       }
   2438 
   2439       break;
   2440 
   2441     case KEY_DHCP_ENABLE:
   2442       if (IfrNvData->InitiatorInfoFromDhcp == 0) {
   2443         IfrNvData->TargetInfoFromDhcp = 0;
   2444       }
   2445 
   2446       break;
   2447 
   2448     case KEY_BOOT_LUN:
   2449       UnicodeStrToAsciiStrS (IfrNvData->BootLun, LunString, sizeof (LunString));
   2450       Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);
   2451       if (EFI_ERROR (Status)) {
   2452         CreatePopUp (
   2453           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
   2454           &Key,
   2455           L"Invalid LUN string!",
   2456           NULL
   2457           );
   2458       } else {
   2459         CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun));
   2460       }
   2461 
   2462       break;
   2463 
   2464     case KEY_AUTH_TYPE:
   2465       switch (Value->u8) {
   2466       case ISCSI_AUTH_TYPE_CHAP:
   2467         IfrNvData->CHAPType = ISCSI_CHAP_UNI;
   2468         break;
   2469       default:
   2470         break;
   2471       }
   2472 
   2473       break;
   2474 
   2475     case KEY_CHAP_NAME:
   2476       UnicodeStrToAsciiStrS (
   2477         IfrNvData->CHAPName,
   2478         Private->Current->AuthConfigData.CHAP.CHAPName,
   2479         sizeof (Private->Current->AuthConfigData.CHAP.CHAPName)
   2480         );
   2481       break;
   2482 
   2483     case KEY_CHAP_SECRET:
   2484       UnicodeStrToAsciiStrS (
   2485         IfrNvData->CHAPSecret,
   2486         Private->Current->AuthConfigData.CHAP.CHAPSecret,
   2487         sizeof (Private->Current->AuthConfigData.CHAP.CHAPSecret)
   2488         );
   2489       break;
   2490 
   2491     case KEY_REVERSE_CHAP_NAME:
   2492       UnicodeStrToAsciiStrS (
   2493         IfrNvData->ReverseCHAPName,
   2494         Private->Current->AuthConfigData.CHAP.ReverseCHAPName,
   2495         sizeof (Private->Current->AuthConfigData.CHAP.ReverseCHAPName)
   2496         );
   2497       break;
   2498 
   2499     case KEY_REVERSE_CHAP_SECRET:
   2500       UnicodeStrToAsciiStrS (
   2501         IfrNvData->ReverseCHAPSecret,
   2502         Private->Current->AuthConfigData.CHAP.ReverseCHAPSecret,
   2503         sizeof (Private->Current->AuthConfigData.CHAP.ReverseCHAPSecret)
   2504         );
   2505       break;
   2506 
   2507     case KEY_CONFIG_ISID:
   2508       IScsiParseIsIdFromString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
   2509       IScsiConvertIsIdToString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
   2510 
   2511       break;
   2512 
   2513     default:
   2514       break;
   2515     }
   2516   }
   2517 
   2518   if (!EFI_ERROR (Status)) {
   2519     //
   2520     // Pass changed uncommitted data back to Form Browser.
   2521     //
   2522     BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
   2523     HiiSetBrowserData (NULL, NULL, BufferSize, (UINT8 *) IfrNvData, NULL);
   2524   }
   2525 
   2526   FreePool (IfrNvData);
   2527   FreePool (IScsiName);
   2528 
   2529   return Status;
   2530 }
   2531 
   2532 
   2533 /**
   2534   Initialize the iSCSI configuration form.
   2535 
   2536   @param[in]  DriverBindingHandle The iSCSI driverbinding handle.
   2537 
   2538   @retval EFI_SUCCESS             The iSCSI configuration form is initialized.
   2539   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
   2540 
   2541 **/
   2542 EFI_STATUS
   2543 IScsiConfigFormInit (
   2544   IN EFI_HANDLE  DriverBindingHandle
   2545   )
   2546 {
   2547   EFI_STATUS                  Status;
   2548   ISCSI_FORM_CALLBACK_INFO    *CallbackInfo;
   2549 
   2550   CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO));
   2551   if (CallbackInfo == NULL) {
   2552     return EFI_OUT_OF_RESOURCES;
   2553   }
   2554 
   2555   CallbackInfo->Signature   = ISCSI_FORM_CALLBACK_INFO_SIGNATURE;
   2556   CallbackInfo->Current     = NULL;
   2557 
   2558   CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig;
   2559   CallbackInfo->ConfigAccess.RouteConfig   = IScsiFormRouteConfig;
   2560   CallbackInfo->ConfigAccess.Callback      = IScsiFormCallback;
   2561 
   2562   //
   2563   // Install Device Path Protocol and Config Access protocol to driver handle.
   2564   //
   2565   Status = gBS->InstallMultipleProtocolInterfaces (
   2566                   &CallbackInfo->DriverHandle,
   2567                   &gEfiDevicePathProtocolGuid,
   2568                   &mIScsiHiiVendorDevicePath,
   2569                   &gEfiHiiConfigAccessProtocolGuid,
   2570                   &CallbackInfo->ConfigAccess,
   2571                   NULL
   2572                   );
   2573   ASSERT_EFI_ERROR (Status);
   2574 
   2575   //
   2576   // Publish our HII data.
   2577   //
   2578   CallbackInfo->RegisteredHandle = HiiAddPackages (
   2579                                      &gIScsiConfigGuid,
   2580                                      CallbackInfo->DriverHandle,
   2581                                      IScsiDxeStrings,
   2582                                      IScsiConfigVfrBin,
   2583                                      NULL
   2584                                      );
   2585   if (CallbackInfo->RegisteredHandle == NULL) {
   2586     gBS->UninstallMultipleProtocolInterfaces (
   2587            &CallbackInfo->DriverHandle,
   2588            &gEfiDevicePathProtocolGuid,
   2589            &mIScsiHiiVendorDevicePath,
   2590            &gEfiHiiConfigAccessProtocolGuid,
   2591            &CallbackInfo->ConfigAccess,
   2592            NULL
   2593            );
   2594     FreePool(CallbackInfo);
   2595     return EFI_OUT_OF_RESOURCES;
   2596   }
   2597 
   2598   mCallbackInfo = CallbackInfo;
   2599 
   2600   return EFI_SUCCESS;
   2601 }
   2602 
   2603 
   2604 /**
   2605   Unload the iSCSI configuration form, this includes: delete all the iSCSI
   2606   configuration entries, uninstall the form callback protocol, and
   2607   free the resources used.
   2608 
   2609   @param[in]  DriverBindingHandle The iSCSI driverbinding handle.
   2610 
   2611   @retval EFI_SUCCESS             The iSCSI configuration form is unloaded.
   2612   @retval Others                  Failed to unload the form.
   2613 
   2614 **/
   2615 EFI_STATUS
   2616 IScsiConfigFormUnload (
   2617   IN EFI_HANDLE  DriverBindingHandle
   2618   )
   2619 {
   2620   ISCSI_ATTEMPT_CONFIG_NVDATA *AttemptConfigData;
   2621   ISCSI_NIC_INFO              *NicInfo;
   2622   LIST_ENTRY                  *Entry;
   2623   EFI_STATUS                  Status;
   2624 
   2625   while (!IsListEmpty (&mPrivate->AttemptConfigs)) {
   2626     Entry = NetListRemoveHead (&mPrivate->AttemptConfigs);
   2627     AttemptConfigData = NET_LIST_USER_STRUCT (Entry, ISCSI_ATTEMPT_CONFIG_NVDATA, Link);
   2628     FreePool (AttemptConfigData);
   2629     mPrivate->AttemptCount--;
   2630   }
   2631 
   2632   ASSERT (mPrivate->AttemptCount == 0);
   2633 
   2634   while (!IsListEmpty (&mPrivate->NicInfoList)) {
   2635     Entry = NetListRemoveHead (&mPrivate->NicInfoList);
   2636     NicInfo = NET_LIST_USER_STRUCT (Entry, ISCSI_NIC_INFO, Link);
   2637     FreePool (NicInfo);
   2638     mPrivate->NicCount--;
   2639   }
   2640 
   2641   ASSERT (mPrivate->NicCount == 0);
   2642 
   2643   //
   2644   // Free attempt is created but not saved to system.
   2645   //
   2646   if (mPrivate->NewAttempt != NULL) {
   2647     FreePool (mPrivate->NewAttempt);
   2648   }
   2649 
   2650   FreePool (mPrivate);
   2651   mPrivate = NULL;
   2652 
   2653   //
   2654   // Remove HII package list.
   2655   //
   2656   HiiRemovePackages (mCallbackInfo->RegisteredHandle);
   2657 
   2658   //
   2659   // Uninstall Device Path Protocol and Config Access protocol.
   2660   //
   2661   Status = gBS->UninstallMultipleProtocolInterfaces (
   2662                   mCallbackInfo->DriverHandle,
   2663                   &gEfiDevicePathProtocolGuid,
   2664                   &mIScsiHiiVendorDevicePath,
   2665                   &gEfiHiiConfigAccessProtocolGuid,
   2666                   &mCallbackInfo->ConfigAccess,
   2667                   NULL
   2668                   );
   2669 
   2670   FreePool (mCallbackInfo);
   2671 
   2672   return Status;
   2673 }
   2674