]> git.proxmox.com Git - mirror_edk2.git/blobdiff - NetworkPkg/HttpBootDxe/HttpBootConfig.c
NetworkPkg: Add URI configuration form to HTTP boot driver.
[mirror_edk2.git] / NetworkPkg / HttpBootDxe / HttpBootConfig.c
diff --git a/NetworkPkg/HttpBootDxe/HttpBootConfig.c b/NetworkPkg/HttpBootDxe/HttpBootConfig.c
new file mode 100644 (file)
index 0000000..db14da0
--- /dev/null
@@ -0,0 +1,723 @@
+/** @file\r
+  Helper functions for configuring or getting the parameters relating to HTTP Boot.\r
+\r
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution.  The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "HttpBootDxe.h"\r
+\r
+CHAR16  mHttpBootConfigStorageName[]     = L"HTTP_BOOT_CONFIG_IFR_NVDATA";\r
+\r
+/**\r
+  Add new boot option for HTTP boot.\r
+\r
+  @param[in]  Private             Pointer to the driver private data.\r
+  @param[in]  UsingIpv6           Set to TRUE if creating boot option for IPv6.\r
+  @param[in]  Description         The description text of the boot option.\r
+  @param[in]  Uri                 The URI string of the boot file.\r
+  \r
+  @retval EFI_SUCCESS             The boot option is created successfully.\r
+  @retval Others                  Failed to create new boot option.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootAddBootOption (\r
+  IN   HTTP_BOOT_PRIVATE_DATA   *Private,\r
+  IN   BOOLEAN                  UsingIpv6,\r
+  IN   CHAR16                   *Description,\r
+  IN   CHAR16                   *Uri\r
+  )\r
+{\r
+  EFI_DEV_PATH               *Node;\r
+  EFI_DEVICE_PATH_PROTOCOL   *TmpDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL   *NewDevicePath;\r
+  UINTN                      Length;\r
+  CHAR8                      AsciiUri[URI_STR_MAX_SIZE];\r
+  CHAR16                     *CurrentOrder;\r
+  EFI_STATUS                 Status;\r
+  UINTN                      OrderCount;\r
+  UINTN                      TargetLocation;\r
+  BOOLEAN                    Found;\r
+  UINT8                      *TempByteBuffer;\r
+  UINT8                      *TempByteStart;\r
+  UINTN                      DescSize;\r
+  UINTN                      FilePathSize;\r
+  CHAR16                     OptionStr[10];\r
+  UINT16                     *NewOrder;\r
+  UINTN                      Index;\r
+\r
+  NewOrder      = NULL;\r
+  TempByteStart = NULL;\r
+  NewDevicePath = NULL;\r
+  NewOrder      = NULL;\r
+  Node          = NULL;\r
+  TmpDevicePath = NULL;\r
+  CurrentOrder  = NULL;\r
+\r
+  if (StrLen (Description) == 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Convert the scheme to all lower case.\r
+  //\r
+  for (Index = 0; Index < StrLen (Uri); Index++) {\r
+    if (Uri[Index] == L':') {\r
+      break;\r
+    }\r
+    if (Uri[Index] >= L'A' && Uri[Index] <= L'Z') {\r
+      Uri[Index] -= (CHAR16)(L'A' - L'a');\r
+    }\r
+  }\r
+\r
+  //\r
+  // Only accept http and https URI.\r
+  //\r
+  if ((StrnCmp (Uri, L"http://", 7) != 0) && (StrnCmp (Uri, L"https://", 7) != 0)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  \r
+  //\r
+  // Create a new device path by appending the IP node and URI node to\r
+  // the driver's parent device path\r
+  //\r
+  if (!UsingIpv6) {\r
+    Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));\r
+    if (Node == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+    Node->Ipv4.Header.Type    = MESSAGING_DEVICE_PATH;\r
+    Node->Ipv4.Header.SubType = MSG_IPv4_DP;\r
+    SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));\r
+  } else {\r
+    Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));\r
+    if (Node == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto ON_EXIT;\r
+    }\r
+    Node->Ipv6.Header.Type     = MESSAGING_DEVICE_PATH;\r
+    Node->Ipv6.Header.SubType  = MSG_IPv6_DP;\r
+    SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));\r
+  }\r
+  TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
+  FreePool (Node);\r
+  if (TmpDevicePath == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  //\r
+  // Update the URI node with the input boot file URI.\r
+  //\r
+  UnicodeStrToAsciiStr (Uri, AsciiUri);\r
+  Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (AsciiUri);\r
+  Node = AllocatePool (Length);\r
+  if (Node == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    FreePool (TmpDevicePath);\r
+    goto ON_EXIT;\r
+  }\r
+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;\r
+  Node->DevPath.SubType = MSG_URI_DP;\r
+  SetDevicePathNodeLength (Node, Length);\r
+  CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), AsciiUri, AsciiStrSize (AsciiUri));\r
+  NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);\r
+  FreePool (Node);\r
+  FreePool (TmpDevicePath);\r
+  if (NewDevicePath == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Get current "BootOrder" variable and find a free target.\r
+  //\r
+  Length = 0;\r
+  Status = GetVariable2 (\r
+             L"BootOrder",\r
+             &gEfiGlobalVariableGuid,\r
+             &CurrentOrder,\r
+             &Length \r
+             );\r
+  if (EFI_ERROR (Status) && Status != EFI_NOT_FOUND) {\r
+    goto ON_EXIT;\r
+  }\r
+  OrderCount = Length / sizeof (UINT16);\r
+  Found = FALSE;\r
+  for (TargetLocation=0; TargetLocation < 0xFFFF; TargetLocation++) {\r
+    Found = TRUE;\r
+    for (Index = 0; Index < OrderCount; Index++) {\r
+      if (CurrentOrder[Index] == TargetLocation) {\r
+        Found = FALSE;\r
+        break;\r
+      }\r
+    }\r
+    if (Found) {\r
+      break;\r
+    }\r
+  }\r
+\r
+  if (TargetLocation == 0xFFFF) {\r
+    DEBUG ((EFI_D_ERROR, "Could not find unused target index.\n"));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  } else {\r
+    DEBUG ((EFI_D_INFO, "TargetIndex = %04x.\n", TargetLocation));\r
+  }\r
+  \r
+  //\r
+  // Construct and set the "Boot####" variable\r
+  //\r
+  DescSize = StrSize(Description);\r
+  FilePathSize = GetDevicePathSize (NewDevicePath);\r
+  TempByteBuffer = AllocateZeroPool(sizeof(EFI_LOAD_OPTION) + DescSize + FilePathSize);\r
+  if (TempByteBuffer == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  TempByteStart = TempByteBuffer;\r
+  *((UINT32 *) TempByteBuffer) = LOAD_OPTION_ACTIVE;      // Attributes\r
+  TempByteBuffer += sizeof (UINT32);\r
+\r
+  *((UINT16 *) TempByteBuffer) = (UINT16)FilePathSize;    // FilePathListLength\r
+  TempByteBuffer += sizeof (UINT16);\r
+\r
+  CopyMem (TempByteBuffer, Description, DescSize);\r
+  TempByteBuffer += DescSize;\r
+  CopyMem (TempByteBuffer, NewDevicePath, FilePathSize);\r
+\r
+  UnicodeSPrint (OptionStr, sizeof(OptionStr), L"%s%04x", L"Boot", TargetLocation);\r
+  Status = gRT->SetVariable (\r
+                  OptionStr,\r
+                  &gEfiGlobalVariableGuid,\r
+                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+                  sizeof(UINT32) + sizeof(UINT16) + DescSize + FilePathSize,\r
+                  TempByteStart\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    goto ON_EXIT;\r
+  }\r
+\r
+  //\r
+  // Insert into the order list and set "BootOrder" variable\r
+  //\r
+  NewOrder = AllocateZeroPool ((OrderCount + 1) * sizeof (UINT16));\r
+  if (NewOrder == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto ON_EXIT;\r
+  }\r
+  CopyMem(NewOrder, CurrentOrder, OrderCount * sizeof(UINT16));\r
+  NewOrder[OrderCount] = (UINT16) TargetLocation;\r
+  Status = gRT->SetVariable (\r
+                  L"BootOrder",\r
+                  &gEfiGlobalVariableGuid,\r
+                  EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+                  ((OrderCount + 1) * sizeof (UINT16)),\r
+                  NewOrder\r
+                  );\r
+  \r
+\r
+ON_EXIT:\r
+\r
+  if (CurrentOrder != NULL) {\r
+    FreePool (CurrentOrder);\r
+  }\r
+  if (NewOrder != NULL) {\r
+    FreePool (NewOrder);\r
+  }\r
+  if (TempByteStart != NULL) {\r
+    FreePool (TempByteStart);\r
+  }\r
+  if (NewDevicePath != NULL) {\r
+    FreePool (NewDevicePath);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+   \r
+  This function allows the caller to request the current\r
+  configuration for one or more named elements. The resulting\r
+  string is in <ConfigAltResp> format. Also, any and all alternative\r
+  configuration strings shall be appended to the end of the\r
+  current configuration string. If they are, they must appear\r
+  after the current configuration. They must contain the same\r
+  routing (GUID, NAME, PATH) as the current configuration string.\r
+  They must have an additional description indicating the type of\r
+  alternative configuration the string represents,\r
+  "ALTCFG=<StringToken>". That <StringToken> (when\r
+  converted from Hex UNICODE to binary) is a reference to a\r
+  string in the associated string pack.\r
+\r
+  @param[in]  This       Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+\r
+  @param[in]  Request    A null-terminated Unicode string in\r
+                         <ConfigRequest> format. Note that this\r
+                         includes the routing information as well as\r
+                         the configurable name / value pairs. It is\r
+                         invalid for this string to be in\r
+                         <MultiConfigRequest> format.\r
+\r
+  @param[out] Progress   On return, points to a character in the\r
+                         Request string. Points to the string's null\r
+                         terminator if request was successful. Points\r
+                         to the most recent "&" before the first\r
+                         failing name / value pair (or the beginning\r
+                         of the string if the failure is in the first\r
+                         name / value pair) if the request was not successful.                        \r
+\r
+  @param[out] Results    A null-terminated Unicode string in\r
+                         <ConfigAltResp> format which has all values\r
+                         filled in for the names in the Request string.\r
+                         String to be allocated by the called function.\r
+\r
+  @retval EFI_SUCCESS             The Results string is filled with the\r
+                                  values corresponding to all requested\r
+                                  names.\r
+\r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+\r
+  @retval EFI_INVALID_PARAMETER   For example, passing in a NULL\r
+                                  for the Request parameter\r
+                                  would result in this type of\r
+                                  error. In this case, the\r
+                                  Progress parameter would be\r
+                                  set to NULL. \r
+\r
+  @retval EFI_NOT_FOUND           Routing data doesn't match any\r
+                                  known driver. Progress set to the\r
+                                  first character in the routing header.\r
+                                  Note: There is no requirement that the\r
+                                  driver validate the routing data. It\r
+                                  must skip the <ConfigHdr> in order to\r
+                                  process the names.\r
+\r
+  @retval EFI_INVALID_PARAMETER   Illegal syntax. Progress set\r
+                                  to most recent "&" before the\r
+                                  error or the beginning of the\r
+                                  string.\r
+\r
+  @retval EFI_INVALID_PARAMETER   Unknown name. Progress points\r
+                                  to the & before the name in\r
+                                  question.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpBootFormExtractConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Request,\r
+  OUT EFI_STRING                             *Progress,\r
+  OUT EFI_STRING                             *Results\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINTN                            BufferSize;\r
+  HTTP_BOOT_FORM_CALLBACK_INFO     *CallbackInfo;\r
+  EFI_STRING                       ConfigRequestHdr;\r
+  EFI_STRING                       ConfigRequest;\r
+  BOOLEAN                          AllocatedRequest;\r
+  UINTN                            Size;\r
+\r
+  if (Progress == NULL || Results == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  *Progress = Request;\r
+  if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+  \r
+  ConfigRequestHdr = NULL;\r
+  ConfigRequest    = NULL;\r
+  AllocatedRequest = FALSE;\r
+  Size             = 0;\r
+\r
+  CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);\r
+  //\r
+  // Convert buffer data to <ConfigResp> by helper function BlockToConfig()\r
+  //\r
+  BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);\r
+  ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);\r
+\r
+  ConfigRequest = Request;\r
+  if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {\r
+    //\r
+    // Request has no request element, construct full request string.\r
+    // Allocate and fill a buffer large enough to hold the <ConfigHdr> template\r
+    // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator\r
+    //\r
+    ConfigRequestHdr = HiiConstructConfigHdr (&gHttpBootConfigGuid, mHttpBootConfigStorageName, CallbackInfo->ChildHandle);\r
+    Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);\r
+    ConfigRequest = AllocateZeroPool (Size);\r
+    ASSERT (ConfigRequest != NULL);\r
+    AllocatedRequest = TRUE;\r
+    UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);\r
+    FreePool (ConfigRequestHdr);\r
+  }\r
+\r
+  Status = gHiiConfigRouting->BlockToConfig (\r
+                                gHiiConfigRouting,\r
+                                ConfigRequest,\r
+                                (UINT8 *) &CallbackInfo->HttpBootNvData,\r
+                                BufferSize,\r
+                                Results,\r
+                                Progress\r
+                                );\r
+  ASSERT_EFI_ERROR (Status);\r
+  \r
+  //\r
+  // Free the allocated config request string.\r
+  //\r
+  if (AllocatedRequest) {\r
+    FreePool (ConfigRequest);\r
+    ConfigRequest = NULL;\r
+  }\r
+  //\r
+  // Set Progress string to the original request string.\r
+  //\r
+  if (Request == NULL) {\r
+    *Progress = NULL;\r
+  } else if (StrStr (Request, L"OFFSET") == NULL) {\r
+    *Progress = Request + StrLen (Request);\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+   \r
+  This function applies changes in a driver's configuration.\r
+  Input is a Configuration, which has the routing data for this\r
+  driver followed by name / value configuration pairs. The driver\r
+  must apply those pairs to its configurable storage. If the\r
+  driver's configuration is stored in a linear block of data\r
+  and the driver's name / value pairs are in <BlockConfig>\r
+  format, it may use the ConfigToBlock helper function (above) to\r
+  simplify the job.\r
+\r
+  @param[in]  This           Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+\r
+  @param[in]  Configuration  A null-terminated Unicode string in\r
+                             <ConfigString> format. \r
+  \r
+  @param[out] Progress       A pointer to a string filled in with the\r
+                             offset of the most recent '&' before the\r
+                             first failing name / value pair (or the\r
+                             beginning of the string if the failure\r
+                             is in the first name / value pair) or\r
+                             the terminating NULL if all was\r
+                             successful.\r
+\r
+  @retval EFI_SUCCESS             The results have been distributed or are\r
+                                  awaiting distribution.\r
+  \r
+  @retval EFI_OUT_OF_RESOURCES    Not enough memory to store the\r
+                                  parts of the results that must be\r
+                                  stored awaiting possible future\r
+                                  protocols.\r
+  \r
+  @retval EFI_INVALID_PARAMETERS  Passing in a NULL for the\r
+                                  Results parameter would result\r
+                                  in this type of error.\r
+  \r
+  @retval EFI_NOT_FOUND           Target for the specified routing data\r
+                                  was not found.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpBootFormRouteConfig (\r
+  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN  CONST EFI_STRING                       Configuration,\r
+  OUT EFI_STRING                             *Progress\r
+  )\r
+{\r
+  EFI_STATUS                       Status;\r
+  UINTN                            BufferSize;\r
+  HTTP_BOOT_FORM_CALLBACK_INFO     *CallbackInfo;\r
+  HTTP_BOOT_PRIVATE_DATA           *Private;\r
+\r
+  if (Progress == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+  *Progress = Configuration;\r
+\r
+  if (Configuration == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // Check routing data in <ConfigHdr>.\r
+  // Note: there is no name for Name/Value storage, only GUID will be checked\r
+  //\r
+  if (!HiiIsConfigHdrMatch (Configuration, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);\r
+  Private      = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO (CallbackInfo);\r
+  \r
+  BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);\r
+  ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);\r
+\r
+  Status = gHiiConfigRouting->ConfigToBlock (\r
+                            gHiiConfigRouting,\r
+                            Configuration,\r
+                            (UINT8 *) &CallbackInfo->HttpBootNvData,\r
+                            &BufferSize,\r
+                            Progress\r
+                            );\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Create a new boot option according to the configuration data.\r
+  //\r
+  Status = HttpBootAddBootOption (\r
+             Private,\r
+             (CallbackInfo->HttpBootNvData.IpVersion == HTTP_BOOT_IP_VERSION_6) ? TRUE : FALSE,\r
+             CallbackInfo->HttpBootNvData.Description,\r
+             CallbackInfo->HttpBootNvData.Uri\r
+             );\r
+  \r
+  return Status;\r
+}\r
+\r
+/**\r
+   \r
+  This function is called to provide results data to the driver.\r
+  This data consists of a unique key that is used to identify\r
+  which data is either being passed back or being asked for.\r
+\r
+  @param[in]       This          Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.\r
+  @param[in]       Action        Specifies the type of action taken by the browser.\r
+  @param[in]       QuestionId    A unique value which is sent to the original\r
+                                 exporting driver so that it can identify the type\r
+                                 of data to expect. The format of the data tends to \r
+                                 vary based on the opcode that generated the callback.\r
+  @param[in]       Type          The type of value for the question.\r
+  @param[in, out]  Value         A pointer to the data being sent to the original\r
+                                 exporting driver.\r
+  @param[out]      ActionRequest On return, points to the action requested by the\r
+                                 callback function.\r
+\r
+  @retval EFI_SUCCESS            The callback successfully handled the action.\r
+  @retval EFI_OUT_OF_RESOURCES   Not enough storage is available to hold the\r
+                                 variable and its data.\r
+  @retval EFI_DEVICE_ERROR       The variable could not be saved.\r
+  @retval EFI_UNSUPPORTED        The specified Action is not supported by the\r
+                                 callback.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+HttpBootFormCallback (\r
+  IN CONST  EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,\r
+  IN        EFI_BROWSER_ACTION               Action,\r
+  IN        EFI_QUESTION_ID                  QuestionId,\r
+  IN        UINT8                            Type,\r
+  IN OUT    EFI_IFR_TYPE_VALUE               *Value,\r
+  OUT       EFI_BROWSER_ACTION_REQUEST       *ActionRequest\r
+  )\r
+{\r
+  return EFI_UNSUPPORTED;\r
+}\r
+\r
+/**\r
+  Initialize the configuration form.\r
+\r
+  @param[in]  Private             Pointer to the driver private data.\r
+\r
+  @retval EFI_SUCCESS             The configuration form is initialized.\r
+  @retval EFI_OUT_OF_RESOURCES    Failed to allocate memory.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootConfigFormInit (\r
+  IN HTTP_BOOT_PRIVATE_DATA     *Private\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  HTTP_BOOT_FORM_CALLBACK_INFO      *CallbackInfo;\r
+  VENDOR_DEVICE_PATH                VendorDeviceNode;\r
+  EFI_SERVICE_BINDING_PROTOCOL      *HttpSb;\r
+  CHAR16                            *MacString;\r
+  CHAR16                            *OldMenuString;\r
+  CHAR16                            MenuString[128];\r
+\r
+  CallbackInfo = &Private->CallbackInfo;\r
+\r
+  if (CallbackInfo->Initilized) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  \r
+  CallbackInfo->Signature = HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE;\r
+\r
+  //\r
+  // Construct device path node for EFI HII Config Access protocol,\r
+  // which consists of controller physical device path and one hardware\r
+  // vendor guid node.\r
+  //\r
+  ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));\r
+  VendorDeviceNode.Header.Type    = HARDWARE_DEVICE_PATH;\r
+  VendorDeviceNode.Header.SubType = HW_VENDOR_DP;\r
+  CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid);\r
+  SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));\r
+  CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (\r
+                                        Private->ParentDevicePath,\r
+                                        (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode\r
+                                        );\r
+  if (CallbackInfo->HiiVendorDevicePath == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  CallbackInfo->ConfigAccess.ExtractConfig = HttpBootFormExtractConfig;\r
+  CallbackInfo->ConfigAccess.RouteConfig   = HttpBootFormRouteConfig;\r
+  CallbackInfo->ConfigAccess.Callback      = HttpBootFormCallback;\r
+  \r
+  //\r
+  // Install Device Path Protocol and Config Access protocol to driver handle.\r
+  //\r
+  Status = gBS->InstallMultipleProtocolInterfaces (\r
+                  &CallbackInfo->ChildHandle,\r
+                  &gEfiDevicePathProtocolGuid,\r
+                  CallbackInfo->HiiVendorDevicePath,\r
+                  &gEfiHiiConfigAccessProtocolGuid,\r
+                  &CallbackInfo->ConfigAccess,\r
+                  NULL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    //\r
+    // Open the Parent Handle for the child\r
+    //\r
+    Status = gBS->OpenProtocol (\r
+                    Private->Controller,\r
+                    &gEfiHttpServiceBindingProtocolGuid,\r
+                    (VOID **) &HttpSb,\r
+                    Private->Image,\r
+                    CallbackInfo->ChildHandle,\r
+                    EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER\r
+                    );\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    goto Error;\r
+  }\r
+\r
+  //\r
+  // Publish our HII data.\r
+  //\r
+  CallbackInfo->RegisteredHandle = HiiAddPackages (\r
+                                     &gHttpBootConfigGuid,\r
+                                     CallbackInfo->ChildHandle,\r
+                                     HttpBootDxeStrings,\r
+                                     HttpBootConfigVfrBin,\r
+                                     NULL\r
+                                     );\r
+  if (CallbackInfo->RegisteredHandle == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  //\r
+  // Append MAC string in the menu help string\r
+  //\r
+  Status = NetLibGetMacString (Private->Controller, Private->Image, &MacString);\r
+  if (!EFI_ERROR (Status)) {\r
+    OldMenuString = HiiGetString (\r
+                      CallbackInfo->RegisteredHandle, \r
+                      STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP), \r
+                      NULL\r
+                      );\r
+    UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);\r
+    HiiSetString (\r
+      CallbackInfo->RegisteredHandle, \r
+      STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP), \r
+      MenuString, \r
+      NULL\r
+      );\r
+    \r
+    FreePool (MacString);\r
+    FreePool (OldMenuString);\r
+\r
+    return EFI_SUCCESS;\r
+  }\r
+  \r
+Error:\r
+\r
+  HttpBootConfigFormUnload (Private);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Unload the configuration form, this includes: delete all the configuration\r
+  entries, uninstall the form callback protocol, and free the resources used.\r
+\r
+  @param[in]  Private             Pointer to the driver private data.\r
+\r
+  @retval EFI_SUCCESS             The configuration form is unloaded.\r
+  @retval Others                  Failed to unload the form.\r
+\r
+**/\r
+EFI_STATUS\r
+HttpBootConfigFormUnload (\r
+  IN HTTP_BOOT_PRIVATE_DATA     *Private\r
+  )\r
+{\r
+  HTTP_BOOT_FORM_CALLBACK_INFO      *CallbackInfo;\r
+\r
+  CallbackInfo = &Private->CallbackInfo;\r
+  if (CallbackInfo->ChildHandle != NULL) {\r
+    //\r
+    // Close the child handle\r
+    //\r
+    gBS->CloseProtocol (\r
+           Private->Controller,\r
+           &gEfiHttpServiceBindingProtocolGuid,\r
+           Private->Image,\r
+           CallbackInfo->ChildHandle\r
+           );\r
+    \r
+    //\r
+    // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL\r
+    //\r
+    gBS->UninstallMultipleProtocolInterfaces (\r
+           CallbackInfo->ChildHandle,\r
+           &gEfiDevicePathProtocolGuid,\r
+           CallbackInfo->HiiVendorDevicePath,\r
+           &gEfiHiiConfigAccessProtocolGuid,\r
+           &CallbackInfo->ConfigAccess,\r
+           NULL\r
+           );\r
+    CallbackInfo->ChildHandle = NULL;\r
+  }\r
+\r
+  if (CallbackInfo->HiiVendorDevicePath != NULL) {\r
+    FreePool (CallbackInfo->HiiVendorDevicePath);\r
+    CallbackInfo->HiiVendorDevicePath = NULL;\r
+  }\r
+\r
+  if (CallbackInfo->RegisteredHandle != NULL) {\r
+    //\r
+    // Remove HII package list\r
+    //\r
+    HiiRemovePackages (CallbackInfo->RegisteredHandle);\r
+    CallbackInfo->RegisteredHandle = NULL;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r