1/** @file
2  Helper functions for configuring or getting the parameters relating to iSCSI.
3
4Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13**/
14
15#include "IScsiImpl.h"
16
17CHAR16          mVendorStorageName[]     = L"ISCSI_CONFIG_IFR_NVDATA";
18BOOLEAN         mIScsiDeviceListUpdated  = FALSE;
19UINTN           mNumberOfIScsiDevices    = 0;
20ISCSI_FORM_CALLBACK_INFO  *mCallbackInfo = NULL;
21
22LIST_ENTRY      mIScsiConfigFormList = {
23  &mIScsiConfigFormList,
24  &mIScsiConfigFormList
25};
26
27HII_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**/
55VOID
56IScsiIpToStr (
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**/
75EFI_STATUS
76IScsiParseIsIdFromString (
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**/
139EFI_STATUS
140IScsiConvertIsIdToString (
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**/
185EFI_STATUS
186IScsiUpdateDeviceList (
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**/
328ISCSI_CONFIG_FORM_ENTRY *
329IScsiGetConfigFormEntryByIndex (
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**/
359VOID
360IScsiConvertDeviceConfigDataToIfrNvData (
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**/
464EFI_STATUS
465EFIAPI
466IScsiFormExtractConfig (
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**/
603EFI_STATUS
604EFIAPI
605IScsiFormRouteConfig (
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**/
654EFI_STATUS
655EFIAPI
656IScsiFormCallback (
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**/
960EFI_STATUS
961IScsiConfigUpdateForm (
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**/
1141EFI_STATUS
1142IScsiConfigFormInit (
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**/
1217EFI_STATUS
1218IScsiConfigFormUnload (
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