1 /** @file
2   Helper functions for configuring or getting the parameters relating to HTTP Boot.
3 
4 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "HttpBootDxe.h"
10 #include <Library/UefiBootManagerLib.h>
11 
12 CHAR16  mHttpBootConfigStorageName[]     = L"HTTP_BOOT_CONFIG_IFR_NVDATA";
13 
14 /**
15   Add new boot option for HTTP boot.
16 
17   @param[in]  Private             Pointer to the driver private data.
18   @param[in]  UsingIpv6           Set to TRUE if creating boot option for IPv6.
19   @param[in]  Description         The description text of the boot option.
20   @param[in]  Uri                 The URI string of the boot file.
21 
22   @retval EFI_SUCCESS             The boot option is created successfully.
23   @retval Others                  Failed to create new boot option.
24 
25 **/
26 EFI_STATUS
27 HttpBootAddBootOption (
28   IN   HTTP_BOOT_PRIVATE_DATA   *Private,
29   IN   BOOLEAN                  UsingIpv6,
30   IN   CHAR16                   *Description,
31   IN   CHAR16                   *Uri
32   )
33 {
34   EFI_DEV_PATH                      *Node;
35   EFI_DEVICE_PATH_PROTOCOL          *TmpDevicePath;
36   EFI_DEVICE_PATH_PROTOCOL          *NewDevicePath;
37   UINTN                             Length;
38   CHAR8                             AsciiUri[URI_STR_MAX_SIZE];
39   EFI_STATUS                        Status;
40   UINTN                             Index;
41   EFI_BOOT_MANAGER_LOAD_OPTION      NewOption;
42 
43   NewDevicePath = NULL;
44   Node          = NULL;
45   TmpDevicePath = NULL;
46 
47   if (StrLen (Description) == 0) {
48     return EFI_INVALID_PARAMETER;
49   }
50 
51   //
52   // Convert the scheme to all lower case.
53   //
54   for (Index = 0; Index < StrLen (Uri); Index++) {
55     if (Uri[Index] == L':') {
56       break;
57     }
58     if (Uri[Index] >= L'A' && Uri[Index] <= L'Z') {
59       Uri[Index] -= (CHAR16)(L'A' - L'a');
60     }
61   }
62 
63   //
64   // Only accept empty URI, or http and https URI.
65   //
66   if ((StrLen (Uri) != 0) && (StrnCmp (Uri, L"http://", 7) != 0) && (StrnCmp (Uri, L"https://", 8) != 0)) {
67     return EFI_INVALID_PARAMETER;
68   }
69 
70   //
71   // Create a new device path by appending the IP node and URI node to
72   // the driver's parent device path
73   //
74   if (!UsingIpv6) {
75     Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
76     if (Node == NULL) {
77       Status = EFI_OUT_OF_RESOURCES;
78       goto ON_EXIT;
79     }
80     Node->Ipv4.Header.Type    = MESSAGING_DEVICE_PATH;
81     Node->Ipv4.Header.SubType = MSG_IPv4_DP;
82     SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
83   } else {
84     Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
85     if (Node == NULL) {
86       Status = EFI_OUT_OF_RESOURCES;
87       goto ON_EXIT;
88     }
89     Node->Ipv6.Header.Type     = MESSAGING_DEVICE_PATH;
90     Node->Ipv6.Header.SubType  = MSG_IPv6_DP;
91     SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
92   }
93   TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
94   FreePool (Node);
95   if (TmpDevicePath == NULL) {
96     return EFI_OUT_OF_RESOURCES;
97   }
98   //
99   // Update the URI node with the input boot file URI.
100   //
101   UnicodeStrToAsciiStrS (Uri, AsciiUri, sizeof (AsciiUri));
102   Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (AsciiUri);
103   Node = AllocatePool (Length);
104   if (Node == NULL) {
105     Status = EFI_OUT_OF_RESOURCES;
106     FreePool (TmpDevicePath);
107     goto ON_EXIT;
108   }
109   Node->DevPath.Type    = MESSAGING_DEVICE_PATH;
110   Node->DevPath.SubType = MSG_URI_DP;
111   SetDevicePathNodeLength (Node, Length);
112   CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), AsciiUri, AsciiStrSize (AsciiUri));
113   NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
114   FreePool (Node);
115   FreePool (TmpDevicePath);
116   if (NewDevicePath == NULL) {
117     Status = EFI_OUT_OF_RESOURCES;
118     goto ON_EXIT;
119   }
120 
121   //
122   // Add a new load option.
123   //
124   Status = EfiBootManagerInitializeLoadOption (
125                  &NewOption,
126                  LoadOptionNumberUnassigned,
127                  LoadOptionTypeBoot,
128                  LOAD_OPTION_ACTIVE,
129                  Description,
130                  NewDevicePath,
131                  NULL,
132                  0
133                  );
134   if (EFI_ERROR (Status)) {
135     goto ON_EXIT;
136   }
137 
138   Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1);
139   EfiBootManagerFreeLoadOption (&NewOption);
140 
141 ON_EXIT:
142 
143   if (NewDevicePath != NULL) {
144     FreePool (NewDevicePath);
145   }
146 
147   return Status;
148 }
149 
150 /**
151 
152   This function allows the caller to request the current
153   configuration for one or more named elements. The resulting
154   string is in <ConfigAltResp> format. Also, any and all alternative
155   configuration strings shall be appended to the end of the
156   current configuration string. If they are, they must appear
157   after the current configuration. They must contain the same
158   routing (GUID, NAME, PATH) as the current configuration string.
159   They must have an additional description indicating the type of
160   alternative configuration the string represents,
161   "ALTCFG=<StringToken>". That <StringToken> (when
162   converted from Hex UNICODE to binary) is a reference to a
163   string in the associated string pack.
164 
165   @param[in]  This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
166 
167   @param[in]  Request    A null-terminated Unicode string in
168                          <ConfigRequest> format. Note that this
169                          includes the routing information as well as
170                          the configurable name / value pairs. It is
171                          invalid for this string to be in
172                          <MultiConfigRequest> format.
173 
174   @param[out] Progress   On return, points to a character in the
175                          Request string. Points to the string's null
176                          terminator if request was successful. Points
177                          to the most recent "&" before the first
178                          failing name / value pair (or the beginning
179                          of the string if the failure is in the first
180                          name / value pair) if the request was not successful.
181 
182   @param[out] Results    A null-terminated Unicode string in
183                          <ConfigAltResp> format which has all values
184                          filled in for the names in the Request string.
185                          String to be allocated by the called function.
186 
187   @retval EFI_SUCCESS             The Results string is filled with the
188                                   values corresponding to all requested
189                                   names.
190 
191   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the
192                                   parts of the results that must be
193                                   stored awaiting possible future
194                                   protocols.
195 
196   @retval EFI_INVALID_PARAMETER   For example, passing in a NULL
197                                   for the Request parameter
198                                   would result in this type of
199                                   error. In this case, the
200                                   Progress parameter would be
201                                   set to NULL.
202 
203   @retval EFI_NOT_FOUND           Routing data doesn't match any
204                                   known driver. Progress set to the
205                                   first character in the routing header.
206                                   Note: There is no requirement that the
207                                   driver validate the routing data. It
208                                   must skip the <ConfigHdr> in order to
209                                   process the names.
210 
211   @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set
212                                   to most recent "&" before the
213                                   error or the beginning of the
214                                   string.
215 
216   @retval EFI_INVALID_PARAMETER   Unknown name. Progress points
217                                   to the & before the name in
218                                   question.
219 
220 **/
221 EFI_STATUS
222 EFIAPI
223 HttpBootFormExtractConfig (
224   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
225   IN  CONST EFI_STRING                       Request,
226   OUT EFI_STRING                             *Progress,
227   OUT EFI_STRING                             *Results
228   )
229 {
230   EFI_STATUS                       Status;
231   UINTN                            BufferSize;
232   HTTP_BOOT_FORM_CALLBACK_INFO     *CallbackInfo;
233   EFI_STRING                       ConfigRequestHdr;
234   EFI_STRING                       ConfigRequest;
235   BOOLEAN                          AllocatedRequest;
236   UINTN                            Size;
237 
238   if (Progress == NULL || Results == NULL) {
239     return EFI_INVALID_PARAMETER;
240   }
241 
242   *Progress = Request;
243   if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {
244     return EFI_NOT_FOUND;
245   }
246 
247   ConfigRequestHdr = NULL;
248   ConfigRequest    = NULL;
249   AllocatedRequest = FALSE;
250   Size             = 0;
251 
252   CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
253   //
254   // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
255   //
256   BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);
257   ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);
258   StrCpyS (CallbackInfo->HttpBootNvData.Description, DESCRIPTION_STR_MAX_SIZE / sizeof (CHAR16), HTTP_BOOT_DEFAULT_DESCRIPTION_STR);
259 
260   ConfigRequest = Request;
261   if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
262     //
263     // Request has no request element, construct full request string.
264     // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
265     // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
266     //
267     ConfigRequestHdr = HiiConstructConfigHdr (&gHttpBootConfigGuid, mHttpBootConfigStorageName, CallbackInfo->ChildHandle);
268     Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
269     ConfigRequest = AllocateZeroPool (Size);
270     if (ConfigRequest == NULL) {
271       return EFI_OUT_OF_RESOURCES;
272     }
273     AllocatedRequest = TRUE;
274     UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
275     FreePool (ConfigRequestHdr);
276   }
277 
278   Status = gHiiConfigRouting->BlockToConfig (
279                                 gHiiConfigRouting,
280                                 ConfigRequest,
281                                 (UINT8 *) &CallbackInfo->HttpBootNvData,
282                                 BufferSize,
283                                 Results,
284                                 Progress
285                                 );
286 
287   //
288   // Free the allocated config request string.
289   //
290   if (AllocatedRequest) {
291     FreePool (ConfigRequest);
292     ConfigRequest = NULL;
293   }
294   //
295   // Set Progress string to the original request string.
296   //
297   if (Request == NULL) {
298     *Progress = NULL;
299   } else if (StrStr (Request, L"OFFSET") == NULL) {
300     *Progress = Request + StrLen (Request);
301   }
302 
303   return Status;
304 }
305 
306 /**
307 
308   This function applies changes in a driver's configuration.
309   Input is a Configuration, which has the routing data for this
310   driver followed by name / value configuration pairs. The driver
311   must apply those pairs to its configurable storage. If the
312   driver's configuration is stored in a linear block of data
313   and the driver's name / value pairs are in <BlockConfig>
314   format, it may use the ConfigToBlock helper function (above) to
315   simplify the job.
316 
317   @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
318 
319   @param[in]  Configuration  A null-terminated Unicode string in
320                              <ConfigString> format.
321 
322   @param[out] Progress       A pointer to a string filled in with the
323                              offset of the most recent '&' before the
324                              first failing name / value pair (or the
325                              beginning of the string if the failure
326                              is in the first name / value pair) or
327                              the terminating NULL if all was
328                              successful.
329 
330   @retval EFI_SUCCESS             The results have been distributed or are
331                                   awaiting distribution.
332 
333   @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the
334                                   parts of the results that must be
335                                   stored awaiting possible future
336                                   protocols.
337 
338   @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the
339                                   Results parameter would result
340                                   in this type of error.
341 
342   @retval EFI_NOT_FOUND           Target for the specified routing data
343                                   was not found.
344 
345 **/
346 EFI_STATUS
347 EFIAPI
348 HttpBootFormRouteConfig (
349   IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
350   IN  CONST EFI_STRING                       Configuration,
351   OUT EFI_STRING                             *Progress
352   )
353 {
354   EFI_STATUS                       Status;
355   UINTN                            BufferSize;
356   HTTP_BOOT_FORM_CALLBACK_INFO     *CallbackInfo;
357   HTTP_BOOT_PRIVATE_DATA           *Private;
358 
359   if (Progress == NULL) {
360     return EFI_INVALID_PARAMETER;
361   }
362   *Progress = Configuration;
363 
364   if (Configuration == NULL) {
365     return EFI_INVALID_PARAMETER;
366   }
367 
368   //
369   // Check routing data in <ConfigHdr>.
370   // Note: there is no name for Name/Value storage, only GUID will be checked
371   //
372   if (!HiiIsConfigHdrMatch (Configuration, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {
373     return EFI_NOT_FOUND;
374   }
375 
376   CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
377   Private      = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO (CallbackInfo);
378 
379   BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);
380   ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);
381 
382   Status = gHiiConfigRouting->ConfigToBlock (
383                             gHiiConfigRouting,
384                             Configuration,
385                             (UINT8 *) &CallbackInfo->HttpBootNvData,
386                             &BufferSize,
387                             Progress
388                             );
389   if (EFI_ERROR (Status)) {
390     return Status;
391   }
392 
393   //
394   // Create a new boot option according to the configuration data.
395   //
396   HttpBootAddBootOption (
397     Private,
398     (CallbackInfo->HttpBootNvData.IpVersion == HTTP_BOOT_IP_VERSION_6) ? TRUE : FALSE,
399     CallbackInfo->HttpBootNvData.Description,
400     CallbackInfo->HttpBootNvData.Uri
401     );
402 
403   return EFI_SUCCESS;
404 }
405 
406 /**
407 
408   This function is called to provide results data to the driver.
409   This data consists of a unique key that is used to identify
410   which data is either being passed back or being asked for.
411 
412   @param[in]       This          Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
413   @param[in]       Action        Specifies the type of action taken by the browser.
414   @param[in]       QuestionId    A unique value which is sent to the original
415                                  exporting driver so that it can identify the type
416                                  of data to expect. The format of the data tends to
417                                  vary based on the opcode that generated the callback.
418   @param[in]       Type          The type of value for the question.
419   @param[in, out]  Value         A pointer to the data being sent to the original
420                                  exporting driver.
421   @param[out]      ActionRequest On return, points to the action requested by the
422                                  callback function.
423 
424   @retval EFI_SUCCESS            The callback successfully handled the action.
425   @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the
426                                  variable and its data.
427   @retval EFI_DEVICE_ERROR       The variable could not be saved.
428   @retval EFI_UNSUPPORTED        The specified Action is not supported by the
429                                  callback.
430 **/
431 EFI_STATUS
432 EFIAPI
433 HttpBootFormCallback (
434   IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
435   IN        EFI_BROWSER_ACTION               Action,
436   IN        EFI_QUESTION_ID                  QuestionId,
437   IN        UINT8                            Type,
438   IN OUT    EFI_IFR_TYPE_VALUE               *Value,
439   OUT       EFI_BROWSER_ACTION_REQUEST       *ActionRequest
440   )
441 {
442   EFI_INPUT_KEY                   Key;
443   CHAR16                          *Uri;
444   UINTN                           UriLen;
445   CHAR8                           *AsciiUri;
446   HTTP_BOOT_FORM_CALLBACK_INFO    *CallbackInfo;
447   EFI_STATUS                      Status;
448 
449   Uri      = NULL;
450   UriLen   = 0;
451   AsciiUri = NULL;
452   Status   = EFI_SUCCESS;
453 
454   if (This == NULL || Value == NULL) {
455     return EFI_INVALID_PARAMETER;
456   }
457 
458   CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
459 
460   if (Action != EFI_BROWSER_ACTION_CHANGING) {
461     return EFI_UNSUPPORTED;
462   }
463 
464   switch (QuestionId) {
465   case KEY_INITIATOR_URI:
466     //
467     // Get user input URI string
468     //
469     Uri = HiiGetString (CallbackInfo->RegisteredHandle, Value->string, NULL);
470     if(Uri == NULL) {
471       return EFI_INVALID_PARAMETER;
472     }
473 
474     //
475     // The URI should be either an empty string (for corporate environment) ,or http(s) for home environment.
476     // Pop up a message box for the unsupported URI.
477     //
478     if (StrLen (Uri) != 0) {
479       UriLen = StrLen (Uri) + 1;
480       AsciiUri = AllocateZeroPool (UriLen);
481       if (AsciiUri == NULL) {
482         FreePool (Uri);
483         return EFI_OUT_OF_RESOURCES;
484       }
485 
486       UnicodeStrToAsciiStrS (Uri, AsciiUri, UriLen);
487 
488       Status = HttpBootCheckUriScheme (AsciiUri);
489 
490       if (Status == EFI_INVALID_PARAMETER) {
491 
492         DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status));
493 
494         CreatePopUp (
495           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
496           &Key,
497           L"ERROR: Unsupported URI!",
498           L"Only supports HTTP and HTTPS",
499           NULL
500           );
501       } else if (Status == EFI_ACCESS_DENIED) {
502 
503         DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status));
504 
505         CreatePopUp (
506           EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
507           &Key,
508           L"ERROR: Unsupported URI!",
509           L"HTTP is disabled",
510           NULL
511           );
512       }
513     }
514 
515     if (Uri != NULL) {
516       FreePool (Uri);
517     }
518 
519     if (AsciiUri != NULL) {
520       FreePool (AsciiUri);
521     }
522 
523     break;
524 
525   default:
526     break;
527   }
528 
529   return Status;
530 }
531 
532 /**
533   Initialize the configuration form.
534 
535   @param[in]  Private             Pointer to the driver private data.
536 
537   @retval EFI_SUCCESS             The configuration form is initialized.
538   @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.
539 
540 **/
541 EFI_STATUS
542 HttpBootConfigFormInit (
543   IN HTTP_BOOT_PRIVATE_DATA     *Private
544   )
545 {
546   EFI_STATUS                        Status;
547   HTTP_BOOT_FORM_CALLBACK_INFO      *CallbackInfo;
548   VENDOR_DEVICE_PATH                VendorDeviceNode;
549   CHAR16                            *MacString;
550   CHAR16                            *OldMenuString;
551   CHAR16                            MenuString[128];
552 
553   CallbackInfo = &Private->CallbackInfo;
554 
555   if (CallbackInfo->Initialized) {
556     return EFI_SUCCESS;
557   }
558 
559   CallbackInfo->Signature = HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE;
560 
561   //
562   // Construct device path node for EFI HII Config Access protocol,
563   // which consists of controller physical device path and one hardware
564   // vendor guid node.
565   //
566   ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));
567   VendorDeviceNode.Header.Type    = HARDWARE_DEVICE_PATH;
568   VendorDeviceNode.Header.SubType = HW_VENDOR_DP;
569   CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid);
570   SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));
571   CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (
572                                         Private->ParentDevicePath,
573                                         (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode
574                                         );
575   if (CallbackInfo->HiiVendorDevicePath == NULL) {
576     Status = EFI_OUT_OF_RESOURCES;
577     goto Error;
578   }
579 
580   CallbackInfo->ConfigAccess.ExtractConfig = HttpBootFormExtractConfig;
581   CallbackInfo->ConfigAccess.RouteConfig   = HttpBootFormRouteConfig;
582   CallbackInfo->ConfigAccess.Callback      = HttpBootFormCallback;
583 
584   //
585   // Install Device Path Protocol and Config Access protocol to driver handle.
586   //
587   Status = gBS->InstallMultipleProtocolInterfaces (
588                   &CallbackInfo->ChildHandle,
589                   &gEfiDevicePathProtocolGuid,
590                   CallbackInfo->HiiVendorDevicePath,
591                   &gEfiHiiConfigAccessProtocolGuid,
592                   &CallbackInfo->ConfigAccess,
593                   NULL
594                   );
595   if (EFI_ERROR (Status)) {
596     goto Error;
597   }
598 
599   //
600   // Publish our HII data.
601   //
602   CallbackInfo->RegisteredHandle = HiiAddPackages (
603                                      &gHttpBootConfigGuid,
604                                      CallbackInfo->ChildHandle,
605                                      HttpBootDxeStrings,
606                                      HttpBootConfigVfrBin,
607                                      NULL
608                                      );
609   if (CallbackInfo->RegisteredHandle == NULL) {
610     Status = EFI_OUT_OF_RESOURCES;
611     goto Error;
612   }
613 
614   //
615   // Append MAC string in the menu help string
616   //
617   Status = NetLibGetMacString (Private->Controller, NULL, &MacString);
618   if (!EFI_ERROR (Status)) {
619     OldMenuString = HiiGetString (
620                       CallbackInfo->RegisteredHandle,
621                       STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP),
622                       NULL
623                       );
624     UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);
625     HiiSetString (
626       CallbackInfo->RegisteredHandle,
627       STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP),
628       MenuString,
629       NULL
630       );
631 
632     FreePool (MacString);
633     FreePool (OldMenuString);
634 
635     CallbackInfo->Initialized = TRUE;
636     return EFI_SUCCESS;
637   }
638 
639 Error:
640 
641   HttpBootConfigFormUnload (Private);
642   return Status;
643 }
644 
645 /**
646   Unload the configuration form, this includes: delete all the configuration
647   entries, uninstall the form callback protocol, and free the resources used.
648   The form will only be unload completely when both IP4 and IP6 stack are stopped.
649 
650   @param[in]  Private             Pointer to the driver private data.
651 
652   @retval EFI_SUCCESS             The configuration form is unloaded.
653   @retval Others                  Failed to unload the form.
654 
655 **/
656 EFI_STATUS
657 HttpBootConfigFormUnload (
658   IN HTTP_BOOT_PRIVATE_DATA     *Private
659   )
660 {
661   HTTP_BOOT_FORM_CALLBACK_INFO      *CallbackInfo;
662 
663   if (Private->Ip4Nic != NULL || Private->Ip6Nic != NULL) {
664     //
665     // Only unload the configuration form when both IP4 and IP6 stack are stopped.
666     //
667     return EFI_SUCCESS;
668   }
669 
670   CallbackInfo = &Private->CallbackInfo;
671   if (CallbackInfo->ChildHandle != NULL) {
672     //
673     // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
674     //
675     gBS->UninstallMultipleProtocolInterfaces (
676            CallbackInfo->ChildHandle,
677            &gEfiDevicePathProtocolGuid,
678            CallbackInfo->HiiVendorDevicePath,
679            &gEfiHiiConfigAccessProtocolGuid,
680            &CallbackInfo->ConfigAccess,
681            NULL
682            );
683     CallbackInfo->ChildHandle = NULL;
684   }
685 
686   if (CallbackInfo->HiiVendorDevicePath != NULL) {
687     FreePool (CallbackInfo->HiiVendorDevicePath);
688     CallbackInfo->HiiVendorDevicePath = NULL;
689   }
690 
691   if (CallbackInfo->RegisteredHandle != NULL) {
692     //
693     // Remove HII package list
694     //
695     HiiRemovePackages (CallbackInfo->RegisteredHandle);
696     CallbackInfo->RegisteredHandle = NULL;
697   }
698 
699   return EFI_SUCCESS;
700 }
701