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 - 2015, 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   // Retrive 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) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {
    731         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
    732         Status = EFI_INVALID_PARAMETER;
    733       } else {
    734         CopyMem (&Private->Current->SessionConfigData.LocalIp, &HostIp.v4, sizeof (HostIp.v4));
    735       }
    736 
    737       break;
    738 
    739     case KEY_SUBNET_MASK:
    740       IScsiUnicodeStrToAsciiStr (IfrNvData->SubnetMask, Ip4String);
    741       Status = IScsiAsciiStrToIp (Ip4String, &SubnetMask.v4);
    742       if (EFI_ERROR (Status) || ((SubnetMask.Addr[0] != 0) && (IScsiGetSubnetMaskPrefixLength (&SubnetMask.v4) == 0))) {
    743         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Subnet Mask!", NULL);
    744         Status = EFI_INVALID_PARAMETER;
    745       } else {
    746         CopyMem (&Private->Current->SessionConfigData.SubnetMask, &SubnetMask.v4, sizeof (SubnetMask.v4));
    747       }
    748 
    749       break;
    750 
    751     case KEY_GATE_WAY:
    752       IScsiUnicodeStrToAsciiStr (IfrNvData->Gateway, Ip4String);
    753       Status = IScsiAsciiStrToIp (Ip4String, &Gateway.v4);
    754       if (EFI_ERROR (Status) || ((Gateway.Addr[0] != 0) && !NetIp4IsUnicast (NTOHL (Gateway.Addr[0]), 0))) {
    755         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid Gateway!", NULL);
    756         Status = EFI_INVALID_PARAMETER;
    757       } else {
    758         CopyMem (&Private->Current->SessionConfigData.Gateway, &Gateway.v4, sizeof (Gateway.v4));
    759       }
    760 
    761       break;
    762 
    763     case KEY_TARGET_IP:
    764       IScsiUnicodeStrToAsciiStr (IfrNvData->TargetIp, Ip4String);
    765       Status = IScsiAsciiStrToIp (Ip4String, &HostIp.v4);
    766       if (EFI_ERROR (Status) || !NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {
    767         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid IP address!", NULL);
    768         Status = EFI_INVALID_PARAMETER;
    769       } else {
    770         CopyMem (&Private->Current->SessionConfigData.TargetIp, &HostIp.v4, sizeof (HostIp.v4));
    771       }
    772 
    773       break;
    774 
    775     case KEY_TARGET_NAME:
    776       IScsiUnicodeStrToAsciiStr (IfrNvData->TargetName, IScsiName);
    777       Status = IScsiNormalizeName (IScsiName, AsciiStrLen (IScsiName));
    778       if (EFI_ERROR (Status)) {
    779         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid iSCSI Name!", NULL);
    780       } else {
    781         AsciiStrCpyS (Private->Current->SessionConfigData.TargetName, ISCSI_NAME_MAX_SIZE, IScsiName);
    782       }
    783 
    784       break;
    785 
    786     case KEY_DHCP_ENABLE:
    787       if (IfrNvData->InitiatorInfoFromDhcp == 0) {
    788         IfrNvData->TargetInfoFromDhcp = 0;
    789       }
    790 
    791       break;
    792 
    793     case KEY_BOOT_LUN:
    794       IScsiUnicodeStrToAsciiStr (IfrNvData->BootLun, LunString);
    795       Status = IScsiAsciiStrToLun (LunString, (UINT8 *) &Lun);
    796       if (EFI_ERROR (Status)) {
    797         CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Invalid LUN string!", NULL);
    798       } else {
    799         CopyMem (Private->Current->SessionConfigData.BootLun, &Lun, sizeof (Lun));
    800       }
    801 
    802       break;
    803 
    804     case KEY_CHAP_NAME:
    805       IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPName, Private->Current->AuthConfigData.CHAPName);
    806       break;
    807 
    808     case KEY_CHAP_SECRET:
    809       IScsiUnicodeStrToAsciiStr (IfrNvData->CHAPSecret, Private->Current->AuthConfigData.CHAPSecret);
    810       break;
    811 
    812     case KEY_REVERSE_CHAP_NAME:
    813       IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPName, Private->Current->AuthConfigData.ReverseCHAPName);
    814       break;
    815 
    816     case KEY_REVERSE_CHAP_SECRET:
    817       IScsiUnicodeStrToAsciiStr (IfrNvData->ReverseCHAPSecret, Private->Current->AuthConfigData.ReverseCHAPSecret);
    818       break;
    819 
    820     case KEY_CONFIG_ISID:
    821       IScsiParseIsIdFromString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
    822       IScsiConvertIsIdToString (IfrNvData->IsId, Private->Current->SessionConfigData.IsId);
    823 
    824       break;
    825 
    826     case KEY_SAVE_CHANGES:
    827       //
    828       // First, update those fields which don't have INTERACTIVE set.
    829       //
    830       Private->Current->SessionConfigData.Enabled               = IfrNvData->Enabled;
    831       Private->Current->SessionConfigData.InitiatorInfoFromDhcp = IfrNvData->InitiatorInfoFromDhcp;
    832       Private->Current->SessionConfigData.TargetPort            = IfrNvData->TargetPort;
    833       if (Private->Current->SessionConfigData.TargetPort == 0) {
    834         Private->Current->SessionConfigData.TargetPort = ISCSI_WELL_KNOWN_PORT;
    835       }
    836 
    837       Private->Current->SessionConfigData.TargetInfoFromDhcp  = IfrNvData->TargetInfoFromDhcp;
    838       Private->Current->AuthConfigData.CHAPType               = IfrNvData->CHAPType;
    839 
    840       //
    841       // Only do full parameter validation if iSCSI is enabled on this device.
    842       //
    843       if (Private->Current->SessionConfigData.Enabled) {
    844         //
    845         // Validate the address configuration of the Initiator if DHCP isn't
    846         // deployed.
    847         //
    848         if (!Private->Current->SessionConfigData.InitiatorInfoFromDhcp) {
    849           CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.LocalIp, sizeof (HostIp.v4));
    850           CopyMem (&SubnetMask.v4, &Private->Current->SessionConfigData.SubnetMask, sizeof (SubnetMask.v4));
    851           CopyMem (&Gateway.v4, &Private->Current->SessionConfigData.Gateway, sizeof (Gateway.v4));
    852 
    853           if ((Gateway.Addr[0] != 0)) {
    854             if (SubnetMask.Addr[0] == 0) {
    855               CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Gateway address is set but subnet mask is zero.", NULL);
    856               Status = EFI_INVALID_PARAMETER;
    857               break;
    858             } else if (!IP4_NET_EQUAL (HostIp.Addr[0], Gateway.Addr[0], SubnetMask.Addr[0])) {
    859               CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Local IP and Gateway are not in the same subnet.", NULL);
    860               Status = EFI_INVALID_PARAMETER;
    861               break;
    862             }
    863           }
    864         }
    865         //
    866         // Validate target configuration if DHCP isn't deployed.
    867         //
    868         if (!Private->Current->SessionConfigData.TargetInfoFromDhcp) {
    869           CopyMem (&HostIp.v4, &Private->Current->SessionConfigData.TargetIp, sizeof (HostIp.v4));
    870           if (!NetIp4IsUnicast (NTOHL (HostIp.Addr[0]), 0)) {
    871             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Target IP is invalid!", NULL);
    872             Status = EFI_INVALID_PARAMETER;
    873             break;
    874           }
    875 
    876           //
    877           // Validate iSCSI target name configuration again:
    878           // The format of iSCSI target name is already verified when user input the name;
    879           // here we only check the case user does not input the name.
    880           //
    881           if (Private->Current->SessionConfigData.TargetName[0] == '\0') {
    882             CreatePopUp (
    883               EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
    884               &Key,
    885               L"iSCSI target name is NULL!",
    886               NULL
    887               );
    888             Status = EFI_INVALID_PARAMETER;
    889             break;
    890           }
    891 
    892         }
    893 
    894         if (IfrNvData->CHAPType != ISCSI_CHAP_NONE) {
    895           if ((IfrNvData->CHAPName[0] == '\0') || (IfrNvData->CHAPSecret[0] == '\0')) {
    896             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"CHAP Name or CHAP Secret is invalid!", NULL);
    897             Status = EFI_INVALID_PARAMETER;
    898             break;
    899           }
    900 
    901           if ((IfrNvData->CHAPType == ISCSI_CHAP_MUTUAL) &&
    902               ((IfrNvData->ReverseCHAPName[0] == '\0') || (IfrNvData->ReverseCHAPSecret[0] == '\0'))
    903               ) {
    904             CreatePopUp (EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE, &Key, L"Reverse CHAP Name or Reverse CHAP Secret is invalid!", NULL);
    905             Status = EFI_INVALID_PARAMETER;
    906             break;
    907           }
    908         }
    909       }
    910 
    911       BufferSize = sizeof (Private->Current->SessionConfigData);
    912       gRT->SetVariable (
    913             Private->Current->MacString,
    914             &gEfiIScsiInitiatorNameProtocolGuid,
    915             ISCSI_CONFIG_VAR_ATTR,
    916             BufferSize,
    917             &Private->Current->SessionConfigData
    918             );
    919 
    920       BufferSize = sizeof (Private->Current->AuthConfigData);
    921       gRT->SetVariable (
    922             Private->Current->MacString,
    923             &gIScsiCHAPAuthInfoGuid,
    924             ISCSI_CONFIG_VAR_ATTR,
    925             BufferSize,
    926             &Private->Current->AuthConfigData
    927             );
    928       *ActionRequest = EFI_BROWSER_ACTION_REQUEST_FORM_APPLY;
    929       break;
    930 
    931     default:
    932       break;
    933     }
    934   }
    935 
    936   if (!EFI_ERROR (Status)) {
    937     //
    938     // Pass changed uncommitted data back to Form Browser
    939     //
    940     HiiSetBrowserData (&gIp4IScsiConfigGuid, mVendorStorageName, sizeof (ISCSI_CONFIG_IFR_NVDATA), (UINT8 *) IfrNvData, NULL);
    941   }
    942 
    943   FreePool (IfrNvData);
    944 
    945   return Status;
    946 }
    947 
    948 /**
    949   Updates the iSCSI configuration form to add/delete an entry for the iSCSI
    950   device specified by the Controller.
    951 
    952   @param[in]  DriverBindingHandle The driverbinding handle.
    953   @param[in]  Controller          The controller handle of the iSCSI device.
    954   @param[in]  AddForm             Whether to add or delete a form entry.
    955 
    956   @retval EFI_SUCCESS             The iSCSI configuration form is updated.
    957   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
    958   @retval Others                  Other errors as indicated.
    959 **/
    960 EFI_STATUS
    961 IScsiConfigUpdateForm (
    962   IN EFI_HANDLE  DriverBindingHandle,
    963   IN EFI_HANDLE  Controller,
    964   IN BOOLEAN     AddForm
    965   )
    966 {
    967   LIST_ENTRY                  *Entry;
    968   ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;
    969   BOOLEAN                     EntryExisted;
    970   EFI_STATUS                  Status;
    971   EFI_MAC_ADDRESS             MacAddress;
    972   UINTN                       HwAddressSize;
    973   UINT16                      VlanId;
    974   CHAR16                      PortString[128];
    975   UINT16                      FormIndex;
    976   UINTN                       BufferSize;
    977   VOID                        *StartOpCodeHandle;
    978   VOID                        *EndOpCodeHandle;
    979   EFI_IFR_GUID_LABEL          *StartLabel;
    980   EFI_IFR_GUID_LABEL          *EndLabel;
    981 
    982   ConfigFormEntry = NULL;
    983   EntryExisted    = FALSE;
    984 
    985   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
    986     ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
    987 
    988     if (ConfigFormEntry->Controller == Controller) {
    989       EntryExisted = TRUE;
    990       break;
    991     }
    992   }
    993 
    994   if (AddForm) {
    995     if (EntryExisted) {
    996       return EFI_SUCCESS;
    997     } else {
    998       //
    999       // Add a new form.
   1000       //
   1001       ConfigFormEntry = (ISCSI_CONFIG_FORM_ENTRY *) AllocateZeroPool (sizeof (ISCSI_CONFIG_FORM_ENTRY));
   1002       if (ConfigFormEntry == NULL) {
   1003         return EFI_OUT_OF_RESOURCES;
   1004       }
   1005 
   1006       InitializeListHead (&ConfigFormEntry->Link);
   1007       ConfigFormEntry->Controller = Controller;
   1008 
   1009       //
   1010       // Get the MAC address and convert it into the formatted string.
   1011       //
   1012       Status = NetLibGetMacAddress (Controller, &MacAddress, &HwAddressSize);
   1013       ASSERT (Status == EFI_SUCCESS);
   1014       VlanId = NetLibGetVlanId (Controller);
   1015 
   1016       IScsiMacAddrToStr (&MacAddress, (UINT32) HwAddressSize, VlanId, ConfigFormEntry->MacString);
   1017 
   1018       //
   1019       // Get the normal session configuration data.
   1020       //
   1021       BufferSize = sizeof (ConfigFormEntry->SessionConfigData);
   1022       Status = gRT->GetVariable (
   1023                       ConfigFormEntry->MacString,
   1024                       &gEfiIScsiInitiatorNameProtocolGuid,
   1025                       NULL,
   1026                       &BufferSize,
   1027                       &ConfigFormEntry->SessionConfigData
   1028                       );
   1029       if (EFI_ERROR (Status)) {
   1030         ZeroMem (&ConfigFormEntry->SessionConfigData, sizeof (ConfigFormEntry->SessionConfigData));
   1031 
   1032         //
   1033         // Generate OUI-format ISID based on MAC address.
   1034         //
   1035         CopyMem (ConfigFormEntry->SessionConfigData.IsId, &MacAddress, 6);
   1036         ConfigFormEntry->SessionConfigData.IsId[0] =
   1037           (UINT8) (ConfigFormEntry->SessionConfigData.IsId[0] & 0x3F);
   1038       }
   1039       //
   1040       // Get the CHAP authentication configuration data.
   1041       //
   1042       BufferSize = sizeof (ConfigFormEntry->AuthConfigData);
   1043       Status = gRT->GetVariable (
   1044                       ConfigFormEntry->MacString,
   1045                       &gIScsiCHAPAuthInfoGuid,
   1046                       NULL,
   1047                       &BufferSize,
   1048                       &ConfigFormEntry->AuthConfigData
   1049                       );
   1050       if (EFI_ERROR (Status)) {
   1051         ZeroMem (&ConfigFormEntry->AuthConfigData, sizeof (ConfigFormEntry->AuthConfigData));
   1052       }
   1053       //
   1054       // Compose the Port string and create a new EFI_STRING_ID.
   1055       //
   1056       UnicodeSPrint (PortString, sizeof (PortString), L"Port %s", ConfigFormEntry->MacString);
   1057       ConfigFormEntry->PortTitleToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);
   1058 
   1059       //
   1060       // Compose the help string of this port and create a new EFI_STRING_ID.
   1061       //
   1062       UnicodeSPrint (PortString, sizeof (PortString), L"Set the iSCSI parameters on port %s", ConfigFormEntry->MacString);
   1063       ConfigFormEntry->PortTitleHelpToken = HiiSetString (mCallbackInfo->RegisteredHandle, 0, PortString, NULL);
   1064 
   1065       InsertTailList (&mIScsiConfigFormList, &ConfigFormEntry->Link);
   1066       mNumberOfIScsiDevices++;
   1067     }
   1068   } else {
   1069     ASSERT (EntryExisted);
   1070 
   1071     mNumberOfIScsiDevices--;
   1072     RemoveEntryList (&ConfigFormEntry->Link);
   1073     FreePool (ConfigFormEntry);
   1074   }
   1075   //
   1076   // Allocate space for creation of Buffer
   1077   //
   1078 
   1079   //
   1080   // Init OpCode Handle
   1081   //
   1082   StartOpCodeHandle = HiiAllocateOpCodeHandle ();
   1083   ASSERT (StartOpCodeHandle != NULL);
   1084 
   1085   EndOpCodeHandle = HiiAllocateOpCodeHandle ();
   1086   ASSERT (EndOpCodeHandle != NULL);
   1087 
   1088   //
   1089   // Create Hii Extend Label OpCode as the start opcode
   1090   //
   1091   StartLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
   1092   StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
   1093   StartLabel->Number       = DEVICE_ENTRY_LABEL;
   1094 
   1095   //
   1096   // Create Hii Extend Label OpCode as the end opcode
   1097   //
   1098   EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
   1099   EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
   1100   EndLabel->Number       = LABEL_END;
   1101 
   1102   FormIndex = 0;
   1103   NET_LIST_FOR_EACH (Entry, &mIScsiConfigFormList) {
   1104     ConfigFormEntry = NET_LIST_USER_STRUCT (Entry, ISCSI_CONFIG_FORM_ENTRY, Link);
   1105 
   1106     HiiCreateGotoOpCode (
   1107       StartOpCodeHandle,                            // Container for dynamic created opcodes
   1108       FORMID_DEVICE_FORM,                           // Target Form ID
   1109       ConfigFormEntry->PortTitleToken,              // Prompt text
   1110       ConfigFormEntry->PortTitleHelpToken,          // Help text
   1111       EFI_IFR_FLAG_CALLBACK,                        // Question flag
   1112       (UINT16)(KEY_DEVICE_ENTRY_BASE + FormIndex)   // Question ID
   1113       );
   1114 
   1115     FormIndex++;
   1116   }
   1117 
   1118   HiiUpdateForm (
   1119     mCallbackInfo->RegisteredHandle,
   1120     &gIp4IScsiConfigGuid,
   1121     FORMID_MAIN_FORM,
   1122     StartOpCodeHandle, // Label DEVICE_ENTRY_LABEL
   1123     EndOpCodeHandle    // LABEL_END
   1124     );
   1125 
   1126   HiiFreeOpCodeHandle (StartOpCodeHandle);
   1127   HiiFreeOpCodeHandle (EndOpCodeHandle);
   1128 
   1129   return EFI_SUCCESS;
   1130 }
   1131 
   1132 /**
   1133   Initialize the iSCSI configuration form.
   1134 
   1135   @param[in]  DriverBindingHandle  The iSCSI driverbinding handle.
   1136 
   1137   @retval EFI_SUCCESS              The iSCSI configuration form is initialized.
   1138   @retval EFI_OUT_OF_RESOURCES     Failed to allocate memory.
   1139   @retval Others                   Other errors as indicated.
   1140 **/
   1141 EFI_STATUS
   1142 IScsiConfigFormInit (
   1143   VOID
   1144   )
   1145 {
   1146   EFI_STATUS                  Status;
   1147   EFI_HII_DATABASE_PROTOCOL   *HiiDatabase;
   1148   ISCSI_FORM_CALLBACK_INFO    *CallbackInfo;
   1149 
   1150   Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, (VOID **)&HiiDatabase);
   1151   if (EFI_ERROR (Status)) {
   1152     return Status;
   1153   }
   1154 
   1155   CallbackInfo = (ISCSI_FORM_CALLBACK_INFO *) AllocateZeroPool (sizeof (ISCSI_FORM_CALLBACK_INFO));
   1156   if (CallbackInfo == NULL) {
   1157     return EFI_OUT_OF_RESOURCES;
   1158   }
   1159 
   1160   CallbackInfo->Signature   = ISCSI_FORM_CALLBACK_INFO_SIGNATURE;
   1161   CallbackInfo->HiiDatabase = HiiDatabase;
   1162   CallbackInfo->Current     = NULL;
   1163 
   1164   CallbackInfo->ConfigAccess.ExtractConfig = IScsiFormExtractConfig;
   1165   CallbackInfo->ConfigAccess.RouteConfig = IScsiFormRouteConfig;
   1166   CallbackInfo->ConfigAccess.Callback = IScsiFormCallback;
   1167 
   1168   Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, (VOID **)&CallbackInfo->ConfigRouting);
   1169   if (EFI_ERROR (Status)) {
   1170     FreePool(CallbackInfo);
   1171     return Status;
   1172   }
   1173 
   1174   //
   1175   // Install Device Path Protocol and Config Access protocol to driver handle
   1176   //
   1177   Status = gBS->InstallMultipleProtocolInterfaces (
   1178                   &CallbackInfo->DriverHandle,
   1179                   &gEfiDevicePathProtocolGuid,
   1180                   &mIScsiHiiVendorDevicePath,
   1181                   &gEfiHiiConfigAccessProtocolGuid,
   1182                   &CallbackInfo->ConfigAccess,
   1183                   NULL
   1184                   );
   1185   ASSERT_EFI_ERROR (Status);
   1186 
   1187   //
   1188   // Publish our HII data
   1189   //
   1190   CallbackInfo->RegisteredHandle = HiiAddPackages (
   1191                                      &gIp4IScsiConfigGuid,
   1192                                      CallbackInfo->DriverHandle,
   1193                                      IScsi4DxeStrings,
   1194                                      IScsiConfigDxeBin,
   1195                                      NULL
   1196                                      );
   1197   if (CallbackInfo->RegisteredHandle == NULL) {
   1198     FreePool(CallbackInfo);
   1199     return EFI_OUT_OF_RESOURCES;
   1200   }
   1201 
   1202   mCallbackInfo = CallbackInfo;
   1203 
   1204   return Status;
   1205 }
   1206 
   1207 /**
   1208   Unload the iSCSI configuration form, this includes: delete all the iSCSI
   1209   device configuration entries, uninstall the form callback protocol and
   1210   free the resources used.
   1211 
   1212   @param[in]  DriverBindingHandle The iSCSI driverbinding handle.
   1213 
   1214   @retval EFI_SUCCESS             The iSCSI configuration form is unloaded.
   1215   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
   1216 **/
   1217 EFI_STATUS
   1218 IScsiConfigFormUnload (
   1219   IN EFI_HANDLE  DriverBindingHandle
   1220   )
   1221 {
   1222   ISCSI_CONFIG_FORM_ENTRY     *ConfigFormEntry;
   1223 
   1224   while (!IsListEmpty (&mIScsiConfigFormList)) {
   1225     //
   1226     // Uninstall the device forms as the iSCSI driver instance may fail to
   1227     // control the controller but still install the device configuration form.
   1228     // In such case, upon driver unloading, the driver instance's driverbinding.
   1229     // stop () won't be called, so we have to take this chance here to uninstall
   1230     // the device form.
   1231     //
   1232     ConfigFormEntry = NET_LIST_USER_STRUCT (mIScsiConfigFormList.ForwardLink, ISCSI_CONFIG_FORM_ENTRY, Link);
   1233     IScsiConfigUpdateForm (DriverBindingHandle, ConfigFormEntry->Controller, FALSE);
   1234   }
   1235 
   1236   //
   1237   // Remove HII package list
   1238   //
   1239   mCallbackInfo->HiiDatabase->RemovePackageList (
   1240                                 mCallbackInfo->HiiDatabase,
   1241                                 mCallbackInfo->RegisteredHandle
   1242                                 );
   1243 
   1244   //
   1245   // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
   1246   //
   1247   gBS->UninstallMultipleProtocolInterfaces (
   1248          mCallbackInfo->DriverHandle,
   1249          &gEfiDevicePathProtocolGuid,
   1250          &mIScsiHiiVendorDevicePath,
   1251          &gEfiHiiConfigAccessProtocolGuid,
   1252          &mCallbackInfo->ConfigAccess,
   1253          NULL
   1254          );
   1255   FreePool (mCallbackInfo);
   1256 
   1257   return EFI_SUCCESS;
   1258 }
   1259