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 LIST_ENTRY      mIScsiConfigFormList = {
     23   &mIScsiConfigFormList,
     24   &mIScsiConfigFormList
     25 };
     26 
     27 HII_VENDOR_DEVICE_PATH  mIScsiHiiVendorDevicePath = {
     28   {
     29     {
     30       HARDWARE_DEVICE_PATH,
     31       HW_VENDOR_DP,
     32       {
     33         (UINT8) (sizeof (VENDOR_DEVICE_PATH)),
     34         (UINT8) ((sizeof (VENDOR_DEVICE_PATH)) >> 8)
     35       }
     36     },
     37     IP4_ISCSI_CONFIG_GUID
     38   },
     39   {
     40     END_DEVICE_PATH_TYPE,
     41     END_ENTIRE_DEVICE_PATH_SUBTYPE,
     42     {
     43       (UINT8) (END_DEVICE_PATH_LENGTH),
     44       (UINT8) ((END_DEVICE_PATH_LENGTH) >> 8)
     45     }
     46   }
     47 };
     48 
     49 /**
     50   Convert the IPv4 address into a dotted string.
     51 
     52   @param[in]   Ip   The IPv4 address.
     53   @param[out]  Str  The dotted IP string.
     54 **/
     55 VOID
     56 IScsiIpToStr (
     57   IN  EFI_IPv4_ADDRESS  *Ip,
     58   OUT CHAR16            *Str
     59   )
     60 {
     61   UnicodeSPrint ( Str, 2 * IP4_STR_MAX_SIZE, L"%d.%d.%d.%d", Ip->Addr[0], Ip->Addr[1], Ip->Addr[2], Ip->Addr[3]);
     62 }
     63 
     64 
     65 /**
     66   Parse IsId in string format and convert it to binary.
     67 
     68   @param[in]        String  The buffer of the string to be parsed.
     69   @param[in, out]   IsId    The buffer to store IsId.
     70 
     71   @retval EFI_SUCCESS              The operation finished successfully.
     72   @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.
     73 
     74 **/
     75 EFI_STATUS
     76 IScsiParseIsIdFromString (
     77   IN CONST CHAR16                    *String,
     78   IN OUT   UINT8                     *IsId
     79   )
     80 {
     81   UINT8                          Index;
     82   CHAR16                         *IsIdStr;
     83   CHAR16                         TempStr[3];
     84   UINTN                          NodeVal;
     85   CHAR16                         PortString[ISCSI_NAME_IFR_MAX_SIZE];
     86   EFI_INPUT_KEY                  Key;
     87 
     88   if ((String == NULL) || (IsId == NULL)) {
     89     return EFI_INVALID_PARAMETER;
     90   }
     91 
     92   IsIdStr = (CHAR16 *) String;
     93 
     94   if (StrLen (IsIdStr) != 6) {
     95     UnicodeSPrint (
     96       PortString,
     97       (UINTN) sizeof (PortString),
     98       L"Error! Input is incorrect, please input 6 hex numbers!\n"
     99       );
    100 
    101     CreatePopUp (
    102       EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    103       &Key,
    104       PortString,
    105       NULL
    106       );
    107 
    108     return EFI_INVALID_PARAMETER;
    109   }
    110 
    111   for (Index = 3; Index < 6; Index++) {
    112     CopyMem (TempStr, IsIdStr, sizeof (TempStr));
    113     TempStr[2] = L'\0';
    114 
    115     //
    116     // Convert the string to IsId. StrHexToUintn stops at the first character
    117     // that is not a valid hex character, '\0' here.
    118     //
    119     NodeVal = StrHexToUintn (TempStr);
    120 
    121     IsId[Index] = (UINT8) NodeVal;
    122 
    123     IsIdStr = IsIdStr + 2;
    124   }
    125 
    126   return EFI_SUCCESS;
    127 }
    128 
    129 /**
    130   Convert IsId from binary to string format.
    131 
    132   @param[out]      String  The buffer to store the converted string.
    133   @param[in]       IsId    The buffer to store IsId.
    134 
    135   @retval EFI_SUCCESS              The string converted successfully.
    136   @retval EFI_INVALID_PARAMETER    Any input parameter is invalid.
    137 
    138 **/
    139 EFI_STATUS
    140 IScsiConvertIsIdToString (
    141   OUT CHAR16                         *String,
    142   IN  UINT8                          *IsId
    143   )
    144 {
    145   UINT8                          Index;
    146   UINTN                          Number;
    147 
    148   if ((String == NULL) || (IsId == NULL)) {
    149     return EFI_INVALID_PARAMETER;
    150   }
    151 
    152   for (Index = 0; Index < 6; Index++) {
    153     if (IsId[Index] <= 0xF) {
    154       Number = UnicodeSPrint (
    155                  String,
    156                  2 * ISID_CONFIGURABLE_STORAGE,
    157                  L"0%X",
    158                  (UINTN) IsId[Index]
    159                  );
    160     } else {
    161       Number = UnicodeSPrint (
    162                  String,
    163                  2 * ISID_CONFIGURABLE_STORAGE,
    164                  L"%X",
    165                  (UINTN) IsId[Index]
    166                  );
    167 
    168     }
    169 
    170     String = String + Number;
    171   }
    172 
    173   *String = L'\0';
    174 
    175   return EFI_SUCCESS;
    176 }
    177 
    178 
    179 /**
    180   Update the list of iSCSI devices the iSCSI driver is controlling.
    181 
    182   @retval EFI_SUCCESS            The callback successfully handled the action.
    183   @retval Others                 Other errors as indicated.
    184 **/
    185 EFI_STATUS
    186 IScsiUpdateDeviceList (
    187   VOID
    188   )
    189 {
    190   EFI_STATUS                  Status;
    191   ISCSI_DEVICE_LIST           *DeviceList;
    192   UINTN                       DataSize;
    193   UINTN                       NumHandles;
    194   EFI_HANDLE                  *Handles;
    195   UINTN                       HandleIndex;
    196   UINTN                       Index;
    197   UINTN                       LastDeviceIndex;
    198   EFI_MAC_ADDRESS             MacAddress;
    199   UINTN                       HwAddressSize;
    200   UINT16                      VlanId;
    201   ISCSI_MAC_INFO              *CurMacInfo;
    202   ISCSI_MAC_INFO              TempMacInfo;
    203   CHAR16                      MacString[70];
    204   UINTN                       DeviceListSize;
    205 
    206   //
    207   // Dump all the handles the Managed Network Service Binding Protocol is installed on.
    208   //
    209   Status = gBS->LocateHandleBuffer (
    210                   ByProtocol,
    211                   &gEfiManagedNetworkServiceBindingProtocolGuid,
    212                   NULL,
    213                   &NumHandles,
    214                   &Handles
    215                   );
    216   if (EFI_ERROR (Status)) {
    217     return Status;
    218   }
    219 
    220   DataSize = 0;
    221   Status = gRT->GetVariable (
    222                   L"iSCSIDeviceList",
    223                   &gIp4IScsiConfigGuid,
    224                   NULL,
    225                   &DataSize,
    226                   NULL
    227                   );
    228   if (Status == EFI_BUFFER_TOO_SMALL) {
    229     DeviceList = (ISCSI_DEVICE_LIST *) AllocatePool (DataSize);
    230     ASSERT (DeviceList != NULL);
    231 
    232     gRT->GetVariable (
    233           L"iSCSIDeviceList",
    234           &gIp4IScsiConfigGuid,
    235           NULL,
    236           &DataSize,
    237           DeviceList
    238           );
    239 
    240     LastDeviceIndex = 0;
    241 
    242     for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) {
    243       Status = NetLibGetMacAddress (Handles[HandleIndex], &MacAddress, &HwAddressSize);
    244       ASSERT (Status == EFI_SUCCESS);
    245       VlanId = NetLibGetVlanId (Handles[HandleIndex]);
    246 
    247       for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {
    248         CurMacInfo = &DeviceList->MacInfo[Index];
    249         if ((CurMacInfo->Len == HwAddressSize) &&
    250             (CurMacInfo->VlanId == VlanId) &&
    251             (NET_MAC_EQUAL (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize))
    252             ) {
    253           //
    254           // The previous configured NIC is still here.
    255           //
    256           if (Index != LastDeviceIndex) {
    257             //
    258             // Swap the current MAC address entry with the one indexed by
    259             // LastDeviceIndex.
    260             //
    261             CopyMem (&TempMacInfo, CurMacInfo, sizeof (ISCSI_MAC_INFO));
    262             CopyMem (CurMacInfo, &DeviceList->MacInfo[LastDeviceIndex], sizeof (ISCSI_MAC_INFO));
    263             CopyMem (&DeviceList->MacInfo[LastDeviceIndex], &TempMacInfo, sizeof (ISCSI_MAC_INFO));
    264           }
    265 
    266           LastDeviceIndex++;
    267         }
    268       }
    269 
    270       if (LastDeviceIndex == DeviceList->NumDevice) {
    271         break;
    272       }
    273     }
    274 
    275     for (Index = LastDeviceIndex; Index < DeviceList->NumDevice; Index++) {
    276       //
    277       // delete the variables
    278       //
    279       CurMacInfo = &DeviceList->MacInfo[Index];
    280       IScsiMacAddrToStr (&CurMacInfo->Mac, CurMacInfo->Len, CurMacInfo->VlanId, MacString);
    281       gRT->SetVariable (MacString, &gEfiIScsiInitiatorNameProtocolGuid, 0, 0, NULL);
    282       gRT->SetVariable (MacString, &gIScsiCHAPAuthInfoGuid, 0, 0, NULL);
    283     }
    284 
    285     FreePool (DeviceList);
    286   } else if (Status != EFI_NOT_FOUND) {
    287     FreePool (Handles);
    288     return Status;
    289   }
    290   //
    291   // Construct the new iSCSI device list.
    292   //
    293   DeviceListSize        = sizeof (ISCSI_DEVICE_LIST) + (NumHandles - 1) * sizeof (ISCSI_MAC_INFO);
    294   DeviceList            = (ISCSI_DEVICE_LIST *) AllocatePool (DeviceListSize);
    295   ASSERT (DeviceList != NULL);
    296   DeviceList->NumDevice = (UINT8) NumHandles;
    297 
    298   for (Index = 0; Index < NumHandles; Index++) {
    299     NetLibGetMacAddress (Handles[Index], &MacAddress, &HwAddressSize);
    300 
    301     CurMacInfo  = &DeviceList->MacInfo[Index];
    302     CopyMem (&CurMacInfo->Mac, MacAddress.Addr, HwAddressSize);
    303     CurMacInfo->Len = (UINT8) HwAddressSize;
    304     CurMacInfo->VlanId = NetLibGetVlanId (Handles[Index]);
    305   }
    306 
    307   gRT->SetVariable (
    308         L"iSCSIDeviceList",
    309         &gIp4IScsiConfigGuid,
    310         ISCSI_CONFIG_VAR_ATTR,
    311         DeviceListSize,
    312         DeviceList
    313         );
    314 
    315   FreePool (DeviceList);
    316   FreePool (Handles);
    317 
    318   return Status;
    319 }
    320 
    321 /**
    322   Get the iSCSI configuration form entry by the index of the goto opcode actived.
    323 
    324   @param[in]  Index The 0-based index of the goto opcode actived.
    325 
    326   @return The iSCSI configuration form entry found.
    327 **/
    328 ISCSI_CONFIG_FORM_ENTRY *
    329 IScsiGetConfigFormEntryByIndex (
    330   IN UINT32 Index
    331   )
    332 {
    333   UINT32                  CurrentIndex;
    334   LIST_ENTRY              *Entry;
    335   ISCSI_CONFIG_FORM_ENTRY *ConfigFormEntry;
    336 
    337   CurrentIndex    = 0;
    338   ConfigFormEntry = NULL;
    339 
    340   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
    341     if (CurrentIndex == Index) {
    342       ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
    343       break;
    344     }
    345 
    346     CurrentIndex++;
    347   }
    348 
    349   return ConfigFormEntry;
    350 }
    351 
    352 /**
    353   Convert the iSCSI configuration data into the IFR data.
    354 
    355   @param[in]   ConfigFormEntry The iSCSI configuration form entry.
    356   @param[out]  IfrNvData       The IFR nv data.
    357 
    358 **/
    359 VOID
    360 IScsiConvertDeviceConfigDataToIfrNvData (
    361   IN ISCSI_CONFIG_FORM_ENTRY      *ConfigFormEntry,
    362   OUT ISCSI_CONFIG_IFR_NVDATA     *IfrNvData
    363   )
    364 {
    365   ISCSI_SESSION_CONFIG_NVDATA   *SessionConfigData;
    366   ISCSI_CHAP_AUTH_CONFIG_NVDATA *AuthConfigData;
    367 
    368   //
    369   // Normal session configuration parameters.
    370   //
    371   SessionConfigData                 = &ConfigFormEntry->SessionConfigData;
    372   IfrNvData->Enabled                = SessionConfigData->Enabled;
    373 
    374   IfrNvData->InitiatorInfoFromDhcp  = SessionConfigData->InitiatorInfoFromDhcp;
    375   IfrNvData->TargetInfoFromDhcp     = SessionConfigData->TargetInfoFromDhcp;
    376   IfrNvData->TargetPort             = SessionConfigData->TargetPort;
    377 
    378   IScsiIpToStr (&SessionConfigData->LocalIp, IfrNvData->LocalIp);
    379   IScsiIpToStr (&SessionConfigData->SubnetMask, IfrNvData->SubnetMask);
    380   IScsiIpToStr (&SessionConfigData->Gateway, IfrNvData->Gateway);
    381   IScsiIpToStr (&SessionConfigData->TargetIp, IfrNvData->TargetIp);
    382 
    383   IScsiAsciiStrToUnicodeStr (SessionConfigData->TargetName, IfrNvData->TargetName);
    384 
    385   IScsiLunToUnicodeStr (SessionConfigData->BootLun, IfrNvData->BootLun);
    386 
    387   IScsiConvertIsIdToString (IfrNvData->IsId, SessionConfigData->IsId);
    388 
    389   //
    390   // CHAP authentication parameters.
    391   //
    392   AuthConfigData      = &ConfigFormEntry->AuthConfigData;
    393 
    394   IfrNvData->CHAPType = AuthConfigData->CHAPType;
    395 
    396   IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPName, IfrNvData->CHAPName);
    397   IScsiAsciiStrToUnicodeStr (AuthConfigData->CHAPSecret, IfrNvData->CHAPSecret);
    398   IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPName, IfrNvData->ReverseCHAPName);
    399   IScsiAsciiStrToUnicodeStr (AuthConfigData->ReverseCHAPSecret, IfrNvData->ReverseCHAPSecret);
    400 }
    401 
    402 /**
    403   This function allows the caller to request the current
    404   configuration for one or more named elements. The resulting
    405   string is in <ConfigAltResp> format. Any and all alternative
    406   configuration strings shall also be appended to the end of the
    407   current configuration string. If they are, they must appear
    408   after the current configuration. They must contain the same
    409   routing (GUID, NAME, PATH) as the current configuration string.
    410   They must have an additional description indicating the type of
    411   alternative configuration the string represents,
    412   "ALTCFG=<StringToken>". That <StringToken> (when
    413   converted from Hex UNICODE to binary) is a reference to a
    414   string in the associated string pack.
    415 
    416   @param[in] This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
    417   @param[in] Request    A null-terminated Unicode string in
    418                         <ConfigRequest> format. Note that this
    419                         includes the routing information as well as
    420                         the configurable name / value pairs. It is
    421                         invalid for this string to be in
    422                         <MultiConfigRequest> format.
    423   @param[out] Progress  On return, points to a character in the
    424                         Request string. Points to the string's null
    425                         terminator if request was successful. Points
    426                         to the most recent "&" before the first
    427                         failing name / value pair (or the beginning
    428                         of the string if the failure is in the first
    429                         name / value pair) if the request was not
    430                         successful.
    431   @param[out] Results   A null-terminated Unicode string in
    432                         <ConfigAltResp> format which has all values
    433                         filled in for the names in the Request string.
    434                         String to be allocated by the called function.
    435 
    436   @retval EFI_SUCCESS             The Results string is filled with the
    437                                   values corresponding to all requested
    438                                   names.
    439   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the
    440                                   parts of the results that must be
    441                                   stored awaiting possible future
    442                                   protocols.
    443   @retval EFI_INVALID_PARAMETER   For example, passing in a NULL
    444                                   for the Request parameter
    445                                   would result in this type of
    446                                   error. In this case, the
    447                                   Progress parameter would be
    448                                   set to NULL.
    449   @retval EFI_NOT_FOUND           Routing data doesn't match any
    450                                   known driver. Progress set to the
    451                                   first character in the routing header.
    452                                   Note: There is no requirement that the
    453                                   driver validate the routing data. It
    454                                   must skip the <ConfigHdr> in order to
    455                                   process the names.
    456   @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set
    457                                   to most recent & before the
    458                                   error or the beginning of the
    459                                   string.
    460   @retval EFI_INVALID_PARAMETER   Unknown name. Progress points
    461                                   to the & before the name in
    462                                   question.Currently not implemented.
    463 **/
    464 EFI_STATUS
    465 EFIAPI
    466 IScsiFormExtractConfig (
    467   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
    468   IN  CONST EFI_STRING                       Request,
    469   OUT EFI_STRING                             *Progress,
    470   OUT EFI_STRING                             *Results
    471   )
    472 {
    473   EFI_STATUS                       Status;
    474   CHAR8                            InitiatorName[ISCSI_NAME_MAX_SIZE];
    475   UINTN                            BufferSize;
    476   ISCSI_CONFIG_IFR_NVDATA          *IfrNvData;
    477   ISCSI_FORM_CALLBACK_INFO         *Private;
    478   EFI_HII_CONFIG_ROUTING_PROTOCOL  *HiiConfigRouting;
    479   EFI_STRING                       ConfigRequestHdr;
    480   EFI_STRING                       ConfigRequest;
    481   BOOLEAN                          AllocatedRequest;
    482   UINTN                            Size;
    483 
    484   if (Progress == NULL || Results == NULL) {
    485     return EFI_INVALID_PARAMETER;
    486   }
    487 
    488   *Progress = Request;
    489   if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gIp4IScsiConfigGuid, mVendorStorageName)) {
    490     return EFI_NOT_FOUND;
    491   }
    492 
    493   ConfigRequestHdr = NULL;
    494   ConfigRequest    = NULL;
    495   AllocatedRequest = FALSE;
    496   Size             = 0;
    497 
    498   if (!mIScsiDeviceListUpdated) {
    499     //
    500     // Update the device list.
    501     //
    502     IScsiUpdateDeviceList ();
    503     mIScsiDeviceListUpdated = TRUE;
    504   }
    505 
    506   Private = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
    507   IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));
    508   ASSERT (IfrNvData != NULL);
    509   if (Private->Current != NULL) {
    510     IScsiConvertDeviceConfigDataToIfrNvData (Private->Current, IfrNvData);
    511   }
    512 
    513   BufferSize  = ISCSI_NAME_MAX_SIZE;
    514   Status      = gIScsiInitiatorName.Get (&gIScsiInitiatorName, &BufferSize, InitiatorName);
    515   if (EFI_ERROR (Status)) {
    516     IfrNvData->InitiatorName[0] = L'\0';
    517   } else {
    518     IScsiAsciiStrToUnicodeStr (InitiatorName, IfrNvData->InitiatorName);
    519   }
    520 
    521   //
    522   // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
    523   //
    524   HiiConfigRouting = Private->ConfigRouting;
    525   BufferSize = sizeof (ISCSI_CONFIG_IFR_NVDATA);
    526   ConfigRequest = Request;
    527   if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
    528     //
    529     // Request has no request element, construct full request string.
    530     // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
    531     // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
    532     //
    533     ConfigRequestHdr = HiiConstructConfigHdr (&gIp4IScsiConfigGuid, mVendorStorageName, Private->DriverHandle);
    534     Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
    535     ConfigRequest = AllocateZeroPool (Size);
    536     ASSERT (ConfigRequest != NULL);
    537     AllocatedRequest = TRUE;
    538     UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
    539     FreePool (ConfigRequestHdr);
    540   }
    541   Status = HiiConfigRouting->BlockToConfig (
    542                                HiiConfigRouting,
    543                                ConfigRequest,
    544                                (UINT8 *) IfrNvData,
    545                                BufferSize,
    546                                Results,
    547                                Progress
    548                                );
    549   FreePool (IfrNvData);
    550   //
    551   // Free the allocated config request string.
    552   //
    553   if (AllocatedRequest) {
    554     FreePool (ConfigRequest);
    555     ConfigRequest = NULL;
    556   }
    557 
    558   //
    559   // Set Progress string to the original request string.
    560   //
    561   if (Request == NULL) {
    562     *Progress = NULL;
    563   } else if (StrStr (Request, L"OFFSET") == NULL) {
    564     *Progress = Request + StrLen (Request);
    565   }
    566 
    567   return Status;
    568 }
    569 
    570 /**
    571   This function applies changes in a driver's configuration.
    572   Input is a Configuration, which has the routing data for this
    573   driver followed by name / value configuration pairs. The driver
    574   must apply those pairs to its configurable storage. If the
    575   driver's configuration is stored in a linear block of data
    576   and the driver's name / value pairs are in <BlockConfig>
    577   format, it may use the ConfigToBlock helper function (above) to
    578   simplify the job. Currently not implemented.
    579 
    580   @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
    581   @param[in]  Configuration  A null-terminated Unicode string in
    582                              <ConfigString> format.
    583   @param[out] Progress       A pointer to a string filled in with the
    584                              offset of the most recent '&' before the
    585                              first failing name / value pair (or the
    586                              beginn ing of the string if the failure
    587                              is in the first name / value pair) or
    588                              the terminating NULL if all was
    589                              successful.
    590 
    591   @retval EFI_SUCCESS             The results have been distributed or are
    592                                   awaiting distribution.
    593   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the
    594                                   parts of the results that must be
    595                                   stored awaiting possible future
    596                                   protocols.
    597   @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the
    598                                   Results parameter would result
    599                                   in this type of error.
    600   @retval EFI_NOT_FOUND           Target for the specified routing data
    601                                   was not found.
    602 **/
    603 EFI_STATUS
    604 EFIAPI
    605 IScsiFormRouteConfig (
    606   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
    607   IN  CONST EFI_STRING                       Configuration,
    608   OUT EFI_STRING                             *Progress
    609   )
    610 {
    611   if (Configuration == NULL || Progress == NULL) {
    612     return EFI_INVALID_PARAMETER;
    613   }
    614 
    615   //
    616   // Check routing data in <ConfigHdr>.
    617   // Note: if only one Storage is used, then this checking could be skipped.
    618   //
    619   if (!HiiIsConfigHdrMatch (Configuration, &gIp4IScsiConfigGuid, mVendorStorageName)) {
    620     *Progress = Configuration;
    621     return EFI_NOT_FOUND;
    622   }
    623 
    624   *Progress = Configuration + StrLen (Configuration);
    625   return EFI_SUCCESS;
    626 }
    627 
    628 /**
    629   This function is called to provide results data to the driver.
    630   This data consists of a unique key that is used to identify
    631   which data is either being passed back or being asked for.
    632 
    633   @param[in]  This               Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
    634   @param[in]  Action             Specifies the type of action taken by the browser.
    635   @param[in]  QuestionId         A unique value which is sent to the original
    636                                  exporting driver so that it can identify the type
    637                                  of data to expect. The format of the data tends to
    638                                  vary based on the opcode that enerated the callback.
    639   @param[in]  Type               The type of value for the question.
    640   @param[in]  Value              A pointer to the data being sent to the original
    641                                  exporting driver.
    642   @param[out]  ActionRequest     On return, points to the action requested by the
    643                                  callback function.
    644 
    645   @retval EFI_SUCCESS            The callback successfully handled the action.
    646   @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the
    647                                  variable and its data.
    648   @retval EFI_DEVICE_ERROR       The variable could not be saved.
    649   @retval EFI_UNSUPPORTED        The specified Action is not supported by the
    650                                  callback.Currently not implemented.
    651   @retval EFI_INVALID_PARAMETERS Passing in wrong parameter.
    652   @retval Others                 Other errors as indicated.
    653 **/
    654 EFI_STATUS
    655 EFIAPI
    656 IScsiFormCallback (
    657   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
    658   IN  EFI_BROWSER_ACTION                     Action,
    659   IN  EFI_QUESTION_ID                        QuestionId,
    660   IN  UINT8                                  Type,
    661   IN  EFI_IFR_TYPE_VALUE                     *Value,
    662   OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
    663   )
    664 {
    665   ISCSI_FORM_CALLBACK_INFO  *Private;
    666   UINTN                     BufferSize;
    667   CHAR8                     IScsiName[ISCSI_NAME_MAX_SIZE];
    668   CHAR16                    PortString[128];
    669   CHAR8                     Ip4String[IP4_STR_MAX_SIZE];
    670   CHAR8                     LunString[ISCSI_LUN_STR_MAX_LEN];
    671   UINT64                    Lun;
    672   EFI_STRING_ID             DeviceFormTitleToken;
    673   ISCSI_CONFIG_IFR_NVDATA   *IfrNvData;
    674   ISCSI_CONFIG_FORM_ENTRY   *ConfigFormEntry;
    675   EFI_IP_ADDRESS            HostIp;
    676   EFI_IP_ADDRESS            SubnetMask;
    677   EFI_IP_ADDRESS            Gateway;
    678   EFI_STATUS                Status;
    679   EFI_INPUT_KEY             Key;
    680 
    681   if (Action != EFI_BROWSER_ACTION_CHANGING && Action != EFI_BROWSER_ACTION_CHANGED) {
    682     return EFI_UNSUPPORTED;
    683   }
    684 
    685   Private   = ISCSI_FORM_CALLBACK_INFO_FROM_FORM_CALLBACK (This);
    686   //
    687   // Retrieve uncommitted data from Browser
    688   //
    689   IfrNvData = AllocateZeroPool (sizeof (ISCSI_CONFIG_IFR_NVDATA));
    690   ASSERT (IfrNvData != NULL);
    691   if (!HiiGetBrowserData (&gIp4IScsiConfigGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData)) {
    692     FreePool (IfrNvData);
    693     return EFI_NOT_FOUND;
    694   }
    695   Status = EFI_SUCCESS;
    696 
    697   if (Action == EFI_BROWSER_ACTION_CHANGING) {
    698     if ((QuestionId >= KEY_DEVICE_ENTRY_BASE) && (QuestionId < (mNumberOfIScsiDevices + KEY_DEVICE_ENTRY_BASE))) {
    699       //
    700       // In case goto the device configuration form, update the device form title.
    701       //
    702       ConfigFormEntry = IScsiGetConfigFormEntryByIndex ((UINT32) (QuestionId - KEY_DEVICE_ENTRY_BASE));
    703       ASSERT (ConfigFormEntry != NULL);
    704 
    705       UnicodeSPrint (PortString, (UINTN) sizeof (PortString), L"Port %s", ConfigFormEntry->MacString);
    706       DeviceFormTitleToken = (EFI_STRING_ID) STR_ISCSI_DEVICE_FORM_TITLE;
    707       HiiSetString (Private->RegisteredHandle, DeviceFormTitleToken, PortString, NULL);
    708 
    709       IScsiConvertDeviceConfigDataToIfrNvData (ConfigFormEntry, IfrNvData);
    710 
    711       Private->Current = ConfigFormEntry;
    712     }
    713   } else if (Action == EFI_BROWSER_ACTION_CHANGED) {
    714     switch (QuestionId) {
    715     case KEY_INITIATOR_NAME:
    716       IScsiUnicodeStrToAsciiStr (IfrNvData->InitiatorName, IScsiName);
    717       BufferSize  = AsciiStrSize (IScsiName);
    718 
    719       Status      = gIScsiInitiatorName.Set (&gIScsiInitiatorName, &BufferSize, IScsiName);
    720       if (EFI_ERROR (Status)) {
    721         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL);
    722       }
    723 
    724       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
    725       break;
    726 
    727     case KEY_LOCAL_IP:
    728       IScsiUnicodeStrToAsciiStr (IfrNvData->LocalIp, Ip4String);
    729       Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);
    730       if (EFI_ERROR (Status) ||
    731           ((Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
    732            !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
    733         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
    734         Status = EFI_INVALID_PARAMETER;
    735       } else {
    736         CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));
    737       }
    738 
    739       break;
    740 
    741     case KEY_SUBNET_MASK:
    742       IScsiUnicodeStrToAsciiStr (IfrNvData->SubnetMask, Ip4String);
    743       Status = IScsiAsciiStrToIp (Ip4String, &SubnetMask.v4);
    744       if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {
    745         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL);
    746         Status = EFI_INVALID_PARAMETER;
    747       } else {
    748         CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));
    749       }
    750 
    751       break;
    752 
    753     case KEY_GATE_WAY:
    754       IScsiUnicodeStrToAsciiStr (IfrNvData->Gateway, Ip4String);
    755       Status = IScsiAsciiStrToIp (Ip4String, &Gateway.v4);
    756       if (EFI_ERROR (Status) ||
    757           ((Gateway.Addr[0] != 0) &&
    758            (Private->Current->SessionConfigData.SubnetMask.Addr[0] != 0) &&
    759            !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), NTOHL(*(UINT32*)Private->Current->SessionConfigData.SubnetMask.Addr)))) {
    760         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL);
    761         Status = EFI_INVALID_PARAMETER;
    762       } else {
    763         CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));
    764       }
    765 
    766       break;
    767 
    768     case KEY_TARGET_IP:
    769       IScsiUnicodeStrToAsciiStr (IfrNvData->TargetIp, Ip4String);
    770       Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);
    771       if (EFI_ERROR (Status) || IP4_IS_LOCAL_BROADCAST (EFI_NTOHL(HostIp.v4)) || IP4_IS_UNSPECIFIED (EFI_NTOHL(HostIp.v4))) {
    772         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
    773         Status = EFI_INVALID_PARAMETER;
    774       } else {
    775         CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp.v4, sizeof (HostIp.v4));
    776       }
    777 
    778       break;
    779 
    780     case KEY_TARGET_NAME:
    781       IScsiUnicodeStrToAsciiStr (IfrNvData->TargetName, IScsiName);
    782       Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName));
    783       if (EFI_ERROR (Status)) {
    784         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL);
    785       } else {
    786         AsciiStrCpyS (Private->Current->SessionConfigData.TargetName, ISCSI_NAME_MAX_SIZE, IScsiName);
    787       }
    788 
    789       break;
    790 
    791     case KEY_DHCP_ENABLE:
    792       if (IfrNvData->InitiatorInfoFromDhcp == 0) {
    793         IfrNvData->TargetInfoFromDhcp = 0;
    794       }
    795 
    796       break;
    797 
    798     case KEY_BOOT_LUN:
    799       IScsiUnicodeStrToAsciiStr (IfrNvData->BootLun, LunString);
    800       Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);
    801       if (EFI_ERROR (Status)) {
    802         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid LUN string!", NULL);
    803       } else {
    804         CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun));
    805       }
    806 
    807       break;
    808 
    809     case KEY_CHAP_NAME:
    810       IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPName, Private->Current->AuthConfigData.CHAPName);
    811       break;
    812 
    813     case KEY_CHAP_SECRET:
    814       IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPSecret, Private->Current->AuthConfigData.CHAPSecret);
    815       break;
    816 
    817     case KEY_REVERSE_CHAP_NAME:
    818       IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPName, Private->Current->AuthConfigData.ReverseCHAPName);
    819       break;
    820 
    821     case KEY_REVERSE_CHAP_SECRET:
    822       IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPSecret, Private->Current->AuthConfigData.ReverseCHAPSecret);
    823       break;
    824 
    825     case KEY_CONFIG_ISID:
    826       IScsiParseIsIdFromString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
    827       IScsiConvertIsIdToString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
    828 
    829       break;
    830 
    831     case KEY_SAVE_CHANGES:
    832       //
    833       // First, update those fields which don't have INTERACTIVE set.
    834       //
    835       Private->Current->SessionConfigData.Enabled               = IfrNvData->Enabled;
    836       Private->Current->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;
    837       Private->Current->SessionConfigData.TargetPort            = IfrNvData->TargetPort;
    838       if (Private->Current->SessionConfigData.TargetPort == 0) {
    839         Private->Current->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
    840       }
    841 
    842       Private->Current->SessionConfigData.TargetInfoFromDhcp  = IfrNvData->TargetInfoFromDhcp;
    843       Private->Current->AuthConfigData.CHAPType               = IfrNvData->CHAPType;
    844 
    845       //
    846       // Only do full parameter validation if iSCSI is enabled on this device.
    847       //
    848       if (Private->Current->SessionConfigData.Enabled) {
    849         //
    850         // Validate the address configuration of the Initiator if DHCP isn't
    851         // deployed.
    852         //
    853         if (!Private->Current->SessionConfigData.InitiatorInfoFromDhcp) {
    854           CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.LocalIp, sizeof (HostIp.v4));
    855           CopyMem (&SubnetMask.v4, &Private->Current->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));
    856           CopyMem (&Gateway.v4, &Private->Current->SessionConfigData.Gateway, sizeof (Gateway.v4));
    857 
    858           if ((Gateway.Addr[0] != 0)) {
    859             if (SubnetMask.Addr[0] == 0) {
    860               CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Gateway address is set but subnet mask is zero.", NULL);
    861               Status = EFI_INVALID_PARAMETER;
    862               break;
    863             } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) {
    864               CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Local IP and Gateway are not in the same subnet.", NULL);
    865               Status = EFI_INVALID_PARAMETER;
    866               break;
    867             }
    868           }
    869         }
    870         //
    871         // Validate target configuration if DHCP isn't deployed.
    872         //
    873         if (!Private->Current->SessionConfigData.TargetInfoFromDhcp) {
    874           CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.TargetIp, sizeof (HostIp.v4));
    875           if (IP4_IS_UNSPECIFIED (NTOHL (HostIp.Addr[0])) || IP4_IS_LOCAL_BROADCAST (NTOHL (HostIp.Addr[0]))) {
    876             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Target IP is invalid!", NULL);
    877             Status = EFI_INVALID_PARAMETER;
    878             break;
    879           }
    880 
    881           //
    882           // Validate iSCSI target name configuration again:
    883           // The format of iSCSI target name is already verified when user input the name;
    884           // here we only check the case user does not input the name.
    885           //
    886           if (Private->Current->SessionConfigData.TargetName[0] == '\0') {
    887             CreatePopUp (
    888               EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    889               &Key,
    890               L"iSCSI target name is NULL!",
    891               NULL
    892               );
    893             Status = EFI_INVALID_PARAMETER;
    894             break;
    895           }
    896 
    897         }
    898 
    899         if (IfrNvData->CHAPType != ISCSI_CHAP_NONE) {
    900           if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) {
    901             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"CHAP Name or CHAP Secret is invalid!", NULL);
    902             Status = EFI_INVALID_PARAMETER;
    903             break;
    904           }
    905 
    906           if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) &&
    907               ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0'))
    908               ) {
    909             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Reverse CHAP Name or Reverse CHAP Secret is invalid!", NULL);
    910             Status = EFI_INVALID_PARAMETER;
    911             break;
    912           }
    913         }
    914       }
    915 
    916       BufferSize = sizeof (Private->Current->SessionConfigData);
    917       gRT->SetVariable (
    918             Private->Current->MacString,
    919             &gEfiIScsiInitiatorNameProtocolGuid,
    920             ISCSI_CONFIG_VAR_ATTR,
    921             BufferSize,
    922             &Private->Current->SessionConfigData
    923             );
    924 
    925       BufferSize = sizeof (Private->Current->AuthConfigData);
    926       gRT->SetVariable (
    927             Private->Current->MacString,
    928             &gIScsiCHAPAuthInfoGuid,
    929             ISCSI_CONFIG_VAR_ATTR,
    930             BufferSize,
    931             &Private->Current->AuthConfigData
    932             );
    933       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
    934       break;
    935 
    936     default:
    937       break;
    938     }
    939   }
    940 
    941   if (!EFI_ERROR (Status)) {
    942     //
    943     // Pass changed uncommitted data back to Form Browser
    944     //
    945     HiiSetBrowserData (&gIp4IScsiConfigGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData, NULL);
    946   }
    947 
    948   FreePool (IfrNvData);
    949 
    950   return Status;
    951 }
    952 
    953 /**
    954   Updates the iSCSI configuration form to add/delete an entry for the iSCSI
    955   device specified by the Controller.
    956 
    957   @param[in]  DriverBindingHandle The driverbinding handle.
    958   @param[in]  Controller          The controller handle of the iSCSI device.
    959   @param[in]  AddForm             Whether to add or delete a form entry.
    960 
    961   @retval EFI_SUCCESS             The iSCSI configuration form is updated.
    962   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
    963   @retval Others                  Other errors as indicated.
    964 **/
    965 EFI_STATUS
    966 IScsiConfigUpdateForm (
    967   IN EFI_HANDLE  DriverBindingHandle,
    968   IN EFI_HANDLE  Controller,
    969   IN BOOLEAN     AddForm
    970   )
    971 {
    972   LIST_ENTRY                  *Entry;
    973   ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;
    974   BOOLEAN                     EntryExisted;
    975   EFI_STATUS                  Status;
    976   EFI_MAC_ADDRESS             MacAddress;
    977   UINTN                       HwAddressSize;
    978   UINT16                      VlanId;
    979   CHAR16                      PortString[128];
    980   UINT16                      FormIndex;
    981   UINTN                       BufferSize;
    982   VOID                        *StartOpCodeHandle;
    983   VOID                        *EndOpCodeHandle;
    984   EFI_IFR_GUID_LABEL          *StartLabel;
    985   EFI_IFR_GUID_LABEL          *EndLabel;
    986 
    987   ConfigFormEntry = NULL;
    988   EntryExisted    = FALSE;
    989 
    990   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
    991     ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
    992 
    993     if (ConfigFormEntry->Controller == Controller) {
    994       EntryExisted = TRUE;
    995       break;
    996     }
    997   }
    998 
    999   if (AddForm) {
   1000     if (EntryExisted) {
   1001       return EFI_SUCCESS;
   1002     } else {
   1003       //
   1004       // Add a new form.
   1005       //
   1006       ConfigFormEntry = (ISCSI_CONFIG_FORM_ENTRY *) AllocateZeroPool (sizeof (ISCSI_CONFIG_FORM_ENTRY));
   1007       if (ConfigFormEntry == NULL) {
   1008         return EFI_OUT_OF_RESOURCES;
   1009       }
   1010 
   1011       InitializeListHead (&ConfigFormEntry->Link);
   1012       ConfigFormEntry->Controller = Controller;
   1013 
   1014       //
   1015       // Get the MAC address and convert it into the formatted string.
   1016       //
   1017       Status = NetLibGetMacAddress (Controller, &MacAddress, &HwAddressSize);
   1018       ASSERT (Status == EFI_SUCCESS);
   1019       VlanId = NetLibGetVlanId (Controller);
   1020 
   1021       IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, ConfigFormEntry->MacString);
   1022 
   1023       //
   1024       // Get the normal session configuration data.
   1025       //
   1026       BufferSize = sizeof (ConfigFormEntry->SessionConfigData);
   1027       Status = gRT->GetVariable (
   1028                       ConfigFormEntry->MacString,
   1029                       &gEfiIScsiInitiatorNameProtocolGuid,
   1030                       NULL,
   1031                       &BufferSize,
   1032                       &ConfigFormEntry->SessionConfigData
   1033                       );
   1034       if (EFI_ERROR (Status)) {
   1035         ZeroMem (&ConfigFormEntry->SessionConfigData, sizeof (ConfigFormEntry->SessionConfigData));
   1036 
   1037         //
   1038         // Generate OUI-format ISID based on MAC address.
   1039         //
   1040         CopyMem (ConfigFormEntry->SessionConfigData.IsId, &MacAddress, 6);
   1041         ConfigFormEntry->SessionConfigData.IsId[0] =
   1042           (UINT8) (ConfigFormEntry->SessionConfigData.IsId[0] & 0x3F);
   1043       }
   1044       //
   1045       // Get the CHAP authentication configuration data.
   1046       //
   1047       BufferSize = sizeof (ConfigFormEntry->AuthConfigData);
   1048       Status = gRT->GetVariable (
   1049                       ConfigFormEntry->MacString,
   1050                       &gIScsiCHAPAuthInfoGuid,
   1051                       NULL,
   1052                       &BufferSize,
   1053                       &ConfigFormEntry->AuthConfigData
   1054                       );
   1055       if (EFI_ERROR (Status)) {
   1056         ZeroMem (&ConfigFormEntry->AuthConfigData, sizeof (ConfigFormEntry->AuthConfigData));
   1057       }
   1058       //
   1059       // Compose the Port string and create a new EFI_STRING_ID.
   1060       //
   1061       UnicodeSPrint (PortString, sizeof (PortString), L"Port %s", ConfigFormEntry->MacString);
   1062       ConfigFormEntry->PortTitleToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);
   1063 
   1064       //
   1065       // Compose the help string of this port and create a new EFI_STRING_ID.
   1066       //
   1067       UnicodeSPrint (PortString, sizeof (PortString), L"Set the iSCSI parameters on port %s", ConfigFormEntry->MacString);
   1068       ConfigFormEntry->PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);
   1069 
   1070       InsertTailList (&mIScsiConfigFormList, &ConfigFormEntry->Link);
   1071       mNumberOfIScsiDevices++;
   1072     }
   1073   } else {
   1074     ASSERT (EntryExisted);
   1075 
   1076     mNumberOfIScsiDevices--;
   1077     RemoveEntryList (&ConfigFormEntry->Link);
   1078     FreePool (ConfigFormEntry);
   1079     mCallbackInfo->Current = NULL;
   1080   }
   1081   //
   1082   // Allocate space for creation of Buffer
   1083   //
   1084 
   1085   //
   1086   // Init OpCode Handle
   1087   //
   1088   StartOpCodeHandle = HiiAllocateOpCodeHandle ();
   1089   ASSERT (StartOpCodeHandle != NULL);
   1090 
   1091   EndOpCodeHandle = HiiAllocateOpCodeHandle ();
   1092   ASSERT (EndOpCodeHandle != NULL);
   1093 
   1094   //
   1095   // Create Hii Extend Label OpCode as the start opcode
   1096   //
   1097   StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
   1098   StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
   1099   StartLabel->Number       = DEVICE_ENTRY_LABEL;
   1100 
   1101   //
   1102   // Create Hii Extend Label OpCode as the end opcode
   1103   //
   1104   EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
   1105   EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
   1106   EndLabel->Number       = LABEL_END;
   1107 
   1108   FormIndex = 0;
   1109   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
   1110     ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
   1111 
   1112     HiiCreateGotoOpCode (
   1113       StartOpCodeHandle,                            // Container for dynamic created opcodes
   1114       FORMID_DEVICE_FORM,                           // Target Form ID
   1115       ConfigFormEntry->PortTitleToken,              // Prompt text
   1116       ConfigFormEntry->PortTitleHelpToken,          // Help text
   1117       EFI_IFR_FLAG_CALLBACK,                        // Question flag
   1118       (UINT16)(KEY_DEVICE_ENTRY_BASE + FormIndex)   // Question ID
   1119       );
   1120 
   1121     FormIndex++;
   1122   }
   1123 
   1124   HiiUpdateForm (
   1125     mCallbackInfo->RegisteredHandle,
   1126     &gIp4IScsiConfigGuid,
   1127     FORMID_MAIN_FORM,
   1128     StartOpCodeHandle, // Label DEVICE_ENTRY_LABEL
   1129     EndOpCodeHandle    // LABEL_END
   1130     );
   1131 
   1132   HiiFreeOpCodeHandle (StartOpCodeHandle);
   1133   HiiFreeOpCodeHandle (EndOpCodeHandle);
   1134 
   1135   return EFI_SUCCESS;
   1136 }
   1137 
   1138 /**
   1139   Initialize the iSCSI configuration form.
   1140 
   1141   @param[in]  DriverBindingHandle  The iSCSI driverbinding handle.
   1142 
   1143   @retval EFI_SUCCESS              The iSCSI configuration form is initialized.
   1144   @retval EFI_OUT_OF_RESOURCES     Failed to allocate memory.
   1145   @retval Others                   Other errors as indicated.
   1146 **/
   1147 EFI_STATUS
   1148 IScsiConfigFormInit (
   1149   VOID
   1150   )
   1151 {
   1152   EFI_STATUS                  Status;
   1153   EFI_HII_DATABASE_PROTOCOL   *HiiDatabase;
   1154   ISCSI_FORM_CALLBACK_INFO    *CallbackInfo;
   1155 
   1156   Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **)&HiiDatabase);
   1157   if (EFI_ERROR (Status)) {
   1158     return Status;
   1159   }
   1160 
   1161   CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO));
   1162   if (CallbackInfo == NULL) {
   1163     return EFI_OUT_OF_RESOURCES;
   1164   }
   1165 
   1166   CallbackInfo->Signature   = ISCSI_FORM_CALLBACK_INFO_SIGNATURE;
   1167   CallbackInfo->HiiDatabase = HiiDatabase;
   1168   CallbackInfo->Current     = NULL;
   1169 
   1170   CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig;
   1171   CallbackInfo->ConfigAccess.RouteConfig = IScsiFormRouteConfig;
   1172   CallbackInfo->ConfigAccess.Callback = IScsiFormCallback;
   1173 
   1174   Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **)&CallbackInfo->ConfigRouting);
   1175   if (EFI_ERROR (Status)) {
   1176     FreePool(CallbackInfo);
   1177     return Status;
   1178   }
   1179 
   1180   //
   1181   // Install Device Path Protocol and Config Access protocol to driver handle
   1182   //
   1183   Status = gBS->InstallMultipleProtocolInterfaces (
   1184                   &CallbackInfo->DriverHandle,
   1185                   &gEfiDevicePathProtocolGuid,
   1186                   &mIScsiHiiVendorDevicePath,
   1187                   &gEfiHiiConfigAccessProtocolGuid,
   1188                   &CallbackInfo->ConfigAccess,
   1189                   NULL
   1190                   );
   1191   ASSERT_EFI_ERROR (Status);
   1192 
   1193   //
   1194   // Publish our HII data
   1195   //
   1196   CallbackInfo->RegisteredHandle = HiiAddPackages (
   1197                                      &gIp4IScsiConfigGuid,
   1198                                      CallbackInfo->DriverHandle,
   1199                                      IScsi4DxeStrings,
   1200                                      IScsiConfigDxeBin,
   1201                                      NULL
   1202                                      );
   1203   if (CallbackInfo->RegisteredHandle == NULL) {
   1204     FreePool(CallbackInfo);
   1205     return EFI_OUT_OF_RESOURCES;
   1206   }
   1207 
   1208   mCallbackInfo = CallbackInfo;
   1209 
   1210   return Status;
   1211 }
   1212 
   1213 /**
   1214   Unload the iSCSI configuration form, this includes: delete all the iSCSI
   1215   device configuration entries, uninstall the form callback protocol and
   1216   free the resources used.
   1217 
   1218   @param[in]  DriverBindingHandle The iSCSI driverbinding handle.
   1219 
   1220   @retval EFI_SUCCESS             The iSCSI configuration form is unloaded.
   1221   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
   1222 **/
   1223 EFI_STATUS
   1224 IScsiConfigFormUnload (
   1225   IN EFI_HANDLE  DriverBindingHandle
   1226   )
   1227 {
   1228   ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;
   1229 
   1230   while (!IsListEmpty (&mIScsiConfigFormList)) {
   1231     //
   1232     // Uninstall the device forms as the iSCSI driver instance may fail to
   1233     // control the controller but still install the device configuration form.
   1234     // In such case, upon driver unloading, the driver instance's driverbinding.
   1235     // stop () won't be called, so we have to take this chance here to uninstall
   1236     // the device form.
   1237     //
   1238     ConfigFormEntry = NET_LIST_USER_STRUCT (mIScsiConfigFormList.ForwardLink, ISCSI_CONFIG_FORM_ENTRY, Link);
   1239     IScsiConfigUpdateForm (DriverBindingHandle, ConfigFormEntry->Controller, FALSE);
   1240   }
   1241 
   1242   //
   1243   // Remove HII package list
   1244   //
   1245   mCallbackInfo->HiiDatabase->RemovePackageList (
   1246                                 mCallbackInfo->HiiDatabase,
   1247                                 mCallbackInfo->RegisteredHandle
   1248                                 );
   1249 
   1250   //
   1251   // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
   1252   //
   1253   gBS->UninstallMultipleProtocolInterfaces (
   1254          mCallbackInfo->DriverHandle,
   1255          &gEfiDevicePathProtocolGuid,
   1256          &mIScsiHiiVendorDevicePath,
   1257          &gEfiHiiConfigAccessProtocolGuid,
   1258          &mCallbackInfo->ConfigAccess,
   1259          NULL
   1260          );
   1261   FreePool (mCallbackInfo);
   1262 
   1263   return EFI_SUCCESS;
   1264 }
   1265