]> git.proxmox.com Git - mirror_edk2.git/commitdiff
NetworkPkg: Add UEFI HTTP boot driver.
authorFu Siyuan <siyuan.fu@intel.com>
Tue, 7 Jul 2015 09:29:28 +0000 (09:29 +0000)
committersfu5 <sfu5@Edk2>
Tue, 7 Jul 2015 09:29:28 +0000 (09:29 +0000)
This patch add the implementation for UEFI HTTP boot driver.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Fu Siyuan <siyuan.fu@intel.com>
Reviewed-by: Ye Ting <ting.ye@intel.com>
git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17857 6f19259b-4bc3-4df7-8a09-765794883524

15 files changed:
NetworkPkg/HttpBootDxe/HttpBootClient.c [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootClient.h [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootComponentName.c [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootComponentName.h [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootDhcp4.c [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootDhcp4.h [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootDxe.c [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootDxe.h [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootDxe.inf [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootDxe.uni [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootImpl.c [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootImpl.h [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootSupport.c [new file with mode: 0644]
NetworkPkg/HttpBootDxe/HttpBootSupport.h [new file with mode: 0644]

diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.c b/NetworkPkg/HttpBootDxe/HttpBootClient.c
new file mode 100644 (file)
index 0000000..2bf28c2
--- /dev/null
@@ -0,0 +1,830 @@
+/** @file
+  Implementation of the boot file download function.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "HttpBootDxe.h"
+
+/**
+  Update the IP and URL device path node to include the boot resource information.
+
+  @param[in]    Private            The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS              Device patch successfully updated.
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources.
+  @retval Others                   Unexpected error happened.
+  
+**/
+EFI_STATUS
+HttpBootUpdateDevicePath (
+  IN   HTTP_BOOT_PRIVATE_DATA   *Private
+  )
+{
+  EFI_DEV_PATH               *Node;
+  EFI_DEVICE_PATH_PROTOCOL   *TmpDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL   *NewDevicePath;
+  UINTN                      Length;
+  EFI_STATUS                 Status;
+
+  TmpDevicePath = NULL;
+  
+  //
+  // Update the IP node with DHCP assigned information.
+  //
+  if (!Private->UsingIpv6) {
+    Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
+    if (Node == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    Node->Ipv4.Header.Type    = MESSAGING_DEVICE_PATH;
+    Node->Ipv4.Header.SubType = MSG_IPv4_DP;
+    SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
+    CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+    Node->Ipv4.RemotePort      = Private->Port;
+    Node->Ipv4.Protocol        = EFI_IP_PROTO_TCP;
+    Node->Ipv4.StaticIpAddress = FALSE;
+    CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
+    CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+    
+    TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+    FreePool (Node);
+    if (TmpDevicePath == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } else {
+    ASSERT (FALSE);
+  }
+
+  //
+  // Update the URI node with the boot file URI.
+  //
+  Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);
+  Node = AllocatePool (Length);
+  if (Node == NULL) {
+    FreePool (TmpDevicePath);
+    return EFI_OUT_OF_RESOURCES;
+  }
+  Node->DevPath.Type    = MESSAGING_DEVICE_PATH;
+  Node->DevPath.SubType = MSG_URI_DP;
+  SetDevicePathNodeLength (Node, Length);
+  CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));
+  
+  NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+  FreePool (Node);
+  FreePool (TmpDevicePath);
+  if (NewDevicePath == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Reinstall the device path protocol of the child handle.
+  //
+  Status = gBS->ReinstallProtocolInterface (
+                  Private->ChildHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  Private->DevicePath,
+                  NewDevicePath
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  
+  FreePool (Private->DevicePath);
+  Private->DevicePath = NewDevicePath;
+  return EFI_SUCCESS;
+}
+
+/**
+  Parse the boot file URI information from the selected Dhcp4 offer packet.
+
+  @param[in]    Private        The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.
+  @retval Others               Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+HttpBootExtractUriInfo (
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private
+  )
+{
+  HTTP_BOOT_DHCP4_PACKET_CACHE    *SelectOffer;
+  HTTP_BOOT_DHCP4_PACKET_CACHE    *HttpOffer;
+  UINT32                          SelectIndex;
+  UINT32                          ProxyIndex;
+  EFI_DHCP4_PACKET_OPTION         *Option;
+  EFI_STATUS                      Status;
+
+  ASSERT (Private != NULL);
+  ASSERT (Private->SelectIndex != 0);
+  SelectIndex = Private->SelectIndex - 1;
+  ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
+
+  Status = EFI_SUCCESS;
+
+  //
+  // SelectOffer contains the IP address configuration and name server configuration.
+  // HttpOffer contains the boot file URL.
+  //
+  SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;
+  if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) || (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
+    HttpOffer = SelectOffer;
+  } else {
+    ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
+    ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+    HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
+  }
+
+  //
+  // Configure the default DNS server if server assigned.
+  //
+  if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) || (SelectOffer->OfferType == HttpOfferTypeDhcpDns)) {
+    Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
+    ASSERT (Option != NULL);
+    Status = HttpBootRegisterIp4Dns (
+               Private,
+               Option->Length,
+               Option->Data
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  //
+  // Extract the port from URL, and use default HTTP port 80 if not provided.
+  //
+  Status = HttpUrlGetPort (
+             (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
+             HttpOffer->UriParser,
+             &Private->Port
+             );
+  if (EFI_ERROR (Status) || Private->Port == 0) {
+    Private->Port = 80;
+  }
+  
+  //
+  // Record the URI of boot file from the selected HTTP offer.
+  //
+  Private->BootFileUriParser = HttpOffer->UriParser;
+  Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
+
+  
+  //
+  // All boot informations are valid here.
+  //
+  AsciiPrint ("\n  URI: %a", Private->BootFileUri);
+
+  //
+  // Update the device path to include the IP and boot URI information.
+  //
+  Status = HttpBootUpdateDevicePath (Private);
+
+  return Status;
+}
+
+/**
+  Discover all the boot information for boot file.
+
+  @param[in, out]    Private        The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS          Successfully obtained all the boot information .
+  @retval Others               Failed to retrieve the boot information.
+
+**/
+EFI_STATUS
+HttpBootDiscoverBootInfo (
+  IN OUT HTTP_BOOT_PRIVATE_DATA   *Private
+  )
+{
+  EFI_STATUS              Status;
+  
+  //
+  // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
+  // other Http boot information.
+  //
+  Status = HttpBootDhcp (Private);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (!Private->UsingIpv6) {
+    Status = HttpBootExtractUriInfo (Private);
+  } else {
+    ASSERT (FALSE);
+  }
+
+  return Status;
+}
+
+/**
+  Create a HttpIo instance for the file download.
+
+  @param[in]    Private        The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS          Successfully created.
+  @retval Others               Failed to create HttpIo.
+
+**/
+EFI_STATUS
+HttpBootCreateHttpIo (
+  IN     HTTP_BOOT_PRIVATE_DATA       *Private
+  )
+{
+  HTTP_IO_CONFIG_DATA          ConfigData;
+  EFI_STATUS                   Status;
+
+  ASSERT (Private != NULL);
+
+  ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));
+  if (!Private->UsingIpv6) {
+    ConfigData.Config4.HttpVersion = HttpVersion11;
+    ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
+    IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);
+    IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);
+  } else {
+    ASSERT (FALSE);
+  }
+
+  Status = HttpIoCreateIo (
+             Private->Image,
+             Private->Controller,
+             Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,
+             &ConfigData,
+             &Private->HttpIo
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Private->HttpCreated = TRUE;
+  return EFI_SUCCESS;
+}
+
+/**
+  Get the file content from cached data.
+
+  @param[in]          Private         The pointer to the driver's private data.
+  @param[in]          Uri             Uri of the file to be retrieved from cache.
+  @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return
+                                      code of EFI_SUCCESS, the amount of data transferred to
+                                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+                                      the size of Buffer required to retrieve the requested file.
+  @param[out]         Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,
+                                      then the size of the requested file is returned in
+                                      BufferSize.
+
+  @retval EFI_SUCCESS          Successfully created.
+  @retval Others               Failed to create HttpIo.
+
+**/
+EFI_STATUS
+HttpBootGetFileFromCache (
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private,
+  IN     CHAR16                   *Uri,
+  IN OUT UINTN                    *BufferSize,
+     OUT UINT8                    *Buffer
+  )
+{
+  LIST_ENTRY                  *Entry;
+  LIST_ENTRY                  *Entry2;
+  HTTP_BOOT_CACHE_CONTENT     *Cache;
+  HTTP_BOOT_ENTITY_DATA       *EntityData;
+  UINTN                       CopyedSize;
+  
+  if (Uri == NULL || BufferSize == 0 || Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  NET_LIST_FOR_EACH (Entry, &Private->CacheList) {
+    Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
+    //
+    // Compare the URI to see whether we already have a cache for this file.
+    //
+    if ((Cache->RequestData != NULL) &&
+        (Cache->RequestData->Url != NULL) &&
+        (StrCmp (Uri, Cache->RequestData->Url) == 0)) 
+    {
+      //
+      // Hit cache, check buffer size.
+      //
+      if (*BufferSize < Cache->EntityLength) {
+        *BufferSize = Cache->EntityLength;
+        return EFI_BUFFER_TOO_SMALL;
+      }
+
+      //
+      // Fill data to buffer.
+      //
+      CopyedSize = 0;
+      NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
+        EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
+        if (*BufferSize > CopyedSize) {
+          CopyMem (
+            Buffer + CopyedSize,
+            EntityData->DataStart,
+            MIN (EntityData->DataLength, *BufferSize - CopyedSize)
+            );
+          CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
+        }
+      }
+      *BufferSize = CopyedSize;
+      return EFI_SUCCESS;
+    }
+  }
+
+  return EFI_NOT_FOUND;
+}
+
+/**
+  Release all the resource of a cache item.
+
+  @param[in]          Cache         The pointer to the cache item.
+
+**/
+VOID
+HttpBootFreeCache (
+  IN  HTTP_BOOT_CACHE_CONTENT    *Cache
+  )
+{
+  UINTN                       Index;
+  LIST_ENTRY                  *Entry;
+  LIST_ENTRY                  *NextEntry;
+  HTTP_BOOT_ENTITY_DATA       *EntityData;
+
+  if (Cache != NULL) {
+    //
+    // Free the request data
+    //
+    if (Cache->RequestData != NULL) {
+      if (Cache->RequestData->Url != NULL) {
+        FreePool (Cache->RequestData->Url);
+      }
+      FreePool (Cache->RequestData);
+    }
+
+    //
+    // Free the response header
+    //
+    if (Cache->ResponseData != NULL) {
+      if (Cache->ResponseData->Headers != NULL) {
+        for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {
+          FreePool (Cache->ResponseData->Headers[Index].FieldName);
+          FreePool (Cache->ResponseData->Headers[Index].FieldValue);
+        }
+        FreePool (Cache->ResponseData->Headers);
+      }
+    }
+
+    //
+    // Free the response body
+    //
+    NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {
+      EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);
+      if (EntityData->Block != NULL) {
+        FreePool (EntityData->Block);
+      }
+      RemoveEntryList (&EntityData->Link);
+      FreePool (EntityData);
+    }
+
+    FreePool (Cache);
+  }
+}
+
+/**
+  Clean up all cached data.
+
+  @param[in]          Private         The pointer to the driver's private data.
+
+**/
+VOID
+HttpBootFreeCacheList (
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private
+  )
+{
+  LIST_ENTRY                  *Entry;
+  LIST_ENTRY                  *NextEntry;
+  HTTP_BOOT_CACHE_CONTENT     *Cache;
+  
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {
+    Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
+    RemoveEntryList (&Cache->Link);
+    HttpBootFreeCache (Cache);
+  }
+}
+
+/**
+  A callback function to intercept events during message parser.
+
+  This function will be invoked during HttpParseMessageBody() with various events type. An error
+  return status of the callback function will cause the HttpParseMessageBody() aborted.
+
+  @param[in]    EventType          Event type of this callback call.
+  @param[in]    Data               A pointer to data buffer.
+  @param[in]    Length             Length in bytes of the Data.
+  @param[in]    Context            Callback context set by HttpInitMsgParser().
+
+  @retval EFI_SUCCESS              Continue to parser the message body.
+  @retval Others                   Abort the parse.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootGetBootFileCallback (
+  IN HTTP_BODY_PARSE_EVENT      EventType,
+  IN CHAR8                      *Data,
+  IN UINTN                      Length,
+  IN VOID                       *Context
+  )
+{
+  HTTP_BOOT_CALLBACK_DATA      *CallbackData;
+  HTTP_BOOT_ENTITY_DATA        *NewEntityData;
+
+  //
+  // We only care about the entity data.
+  //
+  if (EventType != BodyParseEventOnData) {
+    return EFI_SUCCESS;
+  }
+
+  CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
+
+  //
+  // Save the data into cache list.
+  //
+  NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
+  if (NewEntityData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  if (CallbackData->NewBlock) {
+    NewEntityData->Block = CallbackData->Block;
+    CallbackData->Block = NULL;
+  }
+  NewEntityData->DataLength = Length;
+  NewEntityData->DataStart  = (UINT8*) Data;
+  InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
+
+  //
+  // Copy data if caller has provided a buffer.
+  //
+  if (CallbackData->BufferSize > CallbackData->CopyedSize) {
+    CopyMem (
+      CallbackData->Buffer + CallbackData->CopyedSize,
+      Data,
+      MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
+      );
+    CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function download the boot file by using UEFI HTTP protocol.
+  
+  @param[in]       Private         The pointer to the driver's private data.
+  @param[in]       HeaderOnly      Only request the response header, it could save a lot of time if
+                                   the caller only want to know the size of the requested file.
+  @param[in, out]  BufferSize      On input the size of Buffer in bytes. On output with a return
+                                   code of EFI_SUCCESS, the amount of data transferred to
+                                   Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+                                   the size of Buffer required to retrieve the requested file.
+  @param[out]      Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,
+                                   then the size of the requested file is returned in
+                                   BufferSize.
+
+  @retval EFI_SUCCESS              The file was loaded.
+  @retval EFI_INVALID_PARAMETER    BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources
+  @retval EFI_BUFFER_TOO_SMALL     The BufferSize is too small to read the current directory entry.
+                                   BufferSize has been updated with the size needed to complete
+                                   the request.
+  @retval Others                   Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootGetBootFile (
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private,
+  IN     BOOLEAN                  HeaderOnly,
+  IN OUT UINTN                    *BufferSize,
+     OUT UINT8                    *Buffer
+  )
+{
+  EFI_STATUS                 Status;
+  CHAR8                      *HostName;
+  EFI_HTTP_REQUEST_DATA      *RequestData;
+  HTTP_IO_RESOPNSE_DATA      *ResponseData;
+  HTTP_IO_RESOPNSE_DATA      ResponseBody;
+  HTTP_IO                    *HttpIo;
+  HTTP_IO_HEADER             *HttpIoHeader;
+  VOID                       *Parser;
+  HTTP_BOOT_CALLBACK_DATA    Context;
+  UINTN                      ContentLength;
+  HTTP_BOOT_CACHE_CONTENT    *Cache;
+  UINT8                      *Block;
+  CHAR16                     *Url;
+  
+  ASSERT (Private != NULL);
+  ASSERT (Private->HttpCreated);
+
+  if (BufferSize == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (*BufferSize != 0 && Buffer == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // First, check whether we already cached the requested Uri.
+  //
+  Url = AllocatePool ((AsciiStrLen (Private->BootFileUri) + 1) * sizeof (CHAR16));
+  if (Url == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  AsciiStrToUnicodeStr (Private->BootFileUri, Url);
+  if (!HeaderOnly) {
+    Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer);
+    if (Status != EFI_NOT_FOUND) {
+      FreePool (Url);
+      return Status;
+    }
+  }
+
+  //
+  // Not found in cache, try to download it through HTTP.
+  //
+
+  //
+  // 1. Create a temp cache item for the requested URI.
+  //
+  Cache = NULL;
+  if (!HeaderOnly) {
+    Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
+    if (Cache == NULL) {
+      Status = EFI_OUT_OF_RESOURCES;
+      goto ERROR_1;
+    }
+    InitializeListHead (&Cache->EntityDataList);
+  }
+
+  //
+  // 2. Send HTTP request message.
+  //
+
+  //
+  // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
+  //       Host
+  //       Accept
+  //       User-Agent
+  //
+  HttpIoHeader = HttpBootCreateHeader (3);
+  if (HttpIoHeader == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ERROR_2;
+  }
+
+  //
+  // Add HTTP header field 1: Host
+  //
+  HostName = NULL;
+  Status = HttpUrlGetHostName (
+             Private->BootFileUri,
+             Private->BootFileUriParser,
+             &HostName
+             );
+  if (EFI_ERROR (Status)) {
+    goto ERROR_3;
+  }
+  Status = HttpBootSetHeader (
+             HttpIoHeader,
+             HTTP_FIELD_NAME_HOST,
+             HostName
+             );
+  FreePool (HostName);
+  if (EFI_ERROR (Status)) {
+    goto ERROR_3;
+  }
+
+  //
+  // Add HTTP header field 2: Accept
+  //
+  Status = HttpBootSetHeader (
+             HttpIoHeader,
+             HTTP_FIELD_NAME_ACCEPT,
+             "*/*"
+             );
+  if (EFI_ERROR (Status)) {
+    goto ERROR_3;
+  }
+
+  //
+  // Add HTTP header field 3: User-Agent
+  //
+  Status = HttpBootSetHeader (
+             HttpIoHeader,
+             HTTP_FIELD_NAME_USER_AGENT,
+             HTTP_USER_AGENT_EFI_HTTP_BOOT
+             );
+  if (EFI_ERROR (Status)) {
+    goto ERROR_3;
+  }
+
+  //
+  // 2.2 Build the rest of HTTP request info.
+  //
+  RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
+  if (RequestData == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ERROR_3;
+  }
+  RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
+  RequestData->Url = Url;
+  if (RequestData->Url == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ERROR_4;
+  }
+  AsciiStrToUnicodeStr (Private->BootFileUri, RequestData->Url);
+
+  //
+  // 2.3 Record the request info in a temp cache item.
+  //
+  if (!HeaderOnly) {
+    Cache->RequestData = RequestData;
+  }
+
+  //
+  // 2.4 Send out the request to HTTP server.
+  //
+  HttpIo = &Private->HttpIo;
+  Status = HttpIoSendRequest (
+             HttpIo,
+             RequestData,
+             HttpIoHeader->HeaderCount,
+             HttpIoHeader->Headers,
+             0,
+             NULL
+            );
+  if (EFI_ERROR (Status)) {
+    goto ERROR_4;
+  }
+
+  //
+  // 3. Receive HTTP response message.
+  //
+
+  //
+  // 3.1 First step, use zero BodyLength to only receive the response headers.
+  //
+  ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESOPNSE_DATA));
+  if (ResponseData == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ERROR_4;
+  }
+  Status = HttpIoRecvResponse (
+             &Private->HttpIo,
+             TRUE,
+             ResponseData
+             );
+  if (EFI_ERROR (Status)) {
+    goto ERROR_5;
+  }
+
+  //
+  // 3.2 Cache the response header.
+  //
+  if (!HeaderOnly) {
+    Cache->ResponseData = ResponseData;
+  }
+  
+  //
+  // 3.3 Init a message-body parser from the header information.
+  //
+  Parser = NULL;
+  Context.NewBlock   = FALSE;
+  Context.Block      = NULL;
+  Context.CopyedSize = 0;
+  Context.Buffer     = Buffer;
+  Context.BufferSize = *BufferSize;
+  Context.Cache      = Cache;
+  Status = HttpInitMsgParser (
+             HeaderOnly? HttpMethodHead : HttpMethodGet,
+             ResponseData->Response.StatusCode,
+             ResponseData->HeaderCount,
+             ResponseData->Headers,
+             HttpBootGetBootFileCallback,
+             (VOID*) &Context,
+             &Parser
+             );
+  if (EFI_ERROR (Status)) {
+    goto ERROR_6;
+  }
+
+  //
+  // 3.4 Continue to receive and parse message-body if needed.
+  //
+  if (!HeaderOnly) {
+    ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESOPNSE_DATA));
+    while (!HttpIsMessageComplete (Parser)) {
+      //
+      // Allocate a new block to hold the message-body.
+      //
+      Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
+      if (Block == NULL) {
+        Status = EFI_OUT_OF_RESOURCES;
+        goto ERROR_6;
+      }
+      ResponseBody.Body       = (CHAR8*) Block;
+      ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
+      Status = HttpIoRecvResponse (
+                 &Private->HttpIo,
+                 FALSE,
+                 &ResponseBody
+                 );
+      if (EFI_ERROR (Status)) {
+        goto ERROR_6;
+      }
+
+      //
+      // Parse the new received block of the message-body, the block will be saved in cache.
+      //
+      Context.NewBlock = TRUE;
+      Context.Block    = Block;
+      Status = HttpParseMessageBody (
+                 Parser,
+                 ResponseBody.BodyLength,
+                 ResponseBody.Body
+                 );
+      if (EFI_ERROR (Status)) {
+        goto ERROR_6;
+      }
+    }
+  }
+  
+  //
+  // 3.5 Message-body receive & parse is completed, get the file size.
+  //
+  Status = HttpGetEntityLength (Parser, &ContentLength);
+  if (EFI_ERROR (Status)) {
+    goto ERROR_6;
+  }
+
+  if (*BufferSize < ContentLength) {
+    Status = EFI_BUFFER_TOO_SMALL;
+  }
+  *BufferSize = ContentLength;
+
+  //
+  // 4. Save the cache item to driver's cache list and return.
+  //
+  if (!HeaderOnly) {
+    Cache->EntityLength = ContentLength;
+    InsertTailList (&Private->CacheList, &Cache->Link);
+  }
+
+  if (Parser != NULL) {
+    HttpFreeMsgParser (Parser);
+  }
+
+  return EFI_SUCCESS;
+  
+ERROR_6:
+  if (Parser != NULL) {
+    HttpFreeMsgParser (Parser);
+  }
+  if (Context.Block != NULL) {
+    FreePool (Context.Block);
+  }
+  HttpBootFreeCache (Cache);
+  
+ERROR_5:
+  if (ResponseData != NULL) {
+    FreePool (ResponseData);
+  }
+ERROR_4:
+  if (RequestData != NULL) {
+    FreePool (RequestData);
+  }
+ERROR_3:
+  HttpBootFreeHeader (HttpIoHeader);
+ERROR_2:
+  if (Cache != NULL) {
+    FreePool (Cache);
+  }
+ERROR_1:
+  if (Url != NULL) {
+    FreePool (Url);
+  }
+
+  return Status;
+}
diff --git a/NetworkPkg/HttpBootDxe/HttpBootClient.h b/NetworkPkg/HttpBootDxe/HttpBootClient.h
new file mode 100644 (file)
index 0000000..3f66713
--- /dev/null
@@ -0,0 +1,139 @@
+/** @file
+  Declaration of the boot file download function.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_HTTP_BOOT_HTTP_H__
+#define __EFI_HTTP_BOOT_HTTP_H__
+
+#define HTTP_BOOT_REQUEST_TIMEOUT            5000      // 5 seconds in uints of millisecond.
+#define HTTP_BOOT_BLOCK_SIZE                 1024
+
+#define HTTP_FIELD_NAME_USER_AGENT           "User-Agent"
+#define HTTP_FIELD_NAME_HOST                 "Host"
+#define HTTP_FIELD_NAME_ACCEPT               "Accept"
+
+
+#define HTTP_USER_AGENT_EFI_HTTP_BOOT        "UefiHttpBoot/1.0"
+
+//
+// Record the data length and start address of a data block.
+//
+typedef struct {
+  LIST_ENTRY                 Link;        // Link to the EntityDataList in HTTP_BOOT_CACHE_CONTENT
+  UINT8                      *Block;      // If NULL, the data is in previous data block.
+  UINT8                      *DataStart;  // Point to somewhere in the Block
+  UINTN                      DataLength;
+} HTTP_BOOT_ENTITY_DATA;
+
+//
+// Structure for a cache item
+//
+typedef struct {
+  LIST_ENTRY                 Link;            // Link to the CacheList in driver's private data.
+  EFI_HTTP_REQUEST_DATA      *RequestData;
+  HTTP_IO_RESOPNSE_DATA      *ResponseData;   // Not include any message-body data.
+  UINTN                      EntityLength;
+  LIST_ENTRY                 EntityDataList;  // Entity data (message-body)
+} HTTP_BOOT_CACHE_CONTENT;
+
+//
+// Callback data for HTTP_BODY_PARSER_CALLBACK()
+//
+typedef struct {
+  EFI_STATUS                 Status;
+  //
+  // Cache info.
+  //
+  HTTP_BOOT_CACHE_CONTENT    *Cache;
+  BOOLEAN                    NewBlock;
+  UINT8                      *Block;
+
+  //
+  // Caller provided buffer to load the file in.
+  //
+  UINTN                      CopyedSize;
+  UINTN                      BufferSize;
+  UINT8                      *Buffer;
+} HTTP_BOOT_CALLBACK_DATA;
+
+/**
+  Discover all the boot information for boot file.
+
+  @param[in, out]    Private        The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS          Successfully obtained all the boot information .
+  @retval Others               Failed to retrieve the boot information.
+
+**/
+EFI_STATUS
+HttpBootDiscoverBootInfo (
+  IN OUT HTTP_BOOT_PRIVATE_DATA   *Private
+  );
+
+/**
+  Create a HttpIo instance for the file download.
+
+  @param[in]    Private        The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS          Successfully created.
+  @retval Others               Failed to create HttpIo.
+
+**/
+EFI_STATUS
+HttpBootCreateHttpIo (
+  IN     HTTP_BOOT_PRIVATE_DATA       *Private
+  );
+
+/**
+  This function download the boot file by using UEFI HTTP protocol.
+  
+  @param[in]       Private         The pointer to the driver's private data.
+  @param[in]       HeaderOnly      Only request the response header, it could save a lot of time if
+                                   the caller only want to know the size of the requested file.
+  @param[in, out]  BufferSize      On input the size of Buffer in bytes. On output with a return
+                                   code of EFI_SUCCESS, the amount of data transferred to
+                                   Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+                                   the size of Buffer required to retrieve the requested file.
+  @param[out]      Buffer          The memory buffer to transfer the file to. IF Buffer is NULL,
+                                   then the size of the requested file is returned in
+                                   BufferSize.
+
+  @retval EFI_SUCCESS              The file was loaded.
+  @retval EFI_INVALID_PARAMETER    BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
+  @retval EFI_OUT_OF_RESOURCES     Could not allocate needed resources
+  @retval EFI_BUFFER_TOO_SMALL     The BufferSize is too small to read the current directory entry.
+                                   BufferSize has been updated with the size needed to complete
+                                   the request.
+  @retval Others                   Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootGetBootFile (
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private,
+  IN     BOOLEAN                  HeaderOnly,
+  IN OUT UINTN                    *BufferSize,
+     OUT UINT8                    *Buffer
+  );
+
+/**
+  Clean up all cached data.
+
+  @param[in]          Private         The pointer to the driver's private data.
+
+**/
+VOID
+HttpBootFreeCacheList (
+  IN     HTTP_BOOT_PRIVATE_DATA   *Private
+  );
+
+#endif
diff --git a/NetworkPkg/HttpBootDxe/HttpBootComponentName.c b/NetworkPkg/HttpBootDxe/HttpBootComponentName.c
new file mode 100644 (file)
index 0000000..4e4a52a
--- /dev/null
@@ -0,0 +1,180 @@
+/** @file
+  Implementation of EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL protocol.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "HttpBootDxe.h"
+
+///
+/// Component Name Protocol instance
+///
+GLOBAL_REMOVE_IF_UNREFERENCED 
+EFI_COMPONENT_NAME_PROTOCOL  gHttpBootDxeComponentName = {
+  (EFI_COMPONENT_NAME_GET_DRIVER_NAME)    HttpBootDxeComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME)HttpBootDxeComponentNameGetControllerName,
+  "eng"
+};
+
+///
+/// Component Name 2 Protocol instance
+///
+GLOBAL_REMOVE_IF_UNREFERENCED 
+EFI_COMPONENT_NAME2_PROTOCOL  gHttpBootDxeComponentName2 = {
+  HttpBootDxeComponentNameGetDriverName,
+  HttpBootDxeComponentNameGetControllerName,
+  "en"
+};
+
+///
+/// Table of driver names
+///
+GLOBAL_REMOVE_IF_UNREFERENCED 
+EFI_UNICODE_STRING_TABLE mHttpBootDxeDriverNameTable[] = {
+  { "eng;en", (CHAR16 *)L"UEFI HTTP Boot Driver" },
+  { NULL, NULL }
+};
+
+///
+/// Table of controller names
+///
+GLOBAL_REMOVE_IF_UNREFERENCED 
+EFI_UNICODE_STRING_TABLE mHttpBootDxeControllerNameTable[] = {
+  { "eng;en", (CHAR16 *)L"UEFI Http Boot Controller" },
+  { NULL, NULL }
+};
+
+/**
+  Retrieves a Unicode string that is the user-readable name of the EFI Driver.
+
+  @param  This       A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  Language   A pointer to a three-character ISO 639-2 language identifier.
+                     This is the language of the driver name that that the caller
+                     is requesting, and it must match one of the languages specified
+                     in SupportedLanguages.  The number of languages supported by a
+                     driver is up to the driver writer.
+  @param  DriverName A pointer to the Unicode string to return.  This Unicode string
+                     is the name of the driver specified by This in the language
+                     specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by This
+                                and the language specified by Language was returned
+                                in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support the
+                                language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetDriverName (
+  IN EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mHttpBootDxeDriverNameTable,
+           DriverName,
+           (BOOLEAN) (This != &gHttpBootDxeComponentName2)
+           );
+}
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by an EFI Driver.
+
+  @param  This             A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  ControllerHandle The handle of a controller that the driver specified by
+                           This is managing.  This handle specifies the controller
+                           whose name is to be returned.
+  @param  ChildHandle      The handle of the child controller to retrieve the name
+                           of.  This is an optional parameter that may be NULL.  It
+                           will be NULL for device drivers.  It will also be NULL
+                           for a bus drivers that wish to retrieve the name of the
+                           bus controller.  It will not be NULL for a bus driver
+                           that wishes to retrieve the name of a child controller.
+  @param  Language         A pointer to a three character ISO 639-2 language
+                           identifier.  This is the language of the controller name
+                           that the caller is requesting, and it must match one
+                           of the languages specified in SupportedLanguages.  The
+                           number of languages supported by a driver is up to the
+                           driver writer.
+  @param  ControllerName   A pointer to the Unicode string to return.  This Unicode
+                           string is the name of the controller specified by
+                           ControllerHandle and ChildHandle in the language specified
+                           by Language, from the point of view of the driver specified
+                           by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in the
+                                language specified by Language for the driver
+                                specified by This was returned in DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently managing
+                                the controller specified by ControllerHandle and
+                                ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support the
+                                language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  EFI_HANDLE                    ControllerHandle,
+  IN  EFI_HANDLE                    ChildHandle        OPTIONAL,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **ControllerName
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_HANDLE                      NicHandle;
+  UINT32                          *Id;
+
+  if (ControllerHandle == NULL || ChildHandle != NULL) {
+    return EFI_UNSUPPORTED;
+  }
+  
+  NicHandle = HttpBootGetNicByIp4Children (ControllerHandle);
+  if (NicHandle == NULL) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Try to retrieve the private data by caller ID GUID.
+  //
+  Status = gBS->OpenProtocol (
+                  NicHandle,
+                  &gEfiCallerIdGuid,
+                  (VOID **) &Id,
+                  NULL,
+                  NULL,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return LookupUnicodeString2 (
+           Language,
+           This->SupportedLanguages,
+           mHttpBootDxeControllerNameTable,
+           ControllerName,
+           (BOOLEAN)(This != &gHttpBootDxeComponentName2)
+           );
+
+}
diff --git a/NetworkPkg/HttpBootDxe/HttpBootComponentName.h b/NetworkPkg/HttpBootDxe/HttpBootComponentName.h
new file mode 100644 (file)
index 0000000..706fa8d
--- /dev/null
@@ -0,0 +1,99 @@
+/** @file
+  Declaration of HTTP boot driver's EFI_COMPONENT_NAME_PROTOCOL and 
+  EFI_COMPONENT_NAME2_PROTOCOL function.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_UEFI_HTTP_BOOT_COM_NAME_H__
+#define __EFI_UEFI_HTTP_BOOT_COM_NAME_H__
+
+/**
+  Retrieves a Unicode string that is the user-readable name of the EFI Driver.
+
+  @param  This       A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  Language   A pointer to a three-character ISO 639-2 language identifier.
+                     This is the language of the driver name that that the caller
+                     is requesting, and it must match one of the languages specified
+                     in SupportedLanguages.  The number of languages supported by a
+                     driver is up to the driver writer.
+  @param  DriverName A pointer to the Unicode string to return.  This Unicode string
+                     is the name of the driver specified by This in the language
+                     specified by Language.
+
+  @retval EFI_SUCCESS           The Unicode string for the Driver specified by This
+                                and the language specified by Language was returned
+                                in DriverName.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER DriverName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support the
+                                language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetDriverName (
+  IN EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  );
+
+/**
+  Retrieves a Unicode string that is the user readable name of the controller
+  that is being managed by an EFI Driver.
+
+  @param  This             A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+  @param  ControllerHandle The handle of a controller that the driver specified by
+                           This is managing.  This handle specifies the controller
+                           whose name is to be returned.
+  @param  ChildHandle      The handle of the child controller to retrieve the name
+                           of.  This is an optional parameter that may be NULL.  It
+                           will be NULL for device drivers.  It will also be NULL
+                           for a bus drivers that wish to retrieve the name of the
+                           bus controller.  It will not be NULL for a bus driver
+                           that wishes to retrieve the name of a child controller.
+  @param  Language         A pointer to a three character ISO 639-2 language
+                           identifier.  This is the language of the controller name
+                           that the caller is requesting, and it must match one
+                           of the languages specified in SupportedLanguages.  The
+                           number of languages supported by a driver is up to the
+                           driver writer.
+  @param  ControllerName   A pointer to the Unicode string to return.  This Unicode
+                           string is the name of the controller specified by
+                           ControllerHandle and ChildHandle in the language specified
+                           by Language, from the point of view of the driver specified
+                           by This.
+
+  @retval EFI_SUCCESS           The Unicode string for the user-readable name in the
+                                language specified by Language for the driver
+                                specified by This was returned in DriverName.
+  @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+  @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+  @retval EFI_INVALID_PARAMETER Language is NULL.
+  @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+  @retval EFI_UNSUPPORTED       The driver specified by This is not currently managing
+                                the controller specified by ControllerHandle and
+                                ChildHandle.
+  @retval EFI_UNSUPPORTED       The driver specified by This does not support the
+                                language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME2_PROTOCOL  *This,
+  IN  EFI_HANDLE                    ControllerHandle,
+  IN  EFI_HANDLE                    ChildHandle        OPTIONAL,
+  IN  CHAR8                         *Language,
+  OUT CHAR16                        **ControllerName
+  );
+
+#endif
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c
new file mode 100644 (file)
index 0000000..7486d24
--- /dev/null
@@ -0,0 +1,829 @@
+/** @file
+  Functions implementation related with DHCPv4 for HTTP boot driver.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "HttpBootDxe.h"
+
+//
+// This is a map from the interested DHCP4 option tags' index to the tag value.
+//
+UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = {
+  HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN,
+  HTTP_BOOT_DHCP4_TAG_OVERLOAD,
+  HTTP_BOOT_DHCP4_TAG_MSG_TYPE,
+  HTTP_BOOT_DHCP4_TAG_SERVER_ID,
+  HTTP_BOOT_DHCP4_TAG_CLASS_ID,
+  HTTP_BOOT_DHCP4_TAG_BOOTFILE,
+  HTTP_BOOT_DHCP4_TAG_DNS_SERVER
+};
+
+//
+// There are 4 times retries with the value of 4, 8, 16 and 32, refers to UEFI 2.5 spec.
+//
+UINT32 mHttpDhcpTimeout[4] = {4, 8, 16, 32};
+
+/**
+  Build the options buffer for the DHCPv4 request packet.
+
+  @param[in]  Private             Pointer to HTTP boot driver private data.
+  @param[out] OptList             Pointer to the option pointer array.
+  @param[in]  Buffer              Pointer to the buffer to contain the option list.
+
+  @return     Index               The count of the built-in options.
+
+**/
+UINT32
+HttpBootBuildDhcp4Options (
+  IN  HTTP_BOOT_PRIVATE_DATA        *Private,
+  OUT EFI_DHCP4_PACKET_OPTION       **OptList,
+  IN  UINT8                         *Buffer
+  )
+{
+  HTTP_BOOT_DHCP4_OPTION_ENTRY  OptEnt;
+  UINT16                        Value;
+  UINT32                        Index;
+
+  Index      = 0;
+  OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+
+  //
+  // Append parameter request list option.
+  //
+  OptList[Index]->OpCode    = HTTP_BOOT_DHCP4_TAG_PARA_LIST;
+  OptList[Index]->Length    = 27;
+  OptEnt.Para               = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+  OptEnt.Para->ParaList[0]  = HTTP_BOOT_DHCP4_TAG_NETMASK;
+  OptEnt.Para->ParaList[1]  = HTTP_BOOT_DHCP4_TAG_TIME_OFFSET;
+  OptEnt.Para->ParaList[2]  = HTTP_BOOT_DHCP4_TAG_ROUTER;
+  OptEnt.Para->ParaList[3]  = HTTP_BOOT_DHCP4_TAG_TIME_SERVER;
+  OptEnt.Para->ParaList[4]  = HTTP_BOOT_DHCP4_TAG_NAME_SERVER;
+  OptEnt.Para->ParaList[5]  = HTTP_BOOT_DHCP4_TAG_DNS_SERVER;
+  OptEnt.Para->ParaList[6]  = HTTP_BOOT_DHCP4_TAG_HOSTNAME;
+  OptEnt.Para->ParaList[7]  = HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN;
+  OptEnt.Para->ParaList[8]  = HTTP_BOOT_DHCP4_TAG_DOMAINNAME;
+  OptEnt.Para->ParaList[9]  = HTTP_BOOT_DHCP4_TAG_ROOTPATH;
+  OptEnt.Para->ParaList[10] = HTTP_BOOT_DHCP4_TAG_EXTEND_PATH;
+  OptEnt.Para->ParaList[11] = HTTP_BOOT_DHCP4_TAG_EMTU;
+  OptEnt.Para->ParaList[12] = HTTP_BOOT_DHCP4_TAG_TTL;
+  OptEnt.Para->ParaList[13] = HTTP_BOOT_DHCP4_TAG_BROADCAST;
+  OptEnt.Para->ParaList[14] = HTTP_BOOT_DHCP4_TAG_NIS_DOMAIN;
+  OptEnt.Para->ParaList[15] = HTTP_BOOT_DHCP4_TAG_NIS_SERVER;
+  OptEnt.Para->ParaList[16] = HTTP_BOOT_DHCP4_TAG_NTP_SERVER;
+  OptEnt.Para->ParaList[17] = HTTP_BOOT_DHCP4_TAG_VENDOR;
+  OptEnt.Para->ParaList[18] = HTTP_BOOT_DHCP4_TAG_REQUEST_IP;
+  OptEnt.Para->ParaList[19] = HTTP_BOOT_DHCP4_TAG_LEASE;
+  OptEnt.Para->ParaList[20] = HTTP_BOOT_DHCP4_TAG_SERVER_ID;
+  OptEnt.Para->ParaList[21] = HTTP_BOOT_DHCP4_TAG_T1;
+  OptEnt.Para->ParaList[22] = HTTP_BOOT_DHCP4_TAG_T2;
+  OptEnt.Para->ParaList[23] = HTTP_BOOT_DHCP4_TAG_CLASS_ID;
+  OptEnt.Para->ParaList[25] = HTTP_BOOT_DHCP4_TAG_BOOTFILE;
+  OptEnt.Para->ParaList[26] = HTTP_BOOT_DHCP4_TAG_UUID;
+  Index++;
+  OptList[Index]            = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+  //
+  // Append UUID/Guid-based client identifier option
+  //
+  OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_UUID;
+  OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UUID);
+  OptEnt.Uuid             = (HTTP_BOOT_DHCP4_OPTION_UUID *) OptList[Index]->Data;
+  OptEnt.Uuid->Type       = 0;
+  if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
+    //
+    // Zero the Guid to indicate NOT programable if failed to get system Guid.
+    //
+    ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
+  }
+  Index++;
+  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+  //
+  // Append client network device interface option
+  //
+  OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_UNDI;
+  OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UNDI);
+  OptEnt.Undi             = (HTTP_BOOT_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
+
+  if (Private->Nii != NULL) {
+    OptEnt.Undi->Type     = Private->Nii->Type;
+    OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
+    OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
+  } else {
+    OptEnt.Undi->Type     = DEFAULT_UNDI_TYPE;
+    OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
+    OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
+  }
+
+  Index++;
+  OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+  //
+  // Append client system architecture option
+  //
+  OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_ARCH;
+  OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_ARCH);
+  OptEnt.Arch             = (HTTP_BOOT_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
+  Value                   = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE);
+  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
+  Index++;
+  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+  //
+  // Append vendor class identify option
+  //
+  OptList[Index]->OpCode  = HTTP_BOOT_DHCP4_TAG_CLASS_ID;
+  OptList[Index]->Length  = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_CLID);
+  OptEnt.Clid             = (HTTP_BOOT_DHCP4_OPTION_CLID *) OptList[Index]->Data;
+  CopyMem (
+    OptEnt.Clid,
+    DEFAULT_CLASS_ID_DATA,
+    sizeof (HTTP_BOOT_DHCP4_OPTION_CLID)
+    );
+  HttpBootUintnToAscDecWithFormat (
+    EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE,
+    OptEnt.Clid->ArchitectureType,
+    sizeof (OptEnt.Clid->ArchitectureType)
+    );
+
+  if (Private->Nii != NULL) {
+    CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
+    HttpBootUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
+    HttpBootUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
+  }
+
+  Index++;
+
+  return Index;
+}
+
+/**
+  Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.
+
+  @param[in]  Buffer              Pointer to the option buffer.
+  @param[in]  Length              Length of the option buffer.
+  @param[in]  OptTag              Tag of the required option.
+
+  @retval     NULL                Failed to find the required option.
+  @retval     Others              The position of the required option.
+
+**/
+EFI_DHCP4_PACKET_OPTION *
+HttpBootParseDhcp4Options (
+  IN UINT8                      *Buffer,
+  IN UINT32                     Length,
+  IN UINT8                      OptTag
+  )
+{
+  EFI_DHCP4_PACKET_OPTION       *Option;
+  UINT32                        Offset;
+
+  Option  = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+  Offset  = 0;
+
+  while (Offset < Length && Option->OpCode != HTTP_BOOT_DHCP4_TAG_EOP) {
+
+    if (Option->OpCode == OptTag) {
+      //
+      // Found the required option.
+      //
+      return Option;
+    }
+
+    //
+    // Skip the current option to the next.
+    //
+    if (Option->OpCode == HTTP_BOOT_DHCP4_TAG_PAD) {
+      Offset++;
+    } else {
+      Offset += Option->Length + 2;
+    }
+
+    Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
+  }
+
+  return NULL;
+}
+
+/**
+  Cache the DHCPv4 packet.
+
+  @param[in]  Dst          Pointer to the cache buffer for DHCPv4 packet.
+  @param[in]  Src          Pointer to the DHCPv4 packet to be cached.
+
+**/
+VOID
+HttpBootCacheDhcp4Packet (
+  IN EFI_DHCP4_PACKET     *Dst,
+  IN EFI_DHCP4_PACKET     *Src
+  )
+{
+  ASSERT (Dst->Size >= Src->Length);
+
+  CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
+  Dst->Length = Src->Length;
+}
+
+/**
+  Parse the cached DHCPv4 packet, including all the options.
+
+  @param[in]  Cache4           Pointer to cached DHCPv4 packet.
+
+  @retval     EFI_SUCCESS      Parsed the DHCPv4 packet successfully.
+  @retval     EFI_DEVICE_ERROR Failed to parse an invalid packet.
+
+**/
+EFI_STATUS
+HttpBootParseDhcp4Packet (
+  IN HTTP_BOOT_DHCP4_PACKET_CACHE    *Cache4
+  )
+{
+  EFI_DHCP4_PACKET               *Offer;
+  EFI_DHCP4_PACKET_OPTION        **Options;
+  UINTN                          Index;
+  EFI_DHCP4_PACKET_OPTION        *Option;
+  BOOLEAN                        IsProxyOffer;
+  BOOLEAN                        IsHttpOffer;
+  BOOLEAN                        IsDnsOffer;
+  BOOLEAN                        IpExpressedUri;
+  UINT8                          *Ptr8;
+  EFI_STATUS                     Status;
+  HTTP_BOOT_OFFER_TYPE           OfferType;
+  EFI_IPv4_ADDRESS               IpAddr;
+  
+  IsDnsOffer     = FALSE;
+  IpExpressedUri = FALSE;
+  IsProxyOffer   = FALSE;
+  IsHttpOffer    = FALSE;
+
+  ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));
+
+  Offer   = &Cache4->Packet.Offer;
+  Options = Cache4->OptList;
+
+  //
+  // Parse DHCPv4 options in this offer, and store the pointers.
+  // First, try to parse DHCPv4 options from the DHCP optional parameters field.
+  //
+  for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
+    Options[Index] = HttpBootParseDhcp4Options (
+                       Offer->Dhcp4.Option,
+                       GET_OPTION_BUFFER_LEN (Offer),
+                       mInterestedDhcp4Tags[Index]
+                       );
+  }
+  //
+  // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132. 
+  // If yes, try to parse options from the BootFileName field, then ServerName field.
+  //
+  Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD];
+  if (Option != NULL) {
+    if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_FILE) != 0) {
+      for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
+        if (Options[Index] == NULL) {
+          Options[Index] = HttpBootParseDhcp4Options (
+                             (UINT8 *) Offer->Dhcp4.Header.BootFileName,
+                             sizeof (Offer->Dhcp4.Header.BootFileName),
+                             mInterestedDhcp4Tags[Index]
+                             );
+        }
+      }
+    }
+    if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME) != 0) {
+      for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
+        if (Options[Index] == NULL) {
+          Options[Index] = HttpBootParseDhcp4Options (
+                             (UINT8 *) Offer->Dhcp4.Header.ServerName,
+                             sizeof (Offer->Dhcp4.Header.ServerName),
+                             mInterestedDhcp4Tags[Index]
+                             );
+        }
+      }
+    }
+  }
+
+  //
+  // The offer with "yiaddr" is a proxy offer.
+  //
+  if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {
+    IsProxyOffer = TRUE;
+  }
+
+  //
+  // The offer with "HttpClient" is a Http offer.
+  //
+  Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID];
+  if ((Option != NULL) && (Option->Length >= 9) &&
+      (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {
+    IsHttpOffer = TRUE;
+  }
+
+  //
+  // The offer with Domain Server is a DNS offer.
+  //
+  Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
+  if (Option != NULL) {
+    IsDnsOffer = TRUE;
+  }
+
+  //
+  // Parse boot file name:
+  // Boot URI information is provided thru 'file' field in DHCP Header or option 67.
+  // According to RFC 2132, boot file name should be read from DHCP option 67 (bootfile name) if present.
+  // Otherwise, read from boot file field in DHCP header.
+  //
+  if (Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+    //
+    // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null
+    // terminated string. So force to append null terminated character at the end of string.
+    //
+    Ptr8 =  (UINT8*)&Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
+    Ptr8 += Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Length;
+    if (*(Ptr8 - 1) != '\0') {
+      *Ptr8 = '\0';
+    }
+  } else if (Offer->Dhcp4.Header.BootFileName[0] != 0) {
+    //
+    // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.
+    // Do not count dhcp option header here, or else will destroy the serverhostname.
+    //
+    Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)
+                                                    (&Offer->Dhcp4.Header.BootFileName[0] -
+                                                    OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
+  }
+
+  //
+  // Http offer must have a boot URI.
+  //
+  if (IsHttpOffer && Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Try to retrieve the IP of HTTP server from URI. 
+  //
+  if (IsHttpOffer) {
+    Status = HttpParseUrl (
+               (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
+               (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data),
+               FALSE,
+               &Cache4->UriParser
+               );
+    if (EFI_ERROR (Status)) {
+      return EFI_DEVICE_ERROR;
+    }
+
+    Status = HttpUrlGetIp4 (
+               (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
+               Cache4->UriParser,
+               &IpAddr
+               );
+    IpExpressedUri = !EFI_ERROR (Status);
+  }
+
+  //
+  // Determine offer type of the DHCPv4 packet.
+  //
+  if (IsHttpOffer) {
+    if (IpExpressedUri) {
+      OfferType = IsProxyOffer ? HttpOfferTypeProxyIpUri : HttpOfferTypeDhcpIpUri;
+    } else {
+      if (!IsProxyOffer) {
+        OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;
+      } else {
+        OfferType = HttpOfferTypeProxyNameUri;
+      }
+    }
+
+  } else {
+    if (!IsProxyOffer) {
+      OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;
+    } else {
+      return EFI_DEVICE_ERROR;
+    }
+  }
+  
+  Cache4->OfferType = OfferType;
+  return EFI_SUCCESS;
+}
+
+/**
+  Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.
+
+  @param[in]  Private               Pointer to HTTP boot driver private data.
+  @param[in]  RcvdOffer             Pointer to the received offer packet.
+
+**/
+VOID
+HttpBootCacheDhcp4Offer (
+  IN HTTP_BOOT_PRIVATE_DATA  *Private,
+  IN EFI_DHCP4_PACKET        *RcvdOffer
+  )
+{
+  HTTP_BOOT_DHCP4_PACKET_CACHE  *Cache4;
+  EFI_DHCP4_PACKET              *Offer;
+  HTTP_BOOT_OFFER_TYPE          OfferType;
+
+  ASSERT (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM);
+  Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;
+  Offer  = &Cache4->Packet.Offer;
+
+  //
+  // Cache the content of DHCPv4 packet firstly.
+  //
+  HttpBootCacheDhcp4Packet (Offer, RcvdOffer);
+
+  //
+  // Validate the DHCPv4 packet, and parse the options and offer type.
+  //
+  if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) {
+    return;
+  }
+
+  //
+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
+  //
+  OfferType = Cache4->OfferType;
+  ASSERT (OfferType < HttpOfferTypeMax);
+  ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);
+  Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+  Private->OfferCount[OfferType]++;
+  Private->OfferNum++;
+}
+
+/**
+  Select an DHCPv4 offer, and record SelectIndex and SelectProxyType.
+
+  @param[in]  Private             Pointer to HTTP boot driver private data.
+
+**/
+VOID
+HttpBootSelectDhcp4Offer (
+  IN HTTP_BOOT_PRIVATE_DATA  *Private
+  )
+{
+  Private->SelectIndex = 0;
+  Private->SelectProxyType = HttpOfferTypeMax;
+  
+  //
+  // Priority1: HttpOfferTypeDhcpIpUri                           
+  // Priority2: HttpOfferTypeDhcpNameUriDns                      
+  // Priority3: HttpOfferTypeDhcpOnly + HttpOfferTypeProxyIpUri  
+  // Priority4: HttpOfferTypeDhcpDns  + HttpOfferTypeProxyIpUri  
+  // Priority5: HttpOfferTypeDhcpDns  + HttpOfferTypeProxyNameUri
+  // Priority6: HttpOfferTypeDhcpDns  + HttpOfferTypeDhcpNameUri 
+  //    
+  if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) {
+    
+    Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1;
+    
+  } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) {
+  
+    Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1;
+    
+  } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0 &&
+             Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) {
+             
+    Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1;
+    Private->SelectProxyType = HttpOfferTypeProxyIpUri;
+    
+  } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
+             Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) {
+             
+    Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+    Private->SelectProxyType = HttpOfferTypeProxyIpUri;
+    
+  } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
+             Private->OfferCount[HttpOfferTypeProxyNameUri] > 0) {
+             
+    Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+    Private->SelectProxyType = HttpOfferTypeProxyNameUri;
+    
+  } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
+             Private->OfferCount[HttpOfferTypeDhcpNameUri] > 0) {
+             
+    Private->SelectIndex     = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+    Private->SelectProxyType = HttpOfferTypeDhcpNameUri;
+  }
+}
+
+
+/**
+  EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
+  to intercept events that occurred in the configuration process.
+
+  @param[in]  This              Pointer to the EFI DHCPv4 Protocol.
+  @param[in]  Context           Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().
+  @param[in]  CurrentState      The current operational state of the EFI DHCPv4 Protocol driver.
+  @param[in]  Dhcp4Event        The event that occurs in the current state, which usually means a
+                                state transition.
+  @param[in]  Packet            The DHCPv4 packet that is going to be sent or already received.
+  @param[out] NewPacket         The packet that is used to replace the above Packet.
+
+  @retval EFI_SUCCESS           Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
+  @retval EFI_NOT_READY         Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
+                                driver will continue to wait for more DHCPOFFER packets until the
+                                retry timeout expires.
+  @retval EFI_ABORTED           Tells the EFI DHCPv4 Protocol driver to abort the current process
+                                and return to the Dhcp4Init or Dhcp4InitReboot state.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDhcp4CallBack (
+  IN  EFI_DHCP4_PROTOCOL               *This,
+  IN  VOID                             *Context,
+  IN  EFI_DHCP4_STATE                  CurrentState,
+  IN  EFI_DHCP4_EVENT                  Dhcp4Event,
+  IN  EFI_DHCP4_PACKET                 *Packet            OPTIONAL,
+  OUT EFI_DHCP4_PACKET                 **NewPacket        OPTIONAL
+  )
+{
+  HTTP_BOOT_PRIVATE_DATA               *Private;
+  EFI_DHCP4_PACKET_OPTION              *MaxMsgSize;
+  UINT16                               Value;
+  EFI_STATUS                           Status;
+
+  if ((Dhcp4Event != Dhcp4RcvdOffer) && (Dhcp4Event != Dhcp4SelectOffer)) {
+    return EFI_SUCCESS;
+  }
+  
+  Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
+
+  //
+  // Override the Maximum DHCP Message Size.
+  //
+  MaxMsgSize = HttpBootParseDhcp4Options (
+                 Packet->Dhcp4.Option,
+                 GET_OPTION_BUFFER_LEN (Packet),
+                 HTTP_BOOT_DHCP4_TAG_MAXMSG
+                 );
+  if (MaxMsgSize != NULL) {
+    Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE);
+    CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
+  }
+
+  Status = EFI_SUCCESS;
+  switch (Dhcp4Event) {
+  case Dhcp4RcvdOffer:
+    Status = EFI_NOT_READY;
+    if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {
+      //
+      // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
+      // the OfferIndex and OfferCount.
+      //
+      HttpBootCacheDhcp4Offer (Private, Packet);
+    }
+    break;
+
+  case Dhcp4SelectOffer:
+    //
+    // Select offer according to the priority in UEFI spec, and record the SelectIndex 
+    // and SelectProxyType.
+    //
+    HttpBootSelectDhcp4Offer (Private);
+
+    if (Private->SelectIndex == 0) {
+      Status = EFI_ABORTED;
+    } else {
+      *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;
+    }
+    break;
+    
+  default:
+    break;
+  }
+
+  return Status;
+}
+
+/**
+  This function will register the IPv4 gateway address to the network device.
+  
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         The new IP configuration has been configured successfully.
+  @retval     Others              Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootRegisterIp4Gateway (
+  IN HTTP_BOOT_PRIVATE_DATA         *Private
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_IP4_CONFIG2_PROTOCOL        *Ip4Config2;
+
+  ASSERT (!Private->UsingIpv6);
+
+  Ip4Config2 = Private->Ip4Config2;
+
+  //
+  // Configure the gateway if valid.
+  //
+  if (!EFI_IP4_EQUAL (&Private->GatewayIp, &mZeroIp4Addr)) {
+    Status = Ip4Config2->SetData (
+                           Ip4Config2,
+                           Ip4Config2DataTypeGateway,
+                           sizeof (EFI_IPv4_ADDRESS),
+                           &Private->GatewayIp
+                           );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This function will register the default DNS addresses to the network device.
+  
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.
+  @param[in]  DataLength          Size of the buffer pointed to by DnsServerData in bytes.
+  @param[in]  DnsServerData       Point a list of DNS server address in an array
+                                  of EFI_IPv4_ADDRESS instances.
+
+  @retval     EFI_SUCCESS         The DNS configuration has been configured successfully.
+  @retval     Others              Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootRegisterIp4Dns (
+  IN HTTP_BOOT_PRIVATE_DATA         *Private,
+  IN UINTN                          DataLength,
+  IN VOID                           *DnsServerData
+  )
+{
+  EFI_IP4_CONFIG2_PROTOCOL        *Ip4Config2;
+  
+  ASSERT (!Private->UsingIpv6);
+
+  Ip4Config2 = Private->Ip4Config2;
+  
+  return Ip4Config2->SetData (
+                       Ip4Config2,
+                       Ip4Config2DataTypeDnsServer,
+                       DataLength,
+                       DnsServerData
+                       );
+}
+
+
+/**
+  This function will switch the IP4 configuration policy to Static.
+
+  @param[in]  Private             Pointer to HTTP boot driver private data.
+
+  @retval     EFI_SUCCESS         The policy is already configured to static.
+  @retval     Others              Other error as indicated..
+
+**/
+EFI_STATUS
+HttpBootSetIpPolicy (
+  IN HTTP_BOOT_PRIVATE_DATA         *Private
+  )
+{
+  EFI_IP4_CONFIG2_POLICY          Policy;
+  EFI_STATUS                      Status;
+  EFI_IP4_CONFIG2_PROTOCOL        *Ip4Config2;
+  UINTN                           DataSize;
+
+  Ip4Config2 = Private->Ip4Config2;
+
+  DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
+  Status = Ip4Config2->GetData (
+                         Ip4Config2,
+                         Ip4Config2DataTypePolicy,
+                         &DataSize,
+                         &Policy
+                         );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (Policy != Ip4Config2PolicyStatic) {
+    Policy = Ip4Config2PolicyStatic;
+    Status= Ip4Config2->SetData (
+                          Ip4Config2,
+                          Ip4Config2DataTypePolicy,
+                          sizeof (EFI_IP4_CONFIG2_POLICY),
+                          &Policy
+                          );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    } 
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information.
+
+  @param[in]  Private           Pointer to HTTP boot driver private data.
+
+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.
+  @retval Others                Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+HttpBootDhcp4Dora (
+  IN HTTP_BOOT_PRIVATE_DATA         *Private
+  )
+{
+  EFI_DHCP4_PROTOCOL           *Dhcp4;
+  UINT32                       OptCount;
+  EFI_DHCP4_PACKET_OPTION      *OptList[HTTP_BOOT_DHCP4_OPTION_MAX_NUM];
+  UINT8                        Buffer[HTTP_BOOT_DHCP4_OPTION_MAX_SIZE];
+  EFI_DHCP4_CONFIG_DATA        Config;
+  EFI_STATUS                   Status;
+  EFI_DHCP4_MODE_DATA          Mode;
+  
+  Dhcp4 = Private->Dhcp4;
+  ASSERT (Dhcp4 != NULL);
+
+  Status = HttpBootSetIpPolicy (Private);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Build option list for the request packet.
+  //
+  OptCount = HttpBootBuildDhcp4Options (Private, OptList, Buffer);
+  ASSERT (OptCount > 0);
+
+  ZeroMem (&Config, sizeof(Config));
+  Config.OptionCount      = OptCount;
+  Config.OptionList       = OptList;
+  Config.Dhcp4Callback    = HttpBootDhcp4CallBack;
+  Config.CallbackContext  = Private;
+  Config.DiscoverTryCount = HTTP_BOOT_DHCP_RETRIES;
+  Config.DiscoverTimeout  = mHttpDhcpTimeout;
+
+  //
+  // Configure the DHCPv4 instance for HTTP boot.
+  //
+  Status = Dhcp4->Configure (Dhcp4, &Config);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Initialize the record fields for DHCPv4 offer in private data.
+  //
+  Private->OfferNum = 0;
+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+  //
+  // Start DHCPv4 D.O.R.A. process to acquire IPv4 address.
+  //
+  Status = Dhcp4->Start (Dhcp4, NULL);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Get the acquired IPv4 address and store them.
+  //
+  Status = Dhcp4->GetModeData (Dhcp4, &Mode);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  ASSERT (Mode.State == Dhcp4Bound);
+  CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
+  CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+  CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+
+  Status = HttpBootRegisterIp4Gateway (Private);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  AsciiPrint ("\n  Station IP address is ");
+  HttpBootShowIp4Addr (&Private->StationIp.v4);
+  AsciiPrint ("\n");
+
+ON_EXIT:
+  if (EFI_ERROR (Status)) {
+    Dhcp4->Stop (Dhcp4);
+    Dhcp4->Configure (Dhcp4, NULL);
+  } else {
+    ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+    Dhcp4->Configure (Dhcp4, &Config);
+  }
+
+  return Status;
+}
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h b/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h
new file mode 100644 (file)
index 0000000..42a3874
--- /dev/null
@@ -0,0 +1,279 @@
+/** @file
+  Functions declaration related with DHCPv4 for HTTP boot driver.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_UEFI_HTTP_BOOT_DHCP4_H__
+#define __EFI_UEFI_HTTP_BOOT_DHCP4_H__
+
+#define HTTP_BOOT_DHCP4_OPTION_MAX_NUM         16
+#define HTTP_BOOT_DHCP4_OPTION_MAX_SIZE        312
+#define HTTP_BOOT_DHCP4_PACKET_MAX_SIZE        1472
+
+#define HTTP_BOOT_DHCP4_OPCODE_REQUEST         1
+#define HTTP_BOOT_DHCP4_OPCODE_REPLY           2
+#define HTTP_BOOT_DHCP4_MSG_TYPE_REQUEST       3
+#define HTTP_BOOT_DHCP4_MAGIC                  0x63538263 // network byte order
+
+//
+// Dhcp Options
+//
+#define HTTP_BOOT_DHCP4_TAG_PAD                0    // Pad Option
+#define HTTP_BOOT_DHCP4_TAG_EOP                255  // End Option
+#define HTTP_BOOT_DHCP4_TAG_NETMASK            1    // Subnet Mask
+#define HTTP_BOOT_DHCP4_TAG_TIME_OFFSET        2    // Time Offset from UTC
+#define HTTP_BOOT_DHCP4_TAG_ROUTER             3    // Router option,
+#define HTTP_BOOT_DHCP4_TAG_TIME_SERVER        4    // Time Server
+#define HTTP_BOOT_DHCP4_TAG_NAME_SERVER        5    // Name Server
+#define HTTP_BOOT_DHCP4_TAG_DNS_SERVER         6    // Domain Name Server
+#define HTTP_BOOT_DHCP4_TAG_HOSTNAME           12   // Host Name
+#define HTTP_BOOT_DHCP4_TAG_BOOTFILE_LEN       13   // Boot File Size
+#define HTTP_BOOT_DHCP4_TAG_DUMP               14   // Merit Dump File
+#define HTTP_BOOT_DHCP4_TAG_DOMAINNAME         15   // Domain Name
+#define HTTP_BOOT_DHCP4_TAG_ROOTPATH           17   // Root path
+#define HTTP_BOOT_DHCP4_TAG_EXTEND_PATH        18   // Extensions Path
+#define HTTP_BOOT_DHCP4_TAG_EMTU               22   // Maximum Datagram Reassembly Size
+#define HTTP_BOOT_DHCP4_TAG_TTL                23   // Default IP Time-to-live
+#define HTTP_BOOT_DHCP4_TAG_BROADCAST          28   // Broadcast Address
+#define HTTP_BOOT_DHCP4_TAG_NIS_DOMAIN         40   // Network Information Service Domain
+#define HTTP_BOOT_DHCP4_TAG_NIS_SERVER         41   // Network Information Servers
+#define HTTP_BOOT_DHCP4_TAG_NTP_SERVER         42   // Network Time Protocol Servers
+#define HTTP_BOOT_DHCP4_TAG_VENDOR             43   // Vendor Specific Information
+#define HTTP_BOOT_DHCP4_TAG_REQUEST_IP         50   // Requested IP Address
+#define HTTP_BOOT_DHCP4_TAG_LEASE              51   // IP Address Lease Time
+#define HTTP_BOOT_DHCP4_TAG_OVERLOAD           52   // Option Overload
+#define HTTP_BOOT_DHCP4_TAG_MSG_TYPE           53   // DHCP Message Type
+#define HTTP_BOOT_DHCP4_TAG_SERVER_ID          54   // Server Identifier
+#define HTTP_BOOT_DHCP4_TAG_PARA_LIST          55   // Parameter Request List
+#define HTTP_BOOT_DHCP4_TAG_MAXMSG             57   // Maximum DHCP Message Size
+#define HTTP_BOOT_DHCP4_TAG_T1                 58   // Renewal (T1) Time Value
+#define HTTP_BOOT_DHCP4_TAG_T2                 59   // Rebinding (T2) Time Value
+#define HTTP_BOOT_DHCP4_TAG_CLASS_ID           60   // Vendor class identifier
+#define HTTP_BOOT_DHCP4_TAG_CLIENT_ID          61   // Client-identifier
+#define HTTP_BOOT_DHCP4_TAG_TFTP               66   // TFTP server name
+#define HTTP_BOOT_DHCP4_TAG_BOOTFILE           67   // Bootfile name
+#define HTTP_BOOT_DHCP4_TAG_ARCH               93
+#define HTTP_BOOT_DHCP4_TAG_UNDI               94
+#define HTTP_BOOT_DHCP4_TAG_UUID               97
+
+#define HTTP_BOOT_DHCP4_OVERLOAD_FILE          1
+#define HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME   2
+
+///
+/// HTTP Tag definition that identifies the processor 
+/// and programming environment of the client system.
+/// These identifiers are defined by IETF:
+/// http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml
+///
+#if defined (MDE_CPU_IA32)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE    0x000F
+#elif defined (MDE_CPU_X64)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE    0x0010
+#elif defined (MDE_CPU_ARM)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE    0x0012
+#elif defined (MDE_CPU_AARCH64)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE    0x0013
+#endif
+
+/// DHCP offer types among HTTP boot.
+/// Dhcp4 and Dhcp6 share this definition, and corresponding
+/// relatioinship is as follows:
+///   Dhcp4Discover <> Dhcp6Solicit
+///   Dhcp4Offer    <> Dhcp6Advertise
+///   Dhcp4Request  <> Dhcp6Request
+///   Dhcp4Ack      <> DHcp6Reply
+///
+typedef enum {
+  //
+  // <IP address, IP expressed URI> or
+  // <IP address, IP expressed URI, Name-server (will be ignored)>
+  //
+  HttpOfferTypeDhcpIpUri,
+  //
+  // <IP address, Domain-name expressed URI, Name-server>
+  //
+  HttpOfferTypeDhcpNameUriDns,
+  //
+  // <IP address, Name-server>
+  //
+  HttpOfferTypeDhcpDns,
+  //
+  // <IP address>
+  //
+  HttpOfferTypeDhcpOnly,
+  //
+  // <Domain-name expressed URI> or
+  // <Domain-name expressed URI, Name-server (will be ignored)>
+  //
+  HttpOfferTypeProxyNameUri,
+  //
+  // <IP expressed URI> or
+  // <IP expressed URI, Name-server (will be ignored)>
+  //
+  HttpOfferTypeProxyIpUri,
+  //
+  // <IP address, Domain-name expressed URI>
+  //
+  HttpOfferTypeDhcpNameUri,
+  HttpOfferTypeMax
+} HTTP_BOOT_OFFER_TYPE;
+
+#define HTTP_BOOT_DHCP_RETRIES            4
+#define HTTP_BOOT_OFFER_MAX_NUM           16
+
+// The array index of the DHCP4 option tag interested
+//
+#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE_LEN 0
+#define HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD     1
+#define HTTP_BOOT_DHCP4_TAG_INDEX_MSG_TYPE     2
+#define HTTP_BOOT_DHCP4_TAG_INDEX_SERVER_ID    3
+#define HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID     4
+#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE     5
+#define HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER   6
+#define HTTP_BOOT_DHCP4_TAG_INDEX_MAX          7
+
+#pragma pack(1)
+
+typedef struct {
+  UINT8 ParaList[135];
+} HTTP_BOOT_DHCP4_OPTION_PARA;
+
+typedef struct {
+  UINT16  Size;
+} HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE;
+
+typedef struct {
+  UINT8 Type;
+  UINT8 MajorVer;
+  UINT8 MinorVer;
+} HTTP_BOOT_DHCP4_OPTION_UNDI;
+
+typedef struct {
+  UINT8 Type;
+} HTTP_BOOT_DHCP4_OPTION_MESG;
+
+typedef struct {
+  UINT16 Type;
+} HTTP_BOOT_DHCP4_OPTION_ARCH;
+
+typedef struct {
+  UINT8 ClassIdentifier[11];
+  UINT8 ArchitecturePrefix[5];
+  UINT8 ArchitectureType[5];
+  UINT8 Lit3[1];
+  UINT8 InterfaceName[4];
+  UINT8 Lit4[1];
+  UINT8 UndiMajor[3];
+  UINT8 UndiMinor[3];
+} HTTP_BOOT_DHCP4_OPTION_CLID;
+
+typedef struct {
+  UINT8 Type;
+  UINT8 Guid[16];
+} HTTP_BOOT_DHCP4_OPTION_UUID;
+
+typedef struct {
+  UINT16 Type;
+  UINT16 Layer;
+} HTTP_BOOT_OPTION_BOOT_ITEM;
+
+#pragma pack()
+
+typedef union {
+  HTTP_BOOT_DHCP4_OPTION_PARA           *Para;
+  HTTP_BOOT_DHCP4_OPTION_UNDI           *Undi;
+  HTTP_BOOT_DHCP4_OPTION_ARCH           *Arch;
+  HTTP_BOOT_DHCP4_OPTION_CLID           *Clid;
+  HTTP_BOOT_DHCP4_OPTION_UUID           *Uuid;
+  HTTP_BOOT_DHCP4_OPTION_MESG           *Mesg;
+  HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE  *MaxMesgSize;
+} HTTP_BOOT_DHCP4_OPTION_ENTRY;
+
+#define GET_NEXT_DHCP_OPTION(Opt) \
+  (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \
+   sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)
+
+#define GET_OPTION_BUFFER_LEN(Pkt) \
+  ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)
+
+#define DEFAULT_CLASS_ID_DATA "HTTPClient:Arch:xxxxx:UNDI:003000"
+#define DEFAULT_UNDI_TYPE     1
+#define DEFAULT_UNDI_MAJOR    3
+#define DEFAULT_UNDI_MINOR    0
+
+typedef struct {
+  UINT32         Reserved;
+} HTTP_BOOT_VENDOR_OPTION;
+
+typedef union {
+  EFI_DHCP4_PACKET        Offer;
+  EFI_DHCP4_PACKET        Ack;
+  UINT8                   Buffer[HTTP_BOOT_DHCP4_PACKET_MAX_SIZE];
+} HTTP_BOOT_DHCP4_PACKET;
+
+typedef struct {
+  //
+  // URI component
+  //
+  CHAR8                   *Scheme;
+  CHAR8                   *Authority;
+  CHAR8                   *Path;
+  CHAR8                   *Query;
+  CHAR8                   *Fragment; /// TODO: may not required in HTTP URL
+
+  CHAR8                   *RegName; /// Point to somewhere in Authority
+  BOOLEAN                 AddrIsOk;
+  EFI_IP_ADDRESS          Address;
+  UINT16                  Port;
+} HTTP_BOOT_URI_CONTENT;
+
+typedef struct {
+  HTTP_BOOT_DHCP4_PACKET      Packet;
+  HTTP_BOOT_OFFER_TYPE        OfferType;
+  VOID                        *UriParser;
+  EFI_DHCP4_PACKET_OPTION     *OptList[HTTP_BOOT_DHCP4_TAG_INDEX_MAX];
+} HTTP_BOOT_DHCP4_PACKET_CACHE;
+
+/**
+  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information.
+
+  @param[in]  Private           Pointer to HTTP_BOOT private data.
+
+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.
+  @retval Others                Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+HttpBootDhcp4Dora (
+  IN HTTP_BOOT_PRIVATE_DATA         *Private
+  );
+
+/**
+  This function will register the default DNS addresses to the network device.
+  
+  @param[in]  Private             The pointer to HTTP_BOOT_PRIVATE_DATA.
+  @param[in]  DataLength          Size of the buffer pointed to by DnsServerData in bytes.
+  @param[in]  DnsServerData       Point a list of DNS server address in an array
+                                  of EFI_IPv4_ADDRESS instances.
+
+  @retval     EFI_SUCCESS         The DNS configuration has been configured successfully.
+  @retval     Others              Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootRegisterIp4Dns (
+  IN HTTP_BOOT_PRIVATE_DATA         *Private,
+  IN UINTN                          DataLength,
+  IN VOID                           *DnsServerData
+  );
+
+#endif
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.c b/NetworkPkg/HttpBootDxe/HttpBootDxe.c
new file mode 100644 (file)
index 0000000..0d2c7ab
--- /dev/null
@@ -0,0 +1,559 @@
+/** @file
+  Driver Binding functions implementation for UEFI HTTP boot.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
+
+**/
+
+#include "HttpBootDxe.h"
+
+///
+/// Driver Binding Protocol instance
+///
+EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp4DxeDriverBinding = {
+  HttpBootIp4DxeDriverBindingSupported,
+  HttpBootIp4DxeDriverBindingStart,
+  HttpBootIp4DxeDriverBindingStop,
+  HTTP_BOOT_DXE_VERSION,
+  NULL,
+  NULL
+};
+
+/**
+  Destroy the HTTP child based on IPv4 stack.
+
+  @param[in]  This              Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
+  @param[in]  Private           Pointer to HTTP_BOOT_PRIVATE_DATA.
+
+**/
+VOID
+HttpBootDestroyIp4Children (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN HTTP_BOOT_PRIVATE_DATA       *Private
+  )
+{
+  ASSERT (This != NULL);
+  ASSERT (Private != NULL);
+  ASSERT (Private->UsingIpv6 == FALSE);
+
+  if (Private->Dhcp4Child != NULL) {
+    gBS->CloseProtocol (
+          Private->Dhcp4Child,
+          &gEfiDhcp4ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiDhcp4ServiceBindingProtocolGuid,
+      Private->Dhcp4Child
+      );
+  }
+
+  if (Private->HttpCreated) {
+    HttpIoDestroyIo (&Private->HttpIo);
+    Private->HttpCreated = FALSE;
+  }
+
+  gBS->CloseProtocol (
+         Private->Controller,
+         &gEfiCallerIdGuid,
+         This->DriverBindingHandle,
+         Private->ChildHandle
+         );
+
+  gBS->UninstallMultipleProtocolInterfaces (
+         Private->ChildHandle,
+         &gEfiLoadFileProtocolGuid,
+         &Private->LoadFile,
+         &gEfiDevicePathProtocolGuid,
+         Private->DevicePath,
+         NULL
+         );
+
+  if (Private->DevicePath != NULL) {
+    FreePool (Private->DevicePath);
+    Private->DevicePath = NULL;
+  }
+}
+
+/**
+  Tests to see if this driver supports a given controller. If a child device is provided, 
+  it further tests to see if this driver supports creating a handle for the specified child device.
+
+  This function checks to see if the driver specified by This supports the device specified by 
+  ControllerHandle. Drivers will typically use the device path attached to 
+  ControllerHandle and/or the services from the bus I/O abstraction attached to 
+  ControllerHandle to determine if the driver supports ControllerHandle. This function 
+  may be called many times during platform initialization. In order to reduce boot times, the tests 
+  performed by this function must be very small, and take as little time as possible to execute. This 
+  function must not change the state of any hardware devices, and this function must be aware that the 
+  device specified by ControllerHandle may already be managed by the same driver or a 
+  different driver. This function must match its calls to AllocatePages() with FreePages(), 
+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().  
+  Because ControllerHandle may have been previously started by the same driver, if a protocol is 
+  already in the opened state, then it must not be closed with CloseProtocol(). This is required 
+  to guarantee the state of ControllerHandle is not modified by this function.
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in]  ControllerHandle     The handle of the controller to test. This handle 
+                                   must support a protocol interface that supplies 
+                                   an I/O abstraction to the driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This 
+                                   parameter is ignored by device drivers, and is optional for bus 
+                                   drivers. For bus drivers, if this parameter is not NULL, then 
+                                   the bus driver must determine if the bus controller specified 
+                                   by ControllerHandle and the child controller specified 
+                                   by RemainingDevicePath are both supported by this 
+                                   bus driver.
+
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and
+                                   RemainingDevicePath is supported by the driver specified by This.
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed by the driver
+                                   specified by This.
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed by a different
+                                   driver or an application that requires exclusive access.
+                                   Currently not implemented.
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
+                                   RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  )
+{
+  EFI_STATUS                    Status;
+  
+  //
+  // Try to open the DHCP4, HTTP4 and Device Path protocol.
+  //
+  Status = gBS->OpenProtocol (
+                   ControllerHandle,
+                   &gEfiDhcp4ServiceBindingProtocolGuid,
+                   NULL,
+                   This->DriverBindingHandle,
+                   ControllerHandle,
+                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                   );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                   ControllerHandle,
+                   &gEfiHttpServiceBindingProtocolGuid,
+                   NULL,
+                   This->DriverBindingHandle,
+                   ControllerHandle,
+                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                   );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                   ControllerHandle,
+                   &gEfiDevicePathProtocolGuid,
+                   NULL,
+                   This->DriverBindingHandle,
+                   ControllerHandle,
+                   EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                   );
+
+  return Status;
+}
+
+
+/**
+  Starts a device controller or a bus controller.
+
+  The Start() function is designed to be invoked from the EFI boot service ConnectController().
+  As a result, much of the error checking on the parameters to Start() has been moved into this 
+  common boot service. It is legal to call Start() from other locations, 
+  but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE.
+  2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+     EFI_DEVICE_PATH_PROTOCOL.
+  3. Prior to calling Start(), the Supported() function for the driver specified by This must
+     have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.  
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in]  ControllerHandle     The handle of the controller to start. This handle 
+                                   must support a protocol interface that supplies 
+                                   an I/O abstraction to the driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This 
+                                   parameter is ignored by device drivers, and is optional for bus 
+                                   drivers. For a bus driver, if this parameter is NULL, then handles 
+                                   for all the children of Controller are created by this driver.  
+                                   If this parameter is not NULL and the first Device Path Node is 
+                                   not the End of Device Path Node, then only the handle for the 
+                                   child device specified by the first Device Path Node of 
+                                   RemainingDevicePath is created by this driver.
+                                   If the first Device Path Node of RemainingDevicePath is 
+                                   the End of Device Path Node, no child handle is created by this
+                                   driver.
+
+  @retval EFI_SUCCESS              The device was started.
+  @retval EFI_DEVICE_ERROR         The device could not be started due to a device error.Currently not implemented.
+  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a lack of resources.
+  @retval Others                   The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  )
+{
+  EFI_STATUS                 Status;
+  HTTP_BOOT_PRIVATE_DATA     *Private;
+  EFI_DEV_PATH               *Node;
+  EFI_DEVICE_PATH_PROTOCOL   *DevicePath;
+  UINT32                     *Id;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiCallerIdGuid,
+                  (VOID **) &Id,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (!EFI_ERROR (Status)) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  //
+  // Initialize the private data structure.
+  //
+  Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA));
+  if (Private == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+  Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE;
+  Private->Controller = ControllerHandle;
+  Private->Image = This->ImageHandle;
+  Private->UsingIpv6 = FALSE;
+  InitializeListHead (&Private->CacheList);
+
+  //
+  // Create DHCP child instance.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiDhcp4ServiceBindingProtocolGuid,
+             &Private->Dhcp4Child
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Dhcp4Child,
+                  &gEfiDhcp4ProtocolGuid,
+                  (VOID **) &Private->Dhcp4,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Get the Ip4Config2 protocol, it's required to configure the default gateway address.
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiIp4Config2ProtocolGuid,
+                  (VOID **) &Private->Ip4Config2,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Get the NII interface if it exists, it's not required.
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+                  (VOID **) &Private->Nii,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    Private->Nii = NULL;
+  }
+
+  //
+  // Open Device Path Protocol to prepare for appending IP and URI node.
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiDevicePathProtocolGuid,
+                  (VOID **) &Private->ParentDevicePath,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Append IPv4 device path node.
+  //
+  Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
+  if (Node == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+  Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
+  Node->Ipv4.Header.SubType = MSG_IPv4_DP;
+  SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
+  Node->Ipv4.StaticIpAddress = FALSE;
+  DevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+  FreePool (Node);
+  if (DevicePath == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+
+  //
+  // Append URI device path node.
+  //
+  Node = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL));
+  if (Node == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+  Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+  Node->DevPath.SubType = MSG_URI_DP;
+  SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL));
+  Private->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+  FreePool (Node);
+  FreePool (DevicePath);
+  if (Private->DevicePath == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+
+  //
+  // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it.
+  //
+  CopyMem (&Private->LoadFile, &gHttpBootDxeLoadFile, sizeof (Private->LoadFile));
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Private->ChildHandle,
+                  &gEfiLoadFileProtocolGuid,
+                  &Private->LoadFile,
+                  &gEfiDevicePathProtocolGuid,
+                  Private->DevicePath,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between
+  // NIC handle and the private data.
+  //
+  Status = gBS->InstallProtocolInterface (
+                  &ControllerHandle,
+                  &gEfiCallerIdGuid,
+                  EFI_NATIVE_INTERFACE,
+                  &Private->Id
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Open the Caller Id child to setup a parent-child relationship between
+  // real NIC handle and the HTTP boot child handle.
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiCallerIdGuid,
+                  (VOID **) &Id,
+                  This->DriverBindingHandle,
+                  Private->ChildHandle,
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  return EFI_SUCCESS;
+
+ON_ERROR:
+  
+  HttpBootDestroyIp4Children (This, Private);
+  FreePool (Private);
+
+  return Status;
+}
+
+/**
+  Stops a device controller or a bus controller.
+  
+  The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). 
+  As a result, much of the error checking on the parameters to Stop() has been moved 
+  into this common boot service. It is legal to call Stop() from other locations, 
+  but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+     same driver's Start() function.
+  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+     EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+     Start() function, and the Start() function must have called OpenProtocol() on
+     ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+  
+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must 
+                                support a bus specific I/O protocol for the driver 
+                                to use to stop the device.
+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.
+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL 
+                                if NumberOfChildren is 0.
+
+  @retval EFI_SUCCESS           The device was stopped.
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer OPTIONAL
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_LOAD_FILE_PROTOCOL          *LoadFile;
+  HTTP_BOOT_PRIVATE_DATA          *Private;
+  EFI_HANDLE                      NicHandle;
+  UINT32                          *Id;
+
+  //
+  // Try to get the Load File Protocol from the controller handle.
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiLoadFileProtocolGuid,
+                  (VOID **) &LoadFile,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    //
+    // If failed, try to find the NIC handle for this controller.
+    //
+    NicHandle = HttpBootGetNicByIp4Children (ControllerHandle);
+    if (NicHandle == NULL) {
+      return EFI_SUCCESS;
+    }
+
+    //
+    // Try to retrieve the private data by the Caller Id Guid.
+    //
+    Status = gBS->OpenProtocol (
+                    NicHandle,
+                    &gEfiCallerIdGuid,
+                    (VOID **) &Id,
+                    This->DriverBindingHandle,
+                    ControllerHandle,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID (Id);
+  } else {
+    Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (LoadFile);
+    NicHandle = Private->Controller;
+  }
+
+  //
+  // Disable the HTTP boot function.
+  //
+  Status = HttpBootStop (Private);
+  if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) {
+    return Status;
+  }
+
+  //
+  // Destory all child instance and uninstall protocol interface.
+  //
+  HttpBootDestroyIp4Children (This, Private);
+
+  //
+  // Release the cached data.
+  //
+  HttpBootFreeCacheList (Private);
+
+  gBS->UninstallProtocolInterface (
+         NicHandle,
+         &gEfiCallerIdGuid,
+         &Private->Id
+         );
+  FreePool (Private);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  This is the declaration of an EFI image entry point. This entry point is
+  the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+  both device drivers and bus drivers.
+
+  @param[in]  ImageHandle       The firmware allocated handle for the UEFI image.
+  @param[in]  SystemTable       A pointer to the EFI System Table.
+
+  @retval EFI_SUCCESS           The operation completed successfully.
+  @retval Others                An unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeDriverEntryPoint (
+  IN EFI_HANDLE        ImageHandle,
+  IN EFI_SYSTEM_TABLE  *SystemTable
+  )
+{
+  //
+  // Install UEFI Driver Model protocol(s).
+  //
+  return EfiLibInstallDriverBindingComponentName2 (
+           ImageHandle,
+           SystemTable,
+           &gHttpBootIp4DxeDriverBinding,
+           ImageHandle,
+           &gHttpBootDxeComponentName,
+           &gHttpBootDxeComponentName2
+           );
+}
+
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.h b/NetworkPkg/HttpBootDxe/HttpBootDxe.h
new file mode 100644 (file)
index 0000000..8fce03d
--- /dev/null
@@ -0,0 +1,303 @@
+/** @file
+  UEFI HTTP boot driver's private data structure and interfaces declaration.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.              
+
+**/
+
+#ifndef __EFI_HTTP_BOOT_DXE_H__
+#define __EFI_HTTP_BOOT_DXE_H__
+
+#include <Uefi.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/NetLib.h>
+#include <Library/HttpLib.h>
+
+//
+// UEFI Driver Model Protocols
+//
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/ComponentName.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Http.h>
+#include <Protocol/Ip4Config2.h>
+
+//
+// Produced Protocols
+//
+#include <Protocol/LoadFile.h>
+
+//
+// Driver Version
+//
+#define HTTP_BOOT_DXE_VERSION  0xa
+
+//
+// Protocol instances
+//
+extern EFI_DRIVER_BINDING_PROTOCOL  gHttpBootDxeDriverBinding;
+extern EFI_COMPONENT_NAME2_PROTOCOL gHttpBootDxeComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL  gHttpBootDxeComponentName;
+
+//
+// Private data structure
+//
+typedef struct _HTTP_BOOT_PRIVATE_DATA      HTTP_BOOT_PRIVATE_DATA;
+
+//
+// Include files with internal function prototypes
+//
+#include "HttpBootComponentName.h"
+#include "HttpBootDhcp4.h"
+#include "HttpBootImpl.h"
+#include "HttpBootSupport.h"
+#include "HttpBootClient.h"
+
+typedef union {
+  HTTP_BOOT_DHCP4_PACKET_CACHE              Dhcp4;
+} HTTP_BOOT_DHCP_PACKET_CACHE;
+
+struct _HTTP_BOOT_PRIVATE_DATA {
+  UINT32                                    Signature;
+  EFI_HANDLE                                Controller;
+  EFI_HANDLE                                Image;
+
+  //
+  // Cousumed children
+  //
+  EFI_HANDLE                                Dhcp4Child;
+  HTTP_IO                                   HttpIo;
+  BOOLEAN                                   HttpCreated;
+
+  //
+  // Consumed protocol
+  //
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+  EFI_IP4_CONFIG2_PROTOCOL                  *Ip4Config2;
+  EFI_DHCP4_PROTOCOL                        *Dhcp4;
+  EFI_DEVICE_PATH_PROTOCOL                  *ParentDevicePath;
+
+  //
+  // Produced children
+  //
+  EFI_HANDLE                                ChildHandle;
+  
+  //
+  // Produced protocol
+  //
+  EFI_LOAD_FILE_PROTOCOL                    LoadFile;
+  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;
+  UINT32                                    Id;
+
+  //
+  // Mode data
+  //
+  BOOLEAN                                   UsingIpv6;
+  BOOLEAN                                   Started;
+  EFI_IP_ADDRESS                            StationIp;
+  EFI_IP_ADDRESS                            SubnetMask;
+  EFI_IP_ADDRESS                            GatewayIp;
+  UINT16                                    Port;
+  CHAR8                                     *BootFileUri;
+  VOID                                      *BootFileUriParser;
+  UINTN                                     BootFileSize;
+
+  //
+  // Cached HTTP data
+  //
+  LIST_ENTRY                                CacheList;
+
+  //
+  // Cached DHCP offer
+  //
+  // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.
+  //
+  // It supposed that
+  //
+  //   OfferNum:    8
+  //   OfferBuffer: [ProxyNameUri, DhcpNameUri, DhcpIpUri, ProxyNameUri, ProxyIpUri, DhcpOnly, DhcpIpUri, DhcpNameUriDns]
+  //   (OfferBuffer is 0-based.)
+  //
+  // And assume that (DhcpIpUri is the first priority actually.)
+  //
+  //   SelectIndex:     5
+  //   SelectProxyType: HttpOfferTypeProxyIpUri
+  //   (SelectIndex is 1-based, and 0 means no one is selected.)
+  //
+  // So it should be
+  //
+  //                 DhcpIpUri  DhcpNameUriDns  DhcpDns  DhcpOnly  ProxyNameUri  ProxyIpUri  DhcpNameUri
+  //   OfferCount:  [       2,              1,       0,        1,            2,          1,            1]
+  //
+  //   OfferIndex: {[       2,              7,       0,        5,            0,         *4,            1]
+  //                [       6,              0,       0,        0,            3,          0,            0]
+  //                [       0,              0,       0,        0,            0,          0,            0]
+  //                ...                                                                                 ]}
+  //   (OfferIndex is 0-based.)
+  //
+  //
+  UINT32                                    SelectIndex;
+  UINT32                                    SelectProxyType;
+  HTTP_BOOT_DHCP_PACKET_CACHE               OfferBuffer[HTTP_BOOT_OFFER_MAX_NUM];
+  UINT32                                    OfferNum;
+  UINT32                                    OfferCount[HttpOfferTypeMax];
+  UINT32                                    OfferIndex[HttpOfferTypeMax][HTTP_BOOT_OFFER_MAX_NUM];
+};
+
+#define HTTP_BOOT_PRIVATE_DATA_SIGNATURE          SIGNATURE_32 ('H', 'B', 'P', 'D')
+#define HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE(a)   CR (a, HTTP_BOOT_PRIVATE_DATA, LoadFile, HTTP_BOOT_PRIVATE_DATA_SIGNATURE)
+#define HTTP_BOOT_PRIVATE_DATA_FROM_ID(a)         CR (a, HTTP_BOOT_PRIVATE_DATA, Id, HTTP_BOOT_PRIVATE_DATA_SIGNATURE)
+
+extern EFI_LOAD_FILE_PROTOCOL               gHttpBootDxeLoadFile;
+
+/**
+  Tests to see if this driver supports a given controller. If a child device is provided, 
+  it further tests to see if this driver supports creating a handle for the specified child device.
+
+  This function checks to see if the driver specified by This supports the device specified by 
+  ControllerHandle. Drivers will typically use the device path attached to 
+  ControllerHandle and/or the services from the bus I/O abstraction attached to 
+  ControllerHandle to determine if the driver supports ControllerHandle. This function 
+  may be called many times during platform initialization. In order to reduce boot times, the tests 
+  performed by this function must be very small, and take as little time as possible to execute. This 
+  function must not change the state of any hardware devices, and this function must be aware that the 
+  device specified by ControllerHandle may already be managed by the same driver or a 
+  different driver. This function must match its calls to AllocatePages() with FreePages(), 
+  AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().  
+  Because ControllerHandle may have been previously started by the same driver, if a protocol is 
+  already in the opened state, then it must not be closed with CloseProtocol(). This is required 
+  to guarantee the state of ControllerHandle is not modified by this function.
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in]  ControllerHandle     The handle of the controller to test. This handle 
+                                   must support a protocol interface that supplies 
+                                   an I/O abstraction to the driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This 
+                                   parameter is ignored by device drivers, and is optional for bus 
+                                   drivers. For bus drivers, if this parameter is not NULL, then 
+                                   the bus driver must determine if the bus controller specified 
+                                   by ControllerHandle and the child controller specified 
+                                   by RemainingDevicePath are both supported by this 
+                                   bus driver.
+
+  @retval EFI_SUCCESS              The device specified by ControllerHandle and
+                                   RemainingDevicePath is supported by the driver specified by This.
+  @retval EFI_ALREADY_STARTED      The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed by the driver
+                                   specified by This.
+  @retval EFI_ACCESS_DENIED        The device specified by ControllerHandle and
+                                   RemainingDevicePath is already being managed by a different
+                                   driver or an application that requires exclusive access.
+                                   Currently not implemented.
+  @retval EFI_UNSUPPORTED          The device specified by ControllerHandle and
+                                   RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  );
+
+/**
+  Starts a device controller or a bus controller.
+
+  The Start() function is designed to be invoked from the EFI boot service ConnectController().
+  As a result, much of the error checking on the parameters to Start() has been moved into this 
+  common boot service. It is legal to call Start() from other locations, 
+  but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE.
+  2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+     EFI_DEVICE_PATH_PROTOCOL.
+  3. Prior to calling Start(), the Supported() function for the driver specified by This must
+     have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.  
+
+  @param[in]  This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in]  ControllerHandle     The handle of the controller to start. This handle 
+                                   must support a protocol interface that supplies 
+                                   an I/O abstraction to the driver.
+  @param[in]  RemainingDevicePath  A pointer to the remaining portion of a device path.  This 
+                                   parameter is ignored by device drivers, and is optional for bus 
+                                   drivers. For a bus driver, if this parameter is NULL, then handles 
+                                   for all the children of Controller are created by this driver.  
+                                   If this parameter is not NULL and the first Device Path Node is 
+                                   not the End of Device Path Node, then only the handle for the 
+                                   child device specified by the first Device Path Node of 
+                                   RemainingDevicePath is created by this driver.
+                                   If the first Device Path Node of RemainingDevicePath is 
+                                   the End of Device Path Node, no child handle is created by this
+                                   driver.
+
+  @retval EFI_SUCCESS              The device was started.
+  @retval EFI_DEVICE_ERROR         The device could not be started due to a device error.Currently not implemented.
+  @retval EFI_OUT_OF_RESOURCES     The request could not be completed due to a lack of resources.
+  @retval Others                   The driver failded to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  );
+
+/**
+  Stops a device controller or a bus controller.
+  
+  The Stop() function is designed to be invoked from the EFI boot service DisconnectController(). 
+  As a result, much of the error checking on the parameters to Stop() has been moved 
+  into this common boot service. It is legal to call Stop() from other locations, 
+  but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+  1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+     same driver's Start() function.
+  2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+     EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+     Start() function, and the Start() function must have called OpenProtocol() on
+     ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+  
+  @param[in]  This              A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+  @param[in]  ControllerHandle  A handle to the device being stopped. The handle must 
+                                support a bus specific I/O protocol for the driver 
+                                to use to stop the device.
+  @param[in]  NumberOfChildren  The number of child device handles in ChildHandleBuffer.
+  @param[in]  ChildHandleBuffer An array of child handles to be freed. May be NULL 
+                                if NumberOfChildren is 0.
+
+  @retval EFI_SUCCESS           The device was stopped.
+  @retval EFI_DEVICE_ERROR      The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer OPTIONAL
+  );
+
+#endif
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.inf b/NetworkPkg/HttpBootDxe/HttpBootDxe.inf
new file mode 100644 (file)
index 0000000..a608304
--- /dev/null
@@ -0,0 +1,68 @@
+## @file
+#  This modules produce the Load File Protocol for UEFI HTTP boot.
+# 
+#  Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+#  This program and the accompanying materials
+#  are licensed and made available under the terms and conditions of the BSD License
+#  which accompanies this distribution.  The full text of the license may be found at
+#  http://opensource.org/licenses/bsd-license.php
+#  
+#  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#  
+##
+
+[Defines]
+  INF_VERSION               = 0x00010005
+  BASE_NAME                 = HttpBootDxe
+  FILE_GUID                 = ecebcb00-d9c8-11e4-af3d-8cdcd426c973
+  MODULE_TYPE               = UEFI_DRIVER
+  VERSION_STRING            = 1.0
+  ENTRY_POINT               = HttpBootDxeDriverEntryPoint
+  UNLOAD_IMAGE              = NetLibDefaultUnload
+  MODULE_UNI_FILE           = HttpBootDxe.uni
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+[Sources]
+  HttpBootDxe.h
+  HttpBootDxe.c
+  HttpBootComponentName.h
+  HttpBootComponentName.c
+  HttpBootImpl.h
+  HttpBootImpl.c
+  HttpBootDhcp4.h
+  HttpBootDhcp4.c
+  HttpBootSupport.h
+  HttpBootSupport.c
+  HttpBootClient.h
+  HttpBootClient.c
+
+[LibraryClasses]
+  UefiDriverEntryPoint
+  UefiBootServicesTableLib
+  MemoryAllocationLib
+  BaseLib
+  UefiLib
+  DevicePathLib
+  DebugLib
+  NetLib
+  HttpLib
+
+[Protocols]
+  ## TO_START
+  ## BY_START
+  gEfiDevicePathProtocolGuid
+  
+  gEfiLoadFileProtocolGuid                        ## BY_START
+  gEfiHttpServiceBindingProtocolGuid              ## CONSUMES
+  gEfiHttpProtocolGuid                            ## CONSUMES
+  gEfiDhcp4ServiceBindingProtocolGuid             ## TO_START
+  gEfiDhcp4ProtocolGuid                           ## TO_START
+  gEfiIp4Config2ProtocolGuid                      ## TO_START
+  gEfiNetworkInterfaceIdentifierProtocolGuid_31   ## SOMETIMES_CONSUMES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+  HttpBootDxeExtra.uni
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxe.uni b/NetworkPkg/HttpBootDxe/HttpBootDxe.uni
new file mode 100644 (file)
index 0000000..68a3379
Binary files /dev/null and b/NetworkPkg/HttpBootDxe/HttpBootDxe.uni differ
diff --git a/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni b/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni
new file mode 100644 (file)
index 0000000..2d45c4c
Binary files /dev/null and b/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni differ
diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/NetworkPkg/HttpBootDxe/HttpBootImpl.c
new file mode 100644 (file)
index 0000000..711cc3c
--- /dev/null
@@ -0,0 +1,352 @@
+/** @file
+  The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "HttpBootDxe.h"
+
+/**
+  Enable the use of UEFI HTTP boot function.
+
+  @param[in]    Private            The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS              HTTP boot was successfully enabled.
+  @retval EFI_INVALID_PARAMETER    Private is NULL.
+  @retval EFI_ALREADY_STARTED      The driver is already in started state.
+  
+**/
+EFI_STATUS
+HttpBootStart (
+  IN HTTP_BOOT_PRIVATE_DATA           *Private
+  )
+{
+  UINTN          Index;
+
+  if (Private == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Private->Started) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  if (!Private->UsingIpv6) {
+    //
+    // Init the content of cached DHCP offer list.
+    //
+    ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
+    for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
+      Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_BOOT_DHCP4_PACKET_MAX_SIZE;
+    }
+  } else {
+    ASSERT (FALSE);
+  }
+
+  Private->Started = TRUE;
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Attempt to complete a DHCPv4 D.O.R.A sequence to retrieve the boot resource information.
+
+  @param[in]    Private            The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS              Boot info was successfully retrieved.
+  @retval EFI_INVALID_PARAMETER    Private is NULL.
+  @retval EFI_NOT_STARTED          The driver is in stopped state.
+  @retval EFI_DEVICE_ERROR         An unexpected network error occurred.
+  @retval Others                   Other errors as indicated.
+  
+**/
+EFI_STATUS
+HttpBootDhcp (
+  IN HTTP_BOOT_PRIVATE_DATA           *Private
+  )
+{
+  EFI_STATUS                Status;
+
+  if (Private == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  
+  if (!Private->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  Status = EFI_DEVICE_ERROR;
+
+  if (!Private->UsingIpv6) {
+    Status = HttpBootDhcp4Dora (Private);
+  } else {
+    ASSERT (FALSE);
+  }
+
+  return Status;
+}
+
+/**
+  Attempt to download the boot file through HTTP message exchange.
+
+  @param[in]          Private         The pointer to the driver's private data.
+  @param[in, out]     BufferSize      On input the size of Buffer in bytes. On output with a return
+                                      code of EFI_SUCCESS, the amount of data transferred to
+                                      Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+                                      the size of Buffer required to retrieve the requested file.
+  @param[in]          Buffer          The memory buffer to transfer the file to. If Buffer is NULL,
+                                      then the size of the requested file is returned in
+                                      BufferSize.
+
+  @retval EFI_SUCCESS                 Boot file was loaded successfully.
+  @retval EFI_INVALID_PARAMETER       Private is NULL.
+  @retval EFI_NOT_STARTED             The driver is in stopped state.
+  @retval EFI_BUFFER_TOO_SMALL        The BufferSize is too small to read the boot file. BufferSize has 
+                                      been updated with the size needed to complete the request.
+  @retval EFI_DEVICE_ERROR            An unexpected network error occurred.
+  @retval Others                      Other errors as indicated.
+  
+**/
+EFI_STATUS
+HttpBootLoadFile (
+  IN     HTTP_BOOT_PRIVATE_DATA       *Private,
+  IN OUT UINTN                        *BufferSize,
+  IN     VOID                         *Buffer       OPTIONAL
+  )
+{
+  EFI_STATUS             Status;
+
+  if (Private == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  
+  if (!Private->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  Status = EFI_DEVICE_ERROR;
+
+  if (Private->BootFileUri == NULL) {
+    //
+    // Parse the cached offer to get the boot file URL first.
+    //
+    Status = HttpBootDiscoverBootInfo (Private);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  if (!Private->HttpCreated) {
+    //
+    // Create HTTP child.
+    //
+    Status = HttpBootCreateHttpIo (Private);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  if (Private->BootFileSize == 0) {
+    //
+    // Discover the information about the bootfile if we haven't.
+    //
+
+    //
+    // Try to use HTTP HEAD method.
+    //
+    Status = HttpBootGetBootFile (
+               Private,
+               TRUE,
+               &Private->BootFileSize,
+               NULL
+               );
+    if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+      //
+      // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
+      //
+      ASSERT (Private->BootFileSize == 0);
+      Status = HttpBootGetBootFile (
+                 Private,
+                 FALSE,
+                 &Private->BootFileSize,
+                 NULL
+                 );
+      if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+        return Status;
+      }
+    }
+  }
+
+  if (*BufferSize < Private->BootFileSize) {
+    *BufferSize = Private->BootFileSize;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  //
+  // Load the boot file into Buffer
+  //
+  return  HttpBootGetBootFile (
+            Private,
+            FALSE,
+            BufferSize,
+            Buffer
+            );
+}
+
+/**
+  Disable the use of UEFI HTTP boot function.
+
+  @param[in]    Private            The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS              HTTP boot was successfully disabled.
+  @retval EFI_NOT_STARTED          The driver is already in stopped state.
+  @retval EFI_INVALID_PARAMETER    Private is NULL.
+  @retval Others                   Unexpected error when stop the function.
+  
+**/
+EFI_STATUS
+HttpBootStop (
+  IN HTTP_BOOT_PRIVATE_DATA           *Private
+  )
+{
+  UINTN            Index;
+
+  if (Private == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+  
+  if (!Private->Started) {
+    return EFI_NOT_STARTED;
+  }
+  
+  if (Private->HttpCreated) {
+    HttpIoDestroyIo (&Private->HttpIo);
+    Private->HttpCreated = FALSE;
+  }
+  
+  Private->Started = FALSE;
+  ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
+  ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
+  ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
+  Private->Port = 0;
+  Private->BootFileUri = NULL;
+  Private->BootFileUriParser = NULL;
+  Private->BootFileSize = 0;
+  Private->SelectIndex = 0;
+  Private->SelectProxyType = HttpOfferTypeMax;
+
+  if (!Private->UsingIpv6) {
+    for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
+      if (Private->OfferBuffer[Index].Dhcp4.UriParser) {
+        HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);
+      }
+    }
+  } else {
+    ASSERT (FALSE);
+  }
+  
+  ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
+  Private->OfferNum = 0;
+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Causes the driver to load a specified file.
+
+  @param  This       Protocol instance pointer.
+  @param  FilePath   The device specific path of the file to load.
+  @param  BootPolicy If TRUE, indicates that the request originates from the
+                     boot manager is attempting to load FilePath as a boot
+                     selection. If FALSE, then FilePath must match as exact file
+                     to be loaded.
+  @param  BufferSize On input the size of Buffer in bytes. On output with a return
+                     code of EFI_SUCCESS, the amount of data transferred to
+                     Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+                     the size of Buffer required to retrieve the requested file.
+  @param  Buffer     The memory buffer to transfer the file to. IF Buffer is NULL,
+                     then the size of the requested file is returned in
+                     BufferSize.
+
+  @retval EFI_SUCCESS           The file was loaded.
+  @retval EFI_UNSUPPORTED       The device does not support the provided BootPolicy
+  @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
+                                BufferSize is NULL.
+  @retval EFI_NO_MEDIA          No medium was present to load the file.
+  @retval EFI_DEVICE_ERROR      The file was not loaded due to a device error.
+  @retval EFI_NO_RESPONSE       The remote system did not respond.
+  @retval EFI_NOT_FOUND         The file was not found.
+  @retval EFI_ABORTED           The file load process was manually cancelled.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to read the current directory entry.
+                                BufferSize has been updated with the size needed to complete
+                                the request.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeLoadFile (
+  IN EFI_LOAD_FILE_PROTOCOL           *This,
+  IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
+  IN BOOLEAN                          BootPolicy,
+  IN OUT UINTN                        *BufferSize,
+  IN VOID                             *Buffer OPTIONAL
+  )
+{
+  HTTP_BOOT_PRIVATE_DATA        *Private;
+  BOOLEAN                       MediaPresent;
+  EFI_STATUS                    Status;
+
+  if (This == NULL || BufferSize == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Only support BootPolicy
+  //
+  if (!BootPolicy) {
+    return EFI_UNSUPPORTED;
+  }
+
+  Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (This);
+
+  //
+  // Check media status before HTTP boot start
+  //
+  MediaPresent = TRUE;
+  NetLibDetectMedia (Private->Controller, &MediaPresent);
+  if (!MediaPresent) {
+    return EFI_NO_MEDIA;
+  }
+
+  //
+  // Initialize HTTP boot and load the boot file.
+  //
+  Status = HttpBootStart (Private);
+  if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
+    Status = HttpBootLoadFile (Private, BufferSize, Buffer);
+  }
+
+  if (Status != EFI_SUCCESS && Status != EFI_BUFFER_TOO_SMALL) {
+    HttpBootStop (Private);
+  }
+
+  return Status;
+}
+
+///
+/// Load File Protocol instance
+///
+GLOBAL_REMOVE_IF_UNREFERENCED 
+EFI_LOAD_FILE_PROTOCOL  gHttpBootDxeLoadFile = {
+  HttpBootDxeLoadFile
+};
diff --git a/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/NetworkPkg/HttpBootDxe/HttpBootImpl.h
new file mode 100644 (file)
index 0000000..90742dd
--- /dev/null
@@ -0,0 +1,50 @@
+/** @file
+  The declaration of UEFI HTTP boot function.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+#ifndef __EFI_HTTP_BOOT_IMPL_H__
+#define __EFI_HTTP_BOOT_IMPL_H__
+
+/**
+  Attempt to complete a DHCPv4 D.O.R.A sequence to retrieve the boot resource information.
+
+  @param[in]    Private            The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS              Boot info was successfully retrieved.
+  @retval EFI_INVALID_PARAMETER    Private is NULL.
+  @retval EFI_NOT_STARTED          The driver is in stopped state.
+  @retval EFI_DEVICE_ERROR         An unexpected network error occurred.
+  @retval Others                   Other errors as indicated.
+  
+**/
+EFI_STATUS
+HttpBootDhcp (
+  IN HTTP_BOOT_PRIVATE_DATA           *Private
+  );
+
+/**
+  Disable the use of UEFI HTTP boot function.
+
+  @param[in]    Private            The pointer to the driver's private data.
+
+  @retval EFI_SUCCESS              HTTP boot was successfully disabled.
+  @retval EFI_NOT_STARTED          The driver is already in stopped state.
+  @retval EFI_INVALID_PARAMETER    Private is NULL.
+  @retval Others                   Unexpected error when stop the function.
+  
+**/
+EFI_STATUS
+HttpBootStop (
+  IN HTTP_BOOT_PRIVATE_DATA           *Private
+  );
+
+#endif
diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.c b/NetworkPkg/HttpBootDxe/HttpBootSupport.c
new file mode 100644 (file)
index 0000000..e21b50d
--- /dev/null
@@ -0,0 +1,590 @@
+/** @file
+  Support functions implementation for UEFI HTTP boot driver.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "HttpBootDxe.h"
+
+
+/**
+  Get the Nic handle using any child handle in the IPv4 stack.
+
+  @param[in]  ControllerHandle    Pointer to child handle over IPv4.
+
+  @return NicHandle               The pointer to the Nic handle.
+  @return NULL                    Can't find the Nic handle.
+
+**/
+EFI_HANDLE
+HttpBootGetNicByIp4Children (
+  IN EFI_HANDLE                 ControllerHandle
+  )
+{
+  EFI_HANDLE                    NicHandle;
+
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);
+  if (NicHandle == NULL) {
+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
+    if (NicHandle == NULL) {
+      return NULL;
+    }
+  }
+
+  return NicHandle;
+}
+
+
+/**
+  This function is to convert UINTN to ASCII string with the required formatting.
+
+  @param[in]  Number         Numeric value to be converted.
+  @param[in]  Buffer         The pointer to the buffer for ASCII string.
+  @param[in]  Length         The length of the required format.
+
+**/
+VOID
+HttpBootUintnToAscDecWithFormat (
+  IN UINTN                       Number,
+  IN UINT8                       *Buffer,
+  IN INTN                        Length
+  )
+{
+  UINTN                          Remainder;
+
+  while (Length > 0) {
+    Length--;
+    Remainder      = Number % 10;
+    Number        /= 10;
+    Buffer[Length] = (UINT8) ('0' + Remainder);
+  }
+}
+
+/**
+  This function is to display the IPv4 address.
+
+  @param[in]  Ip        The pointer to the IPv4 address.
+
+**/
+VOID
+HttpBootShowIp4Addr (
+  IN EFI_IPv4_ADDRESS   *Ip
+  )
+{
+  UINTN                 Index;
+
+  for (Index = 0; Index < 4; Index++) {
+    AsciiPrint ("%d", Ip->Addr[Index]);
+    if (Index < 3) {
+      AsciiPrint (".");
+    }
+  }
+}
+
+/**
+  Create a HTTP_IO_HEADER to hold the HTTP header items.
+
+  @param[in]  MaxHeaderCount         The maximun number of HTTP header in this holder.
+
+  @return    A pointer of the HTTP header holder or NULL if failed.
+  
+**/
+HTTP_IO_HEADER *
+HttpBootCreateHeader (
+  UINTN                     MaxHeaderCount
+)
+{
+  HTTP_IO_HEADER        *HttpIoHeader;
+
+  if (MaxHeaderCount == 0) {
+    return NULL;
+  }
+
+  HttpIoHeader = AllocateZeroPool (sizeof (HTTP_IO_HEADER) + MaxHeaderCount * sizeof (EFI_HTTP_HEADER));
+  if (HttpIoHeader == NULL) {
+    return NULL;
+  }
+
+  HttpIoHeader->MaxHeaderCount = MaxHeaderCount;
+  HttpIoHeader->Headers = (EFI_HTTP_HEADER *) (HttpIoHeader + 1);
+
+  return HttpIoHeader;
+}
+
+/**
+  Destroy the HTTP_IO_HEADER and release the resouces. 
+
+  @param[in]  HttpIoHeader       Point to the HTTP header holder to be destroyed.
+
+**/
+VOID
+HttpBootFreeHeader (
+  IN  HTTP_IO_HEADER       *HttpIoHeader
+  )
+{
+  UINTN      Index;
+  
+  if (HttpIoHeader != NULL) {
+    if (HttpIoHeader->HeaderCount != 0) {
+      for (Index = 0; Index < HttpIoHeader->HeaderCount; Index++) {
+        FreePool (HttpIoHeader->Headers[Index].FieldName);
+        FreePool (HttpIoHeader->Headers[Index].FieldValue);
+      }
+    }
+    FreePool (HttpIoHeader);
+  }
+}
+
+/**
+  Find a specified header field according to the field name.
+
+  @param[in]   HeaderCount      Number of HTTP header structures in Headers list. 
+  @param[in]   Headers          Array containing list of HTTP headers.
+  @param[in]   FieldName        Null terminated string which describes a field name. 
+
+  @return    Pointer to the found header or NULL.
+
+**/
+EFI_HTTP_HEADER *
+HttpBootFindHeader (
+  IN  UINTN                HeaderCount,
+  IN  EFI_HTTP_HEADER      *Headers,
+  IN  CHAR8                *FieldName
+  )
+{
+  UINTN                 Index;
+  
+  if (HeaderCount == 0 || Headers == NULL || FieldName == NULL) {
+    return NULL;
+  }
+
+  for (Index = 0; Index < HeaderCount; Index++){
+    //
+    // Field names are case-insensitive (RFC 2616).
+    //
+    if (AsciiStriCmp (Headers[Index].FieldName, FieldName) == 0) {
+      return &Headers[Index];
+    }
+  }
+  return NULL;
+}
+
+/**
+  Set or update a HTTP header with the field name and corresponding value.
+
+  @param[in]  HttpIoHeader       Point to the HTTP header holder.
+  @param[in]  FieldName          Null terminated string which describes a field name. 
+  @param[in]  FieldValue         Null terminated string which describes the corresponding field value.
+
+  @retval  EFI_SUCCESS           The HTTP header has been set or updated.
+  @retval  EFI_INVALID_PARAMETER Any input parameter is invalid.
+  @retval  EFI_OUT_OF_RESOURCES  Insufficient resource to complete the operation.
+  @retval  Other                 Unexpected error happened.
+  
+**/
+EFI_STATUS
+HttpBootSetHeader (
+  IN  HTTP_IO_HEADER       *HttpIoHeader,
+  IN  CHAR8                *FieldName,
+  IN  CHAR8                *FieldValue
+  )
+{
+  EFI_HTTP_HEADER       *Header;
+  UINTN                 StrSize;
+  CHAR8                 *NewFieldValue;
+  
+  if (HttpIoHeader == NULL || FieldName == NULL || FieldValue == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Header = HttpBootFindHeader (HttpIoHeader->HeaderCount, HttpIoHeader->Headers, FieldName);
+  if (Header == NULL) {
+    //
+    // Add a new header.
+    //
+    if (HttpIoHeader->HeaderCount >= HttpIoHeader->MaxHeaderCount) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    Header = &HttpIoHeader->Headers[HttpIoHeader->HeaderCount];
+
+    StrSize = AsciiStrSize (FieldName);
+    Header->FieldName = AllocatePool (StrSize);
+    if (Header->FieldName == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    CopyMem (Header->FieldName, FieldName, StrSize);
+    Header->FieldName[StrSize -1] = '\0';
+
+    StrSize = AsciiStrSize (FieldValue);
+    Header->FieldValue = AllocatePool (StrSize);
+    if (Header->FieldValue == NULL) {
+      FreePool (Header->FieldName);
+      return EFI_OUT_OF_RESOURCES;
+    }
+    CopyMem (Header->FieldValue, FieldValue, StrSize);
+    Header->FieldValue[StrSize -1] = '\0';
+
+    HttpIoHeader->HeaderCount++;
+  } else {
+    //
+    // Update an existing one.
+    //
+    StrSize = AsciiStrSize (FieldValue);
+    NewFieldValue = AllocatePool (StrSize);
+    if (NewFieldValue == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    CopyMem (NewFieldValue, FieldValue, StrSize);
+    NewFieldValue[StrSize -1] = '\0';
+    
+    if (Header->FieldValue != NULL) {
+      FreePool (Header->FieldValue);
+    }
+    Header->FieldValue = NewFieldValue;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Notify the callback function when an event is triggered.
+
+  @param[in]  Event           The triggered event.
+  @param[in]  Context         The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpIoCommonNotify (
+  IN EFI_EVENT           Event,
+  IN VOID                *Context
+  )
+{
+  *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+  Create a HTTP_IO to access the HTTP service. It will create and configure
+  a HTTP child handle.
+
+  @param[in]  Image          The handle of the driver image.
+  @param[in]  Controller     The handle of the controller.
+  @param[in]  IpVersion      IP_VERSION_4 or IP_VERSION_6.
+  @param[in]  ConfigData     The HTTP_IO configuration data.
+  @param[out] HttpIo         The HTTP_IO.
+  
+  @retval EFI_SUCCESS            The HTTP_IO is created and configured.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_UNSUPPORTED        One or more of the control options are not
+                                 supported in the implementation.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval Others                 Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+  IN EFI_HANDLE             Image,
+  IN EFI_HANDLE             Controller,
+  IN UINT8                  IpVersion,
+  IN HTTP_IO_CONFIG_DATA    *ConfigData,
+  OUT HTTP_IO               *HttpIo
+  )
+{
+  EFI_STATUS                Status;
+  EFI_HTTP_CONFIG_DATA      HttpConfigData;
+  EFI_HTTPv4_ACCESS_POINT   Http4AccessPoint;
+  EFI_HTTP_PROTOCOL         *Http;
+  EFI_EVENT                 Event;
+  
+  if ((Image == NULL) || (Controller == NULL) || (ConfigData == NULL) || (HttpIo == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (IpVersion != IP_VERSION_4 && IpVersion != IP_VERSION_6) {
+    return EFI_UNSUPPORTED;
+  }
+
+  ZeroMem (HttpIo, sizeof (HTTP_IO));
+  
+  //
+  // Create the HTTP child instance and get the HTTP protocol.
+  //  
+  Status = NetLibCreateServiceChild (
+             Controller,
+             Image,
+             &gEfiHttpServiceBindingProtocolGuid,
+             &HttpIo->Handle
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->OpenProtocol (
+                  HttpIo->Handle,
+                  &gEfiHttpProtocolGuid,
+                  (VOID **) &Http,
+                  Image,
+                  Controller,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status) || (Http == NULL)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Init the configuration data and configure the HTTP child.
+  //
+  HttpIo->Image       = Image;
+  HttpIo->Controller  = Controller;
+  HttpIo->IpVersion   = IpVersion;
+  HttpIo->Http        = Http;
+
+  ZeroMem (&HttpConfigData, sizeof (EFI_HTTP_CONFIG_DATA));
+  HttpConfigData.HttpVersion        = HttpVersion11;
+  HttpConfigData.TimeOutMillisec    = ConfigData->Config4.RequestTimeOut;
+  if (HttpIo->IpVersion == IP_VERSION_4) {
+    HttpConfigData.LocalAddressIsIPv6 = FALSE;
+    
+    Http4AccessPoint.UseDefaultAddress = ConfigData->Config4.UseDefaultAddress;
+    Http4AccessPoint.LocalPort         = ConfigData->Config4.LocalPort;
+    IP4_COPY_ADDRESS (&Http4AccessPoint.LocalAddress, &ConfigData->Config4.LocalIp);
+    IP4_COPY_ADDRESS (&Http4AccessPoint.LocalSubnet, &ConfigData->Config4.SubnetMask);
+    HttpConfigData.AccessPoint.IPv4Node = &Http4AccessPoint;   
+  } else {
+    ASSERT (FALSE);
+  }
+  
+  Status = Http->Configure (Http, &HttpConfigData);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create events for variuos asynchronous operations.
+  //
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  HttpIoCommonNotify,
+                  &HttpIo->IsTxDone,
+                  &Event
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+  HttpIo->ReqToken.Event = Event;
+  HttpIo->ReqToken.Message = &HttpIo->ReqMessage;
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  HttpIoCommonNotify,
+                  &HttpIo->IsRxDone,
+                  &Event
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+  HttpIo->RspToken.Event = Event;
+  HttpIo->RspToken.Message = &HttpIo->RspMessage;
+
+  return EFI_SUCCESS;
+  
+ON_ERROR:
+  HttpIoDestroyIo (HttpIo);
+
+  return Status;
+}
+
+/**
+  Destroy the HTTP_IO and release the resouces. 
+
+  @param[in]  HttpIo          The HTTP_IO which wraps the HTTP service to be destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+  IN HTTP_IO                *HttpIo
+  )
+{
+  EFI_HTTP_PROTOCOL         *Http;
+  EFI_EVENT                 Event;
+
+  if (HttpIo == NULL) {
+    return;
+  }
+
+  Event = HttpIo->ReqToken.Event;
+  if (Event != NULL) {
+    gBS->CloseEvent (Event);
+  }
+
+  Event = HttpIo->RspToken.Event;
+  if (Event != NULL) {
+    gBS->CloseEvent (Event);
+  }
+  
+  Http = HttpIo->Http;
+  if (Http != NULL) {
+    Http->Configure (Http, NULL);
+    gBS->CloseProtocol (
+           HttpIo->Handle,
+           &gEfiHttpProtocolGuid,
+           HttpIo->Image,
+           HttpIo->Controller
+           );
+  }
+
+  NetLibDestroyServiceChild (
+    HttpIo->Controller,
+    HttpIo->Image,
+    &gEfiHttpServiceBindingProtocolGuid,
+    HttpIo->Handle
+    );
+}
+
+/**
+  Synchronously send a HTTP REQUEST message to the server.
+  
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   Request          A pointer to storage such data as URL and HTTP method.
+  @param[in]   HeaderCount      Number of HTTP header structures in Headers list. 
+  @param[in]   Headers          Array containing list of HTTP headers.
+  @param[in]   BodyLength       Length in bytes of the HTTP body.
+  @param[in]   Body             Body associated with the HTTP request. 
+  
+  @retval EFI_SUCCESS            The HTTP request is trasmitted.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+  IN  HTTP_IO                *HttpIo,
+  IN  EFI_HTTP_REQUEST_DATA  *Request,
+  IN  UINTN                  HeaderCount,
+  IN  EFI_HTTP_HEADER        *Headers,
+  IN  UINTN                  BodyLength,
+  IN  VOID                   *Body
+  )
+{
+  EFI_STATUS                 Status;
+  EFI_HTTP_PROTOCOL          *Http;
+
+  if (HttpIo == NULL || HttpIo->Http == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  HttpIo->ReqToken.Status  = EFI_NOT_READY;
+  HttpIo->ReqToken.Message->Data.Request = Request;
+  HttpIo->ReqToken.Message->HeaderCount  = HeaderCount;
+  HttpIo->ReqToken.Message->Headers      = Headers;
+  HttpIo->ReqToken.Message->BodyLength   = BodyLength;
+  HttpIo->ReqToken.Message->Body         = Body;
+
+  //
+  // Queue the request token to HTTP instances.
+  //
+  Http = HttpIo->Http;
+  HttpIo->IsTxDone = FALSE;
+  Status = Http->Request (
+                   Http,
+                   &HttpIo->ReqToken
+                   );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Poll the network until transmit finish.
+  //
+  while (!HttpIo->IsTxDone) {
+    Http->Poll (Http);
+  }
+
+  return HttpIo->ReqToken.Status;
+}
+
+/**
+  Synchronously receive a HTTP RESPONSE message from the server.
+  
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   RecvMsgHeader    TRUE to receive a new HTTP response (from message header).
+                                FALSE to continue receive the previous response message.
+  @param[out]  ResponseData     Point to a wrapper of the received response data.
+  
+  @retval EFI_SUCCESS            The HTTP resopnse is received.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+  IN      HTTP_IO                  *HttpIo,
+  IN      BOOLEAN                  RecvMsgHeader,
+     OUT  HTTP_IO_RESOPNSE_DATA    *ResponseData
+  )
+{
+  EFI_STATUS                 Status;
+  EFI_HTTP_PROTOCOL          *Http;
+
+  if (HttpIo == NULL || HttpIo->Http == NULL || ResponseData == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Queue the response token to HTTP instances.
+  //
+  HttpIo->RspToken.Status  = EFI_NOT_READY;
+  if (RecvMsgHeader) {
+    HttpIo->RspToken.Message->Data.Response = &ResponseData->Response;
+  } else {
+    HttpIo->RspToken.Message->Data.Response = NULL;
+  }
+  HttpIo->RspToken.Message->HeaderCount   = 0;
+  HttpIo->RspToken.Message->Headers       = NULL;
+  HttpIo->RspToken.Message->BodyLength    = ResponseData->BodyLength;
+  HttpIo->RspToken.Message->Body          = ResponseData->Body;
+
+  Http = HttpIo->Http;
+  HttpIo->IsRxDone = FALSE;
+  Status = Http->Response (
+                   Http,
+                   &HttpIo->RspToken
+                   );
+  
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Poll the network until transmit finish.
+  //
+  while (!HttpIo->IsRxDone) {
+    Http->Poll (Http);
+  }
+
+  //
+  // Store the received data into the wrapper.
+  //
+  Status = HttpIo->ReqToken.Status;
+  if (!EFI_ERROR (Status)) {
+    ResponseData->HeaderCount = HttpIo->RspToken.Message->HeaderCount;
+    ResponseData->Headers     = HttpIo->RspToken.Message->Headers;
+    ResponseData->BodyLength  = HttpIo->RspToken.Message->BodyLength;
+  }
+
+  return Status;
+}
diff --git a/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/NetworkPkg/HttpBootDxe/HttpBootSupport.h
new file mode 100644 (file)
index 0000000..d1fa287
--- /dev/null
@@ -0,0 +1,250 @@
+/** @file
+  Support functions declaration for UEFI HTTP boot driver.
+
+Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+This program and the accompanying materials are licensed and made available under 
+the terms and conditions of the BSD License that accompanies this distribution.  
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.                                          
+    
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef __EFI_HTTP_BOOT_SUPPORT_H__
+#define __EFI_HTTP_BOOT_SUPPORT_H__
+
+/**
+  Get the Nic handle using any child handle in the IPv4 stack.
+
+  @param[in]  ControllerHandle    Pointer to child handle over IPv4.
+
+  @return NicHandle               The pointer to the Nic handle.
+  @return NULL                    Can't find the Nic handle.
+
+**/
+EFI_HANDLE
+HttpBootGetNicByIp4Children (
+  IN EFI_HANDLE                 ControllerHandle
+  );
+
+/**
+  This function is to convert UINTN to ASCII string with the required formatting.
+
+  @param[in]  Number         Numeric value to be converted.
+  @param[in]  Buffer         The pointer to the buffer for ASCII string.
+  @param[in]  Length         The length of the required format.
+
+**/
+VOID
+HttpBootUintnToAscDecWithFormat (
+  IN UINTN                       Number,
+  IN UINT8                       *Buffer,
+  IN INTN                        Length
+  );
+
+
+/**
+  This function is to display the IPv4 address.
+
+  @param[in]  Ip        The pointer to the IPv4 address.
+
+**/
+VOID
+HttpBootShowIp4Addr (
+  IN EFI_IPv4_ADDRESS   *Ip
+  );
+
+//
+// A wrapper structure to hold the HTTP headers.
+//
+typedef struct {
+  UINTN                       MaxHeaderCount;
+  UINTN                       HeaderCount;
+  EFI_HTTP_HEADER             *Headers;
+} HTTP_IO_HEADER;
+
+/**
+  Create a HTTP_IO_HEADER to hold the HTTP header items.
+
+  @param[in]  MaxHeaderCount         The maximun number of HTTP header in this holder.
+
+  @return    A pointer of the HTTP header holder or NULL if failed.
+  
+**/
+HTTP_IO_HEADER *
+HttpBootCreateHeader (
+  IN  UINTN                MaxHeaderCount
+  );
+
+/**
+  Destroy the HTTP_IO_HEADER and release the resouces. 
+
+  @param[in]  HttpIoHeader       Point to the HTTP header holder to be destroyed.
+
+**/
+VOID
+HttpBootFreeHeader (
+  IN  HTTP_IO_HEADER       *HttpIoHeader
+  );
+
+/**
+  Set or update a HTTP header with the field name and corresponding value.
+
+  @param[in]  HttpIoHeader       Point to the HTTP header holder.
+  @param[in]  FieldName          Null terminated string which describes a field name. 
+  @param[in]  FieldValue         Null terminated string which describes the corresponding field value.
+
+  @retval  EFI_SUCCESS           The HTTP header has been set or updated.
+  @retval  EFI_INVALID_PARAMETER Any input parameter is invalid.
+  @retval  EFI_OUT_OF_RESOURCES  Insufficient resource to complete the operation.
+  @retval  Other                 Unexpected error happened.
+  
+**/
+EFI_STATUS
+HttpBootSetHeader (
+  IN  HTTP_IO_HEADER       *HttpIoHeader,
+  IN  CHAR8                *FieldName,
+  IN  CHAR8                *FieldValue
+  );
+
+//
+// HTTP_IO configuration data for IPv4
+//
+typedef struct {
+  EFI_HTTP_VERSION          HttpVersion;
+  UINT32                    RequestTimeOut;  // In milliseconds.
+  UINT32                    ResponseTimeOut; // In milliseconds.
+  BOOLEAN                   UseDefaultAddress;
+  EFI_IPv4_ADDRESS          LocalIp;
+  EFI_IPv4_ADDRESS          SubnetMask;
+  UINT16                    LocalPort;
+} HTTP4_IO_CONFIG_DATA;
+
+//
+// HTTP_IO configuration
+//
+typedef union {
+  HTTP4_IO_CONFIG_DATA       Config4;
+} HTTP_IO_CONFIG_DATA;
+
+//
+// HTTO_IO wrapper of the EFI HTTP service.
+//
+typedef struct {
+  UINT8                     IpVersion;
+  EFI_HANDLE                Image;
+  EFI_HANDLE                Controller;
+  EFI_HANDLE                Handle;
+  
+  EFI_HTTP_PROTOCOL         *Http;
+
+  EFI_HTTP_TOKEN            ReqToken;
+  EFI_HTTP_MESSAGE          ReqMessage;
+  EFI_HTTP_TOKEN            RspToken;
+  EFI_HTTP_MESSAGE          RspMessage;
+
+  BOOLEAN                   IsTxDone;
+  BOOLEAN                   IsRxDone;
+} HTTP_IO;
+
+//
+// A wrapper structure to hold the received HTTP response data.
+//
+typedef struct {
+  EFI_HTTP_RESPONSE_DATA      Response;
+  UINTN                       HeaderCount;
+  EFI_HTTP_HEADER             *Headers;
+  UINTN                       BodyLength;
+  CHAR8                       *Body;
+} HTTP_IO_RESOPNSE_DATA;
+
+/**
+  Create a HTTP_IO to access the HTTP service. It will create and configure
+  a HTTP child handle.
+
+  @param[in]  Image          The handle of the driver image.
+  @param[in]  Controller     The handle of the controller.
+  @param[in]  IpVersion      IP_VERSION_4 or IP_VERSION_6.
+  @param[in]  ConfigData     The HTTP_IO configuration data.
+  @param[out] HttpIo         The HTTP_IO.
+  
+  @retval EFI_SUCCESS            The HTTP_IO is created and configured.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_UNSUPPORTED        One or more of the control options are not
+                                 supported in the implementation.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval Others                 Failed to create the HTTP_IO or configure it.
+
+**/
+EFI_STATUS
+HttpIoCreateIo (
+  IN EFI_HANDLE             Image,
+  IN EFI_HANDLE             Controller,
+  IN UINT8                  IpVersion,
+  IN HTTP_IO_CONFIG_DATA    *ConfigData,
+  OUT HTTP_IO               *HttpIo
+  );
+
+/**
+  Destroy the HTTP_IO and release the resouces. 
+
+  @param[in]  HttpIo          The HTTP_IO which wraps the HTTP service to be destroyed.
+
+**/
+VOID
+HttpIoDestroyIo (
+  IN HTTP_IO                *HttpIo
+  );
+
+/**
+  Synchronously send a HTTP REQUEST message to the server.
+  
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   Request          A pointer to storage such data as URL and HTTP method.
+  @param[in]   HeaderCount      Number of HTTP header structures in Headers list. 
+  @param[in]   Headers          Array containing list of HTTP headers.
+  @param[in]   BodyLength       Length in bytes of the HTTP body.
+  @param[in]   Body             Body associated with the HTTP request. 
+  
+  @retval EFI_SUCCESS            The HTTP request is trasmitted.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoSendRequest (
+  IN  HTTP_IO                *HttpIo,
+  IN  EFI_HTTP_REQUEST_DATA  *Request,      OPTIONAL
+  IN  UINTN                  HeaderCount,
+  IN  EFI_HTTP_HEADER        *Headers,      OPTIONAL
+  IN  UINTN                  BodyLength,
+  IN  VOID                   *Body          OPTIONAL
+  );
+
+/**
+  Synchronously receive a HTTP RESPONSE message from the server.
+  
+  @param[in]   HttpIo           The HttpIo wrapping the HTTP service.
+  @param[in]   RecvMsgHeader    TRUE to receive a new HTTP response (from message header).
+                                FALSE to continue receive the previous response message.
+  @param[out]  ResponseData     Point to a wrapper of the received response data.
+  
+  @retval EFI_SUCCESS            The HTTP resopnse is received.
+  @retval EFI_INVALID_PARAMETER  One or more parameters are invalid.
+  @retval EFI_OUT_OF_RESOURCES   Failed to allocate memory.
+  @retval EFI_DEVICE_ERROR       An unexpected network or system error occurred.
+  @retval Others                 Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpIoRecvResponse (
+  IN      HTTP_IO                  *HttpIo,
+  IN      BOOLEAN                  RecvMsgHeader,
+     OUT  HTTP_IO_RESOPNSE_DATA    *ResponseData
+  );
+
+#endif