public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [Patch][edk2-platforms/devel-MinnowBoard3] Fix build error.
@ 2017-01-09  1:39 lushifex
  2017-01-09  2:05 ` Wei, David
  0 siblings, 1 reply; 2+ messages in thread
From: lushifex @ 2017-01-09  1:39 UTC (permalink / raw)
  To: edk2-devel; +Cc: david.wei

Fix build error for latest VS2015 compiler with IA32 tip.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: lushifex <shifeix.a.lu@intel.com>
---
 .../NetworkPkg/UefiPxeBcDxe/ComponentName.c        |  358 +++
 .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c | 1259 ++++++++++
 .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h |  100 +
 .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c           | 1672 ++++++++++++++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h           |  409 ++++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c           | 2096 +++++++++++++++++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h           |  303 +++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c          | 1825 +++++++++++++++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h          |  181 ++
 .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c | 2411 ++++++++++++++++++++
 .../SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h |  225 ++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c           | 1113 +++++++++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h           |  137 ++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c         | 1513 ++++++++++++
 .../NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h         |  515 +++++
 .../NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf       |  109 +
 .../NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni       |  Bin 0 -> 2416 bytes
 .../NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni  |  Bin 0 -> 1332 bytes
 .../BroxtonPlatformPkg/PlatformDsc/Components.dsc  |    4 +-
 Platform/BroxtonPlatformPkg/PlatformPkg.fdf        |    4 +-
 .../Sdio/Dxe/MMC/MmcMediaDeviceDxe/MMCSDBlockIo.c  |    5 +-
 21 files changed, 14233 insertions(+), 6 deletions(-)
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni
 create mode 100644 Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni

diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c
new file mode 100644
index 0000000..1a81e22
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/ComponentName.c
@@ -0,0 +1,358 @@
+/** @file
+  UEFI Component Name(2) protocol implementation for UefiPxeBc driver.
+
+  Copyright (c) 2009 - 2012, 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+/**
+  Retrieves a Unicode string that is the user-readable name of the driver.
+
+  This function retrieves the user-readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user-readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver 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. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param[out]  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
+PxeBcComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_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 a driver.
+
+  This function retrieves the user-readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user-readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param[in]  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[in]  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[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver 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. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param[out]  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
+PxeBcComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **ControllerName
+  );
+
+
+//
+// EFI Component Name Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME_PROTOCOL    gPxeBcComponentName  = {
+  PxeBcComponentNameGetDriverName,
+  PxeBcComponentNameGetControllerName,
+  "eng"
+};
+
+//
+// EFI Component Name 2 Protocol
+//
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_COMPONENT_NAME2_PROTOCOL   gPxeBcComponentName2 = {
+  (EFI_COMPONENT_NAME2_GET_DRIVER_NAME) PxeBcComponentNameGetDriverName,
+  (EFI_COMPONENT_NAME2_GET_CONTROLLER_NAME) PxeBcComponentNameGetControllerName,
+  "en"
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE       mPxeBcDriverNameTable[] = {
+  {
+    "eng;en",
+    L"UEFI PXE Base Code Driver"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+GLOBAL_REMOVE_IF_UNREFERENCED EFI_UNICODE_STRING_TABLE       mPxeBcControllerNameTable[] = {
+  {
+    "eng;en",
+    L"PXE Controller"
+  },
+  {
+    NULL,
+    NULL
+  }
+};
+
+/**
+  Retrieves a Unicode string that is the user-readable name of the driver.
+
+  This function retrieves the user-readable name of a driver in the form of a
+  Unicode string. If the driver specified by This has a user-readable name in
+  the language specified by Language, then a pointer to the driver name is
+  returned in DriverName, and EFI_SUCCESS is returned. If the driver specified
+  by This does not support the language specified by Language,
+  then EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language. This is the
+                                language of the driver 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. Language is specified
+                                in RFC 4646 or ISO 639-2 language code format.
+
+  @param[out]  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
+PxeBcComponentNameGetDriverName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **DriverName
+  )
+{
+  return LookupUnicodeString2(
+           Language,
+           This->SupportedLanguages,
+           mPxeBcDriverNameTable,
+           DriverName,
+           (BOOLEAN)(This == &gPxeBcComponentName)
+           );
+}
+
+
+/**
+  Retrieves a Unicode string that is the user-readable name of the controller
+  that is being managed by a driver.
+
+  This function retrieves the user-readable name of the controller specified by
+  ControllerHandle and ChildHandle in the form of a Unicode string. If the
+  driver specified by This has a user-readable name in the language specified by
+  Language, then a pointer to the controller name is returned in ControllerName,
+  and EFI_SUCCESS is returned.  If the driver specified by This is not currently
+  managing the controller specified by ControllerHandle and ChildHandle,
+  then EFI_UNSUPPORTED is returned.  If the driver specified by This does not
+  support the language specified by Language, then EFI_UNSUPPORTED is returned.
+
+  @param[in]  This              A pointer to the EFI_COMPONENT_NAME2_PROTOCOL or
+                                EFI_COMPONENT_NAME_PROTOCOL instance.
+
+  @param[in]  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[in]  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[in]  Language          A pointer to a Null-terminated ASCII string
+                                array indicating the language.  This is the
+                                language of the driver 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. Language is specified in
+                                RFC 4646 or ISO 639-2 language code format.
+
+  @param[out]  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
+PxeBcComponentNameGetControllerName (
+  IN  EFI_COMPONENT_NAME_PROTOCOL  *This,
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  EFI_HANDLE                   ChildHandle        OPTIONAL,
+  IN  CHAR8                        *Language,
+  OUT CHAR16                       **ControllerName
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_HANDLE                      NicHandle;
+  PXEBC_PRIVATE_PROTOCOL          *Id;
+
+  if (ControllerHandle == NULL || ChildHandle != NULL) {
+    return EFI_UNSUPPORTED;
+  }
+  
+  NicHandle = PxeBcGetNicByIp4Children (ControllerHandle);
+  if (NicHandle == NULL) {
+    NicHandle = PxeBcGetNicByIp6Children (ControllerHandle);
+    if (NicHandle == NULL) {
+      return EFI_UNSUPPORTED;
+    }
+  }
+
+  //
+  // Try to retrieve the private data by PxeBcPrivate protocol.
+  //
+  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,
+           mPxeBcControllerNameTable,
+           ControllerName,
+           (BOOLEAN)(This == &gPxeBcComponentName)
+           );
+}
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
new file mode 100644
index 0000000..504fac6
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.c
@@ -0,0 +1,1259 @@
+/** @file
+  Boot functions implementation for UefiPxeBc Driver.
+
+  Copyright (c) 2009 - 2017, 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+  Display the string of the boot item.
+
+  If the length of the boot item string beyond 70 Char, just display 70 Char.
+
+  @param[in]  Str           The pointer to the string.
+  @param[in]  Len           The length of the string.
+
+**/
+VOID
+PxeBcDisplayBootItem (
+  IN UINT8                 *Str,
+  IN UINT8                 Len
+  )
+{
+  UINT8                    Tmp;
+
+  //
+  // Cut off the chars behind 70th.
+  //
+  Len       = (UINT8) MIN (PXEBC_DISPLAY_MAX_LINE, Len);
+  Tmp       = Str[Len];
+  Str[Len]  = 0;
+  AsciiPrint ("%a \n", Str);
+
+  //
+  // Restore the original 70th char.
+  //
+  Str[Len]  = Tmp;
+}
+
+
+/**
+  Select and maintain the boot prompt if needed.
+
+  @param[in]  Private          Pointer to PxeBc private data.
+
+  @retval EFI_SUCCESS          Selected boot prompt done.
+  @retval EFI_TIMEOUT          Selected boot prompt timed out.
+  @retval EFI_NOT_FOUND        The proxy offer is not Pxe10.
+  @retval EFI_ABORTED          User cancelled the operation.
+  @retval EFI_NOT_READY        Reading the input key from the keyboard has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootPrompt (
+  IN PXEBC_PRIVATE_DATA      *Private
+  )
+{
+  PXEBC_DHCP_PACKET_CACHE    *Cache;
+  PXEBC_VENDOR_OPTION        *VendorOpt;
+  EFI_PXE_BASE_CODE_MODE     *Mode;
+  EFI_EVENT                  TimeoutEvent;
+  EFI_EVENT                  DescendEvent;
+  EFI_INPUT_KEY              InputKey;
+  EFI_STATUS                 Status;
+  UINT32                     OfferType;
+  UINT8                      Timeout;
+  UINT8                      *Prompt;
+  UINT8                      PromptLen;
+  INT32                      SecCol;
+  INT32                      SecRow;
+
+  TimeoutEvent = NULL;
+  DescendEvent = NULL;
+  Mode         = Private->PxeBc.Mode;
+  Cache        = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
+  OfferType    = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
+
+  //
+  // Only DhcpPxe10 and ProxyPxe10 offer needs boot prompt.
+  //
+  if (OfferType != PxeOfferTypeProxyPxe10 && OfferType != PxeOfferTypeDhcpPxe10) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // There is no specified ProxyPxe10 for IPv6 in PXE and UEFI spec.
+  //
+  ASSERT (!Mode->UsingIpv6);
+
+  VendorOpt = &Cache->Dhcp4.VendorOpt;
+  //
+  // According to the PXE specification 2.1, Table 2-1 PXE DHCP Options,
+  // we must not consider a boot prompt or boot menu if all of the following hold:
+  //   - the PXE_DISCOVERY_CONTROL tag(6) is present inside the Vendor Options(43), and has bit 3 set  
+  //   - a boot file name has been presented in the initial DHCP or ProxyDHCP offer packet.
+  //
+  if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) &&
+      Cache->Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+    return EFI_ABORTED;
+  }
+  
+  if (!IS_VALID_BOOT_PROMPT (VendorOpt->BitMap)) {
+    return EFI_TIMEOUT;
+  }
+
+  Timeout   = VendorOpt->MenuPrompt->Timeout;
+  Prompt    = VendorOpt->MenuPrompt->Prompt;
+  PromptLen = (UINT8) (VendorOpt->MenuPromptLen - 1);
+
+  //
+  // The valid scope of Timeout refers to PXE2.1 spec.
+  //
+  if (Timeout == 0) {
+    return EFI_TIMEOUT;
+  }
+  if (Timeout == 255) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Create and start a timer as timeout event.
+  //
+  Status = gBS->CreateEvent (
+                  EVT_TIMER,
+                  TPL_CALLBACK,
+                  NULL,
+                  NULL,
+                  &TimeoutEvent
+                  );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = gBS->SetTimer (
+                  TimeoutEvent,
+                  TimerRelative,
+                  MultU64x32 (Timeout, TICKS_PER_SECOND)
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Create and start a periodic timer as descend event by second.
+  //
+  Status = gBS->CreateEvent (
+                  EVT_TIMER,
+                  TPL_CALLBACK,
+                  NULL,
+                  NULL,
+                  &DescendEvent
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  Status = gBS->SetTimer (
+                  DescendEvent,
+                  TimerPeriodic,
+                  TICKS_PER_SECOND
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Display the boot item and cursor on the screen.
+  //
+  SecCol = gST->ConOut->Mode->CursorColumn;
+  SecRow = gST->ConOut->Mode->CursorRow;
+
+  PxeBcDisplayBootItem (Prompt, PromptLen);
+
+  gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
+  AsciiPrint ("(%d) ", Timeout--);
+
+  Status = EFI_TIMEOUT;
+  while (EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+    if (!EFI_ERROR (gBS->CheckEvent (DescendEvent))) {
+      gST->ConOut->SetCursorPosition (gST->ConOut, SecCol + PromptLen, SecRow);
+      AsciiPrint ("(%d) ", Timeout--);
+    }
+    if (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
+      gBS->Stall (10 * TICKS_PER_MS);
+      continue;
+    }
+    //
+    // Parse the input key by user.
+    // If <F8> or <Ctrl> + <M> is pressed, return success to display the boot menu.
+    //
+    if (InputKey.ScanCode == 0) {
+
+      switch (InputKey.UnicodeChar) {
+
+      case CTRL ('c'):
+        Status = EFI_ABORTED;
+        break;
+
+      case CTRL ('m'):
+      case 'm':
+      case 'M':
+        Status = EFI_SUCCESS;
+        break;
+
+      default:
+        continue;
+      }
+
+    } else {
+
+      switch (InputKey.ScanCode) {
+
+      case SCAN_F8:
+        Status = EFI_SUCCESS;
+        break;
+
+      case SCAN_ESC:
+        Status = EFI_ABORTED;
+        break;
+
+      default:
+        continue;
+      }
+    }
+
+    break;
+  }
+
+  //
+  // Reset the cursor on the screen.
+  //
+  gST->ConOut->SetCursorPosition (gST->ConOut, 0 , SecRow + 1);
+
+ON_EXIT:
+  if (DescendEvent != NULL) {
+    gBS->CloseEvent (DescendEvent);
+  }
+  if (TimeoutEvent != NULL) {
+    gBS->CloseEvent (TimeoutEvent);
+  }
+
+  return Status;
+}
+
+
+/**
+  Select the boot menu by user's input.
+
+  @param[in]  Private         Pointer to PxeBc private data.
+  @param[out] Type            The type of the menu.
+  @param[in]  UseDefaultItem  Use default item or not.
+
+  @retval EFI_ABORTED     User cancel operation.
+  @retval EFI_SUCCESS     Select the boot menu success.
+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcSelectBootMenu (
+  IN  PXEBC_PRIVATE_DATA              *Private,
+  OUT UINT16                          *Type,
+  IN  BOOLEAN                         UseDefaultItem
+  )
+{
+  EFI_PXE_BASE_CODE_MODE     *Mode;
+  PXEBC_DHCP_PACKET_CACHE    *Cache;
+  PXEBC_VENDOR_OPTION        *VendorOpt;
+  EFI_INPUT_KEY              InputKey;
+  UINT32                     OfferType;
+  UINT8                      MenuSize;
+  UINT8                      MenuNum;
+  INT32                      TopRow;
+  UINT16                     Select;
+  UINT16                     LastSelect;
+  UINT8                      Index;
+  BOOLEAN                    Finish;
+  CHAR8                      Blank[PXEBC_DISPLAY_MAX_LINE];
+  PXEBC_BOOT_MENU_ENTRY      *MenuItem;
+  PXEBC_BOOT_MENU_ENTRY      *MenuArray[PXEBC_MENU_MAX_NUM];
+
+  Finish    = FALSE;
+  Select    = 0;
+  Index     = 0;
+  *Type     = 0;
+  Mode      = Private->PxeBc.Mode;
+  Cache     = Mode->ProxyOfferReceived ? &Private->ProxyOffer : &Private->DhcpAck;
+  OfferType = Mode->UsingIpv6 ? Cache->Dhcp6.OfferType : Cache->Dhcp4.OfferType;
+
+  //
+  // There is no specified DhcpPxe10/ProxyPxe10 for IPv6 in PXE and UEFI spec.
+  //
+  ASSERT (!Mode->UsingIpv6);
+  ASSERT (OfferType == PxeOfferTypeProxyPxe10 || OfferType == PxeOfferTypeDhcpPxe10);
+
+  VendorOpt = &Cache->Dhcp4.VendorOpt;
+  if (!IS_VALID_BOOT_MENU (VendorOpt->BitMap)) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Display the boot menu on the screen.
+  //
+  SetMem (Blank, sizeof(Blank), ' ');
+
+  MenuSize  = VendorOpt->BootMenuLen;
+  MenuItem  = VendorOpt->BootMenu;
+
+  if (MenuSize == 0) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  while (MenuSize > 0 && Index < PXEBC_MENU_MAX_NUM) {
+    ASSERT (MenuItem != NULL);
+    MenuArray[Index]  = MenuItem;
+    MenuSize          = (UINT8) (MenuSize - (MenuItem->DescLen + 3));
+    MenuItem          = (PXEBC_BOOT_MENU_ENTRY *) ((UINT8 *) MenuItem + MenuItem->DescLen + 3);
+    Index++;
+  }
+
+  if (UseDefaultItem) {
+    ASSERT (MenuArray[0] != NULL);
+    CopyMem (Type, &MenuArray[0]->Type, sizeof (UINT16));
+    *Type = NTOHS (*Type);
+    return EFI_SUCCESS;
+  }
+
+  MenuNum = Index;
+
+  for (Index = 0; Index < MenuNum; Index++) {
+    ASSERT (MenuArray[Index] != NULL);
+    PxeBcDisplayBootItem (MenuArray[Index]->DescStr, MenuArray[Index]->DescLen);
+  }
+
+  TopRow = gST->ConOut->Mode->CursorRow - MenuNum;
+
+  //
+  // Select the boot item by user in the boot menu.
+  //
+  do {
+    //
+    // Highlight selected row.
+    //
+    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + Select);
+    ASSERT (Select < PXEBC_MENU_MAX_NUM);
+    ASSERT (MenuArray[Select] != NULL);
+    Blank[MenuArray[Select]->DescLen] = 0;
+    AsciiPrint ("%a\r", Blank);
+    PxeBcDisplayBootItem (MenuArray[Select]->DescStr, MenuArray[Select]->DescLen);
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
+    LastSelect = Select;
+
+    while (gST->ConIn->ReadKeyStroke (gST->ConIn, &InputKey) == EFI_NOT_READY) {
+      gBS->Stall (10 * TICKS_PER_MS);
+    }
+
+    if (InputKey.ScanCode == 0) {
+      switch (InputKey.UnicodeChar) {
+      case CTRL ('c'):
+        InputKey.ScanCode = SCAN_ESC;
+        break;
+
+      case CTRL ('j'):  /* linefeed */
+      case CTRL ('m'):  /* return */
+        Finish = TRUE;
+        break;
+
+      case CTRL ('i'):  /* tab */
+      case ' ':
+      case 'd':
+      case 'D':
+        InputKey.ScanCode = SCAN_DOWN;
+        break;
+
+      case CTRL ('h'):  /* backspace */
+      case 'u':
+      case 'U':
+        InputKey.ScanCode = SCAN_UP;
+        break;
+
+      default:
+        InputKey.ScanCode = 0;
+      }
+    }
+
+    switch (InputKey.ScanCode) {
+    case SCAN_LEFT:
+    case SCAN_UP:
+      if (Select != 0) {
+        Select--;
+      }
+      break;
+
+    case SCAN_DOWN:
+    case SCAN_RIGHT:
+      if (++Select == MenuNum) {
+        Select--;
+      }
+      break;
+
+    case SCAN_PAGE_UP:
+    case SCAN_HOME:
+      Select = 0;
+      break;
+
+    case SCAN_PAGE_DOWN:
+    case SCAN_END:
+      Select = (UINT16) (MenuNum - 1);
+      break;
+
+    case SCAN_ESC:
+      return EFI_ABORTED;
+    }
+
+    //
+    // Unhighlight the last selected row.
+    //
+    gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + LastSelect);
+    ASSERT (LastSelect < PXEBC_MENU_MAX_NUM);
+    ASSERT (MenuArray[LastSelect] != NULL);
+    Blank[MenuArray[LastSelect]->DescLen] = 0;
+    AsciiPrint ("%a\r", Blank);
+    PxeBcDisplayBootItem (MenuArray[LastSelect]->DescStr, MenuArray[LastSelect]->DescLen);
+    gST->ConOut->SetCursorPosition (gST->ConOut, 0, TopRow + MenuNum);
+  } while (!Finish);
+
+  //
+  // Swap the byte order.
+  //
+  ASSERT (Select < PXEBC_MENU_MAX_NUM);
+  ASSERT (MenuArray[Select] != NULL);
+  CopyMem (Type, &MenuArray[Select]->Type, sizeof (UINT16));
+  *Type = NTOHS (*Type);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Parse out the boot information from the last Dhcp4 reply packet.
+
+  @param[in, out] Private      Pointer to PxeBc private data.
+  @param[out]     BufferSize   Size of the boot file to be downloaded.
+
+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.
+  @retval Others               Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcDhcp4BootInfo (
+  IN OUT PXEBC_PRIVATE_DATA   *Private,
+     OUT UINT64               *BufferSize
+  )
+{
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
+  EFI_PXE_BASE_CODE_MODE      *Mode;
+  EFI_STATUS                  Status;
+  PXEBC_DHCP4_PACKET_CACHE    *Cache4;
+  UINT16                      Value;
+  PXEBC_VENDOR_OPTION         *VendorOpt;
+  PXEBC_BOOT_SVR_ENTRY        *Entry;
+  
+  PxeBc       = &Private->PxeBc;
+  Mode        = PxeBc->Mode;
+  Status      = EFI_SUCCESS;
+  *BufferSize = 0;
+
+  //
+  // Get the last received Dhcp4 reply packet.
+  //
+  if (Mode->PxeReplyReceived) {
+    Cache4 = &Private->PxeReply.Dhcp4;
+  } else if (Mode->ProxyOfferReceived) {
+    Cache4 = &Private->ProxyOffer.Dhcp4;
+  } else {
+    Cache4 = &Private->DhcpAck.Dhcp4;
+  }
+
+  ASSERT (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL);
+
+  //
+  // Parse the boot server address.
+  // If prompt/discover is disabled, get the first boot server from the boot servers list.
+  // Otherwise, parse the boot server Ipv4 address from next server address field in DHCP header.
+  // If all these fields are not available, use option 54 instead.
+  //
+  VendorOpt = &Cache4->VendorOpt;
+  if (IS_DISABLE_PROMPT_MENU (VendorOpt->DiscoverCtrl) && IS_VALID_BOOT_SERVERS (VendorOpt->BitMap)) {
+    Entry = VendorOpt->BootSvr;
+    if (VendorOpt->BootSvrLen >= sizeof (PXEBC_BOOT_SVR_ENTRY) && Entry->IpCnt > 0) {
+      CopyMem (
+        &Private->ServerIp,
+        &Entry->IpAddr[0],
+        sizeof (EFI_IPv4_ADDRESS)
+        );
+    }
+  }
+  if (Private->ServerIp.Addr[0] == 0) {
+    //
+    // ServerIp.Addr[0] equals zero means we failed to get IP address from boot server list.
+    // Try to use next server address field.
+    //
+    CopyMem (
+      &Private->ServerIp,
+      &Cache4->Packet.Offer.Dhcp4.Header.ServerAddr,
+      sizeof (EFI_IPv4_ADDRESS)
+      );
+  }
+  if (Private->ServerIp.Addr[0] == 0) {
+    //
+    // Still failed , use the IP address from option 54.
+    //
+    CopyMem (
+      &Private->ServerIp,
+      Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
+      sizeof (EFI_IPv4_ADDRESS)
+      );
+  }
+
+  //
+  // Parse the boot file name by option.
+  //
+  Private->BootFileName = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data;
+
+  if (Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN] != NULL) {
+    //
+    // Parse the boot file size by option.
+    //
+    CopyMem (&Value, Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN]->Data, sizeof (Value));
+    Value       = NTOHS (Value);
+    //
+    // The field of boot file size is 512 bytes in unit.
+    //
+    *BufferSize = 512 * Value;
+  } else {
+    //
+    // Get the bootfile size by tftp command if no option available.
+    //
+    Status = PxeBc->Mtftp (
+                      PxeBc,
+                      EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+                      NULL,
+                      FALSE,
+                      BufferSize,
+                      &Private->BlockSize,
+                      &Private->ServerIp,
+                      Private->BootFileName,
+                      NULL,
+                      FALSE
+                      );
+  }
+
+  //
+  // Save the value of boot file size.
+  //
+  Private->BootFileSize = (UINTN) *BufferSize;
+
+  //
+  // Display all the information: boot server address, boot file name and boot file size.
+  //
+  AsciiPrint ("\n  Server IP address is ");
+  PxeBcShowIp4Addr (&Private->ServerIp.v4);
+  AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);
+  AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);
+
+  return Status;
+}
+
+
+/**
+  Parse out the boot information from the last Dhcp6 reply packet.
+
+  @param[in, out] Private      Pointer to PxeBc private data.
+  @param[out]     BufferSize   Size of the boot file to be downloaded.
+
+  @retval EFI_SUCCESS          Successfully parsed out all the boot information.
+  @retval EFI_BUFFER_TOO_SMALL
+  @retval Others               Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcDhcp6BootInfo (
+  IN OUT PXEBC_PRIVATE_DATA   *Private,
+     OUT UINT64               *BufferSize
+  )
+{
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
+  EFI_PXE_BASE_CODE_MODE      *Mode;
+  EFI_STATUS                  Status;
+  PXEBC_DHCP6_PACKET_CACHE    *Cache6;
+  UINT16                      Value;
+
+  PxeBc       = &Private->PxeBc;
+  Mode        = PxeBc->Mode;
+  Status      = EFI_SUCCESS;
+  *BufferSize = 0;
+
+  //
+  // Get the last received Dhcp6 reply packet.
+  //
+  if (Mode->PxeReplyReceived) {
+    Cache6 = &Private->PxeReply.Dhcp6;
+  } else if (Mode->ProxyOfferReceived) {
+    Cache6 = &Private->ProxyOffer.Dhcp6;
+  } else {
+    Cache6 = &Private->DhcpAck.Dhcp6;
+  }
+
+  ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
+
+  //
+  // Parse (m)tftp server ip address and bootfile name.
+  //
+  Status = PxeBcExtractBootFileUrl (
+             &Private->BootFileName,
+             &Private->ServerIp.v6,
+             (CHAR8 *) (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
+             NTOHS (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Set the station address to IP layer.
+  //
+  Status = PxeBcSetIp6Address (Private);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  
+  //
+  // Parse the value of boot file size.
+  //
+  if (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] != NULL) {
+    //
+    // Parse it out if have the boot file parameter option.
+    //
+    Status = PxeBcExtractBootFileParam ((CHAR8 *) Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM]->Data, &Value);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    //
+    // The field of boot file size is 512 bytes in unit.
+    //
+    *BufferSize = 512 * Value;
+  } else {
+    //
+    // Send get file size command by tftp if option unavailable.
+    //
+    Status = PxeBc->Mtftp (
+                      PxeBc,
+                      EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+                      NULL,
+                      FALSE,
+                      BufferSize,
+                      &Private->BlockSize,
+                      &Private->ServerIp,
+                      Private->BootFileName,
+                      NULL,
+                      FALSE
+                      );
+  }
+
+  //
+  // Save the value of boot file size.
+  //
+  Private->BootFileSize = (UINTN) *BufferSize;
+
+  //
+  // Display all the information: boot server address, boot file name and boot file size.
+  //
+  AsciiPrint ("\n  Server IP address is ");
+  PxeBcShowIp6Addr (&Private->ServerIp.v6);
+  AsciiPrint ("\n  NBP filename is %a", Private->BootFileName);
+  AsciiPrint ("\n  NBP filesize is %d Bytes", Private->BootFileSize);
+
+  return Status;
+}
+
+
+/**
+  Extract the discover information and boot server entry from the
+  cached packets if unspecified.
+
+  @param[in]      Private      Pointer to PxeBc private data.
+  @param[in]      Type         The type of bootstrap to perform.
+  @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
+  @param[out]     BootEntry    Pointer to PXEBC_BOOT_SVR_ENTRY.
+  @param[out]     SrvList      Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+  @retval EFI_SUCCESS       Successfully extracted the information.
+  @retval EFI_DEVICE_ERROR  Failed to extract the information.
+
+**/
+EFI_STATUS
+PxeBcExtractDiscoverInfo (
+  IN     PXEBC_PRIVATE_DATA               *Private,
+  IN     UINT16                           Type,
+  IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO  **DiscoverInfo,
+     OUT PXEBC_BOOT_SVR_ENTRY             **BootEntry,
+     OUT EFI_PXE_BASE_CODE_SRVLIST        **SrvList
+  )
+{
+  EFI_PXE_BASE_CODE_MODE          *Mode;
+  PXEBC_DHCP4_PACKET_CACHE        *Cache4;
+  PXEBC_VENDOR_OPTION             *VendorOpt;
+  PXEBC_BOOT_SVR_ENTRY            *Entry;
+  BOOLEAN                         IsFound;
+  EFI_PXE_BASE_CODE_DISCOVER_INFO *Info;
+  UINT16                          Index;
+
+  Mode = Private->PxeBc.Mode;
+  Info = *DiscoverInfo;
+
+  if (Mode->UsingIpv6) {
+    Info->IpCnt    = 1;
+    Info->UseUCast = TRUE;
+
+    Info->SrvList[0].Type              = Type;
+    Info->SrvList[0].AcceptAnyResponse = FALSE;
+
+    //
+    // There is no vendor options specified in DHCPv6, so take BootFileUrl in the last cached packet.
+    //
+    CopyMem (&Info->SrvList[0].IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+
+    *SrvList  = Info->SrvList;
+  } else {
+    Entry     = NULL;
+    IsFound   = FALSE;
+    Cache4    = (Mode->ProxyOfferReceived) ? &Private->ProxyOffer.Dhcp4 : &Private->DhcpAck.Dhcp4;
+    VendorOpt = &Cache4->VendorOpt;
+
+    if (!Mode->DhcpAckReceived || !IS_VALID_DISCOVER_VENDOR_OPTION (VendorOpt->BitMap)) {
+      //
+      // Address is not acquired or no discovery options.
+      //
+      return EFI_INVALID_PARAMETER;
+    }
+
+    //
+    // Parse the boot server entry from the vendor option in the last cached packet.
+    //
+    Info->UseMCast    = (BOOLEAN) !IS_DISABLE_MCAST_DISCOVER (VendorOpt->DiscoverCtrl);
+    Info->UseBCast    = (BOOLEAN) !IS_DISABLE_BCAST_DISCOVER (VendorOpt->DiscoverCtrl);
+    Info->MustUseList = (BOOLEAN) IS_ENABLE_USE_SERVER_LIST (VendorOpt->DiscoverCtrl);
+    Info->UseUCast    = (BOOLEAN) IS_VALID_BOOT_SERVERS (VendorOpt->BitMap);
+
+    if (Info->UseMCast) {
+      //
+      // Get the multicast discover ip address from vendor option if has.
+      //
+      CopyMem (&Info->ServerMCastIp.v4, &VendorOpt->DiscoverMcastIp, sizeof (EFI_IPv4_ADDRESS));
+    }
+
+    Info->IpCnt = 0;
+
+    if (Info->UseUCast) {
+      Entry = VendorOpt->BootSvr;
+
+      while (((UINT8) (Entry - VendorOpt->BootSvr)) < VendorOpt->BootSvrLen) {
+        if (Entry->Type == HTONS (Type)) {
+          IsFound = TRUE;
+          break;
+        }
+        Entry = GET_NEXT_BOOT_SVR_ENTRY (Entry);
+      }
+
+      if (!IsFound) {
+        return EFI_DEVICE_ERROR;
+      }
+
+      Info->IpCnt = Entry->IpCnt;
+      if (Info->IpCnt >= 1) {
+        *DiscoverInfo = AllocatePool (sizeof (*Info) + (Info->IpCnt - 1) * sizeof (**SrvList));
+        if (*DiscoverInfo == NULL) {
+          return EFI_OUT_OF_RESOURCES;       
+        }     
+        CopyMem (*DiscoverInfo, Info, sizeof (*Info));
+        Info = *DiscoverInfo;
+      }
+
+      for (Index = 0; Index < Info->IpCnt; Index++) {
+        CopyMem (&Info->SrvList[Index].IpAddr, &Entry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
+        Info->SrvList[Index].AcceptAnyResponse = !Info->MustUseList;
+        Info->SrvList[Index].Type = NTOHS (Entry->Type);
+      }
+    }
+
+    *BootEntry = Entry;
+    *SrvList   = Info->SrvList;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Build the discover packet and send out for boot server.
+
+  @param[in]  Private               Pointer to PxeBc private data.
+  @param[in]  Type                  PxeBc option boot item type.
+  @param[in]  Layer                 Pointer to option boot item layer.
+  @param[in]  UseBis                Use BIS or not.
+  @param[in]  DestIp                Pointer to the destination address.
+  @param[in]  IpCount               The count of the server address.
+  @param[in]  SrvList               Pointer to the server address list.
+
+  @retval     EFI_SUCCESS           Successfully discovered boot file.
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
+  @retval     Others                Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDiscoverBootServer (
+  IN  PXEBC_PRIVATE_DATA                *Private,
+  IN  UINT16                            Type,
+  IN  UINT16                            *Layer,
+  IN  BOOLEAN                           UseBis,
+  IN  EFI_IP_ADDRESS                    *DestIp,
+  IN  UINT16                            IpCount,
+  IN  EFI_PXE_BASE_CODE_SRVLIST         *SrvList
+  )
+{
+  if (Private->PxeBc.Mode->UsingIpv6) {
+    return PxeBcDhcp6Discover (
+             Private,
+             Type,
+             Layer,
+             UseBis,
+             DestIp
+             );
+  } else {
+    return PxeBcDhcp4Discover (
+             Private,
+             Type,
+             Layer,
+             UseBis,
+             DestIp,
+             IpCount,
+             SrvList
+             );
+  }
+}
+
+
+/**
+  Discover all the boot information for boot file.
+
+  @param[in, out] Private      Pointer to PxeBc private data.
+  @param[out]     BufferSize   Size of the boot file to be downloaded.
+
+  @retval EFI_SUCCESS          Successfully obtained all the boot information .
+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
+  @retval EFI_ABORTED          User cancel current operation.
+  @retval Others               Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcDiscoverBootFile (
+  IN OUT PXEBC_PRIVATE_DATA   *Private,
+     OUT UINT64               *BufferSize
+  )
+{
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
+  EFI_PXE_BASE_CODE_MODE      *Mode;
+  EFI_STATUS                  Status;
+  UINT16                      Type;
+  UINT16                      Layer;
+  BOOLEAN                     UseBis;
+
+  PxeBc = &Private->PxeBc;
+  Mode  = PxeBc->Mode;
+  Type  = EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP;
+  Layer = EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL;
+
+  //
+  // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
+  // other pxe boot information.
+  //
+  Status = PxeBc->Dhcp (PxeBc, TRUE);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Select a boot server from boot server list.
+  //
+  Status = PxeBcSelectBootPrompt (Private);
+
+  if (Status == EFI_SUCCESS) {
+    //
+    // Choose by user's input.
+    //
+    Status = PxeBcSelectBootMenu (Private, &Type, FALSE);
+  } else if (Status == EFI_TIMEOUT) {
+    //
+    // Choose by default item.
+    //
+    Status = PxeBcSelectBootMenu (Private, &Type, TRUE);
+  }
+
+  if (!EFI_ERROR (Status)) {
+
+    if (Type == EFI_PXE_BASE_CODE_BOOT_TYPE_BOOTSTRAP) {
+      //
+      // Local boot(PXE bootstrap server) need abort
+      //
+      return EFI_ABORTED;
+    }
+
+    //
+    // Start to discover the boot server to get (m)tftp server ip address, bootfile
+    // name and bootfile size.
+    //
+    UseBis = (BOOLEAN) (Mode->BisSupported && Mode->BisDetected);
+    Status = PxeBc->Discover (PxeBc, Type, &Layer, UseBis, NULL);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    if (Mode->PxeReplyReceived && !Mode->ProxyOfferReceived) {
+      //
+      // Some network boot loader only search the packet in Mode.ProxyOffer to get its server
+      // IP address, so we need to store a copy of Mode.PxeReply packet into Mode.ProxyOffer.
+      //
+      if (Mode->UsingIpv6) {
+        CopyMem (
+          &Mode->ProxyOffer.Dhcpv6,
+          &Mode->PxeReply.Dhcpv6,
+          Private->PxeReply.Dhcp6.Packet.Ack.Length
+          );
+      } else {
+        CopyMem (
+          &Mode->ProxyOffer.Dhcpv4,
+          &Mode->PxeReply.Dhcpv4,
+          Private->PxeReply.Dhcp4.Packet.Ack.Length
+          );      
+      }
+      Mode->ProxyOfferReceived = TRUE;
+    }
+  }
+
+  //
+  // Parse the boot information.
+  //
+  if (Mode->UsingIpv6) {
+    Status = PxeBcDhcp6BootInfo (Private, BufferSize);
+  } else {
+    Status = PxeBcDhcp4BootInfo (Private, BufferSize);
+  }
+
+  return Status;
+}
+
+
+/**
+  Install PxeBaseCodeCallbackProtocol if not installed before.
+
+  @param[in, out] Private           Pointer to PxeBc private data.
+  @param[out]     NewMakeCallback   If TRUE, it is a new callback.
+                                    Otherwise, it is not new callback.
+  @retval EFI_SUCCESS          PxeBaseCodeCallbackProtocol installed succesfully.
+  @retval Others               Failed to install PxeBaseCodeCallbackProtocol.
+
+**/
+EFI_STATUS
+PxeBcInstallCallback (
+  IN OUT PXEBC_PRIVATE_DATA   *Private,
+     OUT BOOLEAN              *NewMakeCallback
+  )
+{
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
+  EFI_STATUS                  Status;
+
+  //
+  // Check whether PxeBaseCodeCallbackProtocol already installed.
+  //
+  PxeBc  = &Private->PxeBc;
+  Status = gBS->HandleProtocol (
+                  Private->Controller,
+                  &gEfiPxeBaseCodeCallbackProtocolGuid,
+                  (VOID **) &Private->PxeBcCallback
+                  );
+  if (Status == EFI_UNSUPPORTED) {
+
+    CopyMem (
+      &Private->LoadFileCallback,
+      &gPxeBcCallBackTemplate,
+      sizeof (EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL)
+      );
+
+    //
+    // Install a default callback if user didn't offer one.
+    //
+    Status = gBS->InstallProtocolInterface (
+                    &Private->Controller,
+                    &gEfiPxeBaseCodeCallbackProtocolGuid,
+                    EFI_NATIVE_INTERFACE,
+                    &Private->LoadFileCallback
+                    );
+
+    (*NewMakeCallback) = (BOOLEAN) (Status == EFI_SUCCESS);
+
+    Status = PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, NewMakeCallback);
+    if (EFI_ERROR (Status)) {
+      PxeBc->Stop (PxeBc);
+      return Status;
+    }
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Uninstall PxeBaseCodeCallbackProtocol.
+
+  @param[in]  Private           Pointer to PxeBc private data.
+  @param[in]  NewMakeCallback   If TRUE, it is a new callback.
+                                Otherwise, it is not new callback.
+
+**/
+VOID
+PxeBcUninstallCallback (
+  IN PXEBC_PRIVATE_DATA        *Private,
+  IN BOOLEAN                   NewMakeCallback
+  )
+{
+  EFI_PXE_BASE_CODE_PROTOCOL   *PxeBc;
+
+  PxeBc = &Private->PxeBc;
+
+  if (NewMakeCallback) {
+
+    NewMakeCallback = FALSE;
+
+    PxeBc->SetParameters (PxeBc, NULL, NULL, NULL, NULL, &NewMakeCallback);
+
+    gBS->UninstallProtocolInterface (
+          Private->Controller,
+          &gEfiPxeBaseCodeCallbackProtocolGuid,
+          &Private->LoadFileCallback
+          );
+  }
+}
+
+
+/**
+  Download one of boot file in the list, and it's special for IPv6.
+
+  @param[in]      Private           Pointer to PxeBc private data.
+  @param[in, out] BufferSize        Size of user buffer for input;
+                                    required buffer size for output.
+  @param[in]      Buffer            Pointer to user buffer.
+
+  @retval EFI_SUCCESS               Read one of boot file in the list successfully.
+  @retval EFI_BUFFER_TOO_SMALL      The buffer size is not enough for boot file.
+  @retval EFI_NOT_FOUND             There is no proper boot file available.
+  @retval Others                    Failed to download boot file in the list.
+
+**/
+EFI_STATUS
+PxeBcReadBootFileList (
+  IN     PXEBC_PRIVATE_DATA           *Private,
+  IN OUT UINT64                       *BufferSize,
+  IN     VOID                         *Buffer           OPTIONAL
+  )
+{
+  EFI_STATUS                          Status;
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
+
+  PxeBc        = &Private->PxeBc;
+
+  //
+  // Try to download the boot file if everything is ready.
+  //
+  if (Buffer != NULL) {
+    Status = PxeBc->Mtftp (
+                      PxeBc,
+                      EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+                      Buffer,
+                      FALSE,
+                      BufferSize,
+                      &Private->BlockSize,
+                      &Private->ServerIp,
+                      Private->BootFileName,
+                      NULL,
+                      FALSE
+                      );
+
+
+  } else {
+    Status      = EFI_BUFFER_TOO_SMALL;
+  }
+
+  return Status;
+}
+
+
+/**
+  Load boot file into user buffer.
+
+  @param[in]      Private           Pointer to PxeBc private data.
+  @param[in, out] BufferSize        Size of user buffer for input;
+                                    required buffer size for output.
+  @param[in]      Buffer            Pointer to user buffer.
+
+  @retval EFI_SUCCESS          Get all the boot information successfully.
+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
+  @retval EFI_ABORTED          User cancelled the current operation.
+  @retval Others               Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcLoadBootFile (
+  IN     PXEBC_PRIVATE_DATA           *Private,
+  IN OUT UINTN                        *BufferSize,
+  IN     VOID                         *Buffer         OPTIONAL
+  )
+{
+  BOOLEAN                             NewMakeCallback;
+  UINT64                              RequiredSize;
+  UINT64                              CurrentSize;
+  EFI_STATUS                          Status;
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
+  EFI_PXE_BASE_CODE_MODE              *PxeBcMode;
+
+  NewMakeCallback = FALSE;
+  PxeBc           = &Private->PxeBc;
+  PxeBcMode       = &Private->Mode;
+  CurrentSize     = *BufferSize;
+  RequiredSize    = 0;
+
+  //
+  // Install pxebc callback protocol if hasn't been installed yet.
+  //
+  Status = PxeBcInstallCallback (Private, &NewMakeCallback);
+  if (EFI_ERROR(Status)) {
+    return Status;
+  }
+
+  if (Private->BootFileSize == 0) {
+    //
+    // Discover the boot information about the bootfile if hasn't.
+    //
+    Status = PxeBcDiscoverBootFile (Private, &RequiredSize);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    if (PXEBC_IS_SIZE_OVERFLOWED (RequiredSize)) {
+      //
+      // It's error if the required buffer size is beyond the system scope.
+      //
+      Status = EFI_DEVICE_ERROR;
+      goto ON_EXIT;
+    } else if (RequiredSize > 0) {
+      //
+      // Get the right buffer size of the bootfile required.
+      //
+      if (CurrentSize < RequiredSize || Buffer == NULL) {
+        //
+        // It's buffer too small if the size of user buffer is smaller than the required.
+        //
+        CurrentSize = RequiredSize;
+        Status      = EFI_BUFFER_TOO_SMALL;
+        goto ON_EXIT;
+      }
+      CurrentSize = RequiredSize;
+    } else if (RequiredSize == 0 && PxeBcMode->UsingIpv6) {
+      //
+      // Try to download another bootfile in list if failed to get the filesize of the last one.
+      // It's special for the case of IPv6.
+      //
+      Status = PxeBcReadBootFileList (Private, &CurrentSize, Buffer);
+      goto ON_EXIT;
+    }
+  } else if (CurrentSize < Private->BootFileSize || Buffer == NULL ) {
+    //
+    // It's buffer too small if the size of user buffer is smaller than the required.
+    //
+    CurrentSize = Private->BootFileSize;
+    Status      = EFI_BUFFER_TOO_SMALL;
+    goto ON_EXIT;
+  }
+
+  //
+  // Begin to download the bootfile if everything is ready.
+  //
+  AsciiPrint ("\n Downloading NBP file...\n");
+  if (PxeBcMode->UsingIpv6) {
+    Status = PxeBcReadBootFileList (
+               Private,
+               &CurrentSize,
+               Buffer
+               );
+  } else {
+    Status = PxeBc->Mtftp (
+                      PxeBc,
+                      EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+                      Buffer,
+                      FALSE,
+                      &CurrentSize,
+                      &Private->BlockSize,
+                      &Private->ServerIp,
+                      Private->BootFileName,
+                      NULL,
+                      FALSE
+                      );
+  }
+
+ON_EXIT:
+  *BufferSize = (UINTN) CurrentSize;
+  PxeBcUninstallCallback(Private, NewMakeCallback);
+
+  if (Status == EFI_SUCCESS) {
+    AsciiPrint ("\n  Succeed to download NBP file.\n");
+    return EFI_SUCCESS;
+  } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
+    AsciiPrint ("\n  PXE-E05: Buffer size is smaller than the requested file.\n");
+  } else if (Status == EFI_DEVICE_ERROR) {
+    AsciiPrint ("\n  PXE-E07: Network device error.\n");
+  } else if (Status == EFI_OUT_OF_RESOURCES) {
+    AsciiPrint ("\n  PXE-E09: Could not allocate I/O buffers.\n");
+  } else if (Status == EFI_NO_MEDIA) {
+    AsciiPrint ("\n  PXE-E12: Could not detect network connection.\n");
+  } else if (Status == EFI_NO_RESPONSE) {
+    AsciiPrint ("\n  PXE-E16: No offer received.\n");
+  } else if (Status == EFI_TIMEOUT) {
+    AsciiPrint ("\n  PXE-E18: Server response timeout.\n");
+  } else if (Status == EFI_ABORTED) {
+    AsciiPrint ("\n  PXE-E21: Remote boot cancelled.\n");
+  } else if (Status == EFI_ICMP_ERROR) {
+    AsciiPrint ("\n  PXE-E22: Client received ICMP error from server.\n");
+  } else if (Status == EFI_TFTP_ERROR) {
+    AsciiPrint ("\n  PXE-E23: Client received TFTP error from server.\n");
+  } else if (Status == EFI_NOT_FOUND) {
+    AsciiPrint ("\n  PXE-E53: No boot filename received.\n");
+  } else if (Status != EFI_BUFFER_TOO_SMALL) {
+    AsciiPrint ("\n  PXE-E99: Unexpected network error.\n");
+  }
+
+  return Status;
+}
+
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
new file mode 100644
index 0000000..7cfa699
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcBoot.h
@@ -0,0 +1,100 @@
+/** @file
+  Boot functions declaration for UefiPxeBc Driver.
+
+  Copyright (c) 2009 - 2012, 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.
+
+**/
+
+#ifndef __EFI_PXEBC_BOOT_H__
+#define __EFI_PXEBC_BOOT_H__
+
+#define PXEBC_DISPLAY_MAX_LINE             70
+#define PXEBC_DEFAULT_UDP_OVERHEAD_SIZE    8
+#define PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE   4
+
+#define PXEBC_IS_SIZE_OVERFLOWED(x)   ((sizeof (UINTN) < sizeof (UINT64)) && ((x) > 0xFFFFFFFF))
+
+
+/**
+  Extract the discover information and boot server entry from the
+  cached packets if unspecified.
+
+  @param[in]      Private      Pointer to PxeBc private data.
+  @param[in]      Type         The type of bootstrap to perform.
+  @param[in, out] DiscoverInfo Pointer to EFI_PXE_BASE_CODE_DISCOVER_INFO.
+  @param[out]     BootEntry    Pointer to PXEBC_BOOT_SVR_ENTRY.
+  @param[out]     SrvList      Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+  @retval EFI_SUCCESS       Successfully extracted the information.
+  @retval EFI_DEVICE_ERROR  Failed to extract the information.
+
+**/
+EFI_STATUS
+PxeBcExtractDiscoverInfo (
+  IN     PXEBC_PRIVATE_DATA               *Private,
+  IN     UINT16                           Type,
+  IN OUT EFI_PXE_BASE_CODE_DISCOVER_INFO  **DiscoverInfo,
+     OUT PXEBC_BOOT_SVR_ENTRY             **BootEntry,
+     OUT EFI_PXE_BASE_CODE_SRVLIST        **SrvList
+  );
+
+
+/**
+  Build the discover packet and send out for boot.
+
+  @param[in]  Private               Pointer to PxeBc private data.
+  @param[in]  Type                  PxeBc option boot item type.
+  @param[in]  Layer                 Pointer to option boot item layer.
+  @param[in]  UseBis                Use BIS or not.
+  @param[in]  DestIp                Pointer to the server address.
+  @param[in]  IpCount               The total count of the server address.
+  @param[in]  SrvList               Pointer to the server address list.
+
+  @retval     EFI_SUCCESS           Successfully discovered boot file.
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
+  @retval     Others                Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDiscoverBootServer (
+  IN  PXEBC_PRIVATE_DATA                *Private,
+  IN  UINT16                            Type,
+  IN  UINT16                            *Layer,
+  IN  BOOLEAN                           UseBis,
+  IN  EFI_IP_ADDRESS                    *DestIp,
+  IN  UINT16                            IpCount,
+  IN  EFI_PXE_BASE_CODE_SRVLIST         *SrvList
+  );
+
+
+/**
+  Load boot file into user buffer.
+
+  @param[in]      Private           Pointer to PxeBc private data.
+  @param[in, out] BufferSize        Size of user buffer for input;
+                                    required buffer size for output.
+  @param[in]      Buffer            Pointer to user buffer.
+
+  @retval EFI_SUCCESS          Successfully obtained all the boot information.
+  @retval EFI_BUFFER_TOO_SMALL The buffer size is not enough for boot file.
+  @retval EFI_ABORTED          User cancelled the current operation.
+  @retval Others               Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+PxeBcLoadBootFile (
+  IN     PXEBC_PRIVATE_DATA           *Private,
+  IN OUT UINTN                        *BufferSize,
+  IN     VOID                         *Buffer         OPTIONAL
+  );
+
+#endif
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
new file mode 100644
index 0000000..3b83121
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.c
@@ -0,0 +1,1672 @@
+/** @file
+  Functions implementation related with DHCPv4 for UefiPxeBc Driver.
+
+  Copyright (c) 2009 - 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+//
+// This is a map from the interested DHCP4 option tags' index to the tag value.
+//
+UINT8 mInterestedDhcp4Tags[PXEBC_DHCP4_TAG_INDEX_MAX] = {
+  PXEBC_DHCP4_TAG_BOOTFILE_LEN,
+  PXEBC_DHCP4_TAG_VENDOR,
+  PXEBC_DHCP4_TAG_OVERLOAD,
+  PXEBC_DHCP4_TAG_MSG_TYPE,
+  PXEBC_DHCP4_TAG_SERVER_ID,
+  PXEBC_DHCP4_TAG_CLASS_ID,
+  PXEBC_DHCP4_TAG_BOOTFILE
+};
+
+//
+// There are 4 times retries with the value of 4, 8, 16 and 32, refers to PXE2.1 spec.
+//
+UINT32 mPxeDhcpTimeout[4] = {4, 8, 16, 32};
+
+
+/**
+  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 *
+PxeBcParseDhcp4Options (
+  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 != PXEBC_DHCP4_TAG_EOP) {
+
+    if (Option->OpCode == OptTag) {
+      //
+      // Found the required option.
+      //
+      return Option;
+    }
+
+    //
+    // Skip the current option to the next.
+    //
+    if (Option->OpCode == PXEBC_DHCP4_TAG_PAD) {
+      Offset++;
+    } else {
+      Offset += Option->Length + 2;
+    }
+
+    Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
+  }
+
+  return NULL;
+}
+
+
+/**
+  Parse the PXE vender options and extract the information from them.
+
+  @param[in]  Dhcp4Option        Pointer to vendor options in buffer.
+  @param[in]  VendorOption       Pointer to structure to store information in vendor options.
+
+**/
+VOID
+PxeBcParseVendorOptions (
+  IN EFI_DHCP4_PACKET_OPTION    *Dhcp4Option,
+  IN PXEBC_VENDOR_OPTION        *VendorOption
+  )
+{
+  UINT32                        *BitMap;
+  UINT8                         VendorOptionLen;
+  EFI_DHCP4_PACKET_OPTION       *PxeOption;
+  UINT8                         Offset;
+
+  BitMap          = VendorOption->BitMap;
+  VendorOptionLen = Dhcp4Option->Length;
+  PxeOption       = (EFI_DHCP4_PACKET_OPTION *) &Dhcp4Option->Data[0];
+  Offset          = 0;
+
+  ASSERT (PxeOption != NULL);
+
+  while ((Offset < VendorOptionLen) && (PxeOption->OpCode != PXEBC_DHCP4_TAG_EOP)) {
+    //
+    // Parse all the interesting PXE vendor options one by one.
+    //
+    switch (PxeOption->OpCode) {
+
+    case PXEBC_VENDOR_TAG_MTFTP_IP:
+
+      CopyMem (&VendorOption->MtftpIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+      break;
+
+    case PXEBC_VENDOR_TAG_MTFTP_CPORT:
+
+      CopyMem (&VendorOption->MtftpCPort, PxeOption->Data, sizeof (VendorOption->MtftpCPort));
+      break;
+
+    case PXEBC_VENDOR_TAG_MTFTP_SPORT:
+
+      CopyMem (&VendorOption->MtftpSPort, PxeOption->Data, sizeof (VendorOption->MtftpSPort));
+      break;
+
+    case PXEBC_VENDOR_TAG_MTFTP_TIMEOUT:
+
+      VendorOption->MtftpTimeout = *PxeOption->Data;
+      break;
+
+    case PXEBC_VENDOR_TAG_MTFTP_DELAY:
+
+      VendorOption->MtftpDelay = *PxeOption->Data;
+      break;
+
+    case PXEBC_VENDOR_TAG_DISCOVER_CTRL:
+
+      VendorOption->DiscoverCtrl = *PxeOption->Data;
+      break;
+
+    case PXEBC_VENDOR_TAG_DISCOVER_MCAST:
+
+      CopyMem (&VendorOption->DiscoverMcastIp, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+      break;
+
+    case PXEBC_VENDOR_TAG_BOOT_SERVERS:
+
+      VendorOption->BootSvrLen  = PxeOption->Length;
+      VendorOption->BootSvr     = (PXEBC_BOOT_SVR_ENTRY *) PxeOption->Data;
+      break;
+
+    case PXEBC_VENDOR_TAG_BOOT_MENU:
+
+      VendorOption->BootMenuLen = PxeOption->Length;
+      VendorOption->BootMenu    = (PXEBC_BOOT_MENU_ENTRY *) PxeOption->Data;
+      break;
+
+    case PXEBC_VENDOR_TAG_MENU_PROMPT:
+
+      VendorOption->MenuPromptLen = PxeOption->Length;
+      VendorOption->MenuPrompt    = (PXEBC_MENU_PROMPT *) PxeOption->Data;
+      break;
+
+    case PXEBC_VENDOR_TAG_MCAST_ALLOC:
+
+      CopyMem (&VendorOption->McastIpBase, PxeOption->Data, sizeof (EFI_IPv4_ADDRESS));
+      CopyMem (&VendorOption->McastIpBlock, PxeOption->Data + 4, sizeof (VendorOption->McastIpBlock));
+      CopyMem (&VendorOption->McastIpRange, PxeOption->Data + 6, sizeof (VendorOption->McastIpRange));
+      break;
+
+    case PXEBC_VENDOR_TAG_CREDENTIAL_TYPES:
+
+      VendorOption->CredTypeLen = PxeOption->Length;
+      VendorOption->CredType    = (UINT32 *) PxeOption->Data;
+      break;
+
+    case PXEBC_VENDOR_TAG_BOOT_ITEM:
+
+      CopyMem (&VendorOption->BootSrvType, PxeOption->Data, sizeof (VendorOption->BootSrvType));
+      CopyMem (&VendorOption->BootSrvLayer, PxeOption->Data + 2, sizeof (VendorOption->BootSrvLayer));
+      break;
+
+    default:
+      //
+      // Not interesting PXE vendor options.
+      //
+      break;
+    }
+
+    //
+    // Set the bit map for the special PXE options.
+    //
+    SET_VENDOR_OPTION_BIT_MAP (BitMap, PxeOption->OpCode);
+
+    //
+    // Continue to the next option.
+    //
+    if (PxeOption->OpCode == PXEBC_DHCP4_TAG_PAD) {
+      Offset++;
+    } else {
+      Offset = (UINT8) (Offset + PxeOption->Length + 2);
+    }
+
+    PxeOption = (EFI_DHCP4_PACKET_OPTION *) (Dhcp4Option->Data + Offset);
+  }
+}
+
+
+/**
+  Build the options buffer for the DHCPv4 request packet.
+
+  @param[in]  Private             Pointer to PxeBc private data.
+  @param[out] OptList             Pointer to the option pointer array.
+  @param[in]  Buffer              Pointer to the buffer to contain the option list.
+  @param[in]  NeedMsgType         If TRUE, it is necessary to include the Msg type option.
+                                  Otherwise, it is not necessary.
+
+  @return     Index               The count of the built-in options.
+
+**/
+UINT32
+PxeBcBuildDhcp4Options (
+  IN  PXEBC_PRIVATE_DATA       *Private,
+  OUT EFI_DHCP4_PACKET_OPTION  **OptList,
+  IN  UINT8                    *Buffer,
+  IN  BOOLEAN                  NeedMsgType
+  )
+{
+  UINT32                       Index;
+  PXEBC_DHCP4_OPTION_ENTRY     OptEnt;
+  UINT16                       Value;
+
+  Index      = 0;
+  OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+
+  if (NeedMsgType) {
+    //
+    // Append message type.
+    //
+    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MSG_TYPE;
+    OptList[Index]->Length  = 1;
+    OptEnt.Mesg             = (PXEBC_DHCP4_OPTION_MESG *) OptList[Index]->Data;
+    OptEnt.Mesg->Type       = PXEBC_DHCP4_MSG_TYPE_REQUEST;
+    Index++;
+    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+    //
+    // Append max message size.
+    //
+    OptList[Index]->OpCode  = PXEBC_DHCP4_TAG_MAXMSG;
+    OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE);
+    OptEnt.MaxMesgSize      = (PXEBC_DHCP4_OPTION_MAX_MESG_SIZE *) OptList[Index]->Data;
+    Value                   = NTOHS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);
+    CopyMem (&OptEnt.MaxMesgSize->Size, &Value, sizeof (UINT16));
+    Index++;
+    OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+  }
+
+  //
+  // Append parameter request list option.
+  //
+  OptList[Index]->OpCode    = PXEBC_DHCP4_TAG_PARA_LIST;
+  OptList[Index]->Length    = 35;
+  OptEnt.Para               = (PXEBC_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+  OptEnt.Para->ParaList[0]  = PXEBC_DHCP4_TAG_NETMASK;
+  OptEnt.Para->ParaList[1]  = PXEBC_DHCP4_TAG_TIME_OFFSET;
+  OptEnt.Para->ParaList[2]  = PXEBC_DHCP4_TAG_ROUTER;
+  OptEnt.Para->ParaList[3]  = PXEBC_DHCP4_TAG_TIME_SERVER;
+  OptEnt.Para->ParaList[4]  = PXEBC_DHCP4_TAG_NAME_SERVER;
+  OptEnt.Para->ParaList[5]  = PXEBC_DHCP4_TAG_DNS_SERVER;
+  OptEnt.Para->ParaList[6]  = PXEBC_DHCP4_TAG_HOSTNAME;
+  OptEnt.Para->ParaList[7]  = PXEBC_DHCP4_TAG_BOOTFILE_LEN;
+  OptEnt.Para->ParaList[8]  = PXEBC_DHCP4_TAG_DOMAINNAME;
+  OptEnt.Para->ParaList[9]  = PXEBC_DHCP4_TAG_ROOTPATH;
+  OptEnt.Para->ParaList[10] = PXEBC_DHCP4_TAG_EXTEND_PATH;
+  OptEnt.Para->ParaList[11] = PXEBC_DHCP4_TAG_EMTU;
+  OptEnt.Para->ParaList[12] = PXEBC_DHCP4_TAG_TTL;
+  OptEnt.Para->ParaList[13] = PXEBC_DHCP4_TAG_BROADCAST;
+  OptEnt.Para->ParaList[14] = PXEBC_DHCP4_TAG_NIS_DOMAIN;
+  OptEnt.Para->ParaList[15] = PXEBC_DHCP4_TAG_NIS_SERVER;
+  OptEnt.Para->ParaList[16] = PXEBC_DHCP4_TAG_NTP_SERVER;
+  OptEnt.Para->ParaList[17] = PXEBC_DHCP4_TAG_VENDOR;
+  OptEnt.Para->ParaList[18] = PXEBC_DHCP4_TAG_REQUEST_IP;
+  OptEnt.Para->ParaList[19] = PXEBC_DHCP4_TAG_LEASE;
+  OptEnt.Para->ParaList[20] = PXEBC_DHCP4_TAG_SERVER_ID;
+  OptEnt.Para->ParaList[21] = PXEBC_DHCP4_TAG_T1;
+  OptEnt.Para->ParaList[22] = PXEBC_DHCP4_TAG_T2;
+  OptEnt.Para->ParaList[23] = PXEBC_DHCP4_TAG_CLASS_ID;
+  OptEnt.Para->ParaList[24] = PXEBC_DHCP4_TAG_TFTP;
+  OptEnt.Para->ParaList[25] = PXEBC_DHCP4_TAG_BOOTFILE;
+  OptEnt.Para->ParaList[26] = PXEBC_PXE_DHCP4_TAG_UUID;
+  OptEnt.Para->ParaList[27] = 0x80;
+  OptEnt.Para->ParaList[28] = 0x81;
+  OptEnt.Para->ParaList[29] = 0x82;
+  OptEnt.Para->ParaList[30] = 0x83;
+  OptEnt.Para->ParaList[31] = 0x84;
+  OptEnt.Para->ParaList[32] = 0x85;
+  OptEnt.Para->ParaList[33] = 0x86;
+  OptEnt.Para->ParaList[34] = 0x87;
+  Index++;
+  OptList[Index]            = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+  //
+  // Append UUID/Guid-based client identifier option
+  //
+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UUID;
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UUID);
+  OptEnt.Uuid             = (PXEBC_DHCP4_OPTION_UUID *) OptList[Index]->Data;
+  OptEnt.Uuid->Type       = 0;
+  Index++;
+  OptList[Index]          = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+  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));
+  }
+
+  //
+  // Append client network device interface option
+  //
+  OptList[Index]->OpCode  = PXEBC_PXE_DHCP4_TAG_UNDI;
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_UNDI);
+  OptEnt.Undi             = (PXEBC_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  = PXEBC_PXE_DHCP4_TAG_ARCH;
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_ARCH);
+  OptEnt.Arch             = (PXEBC_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
+  Value                   = HTONS (EFI_PXE_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  = PXEBC_DHCP4_TAG_CLASS_ID;
+  OptList[Index]->Length  = (UINT8) sizeof (PXEBC_DHCP4_OPTION_CLID);
+  OptEnt.Clid             = (PXEBC_DHCP4_OPTION_CLID *) OptList[Index]->Data;
+  CopyMem (
+    OptEnt.Clid,
+    DEFAULT_CLASS_ID_DATA,
+    sizeof (PXEBC_DHCP4_OPTION_CLID)
+    );
+  PxeBcUintnToAscDecWithFormat (
+    EFI_PXE_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));
+    PxeBcUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
+    PxeBcUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
+  }
+
+  Index++;
+
+  return Index;
+}
+
+
+/**
+  Create a template DHCPv4 packet as a seed.
+
+  @param[out] Seed           Pointer to the seed packet.
+  @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.
+
+**/
+VOID
+PxeBcSeedDhcp4Packet (
+  OUT EFI_DHCP4_PACKET       *Seed,
+  IN  EFI_UDP4_PROTOCOL      *Udp4
+  )
+{
+  EFI_SIMPLE_NETWORK_MODE    Mode;
+  EFI_DHCP4_HEADER           *Header;
+
+  //
+  // Get IfType and HwAddressSize from SNP mode data.
+  //
+  Udp4->GetModeData (Udp4, NULL, NULL, NULL, &Mode);
+
+  Seed->Size            = sizeof (EFI_DHCP4_PACKET);
+  Seed->Length          = sizeof (Seed->Dhcp4);
+  Header                = &Seed->Dhcp4.Header;
+  ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));
+  Header->OpCode        = PXEBC_DHCP4_OPCODE_REQUEST;
+  Header->HwType        = Mode.IfType;
+  Header->HwAddrLen     = (UINT8) Mode.HwAddressSize;
+  CopyMem (Header->ClientHwAddr, &Mode.CurrentAddress, Header->HwAddrLen);
+
+  Seed->Dhcp4.Magik     = PXEBC_DHCP4_MAGIC;
+  Seed->Dhcp4.Option[0] = PXEBC_DHCP4_TAG_EOP;
+}
+
+
+/**
+  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
+PxeBcCacheDhcp4Packet (
+  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 and invalid packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp4Packet (
+  IN PXEBC_DHCP4_PACKET_CACHE    *Cache4
+  )
+{
+  EFI_DHCP4_PACKET               *Offer;
+  EFI_DHCP4_PACKET_OPTION        **Options;
+  EFI_DHCP4_PACKET_OPTION        *Option;
+  PXEBC_OFFER_TYPE               OfferType;
+  UINTN                          Index;
+  BOOLEAN                        IsProxyOffer;
+  BOOLEAN                        IsPxeOffer;
+  UINT8                          *Ptr8;
+
+  IsProxyOffer = FALSE;
+  IsPxeOffer   = FALSE;
+
+  ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));
+  ZeroMem (&Cache4->VendorOpt, sizeof (Cache4->VendorOpt));
+
+  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 < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
+    Options[Index] = PxeBcParseDhcp4Options (
+                       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[PXEBC_DHCP4_TAG_INDEX_OVERLOAD];
+  if (Option != NULL) {
+    if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_FILE) != 0) {
+      for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
+        if (Options[Index] == NULL) {
+          Options[Index] = PxeBcParseDhcp4Options (
+                             (UINT8 *) Offer->Dhcp4.Header.BootFileName,
+                             sizeof (Offer->Dhcp4.Header.BootFileName),
+                             mInterestedDhcp4Tags[Index]
+                             );
+        }
+      }
+    }
+    if ((Option->Data[0] & PXEBC_DHCP4_OVERLOAD_SERVER_NAME) != 0) {
+      for (Index = 0; Index < PXEBC_DHCP4_TAG_INDEX_MAX; Index++) {
+        if (Options[Index] == NULL) {
+          Options[Index] = PxeBcParseDhcp4Options (
+                             (UINT8 *) Offer->Dhcp4.Header.ServerName,
+                             sizeof (Offer->Dhcp4.Header.ServerName),
+                             mInterestedDhcp4Tags[Index]
+                             );
+        }
+      }
+    }
+  }
+
+  //
+  // The offer with zero "yiaddr" is a proxy offer.
+  //
+  if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {
+    IsProxyOffer = TRUE;
+  }
+
+  //
+  // The offer with "PXEClient" is a PXE offer.
+  //
+  Option = Options[PXEBC_DHCP4_TAG_INDEX_CLASS_ID];
+  if ((Option != NULL) && (Option->Length >= 9) &&
+      (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 9) == 0)) {
+    IsPxeOffer = TRUE;
+  }
+
+  //
+  // Parse PXE vendor options in this offer, and store the contents/pointers.
+  //
+  Option = Options[PXEBC_DHCP4_TAG_INDEX_VENDOR];
+  if (IsPxeOffer && Option != NULL) {
+    PxeBcParseVendorOptions (Option, &Cache4->VendorOpt);
+  }
+
+  //
+  // Parse PXE boot file name:
+  // According to PXE spec, 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[PXEBC_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[PXEBC_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
+    Ptr8 += Options[PXEBC_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[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)
+                                                (&Offer->Dhcp4.Header.BootFileName[0] -
+                                                OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
+
+  }
+
+  //
+  // Determine offer type of the DHCPv4 packet.
+  //
+  Option = Options[PXEBC_DHCP4_TAG_INDEX_MSG_TYPE];
+  if (Option == NULL || Option->Data[0] == 0) {
+    //
+    // It's a Bootp offer.
+    //
+    OfferType = PxeOfferTypeBootp;
+
+    Option    = Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE];
+    if (Option == NULL) {
+      //
+      // If the Bootp offer without bootfilename, discard it.
+      //
+      return EFI_DEVICE_ERROR;
+    }
+  } else {
+
+    if (IS_VALID_DISCOVER_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {
+      //
+      // It's a PXE10 offer with PXEClient and discover vendor option.
+      //
+      OfferType = IsProxyOffer ? PxeOfferTypeProxyPxe10 : PxeOfferTypeDhcpPxe10;
+    } else if (IS_VALID_MTFTP_VENDOR_OPTION (Cache4->VendorOpt.BitMap)) {
+      //
+      // It's a WFM11a offer with PXEClient and mtftp vendor option.
+      // But multi-cast download is not supported currently, so discard it.
+      //
+      return EFI_DEVICE_ERROR;
+    } else if (IsPxeOffer) {
+      //
+      // It's a BINL offer only with PXEClient.
+      //
+      OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
+    } else {
+      //
+      // It's a DHCPv4 only offer, which is a pure DHCPv4 offer packet.
+      //
+      OfferType = PxeOfferTypeDhcpOnly;
+    }
+  }
+
+  Cache4->OfferType = OfferType;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Cache the DHCPv4 ack packet, and parse it on demand.
+
+  @param[in]  Private             Pointer to PxeBc private data.
+  @param[in]  Ack                 Pointer to the DHCPv4 ack packet.
+  @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.
+
+**/
+VOID
+PxeBcCopyDhcp4Ack (
+  IN PXEBC_PRIVATE_DATA   *Private,
+  IN EFI_DHCP4_PACKET     *Ack,
+  IN BOOLEAN              Verified
+  )
+{
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+
+  Mode = Private->PxeBc.Mode;
+
+  PxeBcCacheDhcp4Packet (&Private->DhcpAck.Dhcp4.Packet.Ack, Ack);
+
+  if (Verified) {
+    //
+    // Parse the ack packet and store it into mode data if needed.
+    //
+    PxeBcParseDhcp4Packet (&Private->DhcpAck.Dhcp4);
+    CopyMem (&Mode->DhcpAck.Dhcpv4, &Ack->Dhcp4, Ack->Length);
+    Mode->DhcpAckReceived = TRUE;
+  }
+}
+
+
+/**
+  Cache the DHCPv4 proxy offer packet according to the received order.
+
+  @param[in]  Private               Pointer to PxeBc private data.
+  @param[in]  OfferIndex            The received order of offer packets.
+
+**/
+VOID
+PxeBcCopyProxyOffer (
+  IN PXEBC_PRIVATE_DATA   *Private,
+  IN UINT32               OfferIndex
+  )
+{
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  EFI_DHCP4_PACKET        *Offer;
+
+  ASSERT (OfferIndex < Private->OfferNum);
+  ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
+
+  Mode  = Private->PxeBc.Mode;
+  Offer = &Private->OfferBuffer[OfferIndex].Dhcp4.Packet.Offer;
+
+  //
+  // Cache the proxy offer packet and parse it.
+  //
+  PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Offer);
+  PxeBcParseDhcp4Packet (&Private->ProxyOffer.Dhcp4);
+
+  //
+  // Store this packet into mode data.
+  //
+  CopyMem (&Mode->ProxyOffer.Dhcpv4, &Offer->Dhcp4, Offer->Length);
+  Mode->ProxyOfferReceived = TRUE;
+}
+
+
+/**
+  Retry to request bootfile name by the BINL offer.
+
+  @param[in]  Private              Pointer to PxeBc private data.
+  @param[in]  Index                The received order of offer packets.
+
+  @retval     EFI_SUCCESS          Successfully retried to request bootfile name.
+  @retval     EFI_DEVICE_ERROR     Failed to retry bootfile name.
+
+**/
+EFI_STATUS
+PxeBcRetryBinlOffer (
+  IN PXEBC_PRIVATE_DATA     *Private,
+  IN UINT32                 Index
+  )
+{
+  EFI_DHCP4_PACKET          *Offer;
+  EFI_IP_ADDRESS            ServerIp;
+  EFI_STATUS                Status;
+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;
+  EFI_DHCP4_PACKET          *Reply;
+
+  ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+  ASSERT (Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpBinl ||
+          Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeProxyBinl);
+
+  Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
+
+  //
+  // Prefer to siaddr in header as next server address. If it's zero, then use option 54.
+  //
+  if (Offer->Dhcp4.Header.ServerAddr.Addr[0] == 0) {
+    CopyMem (
+      &ServerIp.Addr[0],
+      Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_SERVER_ID]->Data,
+      sizeof (EFI_IPv4_ADDRESS)
+      );
+  } else {
+    CopyMem (
+      &ServerIp.Addr[0],
+      &Offer->Dhcp4.Header.ServerAddr,
+      sizeof (EFI_IPv4_ADDRESS)
+      );
+  }
+
+  Private->IsDoDiscover = FALSE;
+  Cache4                = &Private->ProxyOffer.Dhcp4;
+  Reply                 = &Cache4->Packet.Offer;
+
+  //
+  // Send another request packet for bootfile name.
+  //
+  Status = PxeBcDhcp4Discover (
+             Private,
+             0,
+             NULL,
+             FALSE,
+             &ServerIp,
+             0,
+             NULL
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Parse the reply for the last request packet.
+  //
+  Status = PxeBcParseDhcp4Packet (Cache4);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (Cache4->OfferType != PxeOfferTypeProxyPxe10 &&
+      Cache4->OfferType != PxeOfferTypeProxyWfm11a &&
+      Cache4->OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
+    //
+    // This BINL ack doesn't have discovery option set or multicast option set
+    // or bootfile name specified.
+    //
+    return EFI_DEVICE_ERROR;
+  }
+
+  //
+  // Store the reply into mode data.
+  //
+  Private->PxeBc.Mode->ProxyOfferReceived = TRUE;
+  CopyMem (&Private->PxeBc.Mode->ProxyOffer.Dhcpv4, &Reply->Dhcp4, Reply->Length);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.
+
+  @param[in]  Private               Pointer to PxeBc private data.
+  @param[in]  RcvdOffer             Pointer to the received offer packet.
+
+**/
+VOID
+PxeBcCacheDhcp4Offer (
+  IN PXEBC_PRIVATE_DATA     *Private,
+  IN EFI_DHCP4_PACKET       *RcvdOffer
+  )
+{
+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;
+  EFI_DHCP4_PACKET          *Offer;
+  PXEBC_OFFER_TYPE          OfferType;
+
+  ASSERT (Private->OfferNum < PXEBC_OFFER_MAX_NUM);
+  Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;
+  Offer  = &Cache4->Packet.Offer;
+
+  //
+  // Cache the content of DHCPv4 packet firstly.
+  //
+  PxeBcCacheDhcp4Packet (Offer, RcvdOffer);
+
+  //
+  // Validate the DHCPv4 packet, and parse the options and offer type.
+  //
+  if (EFI_ERROR (PxeBcParseDhcp4Packet (Cache4))) {
+    return;
+  }
+
+  //
+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
+  //
+  OfferType = Cache4->OfferType;
+  ASSERT (OfferType < PxeOfferTypeMax);
+
+  if (OfferType == PxeOfferTypeBootp) {
+    //
+    // It's a Bootp offer, only cache the first one, and discard the others.
+    //
+    if (Private->OfferCount[OfferType] == 0) {
+      Private->OfferIndex[OfferType][0] = Private->OfferNum;
+      Private->OfferCount[OfferType]    = 1;
+    } else {
+      return;
+    }
+  } else {
+    ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
+    if (IS_PROXY_DHCP_OFFER (Offer)) {
+      //
+      // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
+      //
+      Private->IsProxyRecved = TRUE;
+
+      if (OfferType == PxeOfferTypeProxyBinl) {
+        //
+        // Cache all proxy BINL offers.
+        //
+        Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+        Private->OfferCount[OfferType]++;
+      } else if (Private->OfferCount[OfferType] > 0) {
+        //
+        // Only cache the first PXE10/WFM11a offer, and discard the others.
+        //
+        Private->OfferIndex[OfferType][0] = Private->OfferNum;
+        Private->OfferCount[OfferType]    = 1;
+      } else {
+        return ;
+      }
+    } else {
+      //
+      // It's a DHCPv4 offer with yiaddr, and cache them all.
+      //
+      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 PxeBc private data.
+
+**/
+VOID
+PxeBcSelectDhcp4Offer (
+  IN PXEBC_PRIVATE_DATA       *Private
+  )
+{
+  UINT32                      Index;
+  UINT32                      OfferIndex;
+  EFI_DHCP4_PACKET            *Offer;
+
+  Private->SelectIndex = 0;
+
+  if (Private->IsOfferSorted) {
+    //
+    // Select offer by default policy.
+    //
+    if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
+      //
+      // 1. DhcpPxe10 offer
+      //
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
+      //
+      // 2. DhcpWfm11a offer
+      //
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+               Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
+      //
+      // 3. DhcpOnly offer and ProxyPxe10 offer.
+      //
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+      Private->SelectProxyType = PxeOfferTypeProxyPxe10;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+               Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
+      //
+      // 4. DhcpOnly offer and ProxyWfm11a offer.
+      //
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+      Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
+      //
+      // 5. DhcpBinl offer.
+      //
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+               Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
+      //
+      // 6. DhcpOnly offer and ProxyBinl offer.
+      //
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+      Private->SelectProxyType = PxeOfferTypeProxyBinl;
+
+    } else {
+      //
+      // 7. DhcpOnly offer with bootfilename.
+      //
+      for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
+        OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
+        if (Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+          Private->SelectIndex = OfferIndex + 1;
+          break;
+        }
+      }
+      //
+      // 8. Bootp offer with bootfilename.
+      //
+      OfferIndex = Private->OfferIndex[PxeOfferTypeBootp][0];
+      if (Private->SelectIndex == 0 &&
+          Private->OfferCount[PxeOfferTypeBootp] > 0 &&
+          Private->OfferBuffer[OfferIndex].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+        Private->SelectIndex = OfferIndex + 1;
+      }
+    }
+  } else {
+    //
+    // Select offer by received order.
+    //
+    for (Index = 0; Index < Private->OfferNum; Index++) {
+
+      Offer = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
+
+      if (IS_PROXY_DHCP_OFFER (Offer)) {
+        //
+        // Skip proxy offers
+        //
+        continue;
+      }
+
+      if (!Private->IsProxyRecved &&
+          Private->OfferBuffer[Index].Dhcp4.OfferType == PxeOfferTypeDhcpOnly &&
+          Private->OfferBuffer[Index].Dhcp4.OptList[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
+        //
+        // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
+        //
+        continue;
+      }
+
+      //
+      // Record the index of the select offer.
+      //
+      Private->SelectIndex = Index + 1;
+      break;
+    }
+  }
+}
+
+
+/**
+  Handle the DHCPv4 offer packet.
+
+  @param[in]  Private             Pointer to PxeBc private data.
+
+  @retval     EFI_SUCCESS         Handled the DHCPv4 offer packet successfully.
+  @retval     EFI_NO_RESPONSE     No response to the following request packet.
+  @retval     EFI_NOT_FOUND       No boot filename received.
+
+**/
+EFI_STATUS
+PxeBcHandleDhcp4Offer (
+  IN PXEBC_PRIVATE_DATA     *Private
+  )
+{
+  PXEBC_DHCP4_PACKET_CACHE  *Cache4;
+  EFI_DHCP4_PACKET_OPTION   **Options;
+  UINT32                    Index;
+  EFI_DHCP4_PACKET          *Offer;
+  PXEBC_OFFER_TYPE          OfferType;
+  UINT32                    ProxyIndex;
+  UINT32                    SelectIndex;
+  EFI_STATUS                Status;
+  EFI_PXE_BASE_CODE_MODE    *Mode;
+  EFI_DHCP4_PACKET          *Ack;
+
+  ASSERT (Private->SelectIndex > 0);
+  SelectIndex = (UINT32) (Private->SelectIndex - 1);
+  ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
+  Cache4      = &Private->OfferBuffer[SelectIndex].Dhcp4;
+  Options     = Cache4->OptList;
+  Status      = EFI_SUCCESS;
+
+  if (Cache4->OfferType == PxeOfferTypeDhcpBinl) {
+    //
+    // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
+    //
+    if (EFI_ERROR (PxeBcRetryBinlOffer (Private, SelectIndex))) {
+      Status = EFI_NO_RESPONSE;
+    }
+  } else if (Cache4->OfferType == PxeOfferTypeDhcpOnly) {
+
+    if (Private->IsProxyRecved) {
+      //
+      // DhcpOnly offer is selected, so need try to request bootfile name.
+      //
+      ProxyIndex = 0;
+      if (Private->IsOfferSorted) {
+        //
+        // The proxy offer should be determined if select by default policy.
+        // IsOfferSorted means all offers are labeled by OfferIndex.
+        //
+        ASSERT (Private->SelectProxyType < PxeOfferTypeMax);
+        ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
+
+        if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
+          //
+          // Try all the cached ProxyBinl offer one by one to request bootfile name.
+          //
+          for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
+            ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+            ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
+            if (!EFI_ERROR (PxeBcRetryBinlOffer (Private, ProxyIndex))) {
+              break;
+            }
+          }
+          if (Index == Private->OfferCount[Private->SelectProxyType]) {
+            Status = EFI_NO_RESPONSE;
+          }
+        } else {
+          //
+          // For other proxy offers, only one is buffered.
+          //
+          ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+        }
+      } else {
+        //
+        // The proxy offer should not be determined if select by received order.
+        //
+        Status = EFI_NO_RESPONSE;
+
+        for (Index = 0; Index < Private->OfferNum; Index++) {
+          ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+          Offer     = &Private->OfferBuffer[Index].Dhcp4.Packet.Offer;
+          OfferType = Private->OfferBuffer[Index].Dhcp4.OfferType;
+          if (!IS_PROXY_DHCP_OFFER (Offer)) {
+            //
+            // Skip non proxy DHCPv4 offers.
+            //
+            continue;
+          }
+
+          if (OfferType == PxeOfferTypeProxyBinl) {
+            //
+            // Try all the cached ProxyBinl offer one by one to request bootfile name.
+            //
+            if (EFI_ERROR (PxeBcRetryBinlOffer (Private, Index))) {
+              continue;
+            }
+          }
+
+          Private->SelectProxyType = OfferType;
+          ProxyIndex               = Index;
+          Status                   = EFI_SUCCESS;
+          break;
+        }
+      }
+
+      if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
+        //
+        // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
+        //
+        PxeBcCopyProxyOffer (Private, ProxyIndex);
+      }
+    } else {
+      //
+      //  Othewise, the bootfile name must be included in DhcpOnly offer.
+      //
+      if (Options[PXEBC_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
+        Status = EFI_NOT_FOUND;
+      }
+    }
+  }
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // All PXE boot information is ready by now.
+    //
+    Mode  = Private->PxeBc.Mode;
+    Offer = &Cache4->Packet.Offer;
+    Ack   = &Private->DhcpAck.Dhcp4.Packet.Ack;
+    if (Cache4->OfferType == PxeOfferTypeBootp) {
+      //
+      // Bootp is a special case that only 2 packets involved instead of 4. So the bootp's reply
+      // should be taken as ack.
+      //
+      Ack = Offer;
+    }
+
+    PxeBcCopyDhcp4Ack (Private, Ack, TRUE);
+    Mode->DhcpDiscoverValid = TRUE;
+  }
+
+  return Status;
+}
+
+
+/**
+  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
+PxeBcDhcp4CallBack (
+  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
+  )
+{
+  PXEBC_PRIVATE_DATA                   *Private;
+  EFI_PXE_BASE_CODE_MODE               *Mode;
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *Callback;
+  EFI_DHCP4_PACKET_OPTION              *MaxMsgSize;
+  UINT16                               Value;
+  EFI_STATUS                           Status;
+  BOOLEAN                              Received;
+
+  if ((Dhcp4Event != Dhcp4RcvdOffer) &&
+      (Dhcp4Event != Dhcp4SelectOffer) &&
+      (Dhcp4Event != Dhcp4SendDiscover) &&
+      (Dhcp4Event != Dhcp4RcvdAck)) {
+    return EFI_SUCCESS;
+  }
+
+  Private   = (PXEBC_PRIVATE_DATA *) Context;
+  Mode      = Private->PxeBc.Mode;
+  Callback  = Private->PxeBcCallback;
+
+  //
+  // Override the Maximum DHCP Message Size.
+  //
+  MaxMsgSize = PxeBcParseDhcp4Options (
+                 Packet->Dhcp4.Option,
+                 GET_OPTION_BUFFER_LEN (Packet),
+                 PXEBC_DHCP4_TAG_MAXMSG
+                 );
+  if (MaxMsgSize != NULL) {
+    Value = HTONS (PXEBC_DHCP4_PACKET_MAX_SIZE - 8);
+    CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
+  }
+
+  //
+  // Callback to user if any packets sent or received.
+  //
+  if (Dhcp4Event != Dhcp4SelectOffer && Callback != NULL) {
+    Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck);
+    Status = Callback->Callback (
+                         Callback,
+                         Private->Function,
+                         Received,
+                         Packet->Length,
+                         (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp4
+                         );
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+      return EFI_ABORTED;
+    }
+  }
+
+  Status = EFI_SUCCESS;
+
+  switch (Dhcp4Event) {
+
+  case Dhcp4SendDiscover:
+    //
+    // Cache the DHCPv4 discover packet to mode data directly.
+    // It need to check SendGuid as well as Dhcp4SendRequest.
+    //
+    CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp4, Packet->Length);
+
+  case Dhcp4SendRequest:
+    if (Mode->SendGUID) {
+      //
+      // Send the system Guid instead of the MAC address as the hardware address if required.
+      //
+      if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Packet->Dhcp4.Header.ClientHwAddr))) {
+        //
+        // Zero the Guid to indicate NOT programable if failed to get system Guid.
+        //
+        ZeroMem (Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));
+      }
+      Packet->Dhcp4.Header.HwAddrLen = (UINT8) sizeof (EFI_GUID);
+    }
+    break;
+
+  case Dhcp4RcvdOffer:
+    Status = EFI_NOT_READY;
+    if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
+      //
+      // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
+      // the OfferIndex and OfferCount.
+      //
+      PxeBcCacheDhcp4Offer (Private, Packet);
+    }
+    break;
+
+  case Dhcp4SelectOffer:
+    //
+    // Select offer by the default policy or by order, and record the SelectIndex
+    // and SelectProxyType.
+    //
+    PxeBcSelectDhcp4Offer (Private);
+
+    if (Private->SelectIndex == 0) {
+      Status = EFI_ABORTED;
+    } else {
+      *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;
+    }
+    break;
+
+  case Dhcp4RcvdAck:
+    //
+    // Cache the DHCPv4 ack to Private->Dhcp4Ack, but it's not the final ack in mode data
+    // without verification.
+    //
+    ASSERT (Private->SelectIndex != 0);
+
+    PxeBcCopyDhcp4Ack (Private, Packet, FALSE);
+    break;
+
+  default:
+    break;
+  }
+
+  return Status;
+}
+
+
+/**
+  Build and send out the request packet for the bootfile, and parse the reply.
+
+  @param[in]  Private               Pointer to PxeBc private data.
+  @param[in]  Type                  PxeBc option boot item type.
+  @param[in]  Layer                 Pointer to option boot item layer.
+  @param[in]  UseBis                Use BIS or not.
+  @param[in]  DestIp                Pointer to the server address.
+  @param[in]  IpCount               The total count of the server address.
+  @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+  @retval     EFI_SUCCESS           Successfully discovered boot file.
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
+  @retval     Others                Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Discover (
+  IN  PXEBC_PRIVATE_DATA              *Private,
+  IN  UINT16                          Type,
+  IN  UINT16                          *Layer,
+  IN  BOOLEAN                         UseBis,
+  IN  EFI_IP_ADDRESS                  *DestIp,
+  IN  UINT16                          IpCount,
+  IN  EFI_PXE_BASE_CODE_SRVLIST       *SrvList
+  )
+{
+  EFI_PXE_BASE_CODE_UDP_PORT          Sport;
+  EFI_PXE_BASE_CODE_MODE              *Mode;
+  EFI_DHCP4_PROTOCOL                  *Dhcp4;
+  EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN    Token;
+  BOOLEAN                             IsBCast;
+  EFI_STATUS                          Status;
+  UINT16                              RepIndex;
+  UINT16                              SrvIndex;
+  UINT16                              TryIndex;
+  EFI_DHCP4_LISTEN_POINT              ListenPoint;
+  EFI_DHCP4_PACKET                    *Response;
+  UINT8                               Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];
+  EFI_DHCP4_PACKET_OPTION             *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];
+  UINT32                              OptCount;
+  EFI_DHCP4_PACKET_OPTION             *PxeOpt;
+  PXEBC_OPTION_BOOT_ITEM              *PxeBootItem;
+  UINT8                               VendorOptLen;
+  UINT32                              Xid;
+
+  Mode      = Private->PxeBc.Mode;
+  Dhcp4     = Private->Dhcp4;
+  Status    = EFI_SUCCESS;
+
+  ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));
+
+  //
+  // Use broadcast if destination address not specified.
+  //
+  if (DestIp == NULL) {
+    Sport   = PXEBC_DHCP4_S_PORT;
+    IsBCast = TRUE;
+  } else {
+    Sport   = PXEBC_BS_DISCOVER_PORT;
+    IsBCast = FALSE;
+  }
+
+  if (!UseBis && Layer != NULL) {
+    *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
+  }
+
+  //
+  // Build all the options for the request packet.
+  //
+  OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, TRUE);
+
+  if (Private->IsDoDiscover) {
+    //
+    // Add vendor option of PXE_BOOT_ITEM
+    //
+    VendorOptLen      = (UINT8) ((sizeof (EFI_DHCP4_PACKET_OPTION) - 1) * 2 + sizeof (PXEBC_OPTION_BOOT_ITEM) + 1);
+    OptList[OptCount] = AllocateZeroPool (VendorOptLen);
+    if (OptList[OptCount] == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    OptList[OptCount]->OpCode     = PXEBC_DHCP4_TAG_VENDOR;
+    OptList[OptCount]->Length     = (UINT8) (VendorOptLen - 2);
+    PxeOpt                        = (EFI_DHCP4_PACKET_OPTION *) OptList[OptCount]->Data;
+    PxeOpt->OpCode                = PXEBC_VENDOR_TAG_BOOT_ITEM;
+    PxeOpt->Length                = (UINT8) sizeof (PXEBC_OPTION_BOOT_ITEM);
+    PxeBootItem                   = (PXEBC_OPTION_BOOT_ITEM *) PxeOpt->Data;
+    PxeBootItem->Type             = HTONS (Type);
+    PxeOpt->Data[PxeOpt->Length]  = PXEBC_DHCP4_TAG_EOP;
+
+    if (Layer != NULL) {
+      PxeBootItem->Layer          = HTONS (*Layer);
+    }
+
+    OptCount++;
+  }
+
+  //
+  // Build the request packet with seed packet and option list.
+  //
+  Status = Dhcp4->Build (
+                    Dhcp4,
+                    &Private->SeedPacket,
+                    0,
+                    NULL,
+                    OptCount,
+                    OptList,
+                    &Token.Packet
+                    );
+  //
+  // Free the vendor option of PXE_BOOT_ITEM.
+  //
+  if (Private->IsDoDiscover) {
+    FreePool (OptList[OptCount - 1]);
+  }
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (Mode->SendGUID) {
+    if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) Token.Packet->Dhcp4.Header.ClientHwAddr))) {
+      //
+      // Zero the Guid to indicate NOT programable if failed to get system Guid.
+      //
+      ZeroMem (Token.Packet->Dhcp4.Header.ClientHwAddr, sizeof (EFI_GUID));
+    }
+    Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8)  sizeof (EFI_GUID);
+  }
+
+  //
+  // Set fields of the token for the request packet.
+  //
+  Xid                                 = NET_RANDOM (NetRandomInitSeed ());
+  Token.Packet->Dhcp4.Header.Xid      = HTONL (Xid);
+  Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16) ((IsBCast) ? 0x8000 : 0x0));
+  CopyMem (&Token.Packet->Dhcp4.Header.ClientAddr, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+
+  Token.RemotePort = Sport;
+
+  if (IsBCast) {
+    SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);
+  } else {
+    CopyMem (&Token.RemoteAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
+  }
+
+  CopyMem (&Token.GatewayAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
+
+  if (!IsBCast) {
+    Token.ListenPointCount            = 1;
+    Token.ListenPoints                = &ListenPoint;
+    Token.ListenPoints[0].ListenPort  = PXEBC_BS_DISCOVER_PORT;
+    CopyMem (&Token.ListenPoints[0].ListenAddress, &Private->StationIp, sizeof(EFI_IPv4_ADDRESS));
+    CopyMem (&Token.ListenPoints[0].SubnetMask, &Private->SubnetMask, sizeof(EFI_IPv4_ADDRESS));
+  }
+
+  //
+  // Send out the request packet to discover the bootfile.
+  //
+  for (TryIndex = 1; TryIndex <= PXEBC_BOOT_REQUEST_RETRIES; TryIndex++) {
+
+    Token.TimeoutValue                  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * TryIndex);
+    Token.Packet->Dhcp4.Header.Seconds  = (UINT16) (PXEBC_BOOT_REQUEST_TIMEOUT * (TryIndex - 1));
+
+    Status = Dhcp4->TransmitReceive (Dhcp4, &Token);
+    if (Token.Status != EFI_TIMEOUT) {
+      break;
+    }
+  }
+
+  if (TryIndex > PXEBC_BOOT_REQUEST_RETRIES) {
+    //
+    // No server response our PXE request
+    //
+    Status = EFI_TIMEOUT;
+  }
+
+  if (!EFI_ERROR (Status)) {
+
+    RepIndex  = 0;
+    SrvIndex  = 0;
+    Response  = Token.ResponseList;
+    //
+    // Find the right PXE Reply according to server address.
+    //
+    while (RepIndex < Token.ResponseCount) {
+
+      while (SrvIndex < IpCount) {
+        if (SrvList[SrvIndex].AcceptAnyResponse) {
+          break;
+        }
+        if ((SrvList[SrvIndex].Type == Type) &&
+            EFI_IP4_EQUAL (&Response->Dhcp4.Header.ServerAddr, &SrvList[SrvIndex].IpAddr)) {
+          break;
+        }
+        SrvIndex++;
+      }
+
+      if ((IpCount != SrvIndex) || (IpCount == 0)) {
+        break;
+      }
+
+      SrvIndex = 0;
+      RepIndex++;
+
+      Response = (EFI_DHCP4_PACKET *) ((UINT8 *) Response + Response->Size);
+    }
+
+    if (RepIndex < Token.ResponseCount) {
+      //
+      // Cache the right PXE reply packet here, set valid flag later.
+      // Especially for PXE discover packet, store it into mode data here.
+      //
+      if (Private->IsDoDiscover) {
+        PxeBcCacheDhcp4Packet (&Private->PxeReply.Dhcp4.Packet.Ack, Response);
+        CopyMem (&Mode->PxeDiscover, &Token.Packet->Dhcp4, Token.Packet->Length);
+      } else {
+        PxeBcCacheDhcp4Packet (&Private->ProxyOffer.Dhcp4.Packet.Offer, Response);
+      }
+    } else {
+      //
+      // Not found the right PXE reply packet.
+      //
+      Status = EFI_NOT_FOUND;
+    }
+    if (Token.ResponseList != NULL) {
+      FreePool (Token.ResponseList);
+    }
+  }
+
+  FreePool (Token.Packet);
+  return Status;
+}
+
+/**
+  Switch the Ip4 policy to static.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         The policy is already configured to static.
+  @retval     Others              Other error as indicated..
+
+**/
+EFI_STATUS
+PxeBcSetIp4Policy (   
+  IN PXEBC_PRIVATE_DATA            *Private
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_IP4_CONFIG2_PROTOCOL     *Ip4Config2;
+  EFI_IP4_CONFIG2_POLICY       Policy;
+  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 PXE boot information.
+
+  @param[in]  Private           Pointer to PxeBc private data.
+  @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL
+
+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.
+  @retval Others                Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Dora (
+  IN PXEBC_PRIVATE_DATA        *Private,
+  IN EFI_DHCP4_PROTOCOL        *Dhcp4
+  )
+{
+  EFI_PXE_BASE_CODE_MODE       *PxeMode;
+  EFI_DHCP4_CONFIG_DATA        Config;
+  EFI_DHCP4_MODE_DATA          Mode;
+  EFI_DHCP4_PACKET_OPTION      *OptList[PXEBC_DHCP4_OPTION_MAX_NUM];
+  UINT8                        Buffer[PXEBC_DHCP4_OPTION_MAX_SIZE];
+  UINT32                       OptCount;
+  EFI_STATUS                   Status;
+
+  ASSERT (Dhcp4 != NULL);
+
+  Status   = EFI_SUCCESS;
+  PxeMode  = Private->PxeBc.Mode;
+
+  //
+  // Build option list for the request packet.
+  //
+  OptCount = PxeBcBuildDhcp4Options (Private, OptList, Buffer, FALSE);
+  ASSERT (OptCount> 0);
+
+  ZeroMem (&Mode, sizeof (EFI_DHCP4_MODE_DATA));
+  ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+
+  Config.OptionCount      = OptCount;
+  Config.OptionList       = OptList;
+  Config.Dhcp4Callback    = PxeBcDhcp4CallBack;
+  Config.CallbackContext  = Private;
+  Config.DiscoverTryCount = PXEBC_DHCP_RETRIES;
+  Config.DiscoverTimeout  = mPxeDhcpTimeout;
+
+  //
+  // Configure the DHCPv4 instance for PXE boot.
+  //
+  Status = Dhcp4->Configure (Dhcp4, &Config);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Initialize the record fields for DHCPv4 offer in private data.
+  //
+  Private->IsProxyRecved = FALSE;
+  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. This may 
+  // have already been done, thus do not leave in error if the return
+  // code is EFI_ALREADY_STARTED.
+  //
+  Status = Dhcp4->Start (Dhcp4, NULL);
+  if (EFI_ERROR (Status) && Status != EFI_ALREADY_STARTED) {
+    if (Status == EFI_ICMP_ERROR) {
+      PxeMode->IcmpErrorReceived = TRUE;
+    }
+    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));
+  CopyMem (&PxeMode->StationIp, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+  CopyMem (&PxeMode->SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+
+  Status = PxeBcFlushStationIp (Private, &Private->StationIp, &Private->SubnetMask);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Check the selected offer whether BINL retry is needed.
+  //
+  Status = PxeBcHandleDhcp4Offer (Private);
+
+  AsciiPrint ("\n  Station IP address is ");
+
+  PxeBcShowIp4Addr (&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);
+    Private->IsAddressOk = TRUE;
+  }
+
+  return Status;
+}
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
new file mode 100644
index 0000000..cc2ff98
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp4.h
@@ -0,0 +1,409 @@
+/** @file
+  Functions declaration related with DHCPv4 for UefiPxeBc Driver.
+
+  Copyright (c) 2009 - 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.
+
+**/
+
+#ifndef __EFI_PXEBC_DHCP4_H__
+#define __EFI_PXEBC_DHCP4_H__
+
+#define PXEBC_DHCP4_OPTION_MAX_NUM         16
+#define PXEBC_DHCP4_OPTION_MAX_SIZE        312
+#define PXEBC_DHCP4_PACKET_MAX_SIZE        1472
+#define PXEBC_DHCP4_S_PORT                 67
+#define PXEBC_DHCP4_C_PORT                 68
+#define PXEBC_BS_DOWNLOAD_PORT             69
+#define PXEBC_BS_DISCOVER_PORT             4011
+#define PXEBC_DHCP4_OPCODE_REQUEST         1
+#define PXEBC_DHCP4_OPCODE_REPLY           2
+#define PXEBC_DHCP4_MSG_TYPE_REQUEST       3
+#define PXEBC_DHCP4_MAGIC                  0x63538263 // network byte order
+
+//
+// Dhcp Options
+//
+#define PXEBC_DHCP4_TAG_PAD                0    // Pad Option
+#define PXEBC_DHCP4_TAG_EOP                255  // End Option
+#define PXEBC_DHCP4_TAG_NETMASK            1    // Subnet Mask
+#define PXEBC_DHCP4_TAG_TIME_OFFSET        2    // Time Offset from UTC
+#define PXEBC_DHCP4_TAG_ROUTER             3    // Router option,
+#define PXEBC_DHCP4_TAG_TIME_SERVER        4    // Time Server
+#define PXEBC_DHCP4_TAG_NAME_SERVER        5    // Name Server
+#define PXEBC_DHCP4_TAG_DNS_SERVER         6    // Domain Name Server
+#define PXEBC_DHCP4_TAG_HOSTNAME           12   // Host Name
+#define PXEBC_DHCP4_TAG_BOOTFILE_LEN       13   // Boot File Size
+#define PXEBC_DHCP4_TAG_DUMP               14   // Merit Dump File
+#define PXEBC_DHCP4_TAG_DOMAINNAME         15   // Domain Name
+#define PXEBC_DHCP4_TAG_ROOTPATH           17   // Root path
+#define PXEBC_DHCP4_TAG_EXTEND_PATH        18   // Extensions Path
+#define PXEBC_DHCP4_TAG_EMTU               22   // Maximum Datagram Reassembly Size
+#define PXEBC_DHCP4_TAG_TTL                23   // Default IP Time-to-live
+#define PXEBC_DHCP4_TAG_BROADCAST          28   // Broadcast Address
+#define PXEBC_DHCP4_TAG_NIS_DOMAIN         40   // Network Information Service Domain
+#define PXEBC_DHCP4_TAG_NIS_SERVER         41   // Network Information Servers
+#define PXEBC_DHCP4_TAG_NTP_SERVER         42   // Network Time Protocol Servers
+#define PXEBC_DHCP4_TAG_VENDOR             43   // Vendor Specific Information
+#define PXEBC_DHCP4_TAG_REQUEST_IP         50   // Requested IP Address
+#define PXEBC_DHCP4_TAG_LEASE              51   // IP Address Lease Time
+#define PXEBC_DHCP4_TAG_OVERLOAD           52   // Option Overload
+#define PXEBC_DHCP4_TAG_MSG_TYPE           53   // DHCP Message Type
+#define PXEBC_DHCP4_TAG_SERVER_ID          54   // Server Identifier
+#define PXEBC_DHCP4_TAG_PARA_LIST          55   // Parameter Request List
+#define PXEBC_DHCP4_TAG_MAXMSG             57   // Maximum DHCP Message Size
+#define PXEBC_DHCP4_TAG_T1                 58   // Renewal (T1) Time Value
+#define PXEBC_DHCP4_TAG_T2                 59   // Rebinding (T2) Time Value
+#define PXEBC_DHCP4_TAG_CLASS_ID           60   // Vendor class identifier
+#define PXEBC_DHCP4_TAG_CLIENT_ID          61   // Client-identifier
+#define PXEBC_DHCP4_TAG_TFTP               66   // TFTP server name
+#define PXEBC_DHCP4_TAG_BOOTFILE           67   // Bootfile name
+#define PXEBC_PXE_DHCP4_TAG_ARCH           93
+#define PXEBC_PXE_DHCP4_TAG_UNDI           94
+#define PXEBC_PXE_DHCP4_TAG_UUID           97
+//
+// Sub-Options in Dhcp Vendor Option
+//
+#define PXEBC_VENDOR_TAG_MTFTP_IP          1
+#define PXEBC_VENDOR_TAG_MTFTP_CPORT       2
+#define PXEBC_VENDOR_TAG_MTFTP_SPORT       3
+#define PXEBC_VENDOR_TAG_MTFTP_TIMEOUT     4
+#define PXEBC_VENDOR_TAG_MTFTP_DELAY       5
+#define PXEBC_VENDOR_TAG_DISCOVER_CTRL     6
+#define PXEBC_VENDOR_TAG_DISCOVER_MCAST    7
+#define PXEBC_VENDOR_TAG_BOOT_SERVERS      8
+#define PXEBC_VENDOR_TAG_BOOT_MENU         9
+#define PXEBC_VENDOR_TAG_MENU_PROMPT       10
+#define PXEBC_VENDOR_TAG_MCAST_ALLOC       11
+#define PXEBC_VENDOR_TAG_CREDENTIAL_TYPES  12
+#define PXEBC_VENDOR_TAG_BOOT_ITEM         71
+
+#define PXEBC_BOOT_REQUEST_TIMEOUT         1
+#define PXEBC_BOOT_REQUEST_RETRIES         4
+
+#define PXEBC_DHCP4_OVERLOAD_FILE          1
+#define PXEBC_DHCP4_OVERLOAD_SERVER_NAME   2
+
+
+//
+// The array index of the DHCP4 option tag interested
+//
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE_LEN 0
+#define PXEBC_DHCP4_TAG_INDEX_VENDOR       1
+#define PXEBC_DHCP4_TAG_INDEX_OVERLOAD     2
+#define PXEBC_DHCP4_TAG_INDEX_MSG_TYPE     3
+#define PXEBC_DHCP4_TAG_INDEX_SERVER_ID    4
+#define PXEBC_DHCP4_TAG_INDEX_CLASS_ID     5
+#define PXEBC_DHCP4_TAG_INDEX_BOOTFILE     6
+#define PXEBC_DHCP4_TAG_INDEX_MAX          7
+
+//
+// Dhcp4 and Dhcp6 share this definition, and corresponding
+// relatioinship is as follows:
+//
+//   Dhcp4Discover <> Dhcp6Solicit
+//   Dhcp4Offer    <> Dhcp6Advertise
+//   Dhcp4Request  <> Dhcp6Request
+//   Dhcp4Ack      <> DHcp6Reply
+//
+typedef enum {
+  PxeOfferTypeDhcpOnly,
+  PxeOfferTypeDhcpPxe10,
+  PxeOfferTypeDhcpWfm11a,
+  PxeOfferTypeDhcpBinl,
+  PxeOfferTypeProxyPxe10,
+  PxeOfferTypeProxyWfm11a,
+  PxeOfferTypeProxyBinl,
+  PxeOfferTypeBootp,
+  PxeOfferTypeMax
+} PXEBC_OFFER_TYPE;
+
+#define BIT(x)                (1 << x)
+#define CTRL(x)               (0x1F & (x))
+#define DEFAULT_CLASS_ID_DATA "PXEClient:Arch:xxxxx:UNDI:003000"
+#define DEFAULT_UNDI_TYPE     1
+#define DEFAULT_UNDI_MAJOR    3
+#define DEFAULT_UNDI_MINOR    0
+
+#define MTFTP_VENDOR_OPTION_BIT_MAP \
+  (BIT (PXEBC_VENDOR_TAG_MTFTP_IP) | \
+   BIT (PXEBC_VENDOR_TAG_MTFTP_CPORT) | \
+   BIT (PXEBC_VENDOR_TAG_MTFTP_SPORT) | \
+   BIT (PXEBC_VENDOR_TAG_MTFTP_TIMEOUT) | \
+   BIT (PXEBC_VENDOR_TAG_MTFTP_DELAY))
+
+#define DISCOVER_VENDOR_OPTION_BIT_MAP \
+  (BIT (PXEBC_VENDOR_TAG_DISCOVER_CTRL) | \
+   BIT (PXEBC_VENDOR_TAG_DISCOVER_MCAST) | \
+   BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS) | \
+   BIT (PXEBC_VENDOR_TAG_BOOT_MENU) | \
+   BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))
+
+#define IS_VALID_BOOT_SERVERS(x) \
+  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS)) \
+   == BIT (PXEBC_VENDOR_TAG_BOOT_SERVERS))  
+
+#define IS_VALID_BOOT_PROMPT(x) \
+  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_MENU_PROMPT)) \
+   == BIT (PXEBC_VENDOR_TAG_MENU_PROMPT))
+
+#define IS_VALID_BOOT_MENU(x) \
+  ((((x)[0]) & BIT (PXEBC_VENDOR_TAG_BOOT_MENU)) \
+   == BIT (PXEBC_VENDOR_TAG_BOOT_MENU))
+
+#define IS_VALID_MTFTP_VENDOR_OPTION(x) \
+  (((UINT32) ((x)[0]) & MTFTP_VENDOR_OPTION_BIT_MAP) \
+   == MTFTP_VENDOR_OPTION_BIT_MAP)
+
+#define IS_VALID_DISCOVER_VENDOR_OPTION(x) \
+  (((UINT32) ((x)[0]) & DISCOVER_VENDOR_OPTION_BIT_MAP) != 0)
+
+#define IS_VALID_CREDENTIAL_VENDOR_OPTION(x) \
+  (((UINT32) ((x)[0]) & BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES)) \
+   == BIT (PXEBC_VENDOR_TAG_CREDENTIAL_TYPES))
+
+#define IS_VALID_BOOTITEM_VENDOR_OPTION(x) \
+  (((UINT32) ((x)[PXEBC_VENDOR_TAG_BOOT_ITEM / 32]) & \
+     BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32)) \
+    == BIT (PXEBC_VENDOR_TAG_BOOT_ITEM % 32))
+
+#define SET_VENDOR_OPTION_BIT_MAP(x, y) \
+  (*(x + ((y) / 32)) = (UINT32) ((UINT32) ((x)[(y) / 32]) | BIT ((y) % 32)))
+
+#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 GET_NEXT_BOOT_SVR_ENTRY(Ent) \
+  (PXEBC_BOOT_SVR_ENTRY *) ((UINT8 *) Ent + sizeof (*(Ent)) + \
+   ((Ent)->IpCnt - 1) * sizeof (EFI_IPv4_ADDRESS))
+
+#define IS_PROXY_DHCP_OFFER(Offer) \
+  EFI_IP4_EQUAL (&(Offer)->Dhcp4.Header.YourAddr, &mZeroIp4Addr)
+
+#define IS_DISABLE_BCAST_DISCOVER(x) \
+  (((x) & BIT (0)) == BIT (0))
+
+#define IS_DISABLE_MCAST_DISCOVER(x) \
+  (((x) & BIT (1)) == BIT (1))
+
+#define IS_ENABLE_USE_SERVER_LIST(x) \
+  (((x) & BIT (2)) == BIT (2))
+
+#define IS_DISABLE_PROMPT_MENU(x) \
+  (((x) & BIT (3)) == BIT (3))
+
+
+#pragma pack(1)
+typedef struct {
+  UINT8 ParaList[135];
+} PXEBC_DHCP4_OPTION_PARA;
+
+typedef struct {
+  UINT16  Size;
+} PXEBC_DHCP4_OPTION_MAX_MESG_SIZE;
+
+typedef struct {
+  UINT8 Type;
+  UINT8 MajorVer;
+  UINT8 MinorVer;
+} PXEBC_DHCP4_OPTION_UNDI;
+
+typedef struct {
+  UINT8 Type;
+} PXEBC_DHCP4_OPTION_MESG;
+
+typedef struct {
+  UINT16 Type;
+} PXEBC_DHCP4_OPTION_ARCH;
+
+typedef struct {
+  UINT8 ClassIdentifier[10];
+  UINT8 ArchitecturePrefix[5];
+  UINT8 ArchitectureType[5];
+  UINT8 Lit3[1];
+  UINT8 InterfaceName[4];
+  UINT8 Lit4[1];
+  UINT8 UndiMajor[3];
+  UINT8 UndiMinor[3];
+} PXEBC_DHCP4_OPTION_CLID;
+
+typedef struct {
+  UINT8 Type;
+  UINT8 Guid[16];
+} PXEBC_DHCP4_OPTION_UUID;
+
+typedef struct {
+  UINT16 Type;
+  UINT16 Layer;
+} PXEBC_OPTION_BOOT_ITEM;
+
+#pragma pack()
+
+typedef union {
+  PXEBC_DHCP4_OPTION_PARA           *Para;
+  PXEBC_DHCP4_OPTION_UNDI           *Undi;
+  PXEBC_DHCP4_OPTION_ARCH           *Arch;
+  PXEBC_DHCP4_OPTION_CLID           *Clid;
+  PXEBC_DHCP4_OPTION_UUID           *Uuid;
+  PXEBC_DHCP4_OPTION_MESG           *Mesg;
+  PXEBC_DHCP4_OPTION_MAX_MESG_SIZE  *MaxMesgSize;
+} PXEBC_DHCP4_OPTION_ENTRY;
+
+#pragma pack(1)
+typedef struct {
+  UINT16            Type;
+  UINT8             IpCnt;
+  EFI_IPv4_ADDRESS  IpAddr[1];
+} PXEBC_BOOT_SVR_ENTRY;
+
+typedef struct {
+  UINT16            Type;
+  UINT8             DescLen;
+  UINT8             DescStr[1];
+} PXEBC_BOOT_MENU_ENTRY;
+
+typedef struct {
+  UINT8             Timeout;
+  UINT8             Prompt[1];
+} PXEBC_MENU_PROMPT;
+#pragma pack()
+
+typedef struct {
+  UINT32                BitMap[8];
+  EFI_IPv4_ADDRESS      MtftpIp;
+  UINT16                MtftpCPort;
+  UINT16                MtftpSPort;
+  UINT8                 MtftpTimeout;
+  UINT8                 MtftpDelay;
+  UINT8                 DiscoverCtrl;
+  EFI_IPv4_ADDRESS      DiscoverMcastIp;
+  EFI_IPv4_ADDRESS      McastIpBase;
+  UINT16                McastIpBlock;
+  UINT16                McastIpRange;
+  UINT16                BootSrvType;
+  UINT16                BootSrvLayer;
+  PXEBC_BOOT_SVR_ENTRY  *BootSvr;
+  UINT8                 BootSvrLen;
+  PXEBC_BOOT_MENU_ENTRY *BootMenu;
+  UINT8                 BootMenuLen;
+  PXEBC_MENU_PROMPT     *MenuPrompt;
+  UINT8                 MenuPromptLen;
+  UINT32                *CredType;
+  UINT8                 CredTypeLen;
+} PXEBC_VENDOR_OPTION;
+
+typedef union {
+  EFI_DHCP4_PACKET        Offer;
+  EFI_DHCP4_PACKET        Ack;
+  UINT8                   Buffer[PXEBC_DHCP4_PACKET_MAX_SIZE];
+} PXEBC_DHCP4_PACKET;
+
+typedef struct {
+  PXEBC_DHCP4_PACKET      Packet;
+  PXEBC_OFFER_TYPE        OfferType;
+  EFI_DHCP4_PACKET_OPTION *OptList[PXEBC_DHCP4_TAG_INDEX_MAX];
+  PXEBC_VENDOR_OPTION     VendorOpt;
+} PXEBC_DHCP4_PACKET_CACHE;
+
+
+/**
+  Create a template DHCPv4 packet as a seed.
+
+  @param[out] Seed           Pointer to the seed packet.
+  @param[in]  Udp4           Pointer to EFI_UDP4_PROTOCOL.
+
+**/
+VOID
+PxeBcSeedDhcp4Packet (
+  OUT EFI_DHCP4_PACKET       *Seed,
+  IN  EFI_UDP4_PROTOCOL      *Udp4
+  );
+
+
+/**
+  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 and invalid packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp4Packet (
+  IN PXEBC_DHCP4_PACKET_CACHE    *Cache4
+  );
+
+
+/**
+  Build and send out the request packet for the bootfile, and parse the reply.
+
+  @param[in]  Private               Pointer to PxeBc private data.
+  @param[in]  Type                  PxeBc option boot item type.
+  @param[in]  Layer                 Pointer to option boot item layer.
+  @param[in]  UseBis                Use BIS or not.
+  @param[in]  DestIp                Pointer to the server address.
+  @param[in]  IpCount               The total count of the server address.
+  @param[in]  SrvList               Pointer to EFI_PXE_BASE_CODE_SRVLIST.
+
+  @retval     EFI_SUCCESS           Successfully discovered boot file.
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
+  @retval     Others                Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Discover (
+  IN  PXEBC_PRIVATE_DATA              *Private,
+  IN  UINT16                          Type,
+  IN  UINT16                          *Layer,
+  IN  BOOLEAN                         UseBis,
+  IN  EFI_IP_ADDRESS                  *DestIp,
+  IN  UINT16                          IpCount,
+  IN  EFI_PXE_BASE_CODE_SRVLIST       *SrvList
+  );
+
+/**
+  Switch the Ip4 policy to static.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         The policy is already configured to static.
+  @retval     Others              Other error as indicated..
+
+**/
+EFI_STATUS
+PxeBcSetIp4Policy (   
+  IN PXEBC_PRIVATE_DATA            *Private
+  );
+
+
+/**
+  Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other PXE boot information.
+
+  @param[in]  Private           Pointer to PxeBc private data.
+  @param[in]  Dhcp4             Pointer to the EFI_DHCP4_PROTOCOL
+
+  @retval EFI_SUCCESS           The D.O.R.A process successfully finished.
+  @retval Others                Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+PxeBcDhcp4Dora (
+  IN PXEBC_PRIVATE_DATA         *Private,
+  IN EFI_DHCP4_PROTOCOL         *Dhcp4
+  );
+
+#endif
+
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
new file mode 100644
index 0000000..b1b1fb8
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.c
@@ -0,0 +1,2096 @@
+/** @file
+  Functions implementation related with DHCPv6 for UefiPxeBc Driver.
+
+  (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+  Copyright (c) 2009 - 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+//
+// Well-known multi-cast address defined in section-24.1 of rfc-3315
+//
+//   ALL_DHCP_Relay_Agents_and_Servers address: FF02::1:2
+//
+EFI_IPv6_ADDRESS   mAllDhcpRelayAndServersAddress = {{0xFF, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2}};
+
+/**
+  Parse out a DHCPv6 option by OptTag, and find the position in buffer.
+
+  @param[in]  Buffer        The pointer to the option buffer.
+  @param[in]  Length        Length of the option buffer.
+  @param[in]  OptTag        The required option tag.
+
+  @retval     NULL          Failed to parse the required option.
+  @retval     Others        The postion of the required option in buffer.
+
+**/
+EFI_DHCP6_PACKET_OPTION *
+PxeBcParseDhcp6Options (
+  IN UINT8                       *Buffer,
+  IN UINT32                      Length,
+  IN UINT16                      OptTag
+  )
+{
+  EFI_DHCP6_PACKET_OPTION        *Option;
+  UINT32                         Offset;
+
+  Option  = (EFI_DHCP6_PACKET_OPTION *) Buffer;
+  Offset  = 0;
+
+  //
+  // OpLen and OpCode here are both stored in network order.
+  //
+  while (Offset < Length) {
+
+    if (NTOHS (Option->OpCode) == OptTag) {
+
+      return Option;
+    }
+
+    Offset += (NTOHS(Option->OpLen) + 4);
+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);
+  }
+
+  return NULL;
+}
+
+
+/**
+  Build the options buffer for the DHCPv6 request packet.
+
+  @param[in]  Private             The pointer to PxeBc private data.
+  @param[out] OptList             The pointer to the option pointer array.
+  @param[in]  Buffer              The pointer to the buffer to contain the option list.
+
+  @return     Index               The count of the built-in options.
+
+**/
+UINT32
+PxeBcBuildDhcp6Options (
+  IN  PXEBC_PRIVATE_DATA           *Private,
+  OUT EFI_DHCP6_PACKET_OPTION      **OptList,
+  IN  UINT8                        *Buffer
+  )
+{
+  PXEBC_DHCP6_OPTION_ENTRY         OptEnt;
+  UINT32                           Index;
+  UINT16                           Value;
+
+  Index       = 0;
+  OptList[0]  = (EFI_DHCP6_PACKET_OPTION *) Buffer;
+
+  //
+  // Append client option request option
+  //
+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ORO);
+  OptList[Index]->OpLen      = HTONS (4);
+  OptEnt.Oro                 = (PXEBC_DHCP6_OPTION_ORO *) OptList[Index]->Data;
+  OptEnt.Oro->OpCode[0]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_URL);
+  OptEnt.Oro->OpCode[1]      = HTONS(PXEBC_DHCP6_OPT_BOOT_FILE_PARAM);
+  Index++;
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+  //
+  // Append client network device interface option
+  //
+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_UNDI);
+  OptList[Index]->OpLen      = HTONS ((UINT16)3);
+  OptEnt.Undi                = (PXEBC_DHCP6_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_DHCP6_OPTION (OptList[Index - 1]);
+
+  //
+  // Append client system architecture option
+  //
+  OptList[Index]->OpCode     = HTONS (PXEBC_DHCP6_OPT_ARCH);
+  OptList[Index]->OpLen      = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_ARCH));
+  OptEnt.Arch                = (PXEBC_DHCP6_OPTION_ARCH *) OptList[Index]->Data;
+  Value                      = HTONS (EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE);
+  CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
+  Index++;
+  OptList[Index]             = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+  //
+  // Append vendor class option to store the PXE class identifier.
+  //
+  OptList[Index]->OpCode       = HTONS (PXEBC_DHCP6_OPT_VENDOR_CLASS);
+  OptList[Index]->OpLen        = HTONS ((UINT16) sizeof (PXEBC_DHCP6_OPTION_VENDOR_CLASS));
+  OptEnt.VendorClass           = (PXEBC_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;
+  OptEnt.VendorClass->Vendor   = HTONL (PXEBC_DHCP6_ENTERPRISE_NUM);
+  OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (PXEBC_CLASS_ID));
+  CopyMem (
+    &OptEnt.VendorClass->ClassId,
+    DEFAULT_CLASS_ID_DATA,
+    sizeof (PXEBC_CLASS_ID)
+    );
+  PxeBcUintnToAscDecWithFormat (
+    EFI_PXE_CLIENT_SYSTEM_ARCHITECTURE,
+    OptEnt.VendorClass->ClassId.ArchitectureType,
+    sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
+    );
+
+  if (Private->Nii != NULL) {
+    CopyMem (
+      OptEnt.VendorClass->ClassId.InterfaceName,
+      Private->Nii->StringId,
+      sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
+      );
+    PxeBcUintnToAscDecWithFormat (
+      Private->Nii->MajorVer,
+      OptEnt.VendorClass->ClassId.UndiMajor,
+      sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
+      );
+    PxeBcUintnToAscDecWithFormat (
+      Private->Nii->MinorVer,
+      OptEnt.VendorClass->ClassId.UndiMinor,
+      sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
+      );
+  }
+
+  Index++;
+
+  return Index;
+}
+
+
+/**
+  Cache the DHCPv6 packet.
+
+  @param[in]  Dst          The pointer to the cache buffer for DHCPv6 packet.
+  @param[in]  Src          The pointer to the DHCPv6 packet to be cached.
+
+**/
+VOID
+PxeBcCacheDhcp6Packet (
+  IN EFI_DHCP6_PACKET          *Dst,
+  IN EFI_DHCP6_PACKET          *Src
+  )
+{
+  ASSERT (Dst->Size >= Src->Length);
+
+  CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
+  Dst->Length = Src->Length;
+}
+
+
+/**
+  Free all the nodes in the list for boot file.
+
+  @param[in]  Head            The pointer to the head of list.
+
+**/
+VOID
+PxeBcFreeBootFileOption (
+  IN LIST_ENTRY               *Head
+  )
+{
+  LIST_ENTRY                  *Entry;
+  LIST_ENTRY                  *NextEntry;
+  PXEBC_DHCP6_OPTION_NODE     *Node;
+
+  NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, Head) {
+    Node = NET_LIST_USER_STRUCT (Entry, PXEBC_DHCP6_OPTION_NODE, Link);
+    RemoveEntryList (Entry);
+    FreePool (Node);
+  }
+}
+
+
+/**
+  Parse the Boot File URL option.
+
+  @param[out]     FileName     The pointer to the boot file name.
+  @param[in, out] SrvAddr      The pointer to the boot server address.
+  @param[in]      BootFile     The pointer to the boot file URL option data.
+  @param[in]      Length       The length of the boot file URL option data.
+
+  @retval EFI_ABORTED     User cancel operation.
+  @retval EFI_SUCCESS     Selected the boot menu successfully.
+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileUrl (
+     OUT UINT8               **FileName,
+  IN OUT EFI_IPv6_ADDRESS    *SrvAddr,
+  IN     CHAR8               *BootFile,
+  IN     UINT16              Length
+  )
+{
+  UINT16                     PrefixLen;
+  CHAR8                      *BootFileNamePtr;
+  CHAR8                      *BootFileName;
+  UINT16                     BootFileNameLen;
+  CHAR8                      *TmpStr;
+  CHAR8                      TmpChar;
+  CHAR8                      *ServerAddressOption;
+  CHAR8                      *ServerAddress;
+  CHAR8                      *ModeStr;
+  EFI_STATUS                 Status;
+
+  //
+  // The format of the Boot File URL option is:
+  //
+  //  0                   1                   2                   3
+  //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  // |       OPT_BOOTFILE_URL        |            option-len         |
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  // |                                                               |
+  // .                  bootfile-url  (variable length)              .
+  // |                                                               |
+  // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  //
+
+  //
+  // Based upon RFC 5970 and UEFI 2.3 Errata D specification, bootfile-url format
+  // is tftp://[SERVER_ADDRESS]/BOOTFILE_NAME
+  // As an example where the BOOTFILE_NAME is the EFI loader and
+  // SERVER_ADDRESS is the ASCII encoding of an IPV6 address.
+  //
+  PrefixLen = (UINT16) AsciiStrLen (PXEBC_DHCP6_BOOT_FILE_URL_PREFIX);
+
+  if (Length <= PrefixLen ||
+      CompareMem (BootFile, PXEBC_DHCP6_BOOT_FILE_URL_PREFIX, PrefixLen) != 0) {
+    return EFI_NOT_FOUND;
+  }
+
+  BootFile = BootFile + PrefixLen;
+  Length   = (UINT16) (Length - PrefixLen);
+
+  TmpStr = (CHAR8 *) AllocateZeroPool (Length + 1);
+  if (TmpStr == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  CopyMem (TmpStr, BootFile, Length);
+  TmpStr[Length] = '\0';
+
+  //
+  // Get the part of SERVER_ADDRESS string.
+  //
+  ServerAddressOption = TmpStr;
+  if (*ServerAddressOption != PXEBC_ADDR_START_DELIMITER) {
+    FreePool (TmpStr);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ServerAddressOption ++;
+  ServerAddress = ServerAddressOption;
+  while (*ServerAddress != '\0' && *ServerAddress != PXEBC_ADDR_END_DELIMITER) {
+    ServerAddress++;
+  }
+
+  if (*ServerAddress != PXEBC_ADDR_END_DELIMITER) {
+    FreePool (TmpStr);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  *ServerAddress = '\0';
+
+  //
+  // Convert the string of server address to Ipv6 address format and store it.
+  //
+  Status = NetLibAsciiStrToIp6 (ServerAddressOption, SrvAddr);
+  if (EFI_ERROR (Status)) {
+    FreePool (TmpStr);
+    return Status;
+  }
+
+  //
+  // Get the part of BOOTFILE_NAME string.
+  //
+  BootFileNamePtr = (CHAR8*)((UINTN)ServerAddress + 1);
+  if (*BootFileNamePtr != PXEBC_TFTP_URL_SEPARATOR) {
+    FreePool (TmpStr);
+    return EFI_INVALID_PARAMETER;
+  }
+
+  ++BootFileNamePtr;
+  BootFileNameLen = (UINT16)(Length - (UINT16) ((UINTN)BootFileNamePtr - (UINTN)TmpStr) + 1);
+  if (BootFileNameLen != 0 || FileName != NULL) {
+    //
+    // Remove trailing mode=octet if present and ignore.  All other modes are
+    // invalid for netboot6, so reject them.
+    //
+    ModeStr = AsciiStrStr (BootFileNamePtr, ";mode=octet");
+    if (ModeStr != NULL && *(ModeStr + AsciiStrLen (";mode=octet")) == '\0') {
+      *ModeStr = '\0';
+    } else if (AsciiStrStr (BootFileNamePtr, ";mode=") != NULL) {
+      return EFI_INVALID_PARAMETER;
+    }
+
+    //
+    // Extract boot file name from URL.
+    //
+    BootFileName = (CHAR8 *) AllocateZeroPool (BootFileNameLen);
+    if (BootFileName == NULL) {
+      FreePool (TmpStr);
+      return EFI_OUT_OF_RESOURCES;
+    }
+    *FileName = (UINT8*) BootFileName;
+
+    //
+    // Decode percent-encoding in boot file name.
+    //
+    while (*BootFileNamePtr != '\0') {
+      if (*BootFileNamePtr == '%') {
+        TmpChar = *(BootFileNamePtr+ 3);
+        *(BootFileNamePtr+ 3) = '\0';
+        *BootFileName = (UINT8) AsciiStrHexToUintn ((CHAR8*)(BootFileNamePtr + 1));
+        BootFileName++;
+        *(BootFileNamePtr+ 3) = TmpChar;
+        BootFileNamePtr += 3;
+      } else {
+        *BootFileName = *BootFileNamePtr;
+        BootFileName++;
+        BootFileNamePtr++;
+      }
+    }
+    *BootFileName = '\0';
+  }
+
+  FreePool (TmpStr);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Parse the Boot File Parameter option.
+
+  @param[in]  BootFilePara      The pointer to boot file parameter option data.
+  @param[out] BootFileSize      The pointer to the parsed boot file size.
+
+  @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.
+  @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileParam (
+  IN  CHAR8                  *BootFilePara,
+  OUT UINT16                 *BootFileSize
+  )
+{
+  UINT16                     Length;
+  UINT8                      Index;
+  UINT8                      Digit;
+  UINT32                     Size;
+
+  CopyMem (&Length, BootFilePara, sizeof (UINT16));
+  Length = NTOHS (Length);
+
+  //
+  // The BootFile Size should be 1~5 byte ASCII strings
+  //
+  if (Length < 1 || Length > 5) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Extract the value of BootFile Size.
+  //
+  BootFilePara = BootFilePara + sizeof (UINT16);
+  Size         = 0;
+  for (Index = 0; Index < Length; Index++) {
+    if (EFI_ERROR (PxeBcUniHexToUint8 (&Digit, *(BootFilePara + Index)))) {
+      return EFI_NOT_FOUND;
+    }
+
+    Size = (Size + Digit) * 10;
+  }
+
+  Size = Size / 10;
+  if (Size > PXEBC_DHCP6_MAX_BOOT_FILE_SIZE) {
+    return EFI_NOT_FOUND;
+  }
+
+  *BootFileSize = (UINT16) Size;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Parse the cached DHCPv6 packet, including all the options.
+
+  @param[in]  Cache6           The pointer to a cached DHCPv6 packet.
+
+  @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.
+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid the packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp6Packet (
+  IN PXEBC_DHCP6_PACKET_CACHE  *Cache6
+  )
+{
+  EFI_DHCP6_PACKET             *Offer;
+  EFI_DHCP6_PACKET_OPTION      **Options;
+  EFI_DHCP6_PACKET_OPTION      *Option;
+  PXEBC_OFFER_TYPE             OfferType;
+  BOOLEAN                      IsProxyOffer;
+  BOOLEAN                      IsPxeOffer;
+  UINT32                       Offset;
+  UINT32                       Length;
+  UINT32                       EnterpriseNum;
+
+  IsProxyOffer = TRUE;
+  IsPxeOffer   = FALSE;
+  Offer        = &Cache6->Packet.Offer;
+  Options      = Cache6->OptList;
+
+  ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
+
+  Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);
+  Offset  = 0;
+  Length  = GET_DHCP6_OPTION_SIZE (Offer);
+
+  //
+  // OpLen and OpCode here are both stored in network order, since they are from original packet.
+  //
+  while (Offset < Length) {
+
+    if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_IA_NA) {
+      Options[PXEBC_DHCP6_IDX_IA_NA] = Option;
+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_URL) {
+      //
+      // The server sends this option to inform the client about an URL to a boot file.
+      //
+      Options[PXEBC_DHCP6_IDX_BOOT_FILE_URL] = Option;
+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_BOOT_FILE_PARAM) {
+      Options[PXEBC_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
+    } else if (NTOHS (Option->OpCode) == PXEBC_DHCP6_OPT_VENDOR_CLASS) {
+      Options[PXEBC_DHCP6_IDX_VENDOR_CLASS] = Option;
+    }
+
+    Offset += (NTOHS (Option->OpLen) + 4);
+    Option  = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);
+  }
+
+  //
+  // The offer with assigned client address is NOT a proxy offer.
+  // An ia_na option, embeded with valid ia_addr option and a status_code of success.
+  //
+  Option = Options[PXEBC_DHCP6_IDX_IA_NA];
+  if (Option != NULL) {
+    Option = PxeBcParseDhcp6Options (
+               Option->Data + 12,
+               NTOHS (Option->OpLen),
+               PXEBC_DHCP6_OPT_STATUS_CODE
+               );
+    if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {
+      IsProxyOffer = FALSE;
+    }
+  }
+
+  //
+  // The offer with "PXEClient" is a pxe offer.
+  //
+  Option        = Options[PXEBC_DHCP6_IDX_VENDOR_CLASS];
+  EnterpriseNum = HTONL(PXEBC_DHCP6_ENTERPRISE_NUM);
+
+  if (Option != NULL &&
+      NTOHS(Option->OpLen) >= 13 &&
+      CompareMem (Option->Data, &EnterpriseNum, sizeof (UINT32)) == 0 &&
+      CompareMem (&Option->Data[6], DEFAULT_CLASS_ID_DATA, 9) == 0) {
+    IsPxeOffer = TRUE;
+  }
+
+  //
+  // Determine offer type of the dhcp6 packet.
+  //
+  if (IsPxeOffer) {
+    //
+    // It's a binl offer only with PXEClient.
+    //
+    OfferType = IsProxyOffer ? PxeOfferTypeProxyBinl : PxeOfferTypeDhcpBinl;
+  } else {
+    //
+    // It's a dhcp only offer, which is a pure dhcp6 offer packet.
+    //
+    OfferType = PxeOfferTypeDhcpOnly;
+  }
+
+  Cache6->OfferType = OfferType;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Cache the DHCPv6 ack packet, and parse it on demand.
+
+  @param[in]  Private             The pointer to PxeBc private data.
+  @param[in]  Ack                 The pointer to the DHCPv6 ack packet.
+  @param[in]  Verified            If TRUE, parse the ACK packet and store info into mode data.
+
+**/
+VOID
+PxeBcCopyDhcp6Ack (
+  IN PXEBC_PRIVATE_DATA   *Private,
+  IN EFI_DHCP6_PACKET     *Ack,
+  IN BOOLEAN              Verified
+  )
+{
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+
+  Mode = Private->PxeBc.Mode;
+
+  PxeBcCacheDhcp6Packet (&Private->DhcpAck.Dhcp6.Packet.Ack, Ack);
+
+  if (Verified) {
+    //
+    // Parse the ack packet and store it into mode data if needed.
+    //
+    PxeBcParseDhcp6Packet (&Private->DhcpAck.Dhcp6);
+    CopyMem (&Mode->DhcpAck.Dhcpv6, &Ack->Dhcp6, Ack->Length);
+    Mode->DhcpAckReceived = TRUE;
+  }
+}
+
+
+/**
+  Cache the DHCPv6 proxy offer packet according to the received order.
+
+  @param[in]  Private               The pointer to PxeBc private data.
+  @param[in]  OfferIndex            The received order of offer packets.
+
+**/
+VOID
+PxeBcCopyDhcp6Proxy (
+  IN PXEBC_PRIVATE_DATA     *Private,
+  IN UINT32                 OfferIndex
+  )
+{
+  EFI_PXE_BASE_CODE_MODE    *Mode;
+  EFI_DHCP6_PACKET          *Offer;
+
+  ASSERT (OfferIndex < Private->OfferNum);
+  ASSERT (OfferIndex < PXEBC_OFFER_MAX_NUM);
+
+  Mode  = Private->PxeBc.Mode;
+  Offer = &Private->OfferBuffer[OfferIndex].Dhcp6.Packet.Offer;
+
+  //
+  // Cache the proxy offer packet and parse it.
+  //
+  PxeBcCacheDhcp6Packet (&Private->ProxyOffer.Dhcp6.Packet.Offer, Offer);
+  PxeBcParseDhcp6Packet (&Private->ProxyOffer.Dhcp6);
+
+  //
+  // Store this packet into mode data.
+  //
+  CopyMem (&Mode->ProxyOffer.Dhcpv6, &Offer->Dhcp6, Offer->Length);
+  Mode->ProxyOfferReceived = TRUE;
+}
+
+/**
+  Seek the address of the first byte of the option header.
+
+  @param[in]  Buf           The pointer to the buffer.
+  @param[in]  SeekLen       The length to seek.
+  @param[in]  OptType       The option type.
+
+  @retval     NULL          If it failed to seek the option.
+  @retval     others        The position to the option.
+
+**/
+UINT8 *
+PxeBcDhcp6SeekOption (
+  IN UINT8           *Buf,
+  IN UINT32          SeekLen,
+  IN UINT16          OptType
+  )
+{
+  UINT8              *Cursor;
+  UINT8              *Option;
+  UINT16             DataLen;
+  UINT16             OpCode;
+
+  Option = NULL;
+  Cursor = Buf;
+
+  while (Cursor < Buf + SeekLen) {
+    OpCode = ReadUnaligned16 ((UINT16 *) Cursor);
+    if (OpCode == HTONS (OptType)) {
+      Option = Cursor;
+      break;
+    }
+    DataLen = NTOHS (ReadUnaligned16 ((UINT16 *) (Cursor + 2)));
+    Cursor += (DataLen + 4);
+  }
+
+  return Option;
+}
+
+
+/**
+  Build and send out the request packet for the bootfile, and parse the reply.
+
+  @param[in]  Private               The pointer to PxeBc private data.
+  @param[in]  Index                 PxeBc option boot item type.
+
+  @retval     EFI_SUCCESS           Successfully discovered the boot file.
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
+  @retval     Others                Failed to discover the boot file.
+
+**/
+EFI_STATUS
+PxeBcRequestBootService (
+  IN  PXEBC_PRIVATE_DATA              *Private,
+  IN  UINT32                          Index
+  )
+{
+  EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;
+  EFI_PXE_BASE_CODE_UDP_PORT          DestPort;
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
+  EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;
+  UINTN                               DiscoverLen;
+  EFI_DHCP6_PACKET                    *Request;
+  UINTN                               RequestLen;
+  EFI_DHCP6_PACKET                    *Reply;
+  UINT8                               *RequestOpt;
+  UINT8                               *DiscoverOpt;
+  UINTN                               ReadSize;
+  UINT16                              OpFlags;
+  UINT16                              OpCode;
+  UINT16                              OpLen;
+  EFI_STATUS                          Status;
+  EFI_DHCP6_PACKET                    *ProxyOffer;
+  UINT8                               *Option;
+
+  PxeBc       = &Private->PxeBc;
+  Request     = Private->Dhcp6Request;
+  ProxyOffer = &Private->OfferBuffer[Index].Dhcp6.Packet.Offer;
+  SrcPort     = PXEBC_BS_DISCOVER_PORT;
+  DestPort    = PXEBC_BS_DISCOVER_PORT;
+  OpFlags     = 0;
+
+  if (Request == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
+  if (Discover == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Build the request packet by the cached request packet before.
+  //
+  Discover->TransactionId = ProxyOffer->Dhcp6.Header.TransactionId;
+  Discover->MessageType   = Request->Dhcp6.Header.MessageType;
+  RequestOpt              = Request->Dhcp6.Option;
+  DiscoverOpt             = Discover->DhcpOptions;
+  DiscoverLen             = sizeof (EFI_DHCP6_HEADER);
+  RequestLen              = DiscoverLen;
+
+  //
+  // Find Server ID Option from ProxyOffer.
+  //
+  Option = PxeBcDhcp6SeekOption (
+             ProxyOffer->Dhcp6.Option,
+             ProxyOffer->Length - 4,
+             PXEBC_DHCP6_OPT_SERVER_ID
+             );
+  if (Option == NULL) {
+    return EFI_NOT_FOUND;
+  }
+  
+  //
+  // Add Server ID Option.
+  //
+  OpLen = NTOHS (((EFI_DHCP6_PACKET_OPTION *) Option)->OpLen);
+  CopyMem (DiscoverOpt, Option, OpLen + 4);
+  DiscoverOpt += (OpLen + 4);
+  DiscoverLen += (OpLen + 4);
+
+  while (RequestLen < Request->Length) {
+    OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
+    OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
+    if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
+        OpCode != EFI_DHCP6_IA_TYPE_TA &&
+        OpCode != PXEBC_DHCP6_OPT_SERVER_ID
+        ) {
+      //
+      // Copy all the options except IA option and Server ID
+      //
+      CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
+      DiscoverOpt += (OpLen + 4);
+      DiscoverLen += (OpLen + 4);
+    }
+    RequestOpt += (OpLen + 4);
+    RequestLen += (OpLen + 4);
+  }
+
+  //
+  // Update Elapsed option in the package 
+  //
+  Option = PxeBcDhcp6SeekOption (
+             Discover->DhcpOptions,
+             (UINT32)(RequestLen - 4),
+             PXEBC_DHCP6_OPT_ELAPSED_TIME
+             );
+  if (Option != NULL) {
+    CalcElapsedTime (Private);
+    WriteUnaligned16 ((UINT16*)(Option + 4), HTONS((UINT16) Private->ElapsedTime));
+  }  
+
+  Status = PxeBc->UdpWrite (
+                    PxeBc,
+                    OpFlags,
+                    &Private->ServerIp,
+                    &DestPort,
+                    NULL,
+                    &Private->StationIp,
+                    &SrcPort,
+                    NULL,
+                    NULL,
+                    &DiscoverLen,
+                    (VOID *) Discover
+                    );
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Cache the right PXE reply packet here, set valid flag later.
+  // Especially for PXE discover packet, store it into mode data here.
+  //
+  Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
+  ReadSize = (UINTN) Reply->Size;
+
+  //
+  // Start Udp6Read instance
+  //
+  Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+    
+  Status = PxeBc->UdpRead (
+                    PxeBc,
+                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP | EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
+                    NULL,
+                    &SrcPort,
+                    &Private->ServerIp,
+                    &DestPort,
+                    NULL,
+                    NULL,
+                    &ReadSize,
+                    (VOID *) &Reply->Dhcp6
+                    );
+  //
+  // Stop Udp6Read instance
+  //
+  Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Update length
+  //
+  Reply->Length = (UINT32) ReadSize;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Retry to request bootfile name by the BINL offer.
+
+  @param[in]  Private              The pointer to PxeBc private data.
+  @param[in]  Index                The received order of offer packets.
+
+  @retval     EFI_SUCCESS          Successfully retried a request for the bootfile name.
+  @retval     EFI_DEVICE_ERROR     Failed to retry the bootfile name.
+
+**/
+EFI_STATUS
+PxeBcRetryDhcp6Binl (
+  IN PXEBC_PRIVATE_DATA  *Private,
+  IN UINT32              Index
+  )
+{
+  EFI_PXE_BASE_CODE_MODE    *Mode;
+  PXEBC_DHCP6_PACKET_CACHE  *Offer;
+  PXEBC_DHCP6_PACKET_CACHE  *Cache6;
+  EFI_STATUS                Status;
+
+  ASSERT (Index < PXEBC_OFFER_MAX_NUM);
+  ASSERT (Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeDhcpBinl ||
+          Private->OfferBuffer[Index].Dhcp6.OfferType == PxeOfferTypeProxyBinl);
+
+  Mode                  = Private->PxeBc.Mode;
+  Private->IsDoDiscover = FALSE;
+  Offer                 = &Private->OfferBuffer[Index].Dhcp6;
+  if (Offer->OfferType == PxeOfferTypeDhcpBinl) {
+    //
+    // There is no BootFileUrl option in dhcp6 offer, so use servers multi-cast address instead.
+    //
+    CopyMem (
+      &Private->ServerIp.v6,
+      &mAllDhcpRelayAndServersAddress,
+      sizeof (EFI_IPv6_ADDRESS)
+      );
+  } else {
+    ASSERT (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
+    //
+    // Parse out the next server address from the last offer, and store it
+    //
+    Status = PxeBcExtractBootFileUrl (
+               &Private->BootFileName,
+               &Private->ServerIp.v6,
+               (CHAR8 *) (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->Data),
+               NTOHS (Offer->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL]->OpLen)
+               );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+  }
+
+  //
+  // Retry Dhcp6Binl again for the bootfile, and the reply cached into Private->ProxyOffer.
+  //
+  Status = PxeBcRequestBootService (Private, Index);
+
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Cache6 = &Private->ProxyOffer.Dhcp6;
+  Status = PxeBcParseDhcp6Packet (Cache6);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (Cache6->OfferType != PxeOfferTypeProxyPxe10 &&
+      Cache6->OfferType != PxeOfferTypeProxyWfm11a &&
+      Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
+    //
+    // This BINL ack doesn't have discovery option set or multicast option set
+    // or bootfile name specified.
+    //
+    return EFI_DEVICE_ERROR;
+  }
+
+  Mode->ProxyOfferReceived = TRUE;
+  CopyMem (
+    &Mode->ProxyOffer.Dhcpv6,
+    &Cache6->Packet.Offer.Dhcp6,
+    Cache6->Packet.Offer.Length
+    );
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
+
+  @param[in]  Private               The pointer to PXEBC_PRIVATE_DATA.
+  @param[in]  RcvdOffer             The pointer to the received offer packet.
+
+**/
+VOID
+PxeBcCacheDhcp6Offer (
+  IN PXEBC_PRIVATE_DATA     *Private,
+  IN EFI_DHCP6_PACKET       *RcvdOffer
+  )
+{
+  PXEBC_DHCP6_PACKET_CACHE  *Cache6;
+  EFI_DHCP6_PACKET          *Offer;
+  PXEBC_OFFER_TYPE          OfferType;
+
+  Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
+  Offer  = &Cache6->Packet.Offer;
+
+  //
+  // Cache the content of DHCPv6 packet firstly.
+  //
+  PxeBcCacheDhcp6Packet (Offer, RcvdOffer);
+
+  //
+  // Validate the DHCPv6 packet, and parse the options and offer type.
+  //
+  if (EFI_ERROR (PxeBcParseDhcp6Packet (Cache6))) {
+    return ;
+  }
+
+  //
+  // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
+  //
+  OfferType = Cache6->OfferType;
+  ASSERT (OfferType < PxeOfferTypeMax);
+  ASSERT (Private->OfferCount[OfferType] < PXEBC_OFFER_MAX_NUM);
+
+  if (IS_PROXY_OFFER (OfferType)) {
+    //
+    // It's a proxy offer without yiaddr, including PXE10, WFM11a or BINL offer.
+    //
+    Private->IsProxyRecved = TRUE;
+
+    if (OfferType == PxeOfferTypeProxyBinl) {
+      //
+      // Cache all proxy BINL offers.
+      //
+      Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+      Private->OfferCount[OfferType]++;
+    } else if (Private->OfferCount[OfferType] > 0) {
+      //
+      // Only cache the first PXE10/WFM11a offer, and discard the others.
+      //
+      Private->OfferIndex[OfferType][0] = Private->OfferNum;
+      Private->OfferCount[OfferType]    = 1;
+    } else {
+      return;
+    }
+  } else {
+    //
+    // It's a DHCPv6 offer with yiaddr, and cache them all.
+    //
+    Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+    Private->OfferCount[OfferType]++;
+  }
+
+  Private->OfferNum++;
+}
+
+
+/**
+  Select an DHCPv6 offer, and record SelectIndex and SelectProxyType.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcSelectDhcp6Offer (
+  IN PXEBC_PRIVATE_DATA     *Private
+  )
+{
+  UINT32                Index;
+  UINT32                OfferIndex;
+  PXEBC_OFFER_TYPE      OfferType;
+
+  Private->SelectIndex = 0;
+
+  if (Private->IsOfferSorted) {
+    //
+    // Select offer by default policy.
+    //
+    if (Private->OfferCount[PxeOfferTypeDhcpPxe10] > 0) {
+      //
+      // 1. DhcpPxe10 offer
+      //
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpPxe10][0] + 1;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpWfm11a] > 0) {
+      //
+      // 2. DhcpWfm11a offer
+      //
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpWfm11a][0] + 1;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+               Private->OfferCount[PxeOfferTypeProxyPxe10] > 0) {
+      //
+      // 3. DhcpOnly offer and ProxyPxe10 offer.
+      //
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+      Private->SelectProxyType = PxeOfferTypeProxyPxe10;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+               Private->OfferCount[PxeOfferTypeProxyWfm11a] > 0) {
+      //
+      // 4. DhcpOnly offer and ProxyWfm11a offer.
+      //
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+      Private->SelectProxyType = PxeOfferTypeProxyWfm11a;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpBinl] > 0) {
+      //
+      // 5. DhcpBinl offer.
+      //
+      Private->SelectIndex = Private->OfferIndex[PxeOfferTypeDhcpBinl][0] + 1;
+
+    } else if (Private->OfferCount[PxeOfferTypeDhcpOnly] > 0 &&
+               Private->OfferCount[PxeOfferTypeProxyBinl] > 0) {
+      //
+      // 6. DhcpOnly offer and ProxyBinl offer.
+      //
+      Private->SelectIndex     = Private->OfferIndex[PxeOfferTypeDhcpOnly][0] + 1;
+      Private->SelectProxyType = PxeOfferTypeProxyBinl;
+
+    } else {
+      //
+      // 7. DhcpOnly offer with bootfilename.
+      //
+      for (Index = 0; Index < Private->OfferCount[PxeOfferTypeDhcpOnly]; Index++) {
+        OfferIndex = Private->OfferIndex[PxeOfferTypeDhcpOnly][Index];
+        if (Private->OfferBuffer[OfferIndex].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL) {
+          Private->SelectIndex = OfferIndex + 1;
+          break;
+        }
+      }
+    }
+  } else {
+    //
+    // Select offer by received order.
+    //
+    for (Index = 0; Index < Private->OfferNum; Index++) {
+
+      OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
+
+      if (IS_PROXY_OFFER (OfferType)) {
+        //
+        // Skip proxy offers
+        //
+        continue;
+      }
+
+      if (!Private->IsProxyRecved &&
+          OfferType == PxeOfferTypeDhcpOnly &&
+          Private->OfferBuffer[Index].Dhcp6.OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
+        //
+        // Skip if DhcpOnly offer without any other proxy offers or bootfilename.
+        //
+        continue;
+      }
+
+      Private->SelectIndex = Index + 1;
+      break;
+    }
+  }
+}
+
+
+/**
+  Handle the DHCPv6 offer packet.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         Handled the DHCPv6 offer packet successfully.
+  @retval     EFI_NO_RESPONSE     No response to the following request packet.
+
+**/
+EFI_STATUS
+PxeBcHandleDhcp6Offer (
+  IN PXEBC_PRIVATE_DATA            *Private
+  )
+{
+  PXEBC_DHCP6_PACKET_CACHE         *Cache6;
+  EFI_STATUS                       Status;
+  PXEBC_OFFER_TYPE                 OfferType;
+  UINT32                           ProxyIndex;
+  UINT32                           SelectIndex;
+  UINT32                           Index;
+
+  ASSERT (Private->SelectIndex > 0);
+  SelectIndex = (UINT32) (Private->SelectIndex - 1);
+  ASSERT (SelectIndex < PXEBC_OFFER_MAX_NUM);
+  Cache6      = &Private->OfferBuffer[SelectIndex].Dhcp6;
+  Status      = EFI_SUCCESS;
+
+  if (Cache6->OfferType == PxeOfferTypeDhcpBinl) {
+    //
+    // DhcpBinl offer is selected, so need try to request bootfilename by this offer.
+    //
+    if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, SelectIndex))) {
+      Status = EFI_NO_RESPONSE;
+    }
+  } else if (Cache6->OfferType == PxeOfferTypeDhcpOnly) {
+
+    if (Private->IsProxyRecved) {
+      //
+      // DhcpOnly offer is selected, so need try to request bootfilename.
+      //
+      ProxyIndex = 0;
+      if (Private->IsOfferSorted) {
+        //
+        // The proxy offer should be determined if select by default policy.
+        // IsOfferSorted means all offers are labeled by OfferIndex.
+        //
+        ASSERT (Private->OfferCount[Private->SelectProxyType] > 0);
+
+        if (Private->SelectProxyType == PxeOfferTypeProxyBinl) {
+          //
+          // Try all the cached ProxyBinl offer one by one to request bootfilename.
+          //
+          for (Index = 0; Index < Private->OfferCount[Private->SelectProxyType]; Index++) {
+
+            ProxyIndex = Private->OfferIndex[Private->SelectProxyType][Index];
+            if (!EFI_ERROR (PxeBcRetryDhcp6Binl (Private, ProxyIndex))) {
+              break;
+            }
+          }
+          if (Index == Private->OfferCount[Private->SelectProxyType]) {
+            Status = EFI_NO_RESPONSE;
+          }
+        } else {
+          //
+          // For other proxy offers (pxe10 or wfm11a), only one is buffered.
+          //
+          ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+        }
+      } else {
+        //
+        // The proxy offer should not be determined if select by received order.
+        //
+        Status = EFI_NO_RESPONSE;
+
+        for (Index = 0; Index < Private->OfferNum; Index++) {
+
+          OfferType = Private->OfferBuffer[Index].Dhcp6.OfferType;
+
+          if (!IS_PROXY_OFFER (OfferType)) {
+            //
+            // Skip non proxy dhcp offers.
+            //
+            continue;
+          }
+
+          if (OfferType == PxeOfferTypeProxyBinl) {
+            //
+            // Try all the cached ProxyBinl offer one by one to request bootfilename.
+            //
+            if (EFI_ERROR (PxeBcRetryDhcp6Binl (Private, Index))) {
+              continue;
+            }
+          }
+
+          Private->SelectProxyType = OfferType;
+          ProxyIndex               = Index;
+          Status                   = EFI_SUCCESS;
+          break;
+        }
+      }
+
+      if (!EFI_ERROR (Status) && Private->SelectProxyType != PxeOfferTypeProxyBinl) {
+        //
+        // Success to try to request by a ProxyPxe10 or ProxyWfm11a offer, copy and parse it.
+        //
+        PxeBcCopyDhcp6Proxy (Private, ProxyIndex);
+      }
+    } else {
+      //
+      //  Othewise, the bootfilename must be included in DhcpOnly offer.
+      //
+      ASSERT (Cache6->OptList[PXEBC_DHCP6_IDX_BOOT_FILE_URL] != NULL);
+    }
+  }
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // All PXE boot information is ready by now.
+    //
+    PxeBcCopyDhcp6Ack (Private, &Private->DhcpAck.Dhcp6.Packet.Ack, TRUE);
+    Private->PxeBc.Mode->DhcpDiscoverValid = TRUE;
+  }
+
+  return Status;
+}
+
+
+/**
+  Unregister the address by Ip6Config protocol.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcUnregisterIp6Address (
+  IN PXEBC_PRIVATE_DATA           *Private
+  )
+{
+  if (Private->Ip6Policy != PXEBC_IP6_POLICY_MAX) {
+    //
+    // PXE driver change the policy of IP6 driver, it's a chance to recover.
+    // Keep the point and there is no enough requirements to do recovery.
+    //
+  }
+}
+
+/**
+  Check whether IP driver could route the message which will be sent to ServerIp address.
+  
+  This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
+  route is found in IP6 route table, the address will be filed in GatewayAddr and return.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+  @param[in]  TimeOutInSecond     Timeout value in seconds.
+  @param[out] GatewayAddr         Pointer to store the gateway IP address.
+
+  @retval     EFI_SUCCESS         Found a valid gateway address successfully.
+  @retval     EFI_TIMEOUT         The operation is time out.
+  @retval     Other               Unexpect error happened.
+  
+**/
+EFI_STATUS
+PxeBcCheckRouteTable (
+  IN  PXEBC_PRIVATE_DATA            *Private,
+  IN  UINTN                         TimeOutInSecond,
+  OUT EFI_IPv6_ADDRESS              *GatewayAddr
+  )
+{
+  EFI_STATUS                       Status;
+  EFI_IP6_PROTOCOL                 *Ip6;
+  EFI_IP6_MODE_DATA                Ip6ModeData;
+  UINTN                            Index;
+  EFI_EVENT                        TimeOutEvt;
+  UINTN                            RetryCount;
+  BOOLEAN                          GatewayIsFound;
+
+  ASSERT (GatewayAddr != NULL);
+  ASSERT (Private != NULL);
+
+  Ip6            = Private->Ip6;
+  GatewayIsFound = FALSE;
+  RetryCount     = 0;
+  TimeOutEvt     = NULL;
+  ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));
+
+  while (TRUE) {
+    Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    //
+    // Find out the gateway address which can route the message which send to ServerIp.
+    //
+    for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {
+      if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {
+        IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);
+        GatewayIsFound = TRUE;
+        break;
+      }
+    }
+
+    if (Ip6ModeData.AddressList != NULL) {
+      FreePool (Ip6ModeData.AddressList);
+    }
+    if (Ip6ModeData.GroupTable != NULL) {
+      FreePool (Ip6ModeData.GroupTable);
+    }
+    if (Ip6ModeData.RouteTable != NULL) {
+      FreePool (Ip6ModeData.RouteTable);
+    }
+    if (Ip6ModeData.NeighborCache != NULL) {
+      FreePool (Ip6ModeData.NeighborCache);
+    }
+    if (Ip6ModeData.PrefixTable != NULL) {
+      FreePool (Ip6ModeData.PrefixTable);
+    }
+    if (Ip6ModeData.IcmpTypeList != NULL) {
+      FreePool (Ip6ModeData.IcmpTypeList);
+    }
+    
+    if (GatewayIsFound || RetryCount == TimeOutInSecond) {
+      break;
+    }
+    
+    RetryCount++;
+    
+    //
+    // Delay 1 second then recheck it again.
+    //
+    if (TimeOutEvt == NULL) {
+      Status = gBS->CreateEvent (
+                      EVT_TIMER,
+                      TPL_CALLBACK,
+                      NULL,
+                      NULL,
+                      &TimeOutEvt
+                      );
+      if (EFI_ERROR (Status)) {
+        goto ON_EXIT;
+      }
+    }
+
+    Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+    while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
+      Ip6->Poll (Ip6);
+    }
+  }
+  
+ON_EXIT:
+  if (TimeOutEvt != NULL) {
+    gBS->CloseEvent (TimeOutEvt);
+  }
+  
+  if (GatewayIsFound) {
+    Status = EFI_SUCCESS;
+  } else if (RetryCount == TimeOutInSecond) {
+    Status = EFI_TIMEOUT;
+  }
+
+  return Status; 
+}
+
+/**
+  Register the ready station address and gateway by Ip6Config protocol.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+  @param[in]  Address             The pointer to the ready address.
+
+  @retval     EFI_SUCCESS         Registered the address succesfully.
+  @retval     Others              Failed to register the address.
+
+**/
+EFI_STATUS
+PxeBcRegisterIp6Address (
+  IN PXEBC_PRIVATE_DATA            *Private,
+  IN EFI_IPv6_ADDRESS              *Address
+  )
+{
+  EFI_IP6_PROTOCOL                 *Ip6;
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
+  EFI_IP6_CONFIG_POLICY            Policy;
+  EFI_IP6_CONFIG_MANUAL_ADDRESS    CfgAddr;
+  EFI_IPv6_ADDRESS                 GatewayAddr;
+  UINTN                            DataSize;
+  EFI_EVENT                        MappedEvt;
+  EFI_STATUS                       Status;
+  BOOLEAN                          NoGateway;
+  EFI_IPv6_ADDRESS                 *Ip6Addr;
+  UINTN                            Index;
+
+  Status     = EFI_SUCCESS;
+  MappedEvt  = NULL;
+  Ip6Addr    = NULL;
+  DataSize   = sizeof (EFI_IP6_CONFIG_POLICY);
+  Ip6Cfg     = Private->Ip6Cfg;
+  Ip6        = Private->Ip6;
+  NoGateway  = FALSE;
+
+  ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
+  CopyMem (&CfgAddr.Address, Address, sizeof (EFI_IPv6_ADDRESS));
+
+  Status = Ip6->Configure (Ip6, &Private->Ip6CfgData);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Retrieve the gateway address from IP6 route table.
+  //
+  Status = PxeBcCheckRouteTable (Private, PXEBC_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);
+  if (EFI_ERROR (Status)) {
+    NoGateway = TRUE;
+  }
+  
+  //
+  // There is no channel between IP6 and PXE driver about address setting,
+  // so it has to set the new address by Ip6ConfigProtocol manually.
+  //
+  Policy = Ip6ConfigPolicyManual;
+  Status = Ip6Cfg->SetData (
+                     Ip6Cfg,
+                     Ip6ConfigDataTypePolicy,
+                     sizeof(EFI_IP6_CONFIG_POLICY),
+                     &Policy
+                     );
+  if (EFI_ERROR (Status)) {
+    //
+    // There is no need to recover later.
+    //
+    Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
+    goto ON_EXIT;
+  }
+
+  //
+  // Create a notify event to set address flag when DAD if IP6 driver succeeded.
+  //
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  PxeBcCommonNotify,
+                  &Private->IsAddressOk,
+                  &MappedEvt
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  Private->IsAddressOk = FALSE;
+  Status = Ip6Cfg->RegisterDataNotify (
+                     Ip6Cfg,
+                     Ip6ConfigDataTypeManualAddress,
+                     MappedEvt
+                     );
+  if (EFI_ERROR(Status)) {
+    goto ON_EXIT;
+  }
+
+  Status = Ip6Cfg->SetData (
+                     Ip6Cfg,
+                     Ip6ConfigDataTypeManualAddress,
+                     sizeof(EFI_IP6_CONFIG_MANUAL_ADDRESS),
+                     &CfgAddr
+                     );
+  if (EFI_ERROR(Status) && Status != EFI_NOT_READY) {
+    goto ON_EXIT;
+  } else if (Status == EFI_NOT_READY) {
+    //
+    // Poll the network until the asynchronous process is finished.
+    //
+    while (!Private->IsAddressOk) {
+      Ip6->Poll (Ip6);
+    }
+    //
+    // Check whether the IP6 address setting is successed.
+    //
+    DataSize = 0;
+    Status = Ip6Cfg->GetData (
+                       Ip6Cfg,
+                       Ip6ConfigDataTypeManualAddress,
+                       &DataSize,
+                       NULL
+                       );
+    if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {
+      Status = EFI_DEVICE_ERROR;
+      goto ON_EXIT;
+    }
+
+    Ip6Addr = AllocatePool (DataSize);
+    if (Ip6Addr == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+    Status = Ip6Cfg->GetData (
+                       Ip6Cfg,
+                       Ip6ConfigDataTypeManualAddress,
+                       &DataSize,
+                       (VOID*) Ip6Addr
+                       );
+    if (EFI_ERROR (Status)) {
+      Status = EFI_DEVICE_ERROR;
+      goto ON_EXIT;
+    }
+
+    for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index++) {
+      if (CompareMem (Ip6Addr + Index, Address, sizeof (EFI_IPv6_ADDRESS)) == 0) {
+        break;
+      }
+    }
+    if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {
+      Status = EFI_ABORTED;
+      goto ON_EXIT;
+    }
+  }
+  
+  //
+  // Set the default gateway address back if needed.
+  //
+  if (!NoGateway && !NetIp6IsUnspecifiedAddr (&GatewayAddr)) {
+    Status = Ip6Cfg->SetData (
+                       Ip6Cfg,
+                       Ip6ConfigDataTypeGateway,
+                       sizeof (EFI_IPv6_ADDRESS),
+                       &GatewayAddr
+                       );
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+  }
+
+ON_EXIT:
+  if (MappedEvt != NULL) {
+    Ip6Cfg->UnregisterDataNotify (
+              Ip6Cfg,
+              Ip6ConfigDataTypeManualAddress,
+              MappedEvt
+              );
+    gBS->CloseEvent (MappedEvt);
+  }
+  if (Ip6Addr != NULL) {
+    FreePool (Ip6Addr);
+  }
+  return Status;
+}
+
+/**
+  Set the IP6 policy to Automatic.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         Switch the IP policy succesfully.
+  @retval     Others              Unexpect error happened.
+
+**/
+EFI_STATUS
+PxeBcSetIp6Policy (
+  IN PXEBC_PRIVATE_DATA            *Private
+  )
+{
+  EFI_IP6_CONFIG_POLICY            Policy;
+  EFI_STATUS                       Status;
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
+  UINTN                            DataSize;
+
+  Ip6Cfg      = Private->Ip6Cfg;
+  DataSize    = sizeof (EFI_IP6_CONFIG_POLICY);
+
+  //
+  // Get and store the current policy of IP6 driver.
+  //
+  Status = Ip6Cfg->GetData (
+                     Ip6Cfg,
+                     Ip6ConfigDataTypePolicy,
+                     &DataSize,
+                     &Private->Ip6Policy
+                     );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (Private->Ip6Policy == Ip6ConfigPolicyManual) {
+    Policy = Ip6ConfigPolicyAutomatic;
+    Status = Ip6Cfg->SetData (
+                       Ip6Cfg,
+                       Ip6ConfigDataTypePolicy,
+                       sizeof(EFI_IP6_CONFIG_POLICY),
+                       &Policy
+                       );
+    if (EFI_ERROR (Status)) {
+      //
+      // There is no need to recover later.
+      //
+      Private->Ip6Policy = PXEBC_IP6_POLICY_MAX;
+    }
+  }
+
+  return Status;
+}
+
+/**
+  This function will register the station IP address and flush IP instance to start using the new IP address.
+  
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         The new IP address has been configured successfully.
+  @retval     Others              Failed to configure the address.
+
+**/
+EFI_STATUS
+PxeBcSetIp6Address (
+  IN  PXEBC_PRIVATE_DATA              *Private
+  )
+{
+  EFI_STATUS                  Status;
+  EFI_DHCP6_PROTOCOL          *Dhcp6;
+    
+  Dhcp6 = Private->Dhcp6;
+
+  CopyMem (&Private->StationIp.v6, &Private->TmpStationIp.v6, sizeof (EFI_IPv6_ADDRESS));
+  CopyMem (&Private->PxeBc.Mode->StationIp.v6, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
+
+  Status = PxeBcRegisterIp6Address (Private, &Private->StationIp.v6);
+  if (EFI_ERROR (Status)) {
+    Dhcp6->Stop (Dhcp6);
+    return Status;
+  }
+
+  Status = PxeBcFlushStationIp (Private, &Private->StationIp, NULL);
+  if (EFI_ERROR (Status)) {
+    PxeBcUnregisterIp6Address (Private);
+    Dhcp6->Stop (Dhcp6);
+    return Status;
+  }
+
+  AsciiPrint ("\n  Station IP address is ");
+  PxeBcShowIp6Addr (&Private->StationIp.v6);
+
+  return EFI_SUCCESS;
+}
+
+/**
+  EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
+  to intercept events that occurred in the configuration process.
+
+  @param[in]  This              The pointer to the EFI DHCPv6 Protocol.
+  @param[in]  Context           The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
+  @param[in]  CurrentState      The current operational state of the EFI DHCPv Protocol driver.
+  @param[in]  Dhcp6Event        The event that occurs in the current state, which usually means a
+                                state transition.
+  @param[in]  Packet            The DHCPv6 packet that is going to be sent or was already received.
+  @param[out] NewPacket         The packet that is used to replace the Packet above.
+
+  @retval EFI_SUCCESS           Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
+  @retval EFI_NOT_READY         Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
+                                driver will continue to wait for more packets.
+  @retval EFI_ABORTED           Told the EFI DHCPv6 Protocol driver to abort the current process.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDhcp6CallBack (
+  IN  EFI_DHCP6_PROTOCOL           *This,
+  IN  VOID                         *Context,
+  IN  EFI_DHCP6_STATE              CurrentState,
+  IN  EFI_DHCP6_EVENT              Dhcp6Event,
+  IN  EFI_DHCP6_PACKET             *Packet,
+  OUT EFI_DHCP6_PACKET             **NewPacket     OPTIONAL
+  )
+{
+  PXEBC_PRIVATE_DATA                  *Private;
+  EFI_PXE_BASE_CODE_MODE              *Mode;
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+  EFI_DHCP6_PACKET                    *SelectAd;
+  EFI_STATUS                          Status;
+  BOOLEAN                             Received;
+
+  if ((Dhcp6Event != Dhcp6RcvdAdvertise) &&
+      (Dhcp6Event != Dhcp6SelectAdvertise) &&
+      (Dhcp6Event != Dhcp6SendSolicit) &&
+      (Dhcp6Event != Dhcp6SendRequest) &&
+      (Dhcp6Event != Dhcp6RcvdReply)) {
+    return EFI_SUCCESS;
+  }
+
+  ASSERT (Packet != NULL);
+
+  Private   = (PXEBC_PRIVATE_DATA *) Context;
+  Mode      = Private->PxeBc.Mode;
+  Callback  = Private->PxeBcCallback;
+
+  //
+  // Callback to user when any traffic ocurred if has.
+  //
+  if (Dhcp6Event != Dhcp6SelectAdvertise && Callback != NULL) {
+    Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
+    Status = Callback->Callback (
+                         Callback,
+                         Private->Function,
+                         Received,
+                         Packet->Length,
+                         (EFI_PXE_BASE_CODE_PACKET *) &Packet->Dhcp6
+                         );
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+      return EFI_ABORTED;
+    }
+  }
+
+  Status = EFI_SUCCESS;
+
+  switch (Dhcp6Event) {
+
+  case Dhcp6SendSolicit:
+    //
+    // Record the first Solicate msg time
+    //
+    if (Private->SolicitTimes == 0) {
+      CalcElapsedTime (Private);
+      Private->SolicitTimes++;
+    }
+    //
+    // Cache the dhcp discover packet to mode data directly.
+    //
+    CopyMem (&Mode->DhcpDiscover.Dhcpv4, &Packet->Dhcp6, Packet->Length);
+    break;
+
+  case Dhcp6RcvdAdvertise:
+    Status = EFI_NOT_READY;
+    if (Private->OfferNum < PXEBC_OFFER_MAX_NUM) {
+      //
+      // Cache the dhcp offers to OfferBuffer[] for select later, and record
+      // the OfferIndex and OfferCount.
+      //
+      PxeBcCacheDhcp6Offer (Private, Packet);
+    }
+    break;
+
+  case Dhcp6SendRequest:
+    //
+    // Store the request packet as seed packet for discover.
+    //
+    if (Private->Dhcp6Request != NULL) {
+      FreePool (Private->Dhcp6Request);
+    }
+    Private->Dhcp6Request = AllocateZeroPool (Packet->Size);
+    if (Private->Dhcp6Request != NULL) {
+      CopyMem (Private->Dhcp6Request, Packet, Packet->Size);
+    }
+    break;
+
+  case Dhcp6SelectAdvertise:
+    //
+    // Select offer by the default policy or by order, and record the SelectIndex
+    // and SelectProxyType.
+    //
+    PxeBcSelectDhcp6Offer (Private);
+
+    if (Private->SelectIndex == 0) {
+      Status = EFI_ABORTED;
+    } else {
+      ASSERT (NewPacket != NULL);
+      SelectAd   = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;
+      *NewPacket = AllocateZeroPool (SelectAd->Size);
+      ASSERT (*NewPacket != NULL);
+      CopyMem (*NewPacket, SelectAd, SelectAd->Size);
+    }
+    break;
+
+  case Dhcp6RcvdReply:
+    //
+    // Cache the dhcp ack to Private->Dhcp6Ack, but it's not the final ack in mode data
+    // without verification.
+    //
+    ASSERT (Private->SelectIndex != 0);
+    PxeBcCopyDhcp6Ack (Private, Packet, FALSE);
+    break;
+
+  default:
+    ASSERT (0);
+  }
+
+  return Status;
+}
+
+
+/**
+  Build and send out the request packet for the bootfile, and parse the reply.
+
+  @param[in]  Private               The pointer to PxeBc private data.
+  @param[in]  Type                  PxeBc option boot item type.
+  @param[in]  Layer                 The pointer to option boot item layer.
+  @param[in]  UseBis                Use BIS or not.
+  @param[in]  DestIp                The pointer to the server address.
+
+  @retval     EFI_SUCCESS           Successfully discovered the boot file.
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resources.
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
+  @retval     Others                Failed to discover the boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Discover (
+  IN  PXEBC_PRIVATE_DATA              *Private,
+  IN  UINT16                          Type,
+  IN  UINT16                          *Layer,
+  IN  BOOLEAN                         UseBis,
+  IN  EFI_IP_ADDRESS                  *DestIp
+  )
+{
+  EFI_PXE_BASE_CODE_UDP_PORT          SrcPort;
+  EFI_PXE_BASE_CODE_UDP_PORT          DestPort;
+  EFI_PXE_BASE_CODE_MODE              *Mode;
+  EFI_PXE_BASE_CODE_PROTOCOL          *PxeBc;
+  EFI_PXE_BASE_CODE_DHCPV6_PACKET     *Discover;
+  UINTN                               DiscoverLen;
+  EFI_DHCP6_PACKET                    *Request;
+  UINTN                               RequestLen;
+  EFI_DHCP6_PACKET                    *Reply;
+  UINT8                               *RequestOpt;
+  UINT8                               *DiscoverOpt;
+  UINTN                               ReadSize;
+  UINT16                              OpCode;
+  UINT16                              OpLen;
+  UINT32                              Xid;
+  EFI_STATUS                          Status;
+
+  PxeBc       = &Private->PxeBc;
+  Mode        = PxeBc->Mode;
+  Request     = Private->Dhcp6Request;
+  SrcPort     = PXEBC_BS_DISCOVER_PORT;
+  DestPort    = PXEBC_BS_DISCOVER_PORT;
+
+  if (!UseBis && Layer != NULL) {
+    *Layer &= EFI_PXE_BASE_CODE_BOOT_LAYER_MASK;
+  }
+
+  if (Request == NULL) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Discover = AllocateZeroPool (sizeof (EFI_PXE_BASE_CODE_DHCPV6_PACKET));
+  if (Discover == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  //
+  // Build the discover packet by the cached request packet before.
+  //
+  Xid                     = NET_RANDOM (NetRandomInitSeed ());
+  Discover->TransactionId = HTONL (Xid);
+  Discover->MessageType   = Request->Dhcp6.Header.MessageType;
+  RequestOpt              = Request->Dhcp6.Option;
+  DiscoverOpt             = Discover->DhcpOptions;
+  DiscoverLen             = sizeof (EFI_DHCP6_HEADER);
+  RequestLen              = DiscoverLen;
+
+  while (RequestLen < Request->Length) {
+    OpCode = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpCode);
+    OpLen  = NTOHS (((EFI_DHCP6_PACKET_OPTION *) RequestOpt)->OpLen);
+    if (OpCode != EFI_DHCP6_IA_TYPE_NA &&
+        OpCode != EFI_DHCP6_IA_TYPE_TA) {
+      //
+      // Copy all the options except IA option.
+      //
+      CopyMem (DiscoverOpt, RequestOpt, OpLen + 4);
+      DiscoverOpt += (OpLen + 4);
+      DiscoverLen += (OpLen + 4);
+    }
+    RequestOpt += (OpLen + 4);
+    RequestLen += (OpLen + 4);
+  }
+
+  Status = PxeBc->UdpWrite (
+                    PxeBc,
+                    0,
+                    &Private->ServerIp,
+                    &DestPort,
+                    NULL,
+                    &Private->StationIp,
+                    &SrcPort,
+                    NULL,
+                    NULL,
+                    &DiscoverLen,
+                    (VOID *) Discover
+                    );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Cache the right PXE reply packet here, set valid flag later.
+  // Especially for PXE discover packet, store it into mode data here.
+  //
+  if (Private->IsDoDiscover) {
+    CopyMem (&Mode->PxeDiscover.Dhcpv6, Discover, DiscoverLen);
+    Reply = &Private->PxeReply.Dhcp6.Packet.Ack;
+  } else {
+    Reply = &Private->ProxyOffer.Dhcp6.Packet.Offer;
+  }
+  ReadSize = (UINTN) Reply->Size;
+
+  //
+  // Start Udp6Read instance
+  //
+  Status = Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+  
+  Status = PxeBc->UdpRead (
+                    PxeBc,
+                    EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP,
+                    NULL,
+                    &SrcPort,
+                    &Private->ServerIp,
+                    &DestPort,
+                    NULL,
+                    NULL,
+                    &ReadSize,
+                    (VOID *) &Reply->Dhcp6
+                    );
+  //
+  // Stop Udp6Read instance
+  //
+  Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
+
+  @param[in]  Private           The pointer to PxeBc private data.
+  @param[in]  Dhcp6             The pointer to the EFI_DHCP6_PROTOCOL
+
+  @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.
+  @retval Others                Failed to finish the S.A.R.R. process.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Sarr (
+  IN PXEBC_PRIVATE_DATA            *Private,
+  IN EFI_DHCP6_PROTOCOL            *Dhcp6
+  )
+{
+  EFI_PXE_BASE_CODE_MODE           *PxeMode;
+  EFI_DHCP6_CONFIG_DATA            Config;
+  EFI_DHCP6_MODE_DATA              Mode;
+  EFI_DHCP6_RETRANSMISSION         *Retransmit;
+  EFI_DHCP6_PACKET_OPTION          *OptList[PXEBC_DHCP6_OPTION_MAX_NUM];
+  UINT8                            Buffer[PXEBC_DHCP6_OPTION_MAX_SIZE];
+  UINT32                           OptCount;
+  EFI_STATUS                       Status;
+  EFI_IP6_CONFIG_PROTOCOL          *Ip6Cfg;
+  EFI_STATUS                       TimerStatus;
+  EFI_EVENT                        Timer;
+  UINT64                           GetMappingTimeOut;
+  UINTN                            DataSize;
+  EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS    DadXmits;
+
+  Status     = EFI_SUCCESS;
+  PxeMode    = Private->PxeBc.Mode;
+  Ip6Cfg     = Private->Ip6Cfg;
+  Timer      = NULL;
+
+  //
+  // Build option list for the request packet.
+  //
+  OptCount   = PxeBcBuildDhcp6Options (Private, OptList, Buffer);
+  ASSERT (OptCount> 0);
+
+  Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
+  if (Retransmit == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
+  ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
+
+  Config.OptionCount           = OptCount;
+  Config.OptionList            = OptList;
+  Config.Dhcp6Callback         = PxeBcDhcp6CallBack;
+  Config.CallbackContext       = Private;
+  Config.IaInfoEvent           = NULL;
+  Config.RapidCommit           = FALSE;
+  Config.ReconfigureAccept     = FALSE;
+  Config.IaDescriptor.IaId     = Private->IaId;
+  Config.IaDescriptor.Type     = EFI_DHCP6_IA_TYPE_NA;
+  Config.SolicitRetransmission = Retransmit;
+  Retransmit->Irt              = 4;
+  Retransmit->Mrc              = 4;
+  Retransmit->Mrt              = 32;
+  Retransmit->Mrd              = 60;
+
+  //
+  // Configure the DHCPv6 instance for PXE boot.
+  //
+  Status = Dhcp6->Configure (Dhcp6, &Config);
+  FreePool (Retransmit);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Initialize the record fields for DHCPv6 offer in private data.
+  //
+  Private->IsProxyRecved = FALSE;
+  Private->OfferNum      = 0;
+  Private->SelectIndex   = 0;
+  ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+  ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+
+  //
+  // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
+  //
+  Status = Dhcp6->Start (Dhcp6);
+  if (Status == EFI_NO_MAPPING) {
+    //
+    // IP6 Linklocal address is not available for use, so stop current Dhcp process
+    // and wait for duplicate address detection to finish.
+    //
+    Dhcp6->Stop (Dhcp6);
+
+    //
+    // Get Duplicate Address Detection Transmits count.
+    //
+    DataSize = sizeof (EFI_IP6_CONFIG_DUP_ADDR_DETECT_TRANSMITS);
+    Status = Ip6Cfg->GetData (
+                       Ip6Cfg,
+                       Ip6ConfigDataTypeDupAddrDetectTransmits,
+                       &DataSize,
+                       &DadXmits
+                       );
+    if (EFI_ERROR (Status)) {
+      Dhcp6->Configure (Dhcp6, NULL);
+      return Status;
+    }
+
+    Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
+    if (EFI_ERROR (Status)) {
+      Dhcp6->Configure (Dhcp6, NULL);
+      return Status;
+    }
+
+    GetMappingTimeOut = TICKS_PER_SECOND * DadXmits.DupAddrDetectTransmits + PXEBC_DAD_ADDITIONAL_DELAY;
+    Status = gBS->SetTimer (Timer, TimerRelative, GetMappingTimeOut);
+    if (EFI_ERROR (Status)) {
+      gBS->CloseEvent (Timer);
+      Dhcp6->Configure (Dhcp6, NULL);
+      return Status;
+    }
+
+    do {
+      
+      TimerStatus = gBS->CheckEvent (Timer);
+      if (!EFI_ERROR (TimerStatus)) {
+        Status = Dhcp6->Start (Dhcp6);
+      }
+    } while (TimerStatus == EFI_NOT_READY);
+    
+    gBS->CloseEvent (Timer);
+  }
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_ICMP_ERROR) {
+      PxeMode->IcmpErrorReceived = TRUE;
+    }
+    Dhcp6->Configure (Dhcp6, NULL);
+    return Status;
+  }
+
+  //
+  // Get the acquired IPv6 address and store them.
+  //
+  Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
+  if (EFI_ERROR (Status)) {
+    Dhcp6->Stop (Dhcp6);
+    return Status;
+  }
+
+  ASSERT (Mode.Ia->State == Dhcp6Bound);
+  //
+  // DHCP6 doesn't have an option to specify the router address on the subnet, the only way to get the
+  // router address in IP6 is the router discovery mechanism (the RS and RA, which only be handled when
+  // the IP policy is Automatic). So we just hold the station IP address here and leave the IP policy as
+  // Automatic, until we get the server IP address. This could let IP6 driver finish the router discovery 
+  // to find a valid router address.
+  //
+  CopyMem (&Private->TmpStationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
+
+  //
+  // Check the selected offer whether BINL retry is needed.
+  //
+  Status = PxeBcHandleDhcp6Offer (Private);
+  if (EFI_ERROR (Status)) {
+    Dhcp6->Stop (Dhcp6);
+    return Status;
+  }
+  
+  return EFI_SUCCESS;
+}
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
new file mode 100644
index 0000000..3ebf552
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDhcp6.h
@@ -0,0 +1,303 @@
+/** @file
+  Functions declaration related with DHCPv6 for UefiPxeBc Driver.
+
+  Copyright (c) 2009 - 2014, 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.
+
+**/
+
+#ifndef __EFI_PXEBC_DHCP6_H__
+#define __EFI_PXEBC_DHCP6_H__
+
+#define PXEBC_DHCP6_OPTION_MAX_NUM        16
+#define PXEBC_DHCP6_OPTION_MAX_SIZE       312
+#define PXEBC_DHCP6_PACKET_MAX_SIZE       1472
+#define PXEBC_IP6_POLICY_MAX              0xff
+#define PXEBC_IP6_ROUTE_TABLE_TIMEOUT     10
+
+#define PXEBC_DHCP6_S_PORT                547
+#define PXEBC_DHCP6_C_PORT                546
+
+#define PXEBC_DHCP6_OPT_CLIENT_ID         1
+#define PXEBC_DHCP6_OPT_SERVER_ID         2
+#define PXEBC_DHCP6_OPT_IA_NA             3
+#define PXEBC_DHCP6_OPT_IA_TA             4
+#define PXEBC_DHCP6_OPT_IAADDR            5
+#define PXEBC_DHCP6_OPT_ORO               6
+#define PXEBC_DHCP6_OPT_PREFERENCE        7
+#define PXEBC_DHCP6_OPT_ELAPSED_TIME      8
+#define PXEBC_DHCP6_OPT_REPLAY_MSG        9
+#define PXEBC_DHCP6_OPT_AUTH              11
+#define PXEBC_DHCP6_OPT_UNICAST           12
+#define PXEBC_DHCP6_OPT_STATUS_CODE       13
+#define PXEBC_DHCP6_OPT_RAPID_COMMIT      14
+#define PXEBC_DHCP6_OPT_USER_CLASS        15
+#define PXEBC_DHCP6_OPT_VENDOR_CLASS      16
+#define PXEBC_DHCP6_OPT_VENDOR_OPTS       17
+#define PXEBC_DHCP6_OPT_INTERFACE_ID      18
+#define PXEBC_DHCP6_OPT_RECONFIG_MSG      19
+#define PXEBC_DHCP6_OPT_RECONFIG_ACCEPT   20
+#define PXEBC_DHCP6_OPT_BOOT_FILE_URL     59    // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_OPT_BOOT_FILE_PARAM   60    // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_OPT_ARCH              61    // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_OPT_UNDI              62    // Assigned by IANA, RFC 5970
+#define PXEBC_DHCP6_ENTERPRISE_NUM        343   // TODO: IANA TBD: temporarily using Intel's
+#define PXEBC_DHCP6_MAX_BOOT_FILE_SIZE    65535 //   It's a limitation of bit length, 65535*512 bytes.
+
+
+#define PXEBC_DHCP6_IDX_IA_NA             0
+#define PXEBC_DHCP6_IDX_BOOT_FILE_URL     1
+#define PXEBC_DHCP6_IDX_BOOT_FILE_PARAM   2
+#define PXEBC_DHCP6_IDX_VENDOR_CLASS      3
+#define PXEBC_DHCP6_IDX_MAX               4
+
+#define PXEBC_DHCP6_BOOT_FILE_URL_PREFIX  "tftp://"
+#define PXEBC_TFTP_URL_SEPARATOR          '/'
+#define PXEBC_ADDR_START_DELIMITER        '['
+#define PXEBC_ADDR_END_DELIMITER          ']'
+
+#define GET_NEXT_DHCP6_OPTION(Opt) \
+  (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \
+  sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1)
+
+#define GET_DHCP6_OPTION_SIZE(Pkt)  \
+  ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER))
+
+#define IS_PROXY_OFFER(Type) \
+  ((Type) == PxeOfferTypeProxyBinl || \
+   (Type) == PxeOfferTypeProxyPxe10 || \
+   (Type) == PxeOfferTypeProxyWfm11a)
+
+
+#pragma pack(1)
+typedef struct {
+  UINT16 OpCode[256];
+} PXEBC_DHCP6_OPTION_ORO;
+
+typedef struct {
+  UINT8 Type;
+  UINT8 MajorVer;
+  UINT8 MinorVer;
+} PXEBC_DHCP6_OPTION_UNDI;
+
+typedef struct {
+  UINT16 Type;
+} PXEBC_DHCP6_OPTION_ARCH;
+
+typedef struct {
+  UINT8 ClassIdentifier[10];
+  UINT8 ArchitecturePrefix[5];
+  UINT8 ArchitectureType[5];
+  UINT8 Lit3[1];
+  UINT8 InterfaceName[4];
+  UINT8 Lit4[1];
+  UINT8 UndiMajor[3];
+  UINT8 UndiMinor[3];
+} PXEBC_CLASS_ID;
+
+typedef struct {
+  UINT32         Vendor;
+  UINT16         ClassLen;
+  PXEBC_CLASS_ID ClassId;
+} PXEBC_DHCP6_OPTION_VENDOR_CLASS;
+
+#pragma pack()
+
+typedef union {
+  PXEBC_DHCP6_OPTION_ORO            *Oro;
+  PXEBC_DHCP6_OPTION_UNDI           *Undi;
+  PXEBC_DHCP6_OPTION_ARCH           *Arch;
+  PXEBC_DHCP6_OPTION_VENDOR_CLASS   *VendorClass;
+} PXEBC_DHCP6_OPTION_ENTRY;
+
+typedef struct {
+  LIST_ENTRY              Link;
+  EFI_DHCP6_PACKET_OPTION *Option;
+  UINT8                   Precedence;
+} PXEBC_DHCP6_OPTION_NODE;
+
+typedef union {
+  EFI_DHCP6_PACKET        Offer;
+  EFI_DHCP6_PACKET        Ack;
+  UINT8                   Buffer[PXEBC_DHCP6_PACKET_MAX_SIZE];
+} PXEBC_DHCP6_PACKET;
+
+typedef struct {
+  PXEBC_DHCP6_PACKET      Packet;
+  PXEBC_OFFER_TYPE        OfferType;
+  EFI_DHCP6_PACKET_OPTION *OptList[PXEBC_DHCP6_IDX_MAX];
+} PXEBC_DHCP6_PACKET_CACHE;
+
+
+/**
+  Free all the nodes in the boot file list.
+
+  @param[in]  Head            The pointer to the head of the list.
+
+**/
+VOID
+PxeBcFreeBootFileOption (
+  IN LIST_ENTRY               *Head
+  );
+
+
+/**
+  Parse the Boot File URL option.
+
+  @param[out]     FileName     The pointer to the boot file name.
+  @param[in, out] SrvAddr      The pointer to the boot server address.
+  @param[in]      BootFile     The pointer to the boot file URL option data.
+  @param[in]      Length       Length of the boot file URL option data.
+
+  @retval EFI_ABORTED     User canceled the operation.
+  @retval EFI_SUCCESS     Selected the boot menu successfully.
+  @retval EFI_NOT_READY   Read the input key from the keybroad has not finish.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileUrl (
+     OUT UINT8               **FileName,
+  IN OUT EFI_IPv6_ADDRESS    *SrvAddr,
+  IN     CHAR8               *BootFile,
+  IN     UINT16              Length
+  );
+
+
+/**
+  Parse the Boot File Parameter option.
+
+  @param[in]  BootFilePara      The pointer to the boot file parameter option data.
+  @param[out] BootFileSize      The pointer to the parsed boot file size.
+
+  @retval EFI_SUCCESS     Successfully obtained the boot file size from parameter option.
+  @retval EFI_NOT_FOUND   Failed to extract the boot file size from parameter option.
+
+**/
+EFI_STATUS
+PxeBcExtractBootFileParam (
+  IN  CHAR8                  *BootFilePara,
+  OUT UINT16                 *BootFileSize
+  );
+
+
+/**
+  Parse the cached DHCPv6 packet, including all the options.
+
+  @param[in]  Cache6           The pointer to a cached DHCPv6 packet.
+
+  @retval     EFI_SUCCESS      Parsed the DHCPv6 packet successfully.
+  @retval     EFI_DEVICE_ERROR Failed to parse and invalid packet.
+
+**/
+EFI_STATUS
+PxeBcParseDhcp6Packet (
+  IN PXEBC_DHCP6_PACKET_CACHE  *Cache6
+  );
+
+
+/**
+  Register the ready address by Ip6Config protocol.
+
+  @param[in]  Private             The pointer to the PxeBc private data.
+  @param[in]  Address             The pointer to the ready address.
+
+  @retval     EFI_SUCCESS         Registered the address succesfully.
+  @retval     Others              Failed to register the address.
+
+**/
+EFI_STATUS
+PxeBcRegisterIp6Address (
+  IN PXEBC_PRIVATE_DATA            *Private,
+  IN EFI_IPv6_ADDRESS              *Address
+  );
+
+
+/**
+  Unregister the address by Ip6Config protocol.
+
+  @param[in]  Private             The pointer to the PxeBc private data.
+
+**/
+VOID
+PxeBcUnregisterIp6Address (
+  IN PXEBC_PRIVATE_DATA            *Private
+  );
+
+
+/**
+  Build and send out the request packet for the bootfile, and parse the reply.
+
+  @param[in]  Private               The pointer to the PxeBc private data.
+  @param[in]  Type                  PxeBc option boot item type.
+  @param[in]  Layer                 The pointer to the option boot item layer.
+  @param[in]  UseBis                Use BIS or not.
+  @param[in]  DestIp                The pointer to the server address.
+
+  @retval     EFI_SUCCESS           Successfully discovered theboot file.
+  @retval     EFI_OUT_OF_RESOURCES  Failed to allocate resource.
+  @retval     EFI_NOT_FOUND         Can't get the PXE reply packet.
+  @retval     Others                Failed to discover boot file.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Discover (
+  IN  PXEBC_PRIVATE_DATA            *Private,
+  IN  UINT16                        Type,
+  IN  UINT16                        *Layer,
+  IN  BOOLEAN                       UseBis,
+  IN  EFI_IP_ADDRESS                *DestIp
+  );
+
+/**
+  Set the IP6 policy to Automatic.
+
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         Switch the IP policy succesfully.
+  @retval     Others              Unexpect error happened.
+
+**/
+EFI_STATUS
+PxeBcSetIp6Policy (
+  IN PXEBC_PRIVATE_DATA            *Private
+  );
+
+/**
+  This function will register the station IP address and flush IP instance to start using the new IP address.
+  
+  @param[in]  Private             The pointer to PXEBC_PRIVATE_DATA.
+
+  @retval     EFI_SUCCESS         The new IP address has been configured successfully.
+  @retval     Others              Failed to configure the address.
+
+**/
+EFI_STATUS
+PxeBcSetIp6Address (
+  IN  PXEBC_PRIVATE_DATA              *Private
+  );
+
+/**
+  Start the DHCPv6 S.A.R.R. process to acquire the IPv6 address and other PXE boot information.
+
+  @param[in]  Private           The pointer to the PxeBc private data.
+  @param[in]  Dhcp6             The pointer to EFI_DHCP6_PROTOCOL.
+
+  @retval EFI_SUCCESS           The S.A.R.R. process successfully finished.
+  @retval Others                Failed to finish the S.A.R.R. process.
+
+**/
+EFI_STATUS
+PxeBcDhcp6Sarr (
+  IN PXEBC_PRIVATE_DATA            *Private,
+  IN EFI_DHCP6_PROTOCOL            *Dhcp6
+  );
+
+#endif
+
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
new file mode 100644
index 0000000..de8d59c
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.c
@@ -0,0 +1,1825 @@
+/** @file
+  Driver Binding functions implementationfor for UefiPxeBc Driver.
+
+  (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+  Copyright (c) 2007 - 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+EFI_DRIVER_BINDING_PROTOCOL gPxeBcIp4DriverBinding = {
+  PxeBcIp4DriverBindingSupported,
+  PxeBcIp4DriverBindingStart,
+  PxeBcIp4DriverBindingStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gPxeBcIp6DriverBinding = {
+  PxeBcIp6DriverBindingSupported,
+  PxeBcIp6DriverBindingStart,
+  PxeBcIp6DriverBindingStop,
+  0xa,
+  NULL,
+  NULL
+};
+
+/**
+  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.
+
+**/
+EFI_HANDLE
+PxeBcGetNicByIp4Children (
+  IN EFI_HANDLE                 ControllerHandle
+  )
+{
+  EFI_HANDLE                    NicHandle;
+
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiArpProtocolGuid);
+  if (NicHandle == NULL) {
+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp4ProtocolGuid);
+    if (NicHandle == NULL) {
+      NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp4ProtocolGuid);
+      if (NicHandle == NULL) {
+        NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
+        if (NicHandle == NULL) {
+          NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp4ProtocolGuid);
+          if (NicHandle == NULL) {
+            return NULL;
+          }
+        }
+      }
+    }
+  }
+
+  return NicHandle;
+}
+
+
+/**
+  Get the Nic handle using any child handle in the IPv6 stack.
+
+  @param[in]  ControllerHandle    Pointer to child handle over IPv6.
+
+  @return NicHandle               The pointer to the Nic handle.
+
+**/
+EFI_HANDLE
+PxeBcGetNicByIp6Children (
+  IN EFI_HANDLE                  ControllerHandle
+  )
+{
+  EFI_HANDLE                     NicHandle;
+
+  NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiIp6ProtocolGuid);
+  if (NicHandle == NULL) {
+    NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiUdp6ProtocolGuid);
+    if (NicHandle == NULL) {
+      NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
+      if (NicHandle == NULL) {
+        NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiMtftp6ProtocolGuid);
+        if (NicHandle == NULL) {
+          return NULL;
+        }
+      }
+    }
+  }
+
+  return NicHandle;
+}
+
+
+/**
+  Destroy the opened instances based on IPv4.
+
+  @param[in]  This              Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
+  @param[in]  Private           Pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcDestroyIp4Children (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN PXEBC_PRIVATE_DATA           *Private
+  )
+{
+  ASSERT(Private != NULL);
+
+  if (Private->ArpChild != NULL) {
+    //
+    // Close Arp for PxeBc->Arp and destroy the instance.
+    //
+    gBS->CloseProtocol (
+           Private->ArpChild,
+           &gEfiArpProtocolGuid,
+           This->DriverBindingHandle,
+           Private->Controller
+           );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiArpServiceBindingProtocolGuid,
+      Private->ArpChild
+      );
+  }
+
+  if (Private->Ip4Child != NULL) {
+    //
+    // Close Ip4 for background ICMP error message and destroy the instance.
+    //
+    gBS->CloseProtocol (
+           Private->Ip4Child,
+           &gEfiIp4ProtocolGuid,
+           This->DriverBindingHandle,
+           Private->Controller
+           );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiIp4ServiceBindingProtocolGuid,
+      Private->Ip4Child
+      );
+  }
+
+  if (Private->Udp4WriteChild != NULL) {
+    //
+    // Close Udp4 for PxeBc->UdpWrite and destroy the instance.
+    //
+    gBS->CloseProtocol (
+           Private->Udp4WriteChild,
+           &gEfiUdp4ProtocolGuid,
+           This->DriverBindingHandle,
+           Private->Controller
+           );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiUdp4ServiceBindingProtocolGuid,
+      Private->Udp4WriteChild
+      );
+  }
+
+  if (Private->Udp4ReadChild != NULL) {
+    //
+    // Close Udp4 for PxeBc->UdpRead and destroy the instance.
+    //
+    gBS->CloseProtocol (
+          Private->Udp4ReadChild,
+          &gEfiUdp4ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiUdp4ServiceBindingProtocolGuid,
+      Private->Udp4ReadChild
+      );
+  }
+
+  if (Private->Mtftp4Child != NULL) {
+    //
+    // Close Mtftp4 for PxeBc->Mtftp4 and destroy the instance.
+    //
+    gBS->CloseProtocol (
+          Private->Mtftp4Child,
+          &gEfiMtftp4ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiMtftp4ServiceBindingProtocolGuid,
+      Private->Mtftp4Child
+      );
+  }
+
+  if (Private->Dhcp4Child != NULL) {
+    //
+    // Close Dhcp4 for PxeBc->Dhcp4 and destroy the instance.
+    //
+    gBS->CloseProtocol (
+          Private->Dhcp4Child,
+          &gEfiDhcp4ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiDhcp4ServiceBindingProtocolGuid,
+      Private->Dhcp4Child
+      );
+  }
+
+  if (Private->Ip4Nic != NULL) {
+    //
+    // Close PxeBcPrivate from the parent Nic handle and destroy the virtual handle.
+    //
+    gBS->CloseProtocol (
+           Private->Controller,
+           &gEfiCallerIdGuid,
+           This->DriverBindingHandle,
+           Private->Ip4Nic->Controller
+           );
+
+    gBS->UninstallMultipleProtocolInterfaces (
+           Private->Ip4Nic->Controller,
+           &gEfiDevicePathProtocolGuid,
+           Private->Ip4Nic->DevicePath,
+           &gEfiLoadFileProtocolGuid,
+           &Private->Ip4Nic->LoadFile,
+           &gEfiPxeBaseCodeProtocolGuid,
+           &Private->PxeBc,
+           NULL
+           );
+
+    if (Private->Snp != NULL) { 
+      //
+      // Close SNP from the child virtual handle
+      //
+      gBS->CloseProtocol (
+             Private->Ip4Nic->Controller,
+             &gEfiSimpleNetworkProtocolGuid,
+             This->DriverBindingHandle,
+             Private->Ip4Nic->Controller
+             );
+             
+      gBS->UninstallProtocolInterface (
+             Private->Ip4Nic->Controller,
+             &gEfiSimpleNetworkProtocolGuid,
+             Private->Snp
+             );
+    }
+    FreePool (Private->Ip4Nic);
+  }
+
+  Private->ArpChild         = NULL;
+  Private->Ip4Child         = NULL;
+  Private->Udp4WriteChild   = NULL;
+  Private->Udp4ReadChild    = NULL;
+  Private->Mtftp4Child      = NULL;
+  Private->Dhcp4Child       = NULL;
+  Private->Ip4Nic           = NULL;
+}
+
+
+/**
+  Destroy the opened instances based on IPv6.
+
+  @param[in]  This              Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
+  @param[in]  Private           Pointer to PXEBC_PRIVATE_DATA.
+
+**/
+VOID
+PxeBcDestroyIp6Children (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN PXEBC_PRIVATE_DATA           *Private
+  )
+{
+  ASSERT(Private != NULL);
+
+  if (Private->Ip6Child != NULL) {
+    //
+    // Close Ip6 for Ip6->Ip6Config and destroy the instance.
+    //
+    gBS->CloseProtocol (
+          Private->Ip6Child,
+          &gEfiIp6ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiIp6ServiceBindingProtocolGuid,
+      Private->Ip6Child
+      );
+  }
+
+  if (Private->Udp6WriteChild != NULL) {
+    //
+    // Close Udp6 for PxeBc->UdpWrite and destroy the instance.
+    //
+    gBS->CloseProtocol (
+           Private->Udp6WriteChild,
+           &gEfiUdp6ProtocolGuid,
+           This->DriverBindingHandle,
+           Private->Controller
+           );
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiUdp6ServiceBindingProtocolGuid,
+      Private->Udp6WriteChild
+      );
+  }
+
+  if (Private->Udp6ReadChild != NULL) {
+    //
+    // Close Udp6 for PxeBc->UdpRead and destroy the instance.
+    //
+    gBS->CloseProtocol (
+          Private->Udp6ReadChild,
+          &gEfiUdp6ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiUdp6ServiceBindingProtocolGuid,
+      Private->Udp6ReadChild
+      );
+  }
+
+  if (Private->Mtftp6Child != NULL) {
+    //
+    // Close Mtftp6 for PxeBc->Mtftp and destroy the instance.
+    //
+    gBS->CloseProtocol (
+          Private->Mtftp6Child,
+          &gEfiMtftp6ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiMtftp6ServiceBindingProtocolGuid,
+      Private->Mtftp6Child
+      );
+  }
+
+  if (Private->Dhcp6Child != NULL) {
+    //
+    // Close Dhcp6 for PxeBc->Dhcp and destroy the instance.
+    //
+    gBS->CloseProtocol (
+          Private->Dhcp6Child,
+          &gEfiDhcp6ProtocolGuid,
+          This->DriverBindingHandle,
+          Private->Controller
+          );
+
+    NetLibDestroyServiceChild (
+      Private->Controller,
+      This->DriverBindingHandle,
+      &gEfiDhcp6ServiceBindingProtocolGuid,
+      Private->Dhcp6Child
+      );
+  }
+
+  if (Private->Ip6Nic != NULL) {
+    //
+    // Close PxeBcPrivate from the parent Nic handle and destroy the virtual handle.
+    //
+    gBS->CloseProtocol (
+           Private->Controller,
+           &gEfiCallerIdGuid,
+           This->DriverBindingHandle,
+           Private->Ip6Nic->Controller
+           );
+
+    gBS->UninstallMultipleProtocolInterfaces (
+           Private->Ip6Nic->Controller,
+           &gEfiDevicePathProtocolGuid,
+           Private->Ip6Nic->DevicePath,
+           &gEfiLoadFileProtocolGuid,
+           &Private->Ip6Nic->LoadFile,
+           &gEfiPxeBaseCodeProtocolGuid,
+           &Private->PxeBc,
+           NULL
+           );
+    if (Private->Snp != NULL) {
+      //
+      // Close SNP from the child virtual handle
+      //
+      gBS->CloseProtocol (
+             Private->Ip6Nic->Controller,
+             &gEfiSimpleNetworkProtocolGuid,
+             This->DriverBindingHandle,
+             Private->Ip6Nic->Controller
+             );
+      gBS->UninstallProtocolInterface (
+             Private->Ip6Nic->Controller,
+             &gEfiSimpleNetworkProtocolGuid,
+             Private->Snp
+             );
+    }
+    FreePool (Private->Ip6Nic);
+  }
+
+  Private->Ip6Child           = NULL;
+  Private->Udp6WriteChild     = NULL;
+  Private->Udp6ReadChild      = NULL;
+  Private->Mtftp6Child        = NULL;
+  Private->Dhcp6Child         = NULL;
+  Private->Ip6Nic             = NULL;
+  Private->Mode.Ipv6Available = FALSE;
+}
+
+/**
+  Check whether UNDI protocol supports IPv6.
+
+  @param[in]   ControllerHandle  Controller handle.
+  @param[in]   Private           Pointer to PXEBC_PRIVATE_DATA.
+  @param[out]  Ipv6Support       TRUE if UNDI supports IPv6.
+
+  @retval EFI_SUCCESS            Get the result whether UNDI supports IPv6 by NII or AIP protocol successfully.
+  @retval EFI_NOT_FOUND          Don't know whether UNDI supports IPv6 since NII or AIP is not available.
+
+**/
+EFI_STATUS
+PxeBcCheckIpv6Support (
+  IN  EFI_HANDLE                   ControllerHandle,
+  IN  PXEBC_PRIVATE_DATA           *Private,
+  OUT BOOLEAN                      *Ipv6Support
+  )
+{
+  EFI_HANDLE                       Handle;
+  EFI_ADAPTER_INFORMATION_PROTOCOL *Aip;
+  EFI_STATUS                       Status;
+  EFI_GUID                         *InfoTypesBuffer;
+  UINTN                            InfoTypeBufferCount;
+  UINTN                            TypeIndex;
+  BOOLEAN                          Supported;
+  VOID                             *InfoBlock;
+  UINTN                            InfoBlockSize;
+
+  ASSERT (Private != NULL && Ipv6Support != NULL);
+
+  //
+  // Check whether the UNDI supports IPv6 by NII protocol.
+  //
+  if (Private->Nii != NULL) {
+    *Ipv6Support = Private->Nii->Ipv6Supported;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Check whether the UNDI supports IPv6 by AIP protocol.
+  //
+
+  //
+  // Get the NIC handle by SNP protocol.
+  //  
+  Handle = NetLibGetSnpHandle (ControllerHandle, NULL);
+  if (Handle == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  Aip    = NULL;
+  Status = gBS->HandleProtocol (
+                  Handle,
+                  &gEfiAdapterInformationProtocolGuid,
+                  (VOID *) &Aip
+                  );
+  if (EFI_ERROR (Status) || Aip == NULL) {
+    return EFI_NOT_FOUND;
+  }
+
+  InfoTypesBuffer     = NULL;
+  InfoTypeBufferCount = 0;
+  Status = Aip->GetSupportedTypes (Aip, &InfoTypesBuffer, &InfoTypeBufferCount);
+  if (EFI_ERROR (Status) || InfoTypesBuffer == NULL) {
+    FreePool (InfoTypesBuffer);
+    return EFI_NOT_FOUND;
+  }
+
+  Supported = FALSE;
+  for (TypeIndex = 0; TypeIndex < InfoTypeBufferCount; TypeIndex++) {
+    if (CompareGuid (&InfoTypesBuffer[TypeIndex], &gEfiAdapterInfoUndiIpv6SupportGuid)) {
+      Supported = TRUE;
+      break;
+    }
+  }
+
+  FreePool (InfoTypesBuffer);
+  if (!Supported) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // We now have adapter information block.
+  //
+  InfoBlock     = NULL;
+  InfoBlockSize = 0;
+  Status = Aip->GetInformation (Aip, &gEfiAdapterInfoUndiIpv6SupportGuid, &InfoBlock, &InfoBlockSize);
+  if (EFI_ERROR (Status) || InfoBlock == NULL) {
+    FreePool (InfoBlock);
+    return EFI_NOT_FOUND;
+  }  
+
+  *Ipv6Support = ((EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *) InfoBlock)->Ipv6Support;
+  FreePool (InfoBlock);
+  return EFI_SUCCESS;
+
+}
+
+/**
+  Create the opened instances based on IPv4.
+
+  @param[in]  This              Pointer to EFI_DRIVER_BINDING_PROTOCOL.
+  @param[in]  ControllerHandle  Handle of the child to destroy.
+  @param[in]  Private Handle    Pointer to PXEBC_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS           The instances based on IPv4 were all created successfully.
+  @retval Others                An unexpected error occurred.
+
+**/
+EFI_STATUS
+PxeBcCreateIp4Children (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN PXEBC_PRIVATE_DATA           *Private
+  )
+{
+  EFI_STATUS                      Status;
+  IPv4_DEVICE_PATH                Ip4Node;
+  EFI_PXE_BASE_CODE_MODE          *Mode;
+  EFI_UDP4_CONFIG_DATA            *Udp4CfgData;
+  EFI_IP4_CONFIG_DATA             *Ip4CfgData;
+  EFI_IP4_MODE_DATA               Ip4ModeData;
+  PXEBC_PRIVATE_PROTOCOL          *Id;
+  EFI_SIMPLE_NETWORK_PROTOCOL     *Snp;
+
+  if (Private->Ip4Nic != NULL) {
+    //
+    // Already created before.
+    //
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Create Dhcp4 child and open Dhcp4 protocol for PxeBc->Dhcp.
+  //
+  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;
+  }
+
+  //
+  // Create Mtftp4 child and open Mtftp4 protocol for PxeBc->Mtftp.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiMtftp4ServiceBindingProtocolGuid,
+             &Private->Mtftp4Child
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Mtftp4Child,
+                  &gEfiMtftp4ProtocolGuid,
+                  (VOID **) &Private->Mtftp4,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create Udp4 child and open Udp4 protocol for PxeBc->UdpRead.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiUdp4ServiceBindingProtocolGuid,
+             &Private->Udp4ReadChild
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Udp4ReadChild,
+                  &gEfiUdp4ProtocolGuid,
+                  (VOID **) &Private->Udp4Read,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create Udp4 child and open Udp4 protocol for PxeBc->UdpWrite.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiUdp4ServiceBindingProtocolGuid,
+             &Private->Udp4WriteChild
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Udp4WriteChild,
+                  &gEfiUdp4ProtocolGuid,
+                  (VOID **) &Private->Udp4Write,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create Arp child and open Arp protocol for PxeBc->Arp.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiArpServiceBindingProtocolGuid,
+             &Private->ArpChild
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->ArpChild,
+                  &gEfiArpProtocolGuid,
+                  (VOID **) &Private->Arp,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create Ip4 child and open Ip4 protocol for background ICMP packets.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiIp4ServiceBindingProtocolGuid,
+             &Private->Ip4Child
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Ip4Child,
+                  &gEfiIp4ProtocolGuid,
+                  (VOID **) &Private->Ip4,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Get max packet size from Ip4 to calculate block size for Tftp later.
+  //
+  Status = Private->Ip4->GetModeData (Private->Ip4, &Ip4ModeData, NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Private->Ip4MaxPacketSize = Ip4ModeData.MaxPacketSize;
+
+  Private->Ip4Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));
+  if (Private->Ip4Nic == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Private->Ip4Nic->Private   = Private;
+  Private->Ip4Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;
+
+   //
+  // Locate Ip4->Ip4Config2 and store it for set IPv4 Policy.
+  //
+  Status = gBS->HandleProtocol (
+                  ControllerHandle,
+                  &gEfiIp4Config2ProtocolGuid,
+                  (VOID **) &Private->Ip4Config2
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create a device path node for Ipv4 virtual nic, and append it.
+  //
+  ZeroMem (&Ip4Node, sizeof (IPv4_DEVICE_PATH));
+  Ip4Node.Header.Type     = MESSAGING_DEVICE_PATH;
+  Ip4Node.Header.SubType  = MSG_IPv4_DP;
+  Ip4Node.StaticIpAddress = FALSE;
+
+  SetDevicePathNodeLength (&Ip4Node.Header, sizeof (Ip4Node));
+
+  Private->Ip4Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip4Node.Header);
+
+  if (Private->Ip4Nic->DevicePath == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+
+  CopyMem (
+    &Private->Ip4Nic->LoadFile,
+    &gLoadFileProtocolTemplate,
+    sizeof (EFI_LOAD_FILE_PROTOCOL)
+    );
+
+  //
+  // Create a new handle for IPv4 virtual nic,
+  // and install PxeBaseCode, LoadFile and DevicePath protocols.
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Private->Ip4Nic->Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  Private->Ip4Nic->DevicePath,
+                  &gEfiLoadFileProtocolGuid,
+                  &Private->Ip4Nic->LoadFile,
+                  &gEfiPxeBaseCodeProtocolGuid,
+                  &Private->PxeBc,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  if (Private->Snp != NULL) {
+    //
+    // Install SNP protocol on purpose is for some OS loader backward
+    // compatibility consideration.
+    //
+    Status = gBS->InstallProtocolInterface (
+                    &Private->Ip4Nic->Controller,
+                    &gEfiSimpleNetworkProtocolGuid,
+                    EFI_NATIVE_INTERFACE,
+                    Private->Snp
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    // Open SNP on the child handle BY_DRIVER. It will prevent any additionally 
+    // layering to perform the experiment.
+    //
+    Status = gBS->OpenProtocol (
+                    Private->Ip4Nic->Controller,
+                    &gEfiSimpleNetworkProtocolGuid,
+                    (VOID **) &Snp,
+                    This->DriverBindingHandle,
+                    Private->Ip4Nic->Controller,
+                    EFI_OPEN_PROTOCOL_BY_DRIVER
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+  }
+
+  //
+  // Open PxeBaseCodePrivate protocol by child to setup a parent-child relationship between
+  // real NIC handle and the virtual IPv4 NIC handle.
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiCallerIdGuid,
+                  (VOID **) &Id,
+                  This->DriverBindingHandle,
+                  Private->Ip4Nic->Controller,
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Set default configure data for Udp4Read and Ip4 instance.
+  //
+  Mode                            = Private->PxeBc.Mode;
+  Udp4CfgData                     = &Private->Udp4CfgData;
+  Ip4CfgData                      = &Private->Ip4CfgData;
+
+  Udp4CfgData->AcceptBroadcast    = FALSE;
+  Udp4CfgData->AcceptAnyPort      = TRUE;
+  Udp4CfgData->AllowDuplicatePort = TRUE;
+  Udp4CfgData->TypeOfService      = Mode->ToS;
+  Udp4CfgData->TimeToLive         = Mode->TTL;
+  Udp4CfgData->ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;
+  Udp4CfgData->TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;
+
+  Ip4CfgData->AcceptIcmpErrors    = TRUE;
+  Ip4CfgData->DefaultProtocol     = EFI_IP_PROTO_ICMP;
+  Ip4CfgData->TypeOfService       = Mode->ToS;
+  Ip4CfgData->TimeToLive          = Mode->TTL;
+  Ip4CfgData->ReceiveTimeout      = PXEBC_DEFAULT_LIFETIME;
+  Ip4CfgData->TransmitTimeout     = PXEBC_DEFAULT_LIFETIME;
+
+  return EFI_SUCCESS;
+
+ON_ERROR:
+  PxeBcDestroyIp4Children (This, Private);
+  return Status;
+}
+
+
+/**
+  Create the opened instances based on IPv6.
+
+  @param[in]  This              Pointer to EFI_DRIVER_BINDING_PROTOCOL.
+  @param[in]  ControllerHandle  Handle of the child to destroy.
+  @param[in]  Private Handle    Pointer to PXEBC_PRIVATE_DATA.
+
+  @retval EFI_SUCCESS           The instances based on IPv6 were all created successfully.
+  @retval Others                An unexpected error occurred.
+
+**/
+EFI_STATUS
+PxeBcCreateIp6Children (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN PXEBC_PRIVATE_DATA           *Private
+  )
+{
+  EFI_STATUS                      Status;
+  IPv6_DEVICE_PATH                Ip6Node;
+  EFI_UDP6_CONFIG_DATA            *Udp6CfgData;
+  EFI_IP6_CONFIG_DATA             *Ip6CfgData;
+  EFI_IP6_MODE_DATA               Ip6ModeData;
+  PXEBC_PRIVATE_PROTOCOL          *Id;
+  EFI_SIMPLE_NETWORK_PROTOCOL     *Snp;
+  UINTN                           Index;
+
+  if (Private->Ip6Nic != NULL) {
+    //
+    // Already created before.
+    //
+    return EFI_SUCCESS;
+  }
+
+  Private->Ip6Nic = AllocateZeroPool (sizeof (PXEBC_VIRTUAL_NIC));
+
+  if (Private->Ip6Nic == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  Private->Ip6Nic->Private   = Private;
+  Private->Ip6Nic->Signature = PXEBC_VIRTUAL_NIC_SIGNATURE;
+
+  //
+  // Create Dhcp6 child and open Dhcp6 protocol for PxeBc->Dhcp.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiDhcp6ServiceBindingProtocolGuid,
+             &Private->Dhcp6Child
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Dhcp6Child,
+                  &gEfiDhcp6ProtocolGuid,
+                  (VOID **) &Private->Dhcp6,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Generate a random IAID for the Dhcp6 assigned address.
+  //
+  Private->IaId = NET_RANDOM (NetRandomInitSeed ());
+  if (Private->Snp != NULL) {
+    for (Index = 0; Index < Private->Snp->Mode->HwAddressSize; Index++) {
+      Private->IaId |= (Private->Snp->Mode->CurrentAddress.Addr[Index] << ((Index << 3) & 31));
+    }  
+  }
+
+  //
+  // Create Mtftp6 child and open Mtftp6 protocol for PxeBc->Mtftp.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiMtftp6ServiceBindingProtocolGuid,
+             &Private->Mtftp6Child
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Mtftp6Child,
+                  &gEfiMtftp6ProtocolGuid,
+                  (VOID **) &Private->Mtftp6,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create Udp6 child and open Udp6 protocol for PxeBc->UdpRead.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiUdp6ServiceBindingProtocolGuid,
+             &Private->Udp6ReadChild
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Udp6ReadChild,
+                  &gEfiUdp6ProtocolGuid,
+                  (VOID **) &Private->Udp6Read,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create Udp6 child and open Udp6 protocol for PxeBc->UdpWrite.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiUdp6ServiceBindingProtocolGuid,
+             &Private->Udp6WriteChild
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Udp6WriteChild,
+                  &gEfiUdp6ProtocolGuid,
+                  (VOID **) &Private->Udp6Write,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create Ip6 child and open Ip6 protocol for background ICMP6 packets.
+  //
+  Status = NetLibCreateServiceChild (
+             ControllerHandle,
+             This->DriverBindingHandle,
+             &gEfiIp6ServiceBindingProtocolGuid,
+             &Private->Ip6Child
+             );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Status = gBS->OpenProtocol (
+                  Private->Ip6Child,
+                  &gEfiIp6ProtocolGuid,
+                  (VOID **) &Private->Ip6,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_BY_DRIVER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Get max packet size from Ip6 to calculate block size for Tftp later.
+  //
+  Status = Private->Ip6->GetModeData (Private->Ip6, &Ip6ModeData, NULL, NULL);
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Private->Ip6MaxPacketSize = Ip6ModeData.MaxPacketSize;
+
+  //
+  // Locate Ip6->Ip6Config and store it for set IPv6 address.
+  //
+  Status = gBS->HandleProtocol (
+                  ControllerHandle,
+                  &gEfiIp6ConfigProtocolGuid,
+                  (VOID **) &Private->Ip6Cfg
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Create a device path node for Ipv6 virtual nic, and append it.
+  //
+  ZeroMem (&Ip6Node, sizeof (IPv6_DEVICE_PATH));
+  Ip6Node.Header.Type     = MESSAGING_DEVICE_PATH;
+  Ip6Node.Header.SubType  = MSG_IPv6_DP;
+  Ip6Node.PrefixLength    = IP6_PREFIX_LENGTH;
+
+  SetDevicePathNodeLength (&Ip6Node.Header, sizeof (Ip6Node));
+
+  Private->Ip6Nic->DevicePath = AppendDevicePathNode (Private->DevicePath, &Ip6Node.Header);
+
+  if (Private->Ip6Nic->DevicePath == NULL) {
+    Status = EFI_OUT_OF_RESOURCES;
+    goto ON_ERROR;
+  }
+
+  CopyMem (
+    &Private->Ip6Nic->LoadFile,
+    &gLoadFileProtocolTemplate,
+    sizeof (EFI_LOAD_FILE_PROTOCOL)
+    );
+
+  //
+  // Create a new handle for IPv6 virtual nic,
+  // and install PxeBaseCode, LoadFile and DevicePath protocols.
+  //
+  Status = gBS->InstallMultipleProtocolInterfaces (
+                  &Private->Ip6Nic->Controller,
+                  &gEfiDevicePathProtocolGuid,
+                  Private->Ip6Nic->DevicePath,
+                  &gEfiLoadFileProtocolGuid,
+                  &Private->Ip6Nic->LoadFile,
+                  &gEfiPxeBaseCodeProtocolGuid,
+                  &Private->PxeBc,
+                  NULL
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+  
+  if (Private->Snp != NULL) {
+    //
+    // Install SNP protocol on purpose is for some OS loader backward
+    // compatibility consideration.
+    //
+    Status = gBS->InstallProtocolInterface (
+                    &Private->Ip6Nic->Controller,
+                    &gEfiSimpleNetworkProtocolGuid,
+                    EFI_NATIVE_INTERFACE,
+                    Private->Snp
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    // Open SNP on the child handle BY_DRIVER. It will prevent any additionally 
+    // layering to perform the experiment.
+    //
+    Status = gBS->OpenProtocol (
+                    Private->Ip6Nic->Controller,
+                    &gEfiSimpleNetworkProtocolGuid,
+                    (VOID **) &Snp,
+                    This->DriverBindingHandle,
+                    Private->Ip6Nic->Controller,
+                    EFI_OPEN_PROTOCOL_BY_DRIVER
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+  }
+
+  //
+  // Open PxeBaseCodePrivate protocol by child to setup a parent-child relationship between
+  // real NIC handle and the virtual IPv6 NIC handle.
+  //
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiCallerIdGuid,
+                  (VOID **) &Id,
+                  This->DriverBindingHandle,
+                  Private->Ip6Nic->Controller,
+                  EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Set IPv6 avaiable flag and set default configure data for
+  // Udp6Read and Ip6 instance.
+  //
+  Status = PxeBcCheckIpv6Support (ControllerHandle, Private, &Private->Mode.Ipv6Available);
+  if (EFI_ERROR (Status)) {
+    //
+    // Fail to get the data whether UNDI supports IPv6. Set default value.
+    //
+    Private->Mode.Ipv6Available   = TRUE;
+  }
+
+  if (!Private->Mode.Ipv6Available) {
+    goto ON_ERROR;
+  }
+
+  Udp6CfgData                     = &Private->Udp6CfgData;
+  Ip6CfgData                      = &Private->Ip6CfgData;
+
+  Udp6CfgData->AcceptAnyPort      = TRUE;
+  Udp6CfgData->AllowDuplicatePort = TRUE;
+  Udp6CfgData->HopLimit           = PXEBC_DEFAULT_HOPLIMIT;
+  Udp6CfgData->ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;
+  Udp6CfgData->TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;
+
+  Ip6CfgData->AcceptIcmpErrors    = TRUE;
+  Ip6CfgData->DefaultProtocol     = IP6_ICMP;
+  Ip6CfgData->HopLimit            = PXEBC_DEFAULT_HOPLIMIT;
+  Ip6CfgData->ReceiveTimeout      = PXEBC_DEFAULT_LIFETIME;
+  Ip6CfgData->TransmitTimeout     = PXEBC_DEFAULT_LIFETIME;
+
+  return EFI_SUCCESS;
+
+ON_ERROR:
+  PxeBcDestroyIp6Children (This, Private);
+  return Status;
+}
+
+
+/**
+  The entry point for UefiPxeBc driver that installs the driver
+  binding and component name protocol on its image.
+
+  @param[in]  ImageHandle          The Image handle of the driver.
+  @param[in]  SystemTable          The system table.
+
+  @return EFI_SUCCESS
+  @return Others
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcDriverEntryPoint (
+  IN EFI_HANDLE             ImageHandle,
+  IN EFI_SYSTEM_TABLE       *SystemTable
+  )
+{
+  EFI_STATUS  Status;
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gPxeBcIp4DriverBinding,
+             ImageHandle,
+             &gPxeBcComponentName,
+             &gPxeBcComponentName2
+             );
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  Status = EfiLibInstallDriverBindingComponentName2 (
+             ImageHandle,
+             SystemTable,
+             &gPxeBcIp6DriverBinding,
+             NULL,
+             &gPxeBcComponentName,
+             &gPxeBcComponentName2
+             );
+  if (EFI_ERROR (Status)) {
+    gBS->UninstallMultipleProtocolInterfaces (
+           ImageHandle,
+           &gEfiDriverBindingProtocolGuid,
+           &gPxeBcIp4DriverBinding,
+           &gEfiComponentName2ProtocolGuid,
+           &gPxeBcComponentName2,
+           &gEfiComponentNameProtocolGuid,
+           &gPxeBcComponentName,
+           NULL
+           );
+  }
+
+  return Status;
+}
+
+/**
+  Test to see if this driver supports ControllerHandle. This is the worker function for
+  PxeBcIp4(6)DriverBindingSupported.
+
+  @param[in]  This                The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle    The handle of device to be tested.
+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child
+                                  device to be started.
+  @param[in]  IpVersion           IP_VERSION_4 or IP_VERSION_6.
+  
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval EFI_UNSUPPORTED     This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL,
+  IN UINT8                        IpVersion
+  )
+{
+  EFI_STATUS                      Status;
+  EFI_GUID                        *DhcpServiceBindingGuid;
+  EFI_GUID                        *MtftpServiceBindingGuid;
+  
+  if (IpVersion == IP_VERSION_4) {
+    DhcpServiceBindingGuid  = &gEfiDhcp4ServiceBindingProtocolGuid;
+    MtftpServiceBindingGuid = &gEfiMtftp4ServiceBindingProtocolGuid;
+  } else {
+    DhcpServiceBindingGuid  = &gEfiDhcp6ServiceBindingProtocolGuid;
+    MtftpServiceBindingGuid = &gEfiMtftp6ServiceBindingProtocolGuid;
+  }
+
+  //
+  // Try to open the Mtftp and Dhcp protocol to test whether IP stack is ready.
+  //
+  Status = gBS->OpenProtocol (
+                     ControllerHandle,
+                     DhcpServiceBindingGuid,
+                     NULL,
+                     This->DriverBindingHandle,
+                     ControllerHandle,
+                     EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                     );
+  if (!EFI_ERROR (Status)) {
+    Status = gBS->OpenProtocol (
+                       ControllerHandle,
+                       MtftpServiceBindingGuid,
+                       NULL,
+                       This->DriverBindingHandle,
+                       ControllerHandle,
+                       EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+                       );
+  }
+
+  //
+  // It's unsupported case if IP stack are not ready.
+  //
+  if (EFI_ERROR (Status)) {
+    return EFI_UNSUPPORTED;
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Start this driver on ControllerHandle. This is the worker function for
+  PxeBcIp4(6)DriverBindingStart.
+
+  @param[in]  This                 The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle     The handle of device to be started.
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child
+                                   device to be started.
+  @param[in]  IpVersion            IP_VERSION_4 or IP_VERSION_6.
+
+
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL,
+  IN UINT8                        IpVersion
+  )
+{
+  PXEBC_PRIVATE_DATA              *Private;
+  EFI_STATUS                      Status;
+  PXEBC_PRIVATE_PROTOCOL          *Id;
+  BOOLEAN                         FirstStart;
+
+  FirstStart = FALSE;
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiCallerIdGuid,
+                  (VOID **) &Id,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (!EFI_ERROR (Status)) {
+    //
+    // Skip the initialization if the driver has been started already.
+    //
+    Private = PXEBC_PRIVATE_DATA_FROM_ID (Id);
+  } else {
+    FirstStart = TRUE;
+    //
+    // If the driver has not been started yet, it should do initialization.
+    //
+    Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));
+    if (Private == NULL) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+
+    CopyMem (
+      &Private->PxeBc,
+      &gPxeBcProtocolTemplate,
+      sizeof (EFI_PXE_BASE_CODE_PROTOCOL)
+      );
+
+    Private->Signature          = PXEBC_PRIVATE_DATA_SIGNATURE;
+    Private->Controller         = ControllerHandle;
+    Private->Image              = This->ImageHandle;
+    Private->PxeBc.Mode         = &Private->Mode;
+    Private->Mode.Ipv6Supported = TRUE;
+    Private->Mode.AutoArp       = TRUE;
+    Private->Mode.TTL           = DEFAULT_TTL;
+    Private->Mode.ToS           = DEFAULT_ToS;
+
+    //
+    // Open device path to prepare for appending virtual NIC node.
+    //
+    Status = gBS->OpenProtocol (
+                    ControllerHandle,
+                    &gEfiDevicePathProtocolGuid,
+                    (VOID **) &Private->DevicePath,
+                    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;
+    }
+
+    //
+    // Install PxeBaseCodePrivate protocol onto the real NIC handler.
+    // PxeBaseCodePrivate protocol is only used to keep the relationship between 
+    // NIC handle and virtual child handles.
+    // gEfiCallerIdGuid will be used as its protocol guid.
+    //
+    Status = gBS->InstallProtocolInterface (
+                    &ControllerHandle,
+                    &gEfiCallerIdGuid,
+                    EFI_NATIVE_INTERFACE,
+                    &Private->Id
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    // Try to locate SNP protocol.
+    //
+    NetLibGetSnpHandle(ControllerHandle, &Private->Snp);    
+  }
+
+  if (IpVersion == IP_VERSION_4) {
+    //
+    // Try to create virtual NIC handle for IPv4.
+    //
+    Status = PxeBcCreateIp4Children (This, ControllerHandle, Private);
+  } else {
+    //
+    // Try to create virtual NIC handle for IPv6.
+    //
+    Status = PxeBcCreateIp6Children (This, ControllerHandle, Private);
+  }
+  if (EFI_ERROR (Status)) {
+    //
+    // Failed to start PXE driver if IPv4 and IPv6 stack are both not available.
+    //
+    Status = EFI_DEVICE_ERROR;
+    goto ON_ERROR;
+  }
+
+  return EFI_SUCCESS;
+
+ON_ERROR:
+  if (FirstStart) {
+    gBS->UninstallProtocolInterface (
+           ControllerHandle,
+           &gEfiCallerIdGuid,
+           &Private->Id
+           );
+  }
+
+  if (IpVersion == IP_VERSION_4) {
+    PxeBcDestroyIp4Children (This, Private);
+  } else {
+    PxeBcDestroyIp6Children (This, Private);
+  }
+
+  if (FirstStart && Private != NULL) {
+    FreePool (Private);
+  }
+
+  return Status;
+}
+
+
+/**
+  Stop this driver on ControllerHandle. This is the worker function for
+  PxeBcIp4(6)DriverBindingStop.
+
+  @param[in]  This              Protocol instance pointer.
+  @param[in]  ControllerHandle  Handle of device to stop driver on.
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                                children is zero stop the entire bus driver.
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.
+  @param[in]  IpVersion         IP_VERSION_4 or IP_VERSION_6.
+
+  @retval EFI_SUCCESS           This driver was removed ControllerHandle.
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
+  @retval Others                This driver was not removed from this device
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer,
+  IN UINT8                        IpVersion
+  )
+{
+  PXEBC_PRIVATE_DATA              *Private;
+  PXEBC_VIRTUAL_NIC               *VirtualNic;
+  EFI_LOAD_FILE_PROTOCOL          *LoadFile;
+  EFI_STATUS                      Status;
+  EFI_HANDLE                      NicHandle;
+  PXEBC_PRIVATE_PROTOCOL          *Id;
+
+  Private    = NULL;
+  NicHandle  = NULL;
+  VirtualNic = NULL;
+  LoadFile   = NULL;
+  Id         = NULL;
+
+  Status = gBS->OpenProtocol (
+                  ControllerHandle,
+                  &gEfiLoadFileProtocolGuid,
+                  (VOID **) &LoadFile,
+                  This->DriverBindingHandle,
+                  ControllerHandle,
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    //
+    // Get the Nic handle by any pass-over service child handle.
+    //
+    if (IpVersion == IP_VERSION_4) {
+      NicHandle = PxeBcGetNicByIp4Children (ControllerHandle);
+    } else {
+      NicHandle = PxeBcGetNicByIp6Children (ControllerHandle);
+    }
+    if (NicHandle == NULL) {
+      return EFI_SUCCESS;
+    }
+
+    //
+    // Try to retrieve the private data by PxeBcPrivate protocol.
+    //
+    Status = gBS->OpenProtocol (
+                    NicHandle,
+                    &gEfiCallerIdGuid,
+                    (VOID **) &Id,
+                    This->DriverBindingHandle,
+                    ControllerHandle,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    Private = PXEBC_PRIVATE_DATA_FROM_ID (Id);
+
+  } else {
+    //
+    // It's a virtual handle with LoadFileProtocol.
+    //
+    Status = gBS->OpenProtocol (
+                    ControllerHandle,
+                    &gEfiLoadFileProtocolGuid,
+                    (VOID **) &LoadFile,
+                    This->DriverBindingHandle,
+                    ControllerHandle,
+                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                    );
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (LoadFile);
+    Private    = VirtualNic->Private;
+    NicHandle  = Private->Controller;
+  }
+
+  //
+  // Stop functionality of PXE Base Code protocol
+  //
+  Status = Private->PxeBc.Stop (&Private->PxeBc);
+  if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) {
+    return Status;
+  }
+
+
+  if (Private->Ip4Nic != NULL && IpVersion == IP_VERSION_4) {
+    PxeBcDestroyIp4Children (This, Private);
+  }
+
+  if (Private->Ip6Nic != NULL && IpVersion == IP_VERSION_6) {
+    PxeBcDestroyIp6Children (This, Private);
+  }
+
+  if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) {
+    gBS->UninstallProtocolInterface (
+           NicHandle,
+           &gEfiCallerIdGuid,
+           &Private->Id
+           );
+    FreePool (Private);
+  }
+
+  return EFI_SUCCESS;
+}
+
+/**
+  Test to see if this driver supports ControllerHandle. This service
+  is called by the EFI boot service ConnectController(). In
+  order to make drivers as small as possible, there are a few calling
+  restrictions for this service. ConnectController() must
+  follow these calling restrictions. If any other agent wishes to call
+  Supported() it must also follow these calling restrictions.
+
+  @param[in]  This                The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle    The handle of device to be tested.
+  @param[in]  RemainingDevicePath Optional parameter used to pick a specific child
+                                  device to be started.
+
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval EFI_UNSUPPORTED     This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp4DriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  )
+{
+  return PxeBcSupported (
+           This,
+           ControllerHandle,
+           RemainingDevicePath,
+           IP_VERSION_4
+           );
+}
+
+/**
+  Start this driver on ControllerHandle. This service is called by the
+  EFI boot service ConnectController(). In order to make
+  drivers as small as possible, there are a few calling restrictions for
+  this service. ConnectController() must follow these
+  calling restrictions. If any other agent wishes to call Start() it
+  must also follow these calling restrictions.
+
+  @param[in]  This                 The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle     The handle of device to be started.
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child
+                                   device to be started.
+
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp4DriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  )
+{
+  return PxeBcStart (
+           This,
+           ControllerHandle,
+           RemainingDevicePath,
+           IP_VERSION_4
+           );
+}
+
+/**
+  Stop this driver on ControllerHandle. This service is called by the
+  EFI boot service DisconnectController(). In order to
+  make drivers as small as possible, there are a few calling
+  restrictions for this service. DisconnectController()
+  must follow these calling restrictions. If any other agent wishes
+  to call Stop() it must also follow these calling restrictions.
+
+  @param[in]  This              Protocol instance pointer.
+  @param[in]  ControllerHandle  Handle of device to stop driver on
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                                children is zero stop the entire bus driver.
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS           This driver is removed ControllerHandle
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
+  @retval Others                This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp4DriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer
+  )
+{
+  return PxeBcStop (
+           This,
+           ControllerHandle,
+           NumberOfChildren,
+           ChildHandleBuffer,
+           IP_VERSION_4
+           );
+}
+
+/**
+  Test to see if this driver supports ControllerHandle. This service
+  is called by the EFI boot service ConnectController(). In
+  order to make drivers as small as possible, there are a few calling
+  restrictions for this service. ConnectController() must
+  follow these calling restrictions. If any other agent wishes to call
+  Supported() it must also follow these calling restrictions.
+
+  @param[in]  This                The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle    The handle of device to be tested.
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child
+                                  device to be started.
+
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval EFI_UNSUPPORTED     This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp6DriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  )
+{
+  return PxeBcSupported (
+           This,
+           ControllerHandle,
+           RemainingDevicePath,
+           IP_VERSION_6
+           );
+}
+
+/**
+  Start this driver on ControllerHandle. This service is called by the
+  EFI boot service ConnectController(). In order to make
+  drivers as small as possible, there are a few calling restrictions for
+  this service. ConnectController() must follow these
+  calling restrictions. If any other agent wishes to call Start() it
+  must also follow these calling restrictions.
+
+  @param[in]  This                 The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle     The handle of device to be started.
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child
+                                   device to be started.
+
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp6DriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  )
+{
+  return PxeBcStart (
+           This,
+           ControllerHandle,
+           RemainingDevicePath,
+           IP_VERSION_6
+           );
+}
+
+/**
+  Stop this driver on ControllerHandle. This service is called by the
+  EFI boot service DisconnectController(). In order to
+  make drivers as small as possible, there are a few calling
+  restrictions for this service. DisconnectController()
+  must follow these calling restrictions. If any other agent wishes
+  to call Stop() it must also follow these calling restrictions.
+
+  @param[in]  This              Protocol instance pointer.
+  @param[in]  ControllerHandle  Handle of device to stop driver on
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                                children is zero stop the entire bus driver.
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS           This driver is removed ControllerHandle
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
+  @retval Others                This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp6DriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer
+  )
+{
+  return PxeBcStop (
+           This,
+           ControllerHandle,
+           NumberOfChildren,
+           ChildHandleBuffer,
+           IP_VERSION_6
+           );
+}
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
new file mode 100644
index 0000000..5d040b7
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcDriver.h
@@ -0,0 +1,181 @@
+/** @file
+  Driver Binding functions declaration for UefiPxeBc Driver.
+
+  Copyright (c) 2007 - 2013, 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.
+
+**/
+
+#ifndef __EFI_PXEBC_DRIVER_H__
+#define __EFI_PXEBC_DRIVER_H__
+
+extern EFI_COMPONENT_NAME_PROTOCOL  gPxeBcComponentName;
+extern EFI_COMPONENT_NAME2_PROTOCOL gPxeBcComponentName2;
+
+/**
+  Test to see if this driver supports ControllerHandle. This service
+  is called by the EFI boot service ConnectController(). In
+  order to make drivers as small as possible, there are a few calling
+  restrictions for this service. ConnectController() must
+  follow these calling restrictions. If any other agent wishes to call
+  Supported() it must also follow these calling restrictions.
+
+  @param[in]  This                The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle    The handle of device to be tested.
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child
+                                  device to be started.
+
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval EFI_UNSUPPORTED     This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp4DriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  );
+
+/**
+  Start this driver on ControllerHandle. This service is called by the
+  EFI boot service ConnectController(). In order to make
+  drivers as small as possible, there are a few calling restrictions for
+  this service. ConnectController() must follow these
+  calling restrictions. If any other agent wishes to call Start() it
+  must also follow these calling restrictions.
+
+  @param[in]  This                 The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle     The handle of device to be started.
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child
+                                   device to be started.
+
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp4DriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  );
+
+
+/**
+  Stop this driver on ControllerHandle. This service is called by the
+  EFI boot service DisconnectController(). In order to
+  make drivers as small as possible, there are a few calling
+  restrictions for this service. DisconnectController()
+  must follow these calling restrictions. If any other agent wishes
+  to call Stop() it must also follow these calling restrictions.
+
+  @param[in]  This              Protocol instance pointer.
+  @param[in]  ControllerHandle  Handle of device to stop driver on
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                                children is zero stop the entire bus driver.
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS           This driver is removed ControllerHandle
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
+  @retval Others                This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp4DriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer
+  );
+
+/**
+  Test to see if this driver supports ControllerHandle. This service
+  is called by the EFI boot service ConnectController(). In
+  order to make drivers as small as possible, there are a few calling
+  restrictions for this service. ConnectController() must
+  follow these calling restrictions. If any other agent wishes to call
+  Supported() it must also follow these calling restrictions.
+
+  @param[in]  This                The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle    The handle of device to be tested.
+  @param[in]  RemainingDevicePath Optional parameter use to pick a specific child
+                                  device to be started.
+
+  @retval EFI_SUCCESS         This driver supports this device.
+  @retval EFI_UNSUPPORTED     This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp6DriverBindingSupported (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  );
+
+/**
+  Start this driver on ControllerHandle. This service is called by the
+  EFI boot service ConnectController(). In order to make
+  drivers as small as possible, there are a few calling restrictions for
+  this service. ConnectController() must follow these
+  calling restrictions. If any other agent wishes to call Start() it
+  must also follow these calling restrictions.
+
+  @param[in]  This                 The pointer to the driver binding protocol.
+  @param[in]  ControllerHandle     The handle of device to be started.
+  @param[in]  RemainingDevicePath  Optional parameter used to pick a specific child
+                                   device to be started.
+
+  @retval EFI_SUCCESS          This driver is installed to ControllerHandle.
+  @retval EFI_ALREADY_STARTED  This driver is already running on ControllerHandle.
+  @retval other                This driver does not support this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp6DriverBindingStart (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath OPTIONAL
+  );
+
+/**
+  Stop this driver on ControllerHandle. This service is called by the
+  EFI boot service DisconnectController(). In order to
+  make drivers as small as possible, there are a few calling
+  restrictions for this service. DisconnectController()
+  must follow these calling restrictions. If any other agent wishes
+  to call Stop() it must also follow these calling restrictions.
+
+  @param[in]  This              Protocol instance pointer.
+  @param[in]  ControllerHandle  Handle of device to stop driver on
+  @param[in]  NumberOfChildren  Number of Handles in ChildHandleBuffer. If number of
+                                children is zero stop the entire bus driver.
+  @param[in]  ChildHandleBuffer List of Child Handles to Stop.
+
+  @retval EFI_SUCCESS           This driver is removed ControllerHandle
+  @retval EFI_DEVICE_ERROR      An unexpected system or network error occurred.
+  @retval Others                This driver was not removed from this device.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcIp6DriverBindingStop (
+  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
+  IN EFI_HANDLE                   ControllerHandle,
+  IN UINTN                        NumberOfChildren,
+  IN EFI_HANDLE                   *ChildHandleBuffer
+  );
+#endif
+
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
new file mode 100644
index 0000000..bda4d16
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.c
@@ -0,0 +1,2411 @@
+/** @file
+  This implementation of EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.
+
+  Copyright (c) 2007 - 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+  Enables the use of the PXE Base Code Protocol functions.
+
+  This function enables the use of the PXE Base Code Protocol functions. If the
+  Started field of the EFI_PXE_BASE_CODE_MODE structure is already TRUE, then
+  EFI_ALREADY_STARTED will be returned. If UseIpv6 is TRUE, then IPv6 formatted
+  addresses will be used in this session. If UseIpv6 is FALSE, then IPv4 formatted
+  addresses will be used in this session. If UseIpv6 is TRUE, and the Ipv6Supported
+  field of the EFI_PXE_BASE_CODE_MODE structure is FALSE, then EFI_UNSUPPORTED will
+  be returned. If there is not enough memory or other resources to start the PXE
+  Base Code Protocol, then EFI_OUT_OF_RESOURCES will be returned. Otherwise, the
+  PXE Base Code Protocol will be started.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  UseIpv6           Specifies the type of IP addresses that are to be
+                                used during the session that is being started.
+                                Set to TRUE for IPv6, and FALSE for IPv4.
+
+  @retval EFI_SUCCESS           The PXE Base Code Protocol was started.
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
+  @retval EFI_UNSUPPORTED       UseIpv6 is TRUE, but the Ipv6Supported field of the
+                                EFI_PXE_BASE_CODE_MODE structure is FALSE.
+  @retval EFI_ALREADY_STARTED   The PXE Base Code Protocol is already in the started state.
+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+                                EFI_PXE_BASE_CODE_PROTOCOL structure.
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory or other resources to start the
+                                PXE Base Code Protocol.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcStart (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN BOOLEAN                          UseIpv6
+  )
+{
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  UINTN                   Index;
+  EFI_STATUS              Status;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode    = Private->PxeBc.Mode;
+
+  if (Mode->Started) {
+    return EFI_ALREADY_STARTED;
+  }
+
+  //
+  // Detect whether using IPv6 or not, and set it into mode data.
+  //
+  if (UseIpv6 && Mode->Ipv6Available && Mode->Ipv6Supported && Private->Ip6Nic != NULL) {
+    Mode->UsingIpv6 = TRUE;
+  } else if (!UseIpv6 && Private->Ip4Nic != NULL) {
+    Mode->UsingIpv6 = FALSE;
+  } else {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (Mode->UsingIpv6) {
+    AsciiPrint ("\n>>Start PXE over IPv6");
+    //
+    // Configure udp6 instance to receive data.
+    //
+    Status = Private->Udp6Read->Configure (
+                                  Private->Udp6Read,
+                                  &Private->Udp6CfgData
+                                  );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+    
+    //
+    // Configure block size for TFTP as a default value to handle all link layers.
+    //
+    Private->BlockSize = (UINTN) (Private->Ip6MaxPacketSize -
+                           PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);
+
+    //
+    // PXE over IPv6 starts here, initialize the fields and list header.
+    //
+    Private->Ip6Policy                          = PXEBC_IP6_POLICY_MAX;
+    Private->ProxyOffer.Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
+    Private->DhcpAck.Dhcp6.Packet.Ack.Size      = PXEBC_DHCP6_PACKET_MAX_SIZE;
+    Private->PxeReply.Dhcp6.Packet.Ack.Size     = PXEBC_DHCP6_PACKET_MAX_SIZE;
+
+    for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
+      Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = PXEBC_DHCP6_PACKET_MAX_SIZE;
+    }
+
+    //
+    // Create event and set status for token to capture ICMP6 error message.
+    //
+    Private->Icmp6Token.Status = EFI_NOT_READY;
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    PxeBcIcmp6ErrorUpdate,
+                    Private,
+                    &Private->Icmp6Token.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    // Set Ip6 policy to Automatic to start the IP6 router discovery.
+    //
+    Status = PxeBcSetIp6Policy (Private);
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+  } else {
+    AsciiPrint ("\n>>Start PXE over IPv4");
+    //
+    // Configure udp4 instance to receive data.
+    //
+    Status = Private->Udp4Read->Configure (
+                                  Private->Udp4Read,
+                                  &Private->Udp4CfgData
+                                  );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+    
+    //
+    // Configure block size for TFTP as a default value to handle all link layers.
+    //
+    Private->BlockSize = (UINTN) (Private->Ip4MaxPacketSize -
+                           PXEBC_DEFAULT_UDP_OVERHEAD_SIZE - PXEBC_DEFAULT_TFTP_OVERHEAD_SIZE);
+
+    //
+    // PXE over IPv4 starts here, initialize the fields.
+    //
+    Private->ProxyOffer.Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
+    Private->DhcpAck.Dhcp4.Packet.Ack.Size      = PXEBC_DHCP4_PACKET_MAX_SIZE;
+    Private->PxeReply.Dhcp4.Packet.Ack.Size     = PXEBC_DHCP4_PACKET_MAX_SIZE;
+
+    for (Index = 0; Index < PXEBC_OFFER_MAX_NUM; Index++) {
+      Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = PXEBC_DHCP4_PACKET_MAX_SIZE;
+    }
+
+    PxeBcSeedDhcp4Packet (&Private->SeedPacket, Private->Udp4Read);
+
+    //
+    // Create the event for Arp cache update.
+    //
+    Status = gBS->CreateEvent (
+                    EVT_TIMER | EVT_NOTIFY_SIGNAL,
+                    TPL_CALLBACK,
+                    PxeBcArpCacheUpdate,
+                    Private,
+                    &Private->ArpUpdateEvent
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    // Start a periodic timer by second to update Arp cache.
+    //
+    Status = gBS->SetTimer (
+                    Private->ArpUpdateEvent,
+                    TimerPeriodic,
+                    TICKS_PER_SECOND
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    // Create event and set status for token to capture ICMP error message.
+    //
+    Private->Icmp6Token.Status = EFI_NOT_READY;
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    PxeBcIcmpErrorUpdate,
+                    Private,
+                    &Private->IcmpToken.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+
+    //
+    //DHCP4 service allows only one of its children to be configured in  
+    //the active state, If the DHCP4 D.O.R.A started by IP4 auto  
+    //configuration and has not been completed, the Dhcp4 state machine 
+    //will not be in the right state for the PXE to start a new round D.O.R.A. 
+    //so we need to switch it's policy to static.
+    //
+    Status = PxeBcSetIp4Policy (Private);
+    if (EFI_ERROR (Status)) {
+      goto ON_ERROR;
+    }
+  }
+
+  //
+  // If PcdTftpBlockSize is set to non-zero, override the default value.
+  //
+  if (PcdGet64 (PcdTftpBlockSize) != 0) {
+    Private->BlockSize   = (UINTN) PcdGet64 (PcdTftpBlockSize);
+  }
+
+  //
+  // Create event for UdpRead/UdpWrite timeout since they are both blocking API.
+  //
+  Status = gBS->CreateEvent (
+                  EVT_TIMER,
+                  TPL_CALLBACK,
+                  NULL,
+                  NULL,
+                  &Private->UdpTimeOutEvent
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  Private->IsAddressOk = FALSE;
+  Mode->Started        = TRUE;
+
+  return EFI_SUCCESS;
+
+ON_ERROR:
+  if (Mode->UsingIpv6) {
+    if (Private->Icmp6Token.Event != NULL) {
+      gBS->CloseEvent (Private->Icmp6Token.Event);
+      Private->Icmp6Token.Event = NULL;
+    }
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+    Private->Ip6->Configure (Private->Ip6, NULL);
+  } else {
+    if (Private->ArpUpdateEvent != NULL) {
+      gBS->CloseEvent (Private->ArpUpdateEvent);
+      Private->ArpUpdateEvent = NULL;
+    }
+    if (Private->IcmpToken.Event != NULL) {
+      gBS->CloseEvent (Private->IcmpToken.Event);
+      Private->IcmpToken.Event = NULL;
+    }
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+    Private->Ip4->Configure (Private->Ip4, NULL);
+  }
+  return Status;
+}
+
+
+/**
+  Disable the use of the PXE Base Code Protocol functions.
+
+  This function stops all activity on the network device. All the resources allocated
+  in Start() are released, the Started field of the EFI_PXE_BASE_CODE_MODE structure is
+  set to FALSE, and EFI_SUCCESS is returned. If the Started field of the EFI_PXE_BASE_CODE_MODE
+  structure is already FALSE, then EFI_NOT_STARTED will be returned.
+
+  @param[in]  This               Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+
+  @retval EFI_SUCCESS           The PXE Base Code Protocol was stopped.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is already in the stopped state.
+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+                                EFI_PXE_BASE_CODE_PROTOCOL structure.
+  @retval Others
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcStop (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This
+  )
+{
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  BOOLEAN                 Ipv6Supported;
+  BOOLEAN                 Ipv6Available;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode          = Private->PxeBc.Mode;
+  Ipv6Supported = Mode->Ipv6Supported;
+  Ipv6Available = Mode->Ipv6Available;
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (Mode->UsingIpv6) {
+    //
+    // Configure all the instances for IPv6 as NULL.
+    //
+    ZeroMem (&Private->Udp6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));
+    ZeroMem (&Private->Ip6CfgData.StationAddress, sizeof (EFI_IPv6_ADDRESS));
+    Private->Dhcp6->Stop (Private->Dhcp6);
+    Private->Dhcp6->Configure (Private->Dhcp6, NULL);
+    Private->Udp6Write->Configure (Private->Udp6Write, NULL);
+    Private->Udp6Read->Groups (Private->Udp6Read, FALSE, NULL);
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+    Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);
+    Private->Ip6->Configure (Private->Ip6, NULL);
+    PxeBcUnregisterIp6Address (Private);
+    if (Private->Icmp6Token.Event != NULL) {
+      gBS->CloseEvent (Private->Icmp6Token.Event);
+      Private->Icmp6Token.Event = NULL;
+    }
+    if (Private->Dhcp6Request != NULL) {
+      FreePool (Private->Dhcp6Request);
+      Private->Dhcp6Request = NULL;
+    }
+    if (Private->BootFileName != NULL) {
+      FreePool (Private->BootFileName);
+      Private->BootFileName = NULL;
+    }
+  } else {
+    //
+    // Configure all the instances for IPv4 as NULL.
+    //
+    ZeroMem (&Private->Udp4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+    ZeroMem (&Private->Udp4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+    ZeroMem (&Private->Ip4CfgData.StationAddress, sizeof (EFI_IPv4_ADDRESS));
+    ZeroMem (&Private->Ip4CfgData.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+    Private->Dhcp4->Stop (Private->Dhcp4);
+    Private->Dhcp4->Configure (Private->Dhcp4, NULL);
+    Private->Udp4Write->Configure (Private->Udp4Write, NULL);
+    Private->Udp4Read->Groups (Private->Udp4Read, FALSE, NULL);
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+    Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);
+    Private->Ip4->Configure (Private->Ip4, NULL);
+    if (Private->ArpUpdateEvent != NULL) {
+      gBS->CloseEvent (Private->ArpUpdateEvent);
+      Private->ArpUpdateEvent = NULL;
+    }
+    if (Private->IcmpToken.Event != NULL) {
+      gBS->CloseEvent (Private->IcmpToken.Event);
+      Private->IcmpToken.Event = NULL;
+    }
+    Private->BootFileName = NULL;
+  }
+
+  gBS->CloseEvent (Private->UdpTimeOutEvent);
+  Private->CurSrcPort   = 0;
+  Private->BootFileSize = 0;
+  Private->SolicitTimes = 0;
+  Private->ElapsedTime  = 0;
+  ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
+  ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
+  ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
+  ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+
+  //
+  // Reset the mode data.
+  //
+  ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));
+  Mode->Ipv6Available = Ipv6Available;
+  Mode->Ipv6Supported = Ipv6Supported;
+  Mode->AutoArp       = TRUE;
+  Mode->TTL           = DEFAULT_TTL;
+  Mode->ToS           = DEFAULT_ToS;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Attempts to complete a DHCPv4 D.O.R.A. (discover / offer / request / acknowledge) or DHCPv6
+  S.A.R.R (solicit / advertise / request / reply) sequence.
+
+  If SortOffers is TRUE, then the cached DHCP offer packets will be sorted before
+  they are tried. If SortOffers is FALSE, then the cached DHCP offer packets will
+  be tried in the order in which they are received. Please see the Preboot Execution
+  Environment (PXE) Specification and Unified Extensible Firmware Interface (UEFI)
+  Specification for additional details on the implementation of DHCP.
+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+  then the DHCP sequence will be stopped and EFI_ABORTED will be returned.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  SortOffers        TRUE if the offers received should be sorted. Set to FALSE to
+                                try the offers in the order that they are received.
+
+  @retval EFI_SUCCESS           Valid DHCP has completed.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER The This parameter is NULL or does not point to a valid
+                                EFI_PXE_BASE_CODE_PROTOCOL structure.
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete the DHCP Protocol.
+  @retval EFI_ABORTED           The callback function aborted the DHCP Protocol.
+  @retval EFI_TIMEOUT           The DHCP Protocol timed out.
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the DHCP session.
+  @retval EFI_NO_RESPONSE       Valid PXE offer was not received.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcDhcp (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN BOOLEAN                          SortOffers
+  )
+{
+  PXEBC_PRIVATE_DATA           *Private;
+  EFI_PXE_BASE_CODE_MODE       *Mode;
+  EFI_STATUS                   Status;
+  EFI_PXE_BASE_CODE_IP_FILTER  IpFilter;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Status                  = EFI_SUCCESS;
+  Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode                    = Private->PxeBc.Mode;
+  Mode->IcmpErrorReceived = FALSE;
+  Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DHCP;
+  Private->IsOfferSorted  = SortOffers;
+  Private->SolicitTimes   = 0;
+  Private->ElapsedTime    = 0;
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (Mode->UsingIpv6) {
+
+    //
+    // Stop Udp6Read instance
+    //
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+
+    //
+    // Start S.A.R.R. process to get a IPv6 address and other boot information.
+    //
+    Status = PxeBcDhcp6Sarr (Private, Private->Dhcp6);
+  } else {
+
+    //
+    // Stop Udp4Read instance
+    //
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+
+    //
+    // Start D.O.R.A. process to get a IPv4 address and other boot information.
+    //
+    Status = PxeBcDhcp4Dora (Private, Private->Dhcp4);
+  }
+
+  //
+  // Reconfigure the UDP instance with the default configuration.
+  //
+  if (Mode->UsingIpv6) {
+    Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
+  } else {
+    Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
+  }
+  //
+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+  //
+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+  This->SetIpFilter (This, &IpFilter);
+
+  return Status;
+}
+
+
+/**
+  Attempts to complete the PXE Boot Server and/or boot image discovery sequence.
+
+  This function attempts to complete the PXE Boot Server and/or boot image discovery
+  sequence. If this sequence is completed, then EFI_SUCCESS is returned, and the
+  PxeDiscoverValid, PxeDiscover, PxeReplyReceived, and PxeReply fields of the
+  EFI_PXE_BASE_CODE_MODE structure are filled in. If UseBis is TRUE, then the
+  PxeBisReplyReceived and PxeBisReply fields of the EFI_PXE_BASE_CODE_MODE structure
+  will also be filled in. If UseBis is FALSE, then PxeBisReplyValid will be set to FALSE.
+  In the structure referenced by parameter Info, the PXE Boot Server list, SrvList[],
+  has two uses: It is the Boot Server IP address list used for unicast discovery
+  (if the UseUCast field is TRUE), and it is the list used for Boot Server verification
+  (if the MustUseList field is TRUE). Also, if the MustUseList field in that structure
+  is TRUE and the AcceptAnyResponse field in the SrvList[] array is TRUE, any Boot
+  Server reply of that type will be accepted. If the AcceptAnyResponse field is
+  FALSE, only responses from Boot Servers with matching IP addresses will be accepted.
+  This function can take at least 10 seconds to timeout and return control to the
+  caller. If the Discovery sequence does not complete, then EFI_TIMEOUT will be
+  returned. Please see the Preboot Execution Environment (PXE) Specification for
+  additional details on the implementation of the Discovery sequence.
+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+  then the Discovery sequence is stopped and EFI_ABORTED will be returned.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  Type              The type of bootstrap to perform.
+  @param[in]  Layer             Pointer to the boot server layer number to discover, which must be
+                                PXE_BOOT_LAYER_INITIAL when a new server type is being
+                                discovered.
+  @param[in]  UseBis            TRUE if Boot Integrity Services are to be used. FALSE otherwise.
+  @param[in]  Info              Pointer to a data structure that contains additional information
+                                on the type of discovery operation that is to be performed.
+                                It is optional.
+
+  @retval EFI_SUCCESS           The Discovery sequence has been completed.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
+  @retval EFI_OUT_OF_RESOURCES  Could not allocate enough memory to complete Discovery.
+  @retval EFI_ABORTED           The callback function aborted the Discovery sequence.
+  @retval EFI_TIMEOUT           The Discovery sequence timed out.
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the PXE discovery
+                                session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcDiscover (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN UINT16                           Type,
+  IN UINT16                           *Layer,
+  IN BOOLEAN                          UseBis,
+  IN EFI_PXE_BASE_CODE_DISCOVER_INFO  *Info   OPTIONAL
+  )
+{
+  PXEBC_PRIVATE_DATA              *Private;
+  EFI_PXE_BASE_CODE_MODE          *Mode;
+  EFI_PXE_BASE_CODE_DISCOVER_INFO DefaultInfo;
+  EFI_PXE_BASE_CODE_SRVLIST       *SrvList;
+  PXEBC_BOOT_SVR_ENTRY            *BootSvrEntry;
+  UINT16                          Index;
+  EFI_STATUS                      Status;
+  EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;
+  EFI_PXE_BASE_CODE_DISCOVER_INFO *NewCreatedInfo;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private                 = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode                    = Private->PxeBc.Mode;
+  Mode->IcmpErrorReceived = FALSE;
+  BootSvrEntry            = NULL;
+  SrvList                 = NULL;
+  Status                  = EFI_DEVICE_ERROR;
+  Private->Function       = EFI_PXE_BASE_CODE_FUNCTION_DISCOVER;
+  NewCreatedInfo          = NULL;
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  //
+  // Station address should be ready before do discover.
+  //
+  if (!Private->IsAddressOk) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Mode->UsingIpv6) {
+
+    //
+    // Stop Udp6Read instance
+    //
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+  } else {
+
+    //
+    // Stop Udp4Read instance
+    //
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+  }
+
+  //
+  // There are 3 methods to get the information for discover.
+  //
+  ZeroMem (&DefaultInfo, sizeof (EFI_PXE_BASE_CODE_DISCOVER_INFO));
+  if (*Layer != EFI_PXE_BASE_CODE_BOOT_LAYER_INITIAL) {
+    //
+    // 1. Take the previous setting as the discover info.
+    //
+    if (!Mode->PxeDiscoverValid ||
+        !Mode->PxeReplyReceived ||
+        (!Mode->PxeBisReplyReceived && UseBis)) {
+      Status = EFI_INVALID_PARAMETER;
+      goto ON_EXIT;
+    }
+
+    Info                         = &DefaultInfo;
+    Info->IpCnt                  = 1;
+    Info->UseUCast               = TRUE;
+    SrvList                      = Info->SrvList;
+    SrvList[0].Type              = Type;
+    SrvList[0].AcceptAnyResponse = FALSE;
+
+    CopyMem (&SrvList->IpAddr, &Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+
+  } else if (Info == NULL) {
+    //
+    // 2. Extract the discover information from the cached packets if unspecified.
+    //
+    NewCreatedInfo = &DefaultInfo;
+    Status = PxeBcExtractDiscoverInfo (Private, Type, &NewCreatedInfo, &BootSvrEntry, &SrvList);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+    ASSERT (NewCreatedInfo != NULL);
+    Info = NewCreatedInfo;
+  } else {
+    //
+    // 3. Take the pass-in information as the discover info, and validate the server list.
+    //
+    SrvList = Info->SrvList;
+
+    if (!SrvList[0].AcceptAnyResponse) {
+      for (Index = 1; Index < Info->IpCnt; Index++) {
+        if (SrvList[Index].AcceptAnyResponse) {
+          break;
+        }
+      }
+      if (Index != Info->IpCnt) {
+        //
+        // It's invalid if the first server doesn't accecpt any response
+        // but any of the other servers does accept any response.
+        //
+        Status = EFI_INVALID_PARAMETER;
+        goto ON_EXIT;
+      }
+    }
+  }
+
+  //
+  // Info and BootSvrEntry/SrvList are all ready by now, so execute discover by UniCast/BroadCast/MultiCast.
+  //
+  if ((!Info->UseUCast && !Info->UseBCast && !Info->UseMCast) ||
+      (Info->MustUseList && Info->IpCnt == 0)) {
+    Status = EFI_INVALID_PARAMETER;
+    goto ON_EXIT;
+  }
+
+  Private->IsDoDiscover = TRUE;
+
+  if (Info->UseMCast) {
+    //
+    // Do discover by multicast.
+    //
+    Status = PxeBcDiscoverBootServer (
+               Private,
+               Type,
+               Layer,
+               UseBis,
+               &Info->ServerMCastIp,
+               Info->IpCnt,
+               SrvList
+               );
+
+  } else if (Info->UseBCast) {
+    //
+    // Do discover by broadcast, but only valid for IPv4.
+    //
+    ASSERT (!Mode->UsingIpv6);
+    Status = PxeBcDiscoverBootServer (
+               Private,
+               Type,
+               Layer,
+               UseBis,
+               NULL,
+               Info->IpCnt,
+               SrvList
+               );
+
+  } else if (Info->UseUCast) {
+    //
+    // Do discover by unicast.
+    //
+    for (Index = 0; Index < Info->IpCnt; Index++) {
+      if (BootSvrEntry == NULL) {
+        CopyMem (&Private->ServerIp, &SrvList[Index].IpAddr, sizeof (EFI_IP_ADDRESS));
+      } else {
+        ASSERT (!Mode->UsingIpv6);
+        ZeroMem (&Private->ServerIp, sizeof (EFI_IP_ADDRESS));
+        CopyMem (&Private->ServerIp, &BootSvrEntry->IpAddr[Index], sizeof (EFI_IPv4_ADDRESS));
+      }
+
+      Status = PxeBcDiscoverBootServer (
+                 Private,
+                 Type,
+                 Layer,
+                 UseBis,
+                 &Private->ServerIp,
+                 Info->IpCnt,
+                 SrvList
+                 );
+      }
+  }
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // Parse the cached PXE reply packet, and store it into mode data if valid.
+    //
+    if (Mode->UsingIpv6) {
+      Status = PxeBcParseDhcp6Packet (&Private->PxeReply.Dhcp6);
+      if (!EFI_ERROR (Status)) {
+        CopyMem (
+          &Mode->PxeReply.Dhcpv6,
+          &Private->PxeReply.Dhcp6.Packet.Ack.Dhcp6,
+          Private->PxeReply.Dhcp6.Packet.Ack.Length
+          );
+        Mode->PxeReplyReceived = TRUE;
+        Mode->PxeDiscoverValid = TRUE;
+      }
+    } else {
+      Status = PxeBcParseDhcp4Packet (&Private->PxeReply.Dhcp4);
+      if (!EFI_ERROR (Status)) {
+        CopyMem (
+          &Mode->PxeReply.Dhcpv4,
+          &Private->PxeReply.Dhcp4.Packet.Ack.Dhcp4,
+          Private->PxeReply.Dhcp4.Packet.Ack.Length
+          );
+        Mode->PxeReplyReceived = TRUE;
+        Mode->PxeDiscoverValid = TRUE;
+      }
+    }
+  }
+
+ON_EXIT:
+
+  if (NewCreatedInfo != NULL && NewCreatedInfo != &DefaultInfo) {
+    FreePool (NewCreatedInfo);
+  }
+  
+  if (Mode->UsingIpv6) {
+    Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
+  } else {
+    Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
+  }
+  
+  //
+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+  //
+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+  This->SetIpFilter (This, &IpFilter);
+
+  return Status;
+}
+
+
+/**
+  Used to perform TFTP and MTFTP services.
+
+  This function is used to perform TFTP and MTFTP services. This includes the
+  TFTP operations to get the size of a file, read a directory, read a file, and
+  write a file. It also includes the MTFTP operations to get the size of a file,
+  read a directory, and read a file. The type of operation is specified by Operation.
+  If the callback function that is invoked during the TFTP/MTFTP operation does
+  not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will
+  be returned.
+  For read operations, the return data will be placed in the buffer specified by
+  BufferPtr. If BufferSize is too small to contain the entire downloaded file,
+  then EFI_BUFFER_TOO_SMALL will be returned and BufferSize will be set to zero,
+  or the size of the requested file. (NOTE: the size of the requested file is only returned
+  if the TFTP server supports TFTP options). If BufferSize is large enough for the
+  read operation, then BufferSize will be set to the size of the downloaded file,
+  and EFI_SUCCESS will be returned. Applications using the PxeBc.Mtftp() services
+  should use the get-file-size operations to determine the size of the downloaded
+  file prior to using the read-file operations-especially when downloading large
+  (greater than 64 MB) files-instead of making two calls to the read-file operation.
+  Following this recommendation will save time if the file is larger than expected
+  and the TFTP server does not support TFTP option extensions. Without TFTP option
+  extension support, the client must download the entire file, counting and discarding
+  the received packets, to determine the file size.
+  For write operations, the data to be sent is in the buffer specified by BufferPtr.
+  BufferSize specifies the number of bytes to send. If the write operation completes
+  successfully, then EFI_SUCCESS will be returned.
+  For TFTP "get file size" operations, the size of the requested file or directory
+  is returned in BufferSize, and EFI_SUCCESS will be returned. If the TFTP server
+  does not support options, the file will be downloaded into a bit bucket and the
+  length of the downloaded file will be returned. For MTFTP "get file size" operations,
+  if the MTFTP server does not support the "get file size" option, EFI_UNSUPPORTED
+  will be returned.
+  This function can take up to 10 seconds to timeout and return control to the caller.
+  If the TFTP sequence does not complete, EFI_TIMEOUT will be returned.
+  If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+  then the TFTP sequence is stopped and EFI_ABORTED will be returned.
+
+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]      Operation     The type of operation to perform.
+  @param[in, out] BufferPtr     A pointer to the data buffer.
+  @param[in]      Overwrite     Only used on write file operations. TRUE if a file on a remote
+                                server can be overwritten.
+  @param[in, out] BufferSize    For get-file-size operations, *BufferSize returns the size of the
+                                requested file.
+  @param[in]      BlockSize     The requested block size to be used during a TFTP transfer.
+  @param[in]      ServerIp      The TFTP / MTFTP server IP address.
+  @param[in]      Filename      A Null-terminated ASCII string that specifies a directory name
+                                or a file name.
+  @param[in]      Info          Pointer to the MTFTP information.
+  @param[in]      DontUseBuffer Set to FALSE for normal TFTP and MTFTP read file operation.
+
+  @retval EFI_SUCCESS           The TFTP/MTFTP operation was completed.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
+  @retval EFI_BUFFER_TOO_SMALL  The buffer is not large enough to complete the read operation.
+  @retval EFI_ABORTED           The callback function aborted the TFTP/MTFTP operation.
+  @retval EFI_TIMEOUT           The TFTP/MTFTP operation timed out.
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the MTFTP session.
+  @retval EFI_TFTP_ERROR        A TFTP error packet was received during the MTFTP session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcMtftp (
+  IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN     EFI_PXE_BASE_CODE_TFTP_OPCODE    Operation,
+  IN OUT VOID                             *BufferPtr    OPTIONAL,
+  IN     BOOLEAN                          Overwrite,
+  IN OUT UINT64                           *BufferSize,
+  IN     UINTN                            *BlockSize    OPTIONAL,
+  IN     EFI_IP_ADDRESS                   *ServerIp,
+  IN     UINT8                            *Filename,
+  IN     EFI_PXE_BASE_CODE_MTFTP_INFO     *Info         OPTIONAL,
+  IN     BOOLEAN                          DontUseBuffer
+  )
+{
+  PXEBC_PRIVATE_DATA              *Private;
+  EFI_PXE_BASE_CODE_MODE          *Mode;
+  EFI_MTFTP4_CONFIG_DATA          Mtftp4Config;
+  EFI_MTFTP6_CONFIG_DATA          Mtftp6Config;
+  VOID                            *Config;
+  EFI_STATUS                      Status;
+  EFI_PXE_BASE_CODE_IP_FILTER     IpFilter;
+
+
+  if ((This == NULL) ||
+      (Filename == NULL) ||
+      (BufferSize == NULL) ||
+      (ServerIp == NULL) ||
+      ((BufferPtr == NULL) && DontUseBuffer) ||
+      ((BlockSize != NULL) && (*BlockSize < PXE_MTFTP_DEFAULT_BLOCK_SIZE)) ||
+      (!NetIp4IsUnicast (NTOHL (ServerIp->Addr[0]), 0) && !NetIp6IsValidUnicast (&ServerIp->v6))) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Config    = NULL;
+  Status    = EFI_DEVICE_ERROR;
+  Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode      = Private->PxeBc.Mode;
+
+  if (Mode->UsingIpv6) {
+    //
+    // Set configuration data for Mtftp6 instance.
+    //
+    ZeroMem (&Mtftp6Config, sizeof (EFI_MTFTP6_CONFIG_DATA));
+    Config                         = &Mtftp6Config;
+    Mtftp6Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;
+    Mtftp6Config.TryCount          = PXEBC_MTFTP_RETRIES;
+    CopyMem (&Mtftp6Config.StationIp, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
+    CopyMem (&Mtftp6Config.ServerIp, &ServerIp->v6, sizeof (EFI_IPv6_ADDRESS));
+    //
+    // Stop Udp6Read instance
+    //
+    Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+  } else {
+    //
+    // Set configuration data for Mtftp4 instance.
+    //
+    ZeroMem (&Mtftp4Config, sizeof (EFI_MTFTP4_CONFIG_DATA));
+    Config                         = &Mtftp4Config;
+    Mtftp4Config.UseDefaultSetting = FALSE;
+    Mtftp4Config.TimeoutValue      = PXEBC_MTFTP_TIMEOUT;
+    Mtftp4Config.TryCount          = PXEBC_MTFTP_RETRIES;
+    CopyMem (&Mtftp4Config.StationIp, &Private->StationIp.v4, sizeof (EFI_IPv4_ADDRESS));
+    CopyMem (&Mtftp4Config.SubnetMask, &Private->SubnetMask.v4, sizeof (EFI_IPv4_ADDRESS));
+    CopyMem (&Mtftp4Config.GatewayIp, &Private->GatewayIp.v4, sizeof (EFI_IPv4_ADDRESS));
+    CopyMem (&Mtftp4Config.ServerIp, &ServerIp->v4, sizeof (EFI_IPv4_ADDRESS));
+    //
+    // Stop Udp4Read instance
+    //
+    Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+  }
+
+  Mode->TftpErrorReceived = FALSE;
+  Mode->IcmpErrorReceived = FALSE;
+
+  switch (Operation) {
+
+  case EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE:
+    //
+    // Send TFTP request to get file size.
+    //
+    Status = PxeBcTftpGetFileSize (
+               Private,
+               Config,
+               Filename,
+               BlockSize,
+               BufferSize
+               );
+
+    break;
+
+  case EFI_PXE_BASE_CODE_TFTP_READ_FILE:
+    //
+    // Send TFTP request to read file.
+    //
+    Status = PxeBcTftpReadFile (
+               Private,
+               Config,
+               Filename,
+               BlockSize,
+               BufferPtr,
+               BufferSize,
+               DontUseBuffer
+               );
+
+    break;
+
+  case EFI_PXE_BASE_CODE_TFTP_WRITE_FILE:
+    //
+    // Send TFTP request to write file.
+    //
+    Status = PxeBcTftpWriteFile (
+               Private,
+               Config,
+               Filename,
+               Overwrite,
+               BlockSize,
+               BufferPtr,
+               BufferSize
+               );
+
+    break;
+
+  case EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY:
+    //
+    // Send TFTP request to read directory.
+    //
+    Status = PxeBcTftpReadDirectory (
+               Private,
+               Config,
+               Filename,
+               BlockSize,
+               BufferPtr,
+               BufferSize,
+               DontUseBuffer
+               );
+
+    break;
+
+  case EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE:
+  case EFI_PXE_BASE_CODE_MTFTP_READ_FILE:
+  case EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY:
+    Status = EFI_UNSUPPORTED;
+
+    break;
+
+  default:
+    Status = EFI_INVALID_PARAMETER;
+
+    break;
+  }
+
+  if (Status == EFI_ICMP_ERROR) {
+    Mode->IcmpErrorReceived = TRUE;
+  }
+
+  //
+  // Reconfigure the UDP instance with the default configuration.
+  //
+  if (Mode->UsingIpv6) {
+    Private->Udp6Read->Configure (Private->Udp6Read, &Private->Udp6CfgData);
+  } else {
+    Private->Udp4Read->Configure (Private->Udp4Read, &Private->Udp4CfgData);
+  }
+  //
+  // Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP
+  // receive filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+  //
+  ZeroMem(&IpFilter, sizeof (EFI_PXE_BASE_CODE_IP_FILTER));
+  IpFilter.Filters = EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP;
+  This->SetIpFilter (This, &IpFilter);
+
+  return Status;
+}
+
+
+/**
+  Writes a UDP packet to the network interface.
+
+  This function writes a UDP packet specified by the (optional HeaderPtr and)
+  BufferPtr parameters to the network interface. The UDP header is automatically
+  built by this routine. It uses the parameters OpFlags, DestIp, DestPort, GatewayIp,
+  SrcIp, and SrcPort to build this header. If the packet is successfully built and
+  transmitted through the network interface, then EFI_SUCCESS will be returned.
+  If a timeout occurs during the transmission of the packet, then EFI_TIMEOUT will
+  be returned. If an ICMP error occurs during the transmission of the packet, then
+  the IcmpErrorReceived field is set to TRUE, the IcmpError field is filled in and
+  EFI_ICMP_ERROR will be returned. If the Callback Protocol does not return
+  EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE, then EFI_ABORTED will be returned.
+
+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]      OpFlags       The UDP operation flags.
+  @param[in]      DestIp        The destination IP address.
+  @param[in]      DestPort      The destination UDP port number.
+  @param[in]      GatewayIp     The gateway IP address.
+  @param[in]      SrcIp         The source IP address.
+  @param[in, out] SrcPort       The source UDP port number.
+  @param[in]      HeaderSize    An optional field which may be set to the length of a header
+                                at HeaderPtr to be prefixed to the data at BufferPtr.
+  @param[in]  HeaderPtr         If HeaderSize is not NULL, a pointer to a header to be
+                                prefixed to the data at BufferPtr.
+  @param[in]  BufferSize        A pointer to the size of the data at BufferPtr.
+  @param[in]  BufferPtr         A pointer to the data to be written.
+
+  @retval EFI_SUCCESS           The UDP Write operation completed.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_BAD_BUFFER_SIZE   The buffer is too long to be transmitted.
+  @retval EFI_ABORTED           The callback function aborted the UDP Write operation.
+  @retval EFI_TIMEOUT           The UDP Write operation timed out.
+  @retval EFI_ICMP_ERROR        An ICMP error packet was received during the UDP write session.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcUdpWrite (
+  IN     EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN     UINT16                           OpFlags,
+  IN     EFI_IP_ADDRESS                   *DestIp,
+  IN     EFI_PXE_BASE_CODE_UDP_PORT       *DestPort,
+  IN     EFI_IP_ADDRESS                   *GatewayIp  OPTIONAL,
+  IN     EFI_IP_ADDRESS                   *SrcIp      OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT       *SrcPort    OPTIONAL,
+  IN     UINTN                            *HeaderSize OPTIONAL,
+  IN     VOID                             *HeaderPtr  OPTIONAL,
+  IN     UINTN                            *BufferSize,
+  IN     VOID                             *BufferPtr
+  )
+{
+  PXEBC_PRIVATE_DATA        *Private;
+  EFI_PXE_BASE_CODE_MODE    *Mode;
+  EFI_UDP4_SESSION_DATA     Udp4Session;
+  EFI_UDP6_SESSION_DATA     Udp6Session;
+  EFI_STATUS                Status;
+  BOOLEAN                   DoNotFragment;
+
+  if (This == NULL || DestIp == NULL || DestPort == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode    = Private->PxeBc.Mode;
+
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_MAY_FRAGMENT) != 0) {
+    DoNotFragment = FALSE;
+  } else {
+    DoNotFragment = TRUE;
+  }
+
+  if (!Mode->UsingIpv6 && GatewayIp != NULL && !NetIp4IsUnicast (NTOHL (GatewayIp->Addr[0]), 0)) {
+    //
+    // Gateway is provided but it's not a unicast IPv4 address, while it will be ignored for IPv6.
+    //
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (HeaderSize != NULL && (*HeaderSize == 0 || HeaderPtr == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (BufferSize == NULL || (*BufferSize != 0 && BufferPtr == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (!Private->IsAddressOk && SrcIp == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (Private->CurSrcPort == 0 ||
+      (SrcPort != NULL && *SrcPort != Private->CurSrcPort)) {
+    //
+    // Reconfigure UDPv4/UDPv6 for UdpWrite if the source port changed.
+    //
+    if (SrcPort != NULL) {
+      Private->CurSrcPort = *SrcPort;
+    }
+  }
+
+  if (Mode->UsingIpv6) {
+    Status = PxeBcConfigUdp6Write (
+               Private->Udp6Write,
+               &Private->StationIp.v6,
+               &Private->CurSrcPort
+               );
+  } else {
+    //
+    // Configure the UDPv4 instance with gateway information from DHCP server as default.
+    //
+    Status = PxeBcConfigUdp4Write (
+               Private->Udp4Write,
+               &Private->StationIp.v4,
+               &Private->SubnetMask.v4,
+               &Private->GatewayIp.v4,
+               &Private->CurSrcPort,
+               DoNotFragment
+               );
+  }
+
+  if (EFI_ERROR (Status)) {
+    Private->CurSrcPort = 0;
+    return EFI_INVALID_PARAMETER;
+  } else if (SrcPort != NULL) {
+    *SrcPort = Private->CurSrcPort;
+  }
+
+  //
+  // Start a timer as timeout event for this blocking API.
+  //
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);
+
+  if (Mode->UsingIpv6) {
+    //
+    // Construct UDPv6 session data.
+    //
+    ZeroMem (&Udp6Session, sizeof (EFI_UDP6_SESSION_DATA));
+    CopyMem (&Udp6Session.DestinationAddress, DestIp, sizeof (EFI_IPv6_ADDRESS));
+    Udp6Session.DestinationPort = *DestPort;
+    if (SrcIp != NULL) {
+      CopyMem (&Udp6Session.SourceAddress, SrcIp, sizeof (EFI_IPv6_ADDRESS));
+    }
+    if (SrcPort != NULL) {
+      Udp6Session.SourcePort = *SrcPort;
+    }
+
+    Status = PxeBcUdp6Write (
+               Private->Udp6Write,
+               &Udp6Session,
+               Private->UdpTimeOutEvent,
+               HeaderSize,
+               HeaderPtr,
+               BufferSize,
+               BufferPtr
+               );
+  } else {
+    //
+    // Construct UDPv4 session data.
+    //
+    ZeroMem (&Udp4Session, sizeof (EFI_UDP4_SESSION_DATA));
+    CopyMem (&Udp4Session.DestinationAddress, DestIp, sizeof (EFI_IPv4_ADDRESS));
+    Udp4Session.DestinationPort = *DestPort;
+    if (SrcIp != NULL) {
+      CopyMem (&Udp4Session.SourceAddress, SrcIp, sizeof (EFI_IPv4_ADDRESS));
+    }
+    if (SrcPort != NULL) {
+      Udp4Session.SourcePort = *SrcPort;
+    }
+    //
+    // Override the gateway information if user specified.
+    //
+    Status = PxeBcUdp4Write (
+               Private->Udp4Write,
+               &Udp4Session,
+               Private->UdpTimeOutEvent,
+               (EFI_IPv4_ADDRESS *) GatewayIp,
+               HeaderSize,
+               HeaderPtr,
+               BufferSize,
+               BufferPtr
+               );
+  }
+
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);
+
+
+  //
+  // Reset the UdpWrite instance.
+  //
+  if (Mode->UsingIpv6) {
+    Private->Udp6Write->Configure (Private->Udp6Write, NULL);
+  } else {
+    Private->Udp4Write->Configure (Private->Udp4Write, NULL);
+  }
+
+  return Status;
+}
+
+
+/**
+  Reads a UDP packet from the network interface.
++
+  This function reads a UDP packet from a network interface. The data contents
+  are returned in (the optional HeaderPtr and) BufferPtr, and the size of the
+  buffer received is returned in BufferSize . If the input BufferSize is smaller
+  than the UDP packet received (less optional HeaderSize), it will be set to the
+  required size, and EFI_BUFFER_TOO_SMALL will be returned. In this case, the
+  contents of BufferPtr are undefined, and the packet is lost. If a UDP packet is
+  successfully received, then EFI_SUCCESS will be returned, and the information
+  from the UDP header will be returned in DestIp, DestPort, SrcIp, and SrcPort if
+  they are not NULL. Depending on the values of OpFlags and the DestIp, DestPort,
+  SrcIp, and SrcPort input values, different types of UDP packet receive filtering
+  will be performed. The following tables summarize these receive filter operations.
+
+  @param[in]      This          Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]      OpFlags       The UDP operation flags.
+  @param[in, out] DestIp        The destination IP address.
+  @param[in, out] DestPort      The destination UDP port number.
+  @param[in, out] SrcIp         The source IP address.
+  @param[in, out] SrcPort       The source UDP port number.
+  @param[in]      HeaderSize    An optional field which may be set to the length of a
+                                header at HeaderPtr to be prefixed to the data at BufferPtr.
+  @param[in]      HeaderPtr     If HeaderSize is not NULL, a pointer to a header to be
+                                prefixed to the data at BufferPtr.
+  @param[in, out] BufferSize    A pointer to the size of the data at BufferPtr.
+  @param[in]      BufferPtr     A pointer to the data to be read.
+
+  @retval EFI_SUCCESS           The UDP Read operation was completed.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
+  @retval EFI_BUFFER_TOO_SMALL  The packet is larger than Buffer can hold.
+  @retval EFI_ABORTED           The callback function aborted the UDP Read operation.
+  @retval EFI_TIMEOUT           The UDP Read operation timed out.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcUdpRead (
+  IN     EFI_PXE_BASE_CODE_PROTOCOL   *This,
+  IN     UINT16                       OpFlags,
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL,
+  IN     UINTN                        *HeaderSize  OPTIONAL,
+  IN     VOID                         *HeaderPtr   OPTIONAL,
+  IN OUT UINTN                        *BufferSize,
+  IN     VOID                         *BufferPtr
+  )
+{
+  PXEBC_PRIVATE_DATA          *Private;
+  EFI_PXE_BASE_CODE_MODE      *Mode;
+  EFI_UDP4_COMPLETION_TOKEN   Udp4Token;
+  EFI_UDP6_COMPLETION_TOKEN   Udp6Token;
+  EFI_UDP4_RECEIVE_DATA       *Udp4Rx;
+  EFI_UDP6_RECEIVE_DATA       *Udp6Rx;
+  EFI_STATUS                  Status;
+  BOOLEAN                     IsDone;
+  BOOLEAN                     IsMatched;
+  UINTN                       CopiedLen;
+  UINTN                       HeaderLen;
+  UINTN                       HeaderCopiedLen;
+  UINTN                       BufferCopiedLen;
+  UINT32                      FragmentLength;
+  UINTN                       FragmentIndex;
+  UINT8                       *FragmentBuffer;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private   = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode      = Private->PxeBc.Mode;
+  IsDone    = FALSE;
+  IsMatched = FALSE;
+  Udp4Rx    = NULL;
+  Udp6Rx    = NULL;
+
+  if (((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) == 0 && DestPort == NULL) ||
+      ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) == 0 && SrcIp == NULL) ||
+      ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) == 0 && SrcPort == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((HeaderSize != NULL && *HeaderSize == 0) || (HeaderSize != NULL && HeaderPtr == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if ((BufferSize == NULL) || (BufferPtr == NULL)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  ZeroMem (&Udp6Token, sizeof (EFI_UDP6_COMPLETION_TOKEN));
+  ZeroMem (&Udp4Token, sizeof (EFI_UDP4_COMPLETION_TOKEN));
+
+  if (Mode->UsingIpv6) {
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    PxeBcCommonNotify,
+                    &IsDone,
+                    &Udp6Token.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  } else {
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    PxeBcCommonNotify,
+                    &IsDone,
+                    &Udp4Token.Event
+                    );
+    if (EFI_ERROR (Status)) {
+      return EFI_OUT_OF_RESOURCES;
+    }
+  }
+
+  //
+  // Start a timer as timeout event for this blocking API.
+  //
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerRelative, PXEBC_UDP_TIMEOUT);
+  Mode->IcmpErrorReceived = FALSE;
+
+  //
+  // Read packet by Udp4Read/Udp6Read until matched or timeout.
+  //
+  while (!IsMatched && !EFI_ERROR (Status)) {
+    if (Mode->UsingIpv6) {
+      Status = PxeBcUdp6Read (
+                 Private->Udp6Read,
+                 &Udp6Token,
+                 Mode,
+                 Private->UdpTimeOutEvent,
+                 OpFlags,
+                 &IsDone,
+                 &IsMatched,
+                 DestIp,
+                 DestPort,
+                 SrcIp,
+                 SrcPort
+                 );
+    } else {
+      Status = PxeBcUdp4Read (
+                 Private->Udp4Read,
+                 &Udp4Token,
+                 Mode,
+                 Private->UdpTimeOutEvent,
+                 OpFlags,
+                 &IsDone,
+                 &IsMatched,
+                 DestIp,
+                 DestPort,
+                 SrcIp,
+                 SrcPort
+                 );
+    }
+  }
+
+  if (Status == EFI_ICMP_ERROR ||
+      Status == EFI_NETWORK_UNREACHABLE ||
+      Status == EFI_HOST_UNREACHABLE ||
+      Status == EFI_PROTOCOL_UNREACHABLE ||
+      Status == EFI_PORT_UNREACHABLE) {
+    //
+    // Get different return status for icmp error from Udp, refers to UEFI spec.
+    //
+    Mode->IcmpErrorReceived = TRUE;
+  }
+  gBS->SetTimer (Private->UdpTimeOutEvent, TimerCancel, 0);
+
+  if (IsMatched) {
+    //
+    // Copy the rececived packet to user if matched by filter.
+    //
+    if (Mode->UsingIpv6) {
+      Udp6Rx = Udp6Token.Packet.RxData;
+      ASSERT (Udp6Rx != NULL);
+
+      HeaderLen = 0;
+      if (HeaderSize != NULL) {
+        HeaderLen = MIN (*HeaderSize, Udp6Rx->DataLength);
+      }
+
+      if (Udp6Rx->DataLength - HeaderLen > *BufferSize) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      } else {
+        if (HeaderSize != NULL) {
+          *HeaderSize = HeaderLen;
+        }
+        *BufferSize = Udp6Rx->DataLength - HeaderLen;
+
+        HeaderCopiedLen = 0;
+        BufferCopiedLen = 0;
+        for (FragmentIndex = 0; FragmentIndex < Udp6Rx->FragmentCount; FragmentIndex++) {
+          FragmentLength = Udp6Rx->FragmentTable[FragmentIndex].FragmentLength;
+          FragmentBuffer = Udp6Rx->FragmentTable[FragmentIndex].FragmentBuffer;
+          if (HeaderCopiedLen + FragmentLength < HeaderLen) {
+            //
+            // Copy the header part of received data.
+            //
+            CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength);
+            HeaderCopiedLen += FragmentLength;
+          } else if (HeaderCopiedLen < HeaderLen) {
+            //
+            // Copy the header part of received data.
+            //
+            CopiedLen = HeaderLen - HeaderCopiedLen;
+            CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen);
+            HeaderCopiedLen += CopiedLen;
+
+            //
+            // Copy the other part of received data.
+            //
+            CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen);
+            BufferCopiedLen += (FragmentLength - CopiedLen);
+          } else {
+            //
+            // Copy the other part of received data.
+            //
+            CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength);
+            BufferCopiedLen += FragmentLength;
+          }
+        }
+      }
+      //
+      // Recycle the receiving buffer after copy to user.
+      //
+      gBS->SignalEvent (Udp6Rx->RecycleSignal);
+    } else {
+      Udp4Rx = Udp4Token.Packet.RxData;
+      ASSERT (Udp4Rx != NULL);
+
+      HeaderLen = 0;
+      if (HeaderSize != NULL) {
+        HeaderLen = MIN (*HeaderSize, Udp4Rx->DataLength);
+      }
+
+      if (Udp4Rx->DataLength - HeaderLen > *BufferSize) {
+        Status = EFI_BUFFER_TOO_SMALL;
+      } else {
+        if (HeaderSize != NULL) {
+          *HeaderSize = HeaderLen;
+        }
+        *BufferSize = Udp4Rx->DataLength - HeaderLen;
+
+        HeaderCopiedLen = 0;
+        BufferCopiedLen = 0;
+        for (FragmentIndex = 0; FragmentIndex < Udp4Rx->FragmentCount; FragmentIndex++) {
+          FragmentLength = Udp4Rx->FragmentTable[FragmentIndex].FragmentLength;
+          FragmentBuffer = Udp4Rx->FragmentTable[FragmentIndex].FragmentBuffer;
+          if (HeaderCopiedLen + FragmentLength < HeaderLen) {
+            //
+            // Copy the header part of received data.
+            //
+            CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, FragmentLength);
+            HeaderCopiedLen += FragmentLength;
+          } else if (HeaderCopiedLen < HeaderLen) {
+            //
+            // Copy the header part of received data.
+            //
+            CopiedLen = HeaderLen - HeaderCopiedLen;
+            CopyMem ((UINT8 *) HeaderPtr + HeaderCopiedLen, FragmentBuffer, CopiedLen);
+            HeaderCopiedLen += CopiedLen;
+
+            //
+            // Copy the other part of received data.
+            //
+            CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer + CopiedLen, FragmentLength - CopiedLen);
+            BufferCopiedLen += (FragmentLength - CopiedLen);
+          } else {
+            //
+            // Copy the other part of received data.
+            //
+            CopyMem ((UINT8 *) BufferPtr + BufferCopiedLen, FragmentBuffer, FragmentLength);
+            BufferCopiedLen += FragmentLength;
+          }
+        }
+      }
+      //
+      // Recycle the receiving buffer after copy to user.
+      //
+      gBS->SignalEvent (Udp4Rx->RecycleSignal);
+    }
+  }
+
+  if (Mode->UsingIpv6) {
+    Private->Udp6Read->Cancel (Private->Udp6Read, &Udp6Token);
+    gBS->CloseEvent (Udp6Token.Event);
+  } else {
+    Private->Udp4Read->Cancel (Private->Udp4Read, &Udp4Token);
+    gBS->CloseEvent (Udp4Token.Event);
+  }
+
+  return Status;
+}
+
+
+/**
+  Updates the IP receive filters of a network device and enables software filtering.
+
+  The NewFilter field is used to modify the network device's current IP receive
+  filter settings and to enable a software filter. This function updates the IpFilter
+  field of the EFI_PXE_BASE_CODE_MODE structure with the contents of NewIpFilter.
+  The software filter is used when the USE_FILTER in OpFlags is set to UdpRead().
+  The current hardware filter remains in effect no matter what the settings of OpFlags.
+  This is so that the meaning of ANY_DEST_IP set in OpFlags to UdpRead() is from those
+  packets whose reception is enabled in hardware-physical NIC address (unicast),
+  broadcast address, logical address or addresses (multicast), or all (promiscuous).
+  UdpRead() does not modify the IP filter settings.
+  Dhcp(), Discover(), and Mtftp() set the IP filter, and return with the IP receive
+  filter list emptied and the filter set to EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP.
+  If an application or driver wishes to preserve the IP receive filter settings,
+  it will have to preserve the IP receive filter settings before these calls, and
+  use SetIpFilter() to restore them after the calls. If incompatible filtering is
+  requested (for example, PROMISCUOUS with anything else), or if the device does not
+  support a requested filter setting and it cannot be accommodated in software
+  (for example, PROMISCUOUS not supported), EFI_INVALID_PARAMETER will be returned.
+  The IPlist field is used to enable IPs other than the StationIP. They may be
+  multicast or unicast. If IPcnt is set as well as EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP,
+  then both the StationIP and the IPs from the IPlist will be used.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  NewFilter         Pointer to the new set of IP receive filters.
+
+  @retval EFI_SUCCESS           The IP receive filter settings were updated.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetIpFilter (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN EFI_PXE_BASE_CODE_IP_FILTER      *NewFilter
+  )
+{
+  EFI_STATUS                Status;
+  PXEBC_PRIVATE_DATA        *Private;
+  EFI_PXE_BASE_CODE_MODE    *Mode;
+  EFI_UDP4_CONFIG_DATA      *Udp4Cfg;
+  EFI_UDP6_CONFIG_DATA      *Udp6Cfg;
+  UINTN                     Index;
+  BOOLEAN                   NeedPromiscuous;
+  BOOLEAN                   AcceptPromiscuous;
+  BOOLEAN                   AcceptBroadcast;
+  BOOLEAN                   MultiCastUpdate;
+
+  if (This == NULL || NewFilter == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private         = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode            = Private->PxeBc.Mode;
+  Status          = EFI_SUCCESS;
+  NeedPromiscuous = FALSE;
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  for (Index = 0; Index < NewFilter->IpCnt; Index++) {
+    ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);
+    if (!Mode->UsingIpv6 &&
+        IP4_IS_LOCAL_BROADCAST (EFI_IP4 (NewFilter->IpList[Index].v4))) {
+      //
+      // IPv4 broadcast address should not be in IP filter.
+      //
+      return EFI_INVALID_PARAMETER;
+    }
+    if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&
+        (NetIp4IsUnicast (EFI_IP4 (NewFilter->IpList[Index].v4), 0) ||
+         NetIp6IsValidUnicast (&NewFilter->IpList[Index].v6))) {
+      //
+      // If EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP is set and IPv4/IPv6 address
+      // is in IpList, promiscuous mode is needed.
+      //
+      NeedPromiscuous = TRUE;
+    }
+  }
+
+  AcceptPromiscuous = FALSE;
+  AcceptBroadcast   = FALSE;
+  MultiCastUpdate   = FALSE;
+
+  if (NeedPromiscuous ||
+      (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0 ||
+      (NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0) {
+    //
+    // Configure UDPv4/UDPv6 as promiscuous mode to receive all packets.
+    //
+    AcceptPromiscuous = TRUE;
+  } else if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0) {
+    //
+    // Configure UDPv4 to receive all broadcast packets.
+    //
+    AcceptBroadcast  = TRUE;
+  }
+
+  //
+  // In multicast condition when Promiscuous FALSE and IpCnt no-zero.
+  // Here check if there is any update of the multicast ip address. If yes,
+  // we need leave the old multicast group (by Config UDP instance to NULL),
+  // and join the new multicast group.
+  //
+  if (!AcceptPromiscuous) {
+    if ((NewFilter->Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0) {
+      if (Mode->IpFilter.IpCnt != NewFilter->IpCnt) {
+        MultiCastUpdate = TRUE;
+      } else if (CompareMem (Mode->IpFilter.IpList, NewFilter->IpList, NewFilter->IpCnt * sizeof (EFI_IP_ADDRESS)) != 0 ) {
+        MultiCastUpdate = TRUE;
+      }
+    }
+  }
+
+  if (!Mode->UsingIpv6) {
+    //
+    // Check whether we need reconfigure the UDP4 instance.
+    //
+    Udp4Cfg = &Private->Udp4CfgData;
+    if ((AcceptPromiscuous != Udp4Cfg->AcceptPromiscuous)   ||
+    	  (AcceptBroadcast != Udp4Cfg->AcceptBroadcast)     || MultiCastUpdate) {
+      //
+      // Clear the UDP4 instance configuration, all joined groups will be left
+      // during the operation.
+      //
+      Private->Udp4Read->Configure (Private->Udp4Read, NULL);
+  
+      //
+      // Configure the UDP instance with the new configuration.
+      //
+      Udp4Cfg->AcceptPromiscuous = AcceptPromiscuous;
+      Udp4Cfg->AcceptBroadcast   = AcceptBroadcast;
+      Status = Private->Udp4Read->Configure (Private->Udp4Read, Udp4Cfg);
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+  
+      //
+      // In not Promiscuous mode, need to join the new multicast group.
+      //
+      if (!AcceptPromiscuous) {
+        for (Index = 0; Index < NewFilter->IpCnt; ++Index) {
+          if (IP4_IS_MULTICAST (EFI_NTOHL (NewFilter->IpList[Index].v4))) {
+            //
+            // Join the mutilcast group.
+            //
+            Status = Private->Udp4Read->Groups (Private->Udp4Read, TRUE, &NewFilter->IpList[Index].v4);
+            if (EFI_ERROR (Status)) {
+              return Status;
+            }
+          }
+        }
+      }
+    }
+  } else {
+    //
+    // Check whether we need reconfigure the UDP6 instance.
+    //
+    Udp6Cfg = &Private->Udp6CfgData;
+    if ((AcceptPromiscuous != Udp6Cfg->AcceptPromiscuous) || MultiCastUpdate) {
+      //
+      // Clear the UDP6 instance configuration, all joined groups will be left
+      // during the operation.
+      //
+      Private->Udp6Read->Configure (Private->Udp6Read, NULL);
+  
+      //
+      // Configure the UDP instance with the new configuration.
+      //
+      Udp6Cfg->AcceptPromiscuous = AcceptPromiscuous;
+      Status = Private->Udp6Read->Configure (Private->Udp6Read, Udp6Cfg);
+      if (EFI_ERROR (Status)) {
+        return Status;
+      }
+  
+      //
+      // In not Promiscuous mode, need to join the new multicast group.
+      //
+      if (!AcceptPromiscuous) {
+        for (Index = 0; Index < NewFilter->IpCnt; ++Index) {
+          if (IP6_IS_MULTICAST (&NewFilter->IpList[Index].v6)) {
+            //
+            // Join the mutilcast group.
+            //
+            Status = Private->Udp6Read->Groups (Private->Udp6Read, TRUE, &NewFilter->IpList[Index].v6);
+            if (EFI_ERROR (Status)) {
+              return Status;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  //
+  // Save the new IP filter into mode data.
+  //
+  CopyMem (&Mode->IpFilter, NewFilter, sizeof (Mode->IpFilter));
+
+  return Status;
+}
+
+
+/**
+  Uses the ARP protocol to resolve a MAC address. It is not supported for IPv6.
+
+  This function uses the ARP protocol to resolve a MAC address. The IP address specified
+  by IpAddr is used to resolve a MAC address. If the ARP protocol succeeds in resolving
+  the specified address, then the ArpCacheEntries and ArpCache fields of the mode data
+  are updated, and EFI_SUCCESS is returned. If MacAddr is not NULL, the resolved
+  MAC address is placed there as well.  If the PXE Base Code protocol is in the
+  stopped state, then EFI_NOT_STARTED is returned. If the ARP protocol encounters
+  a timeout condition while attempting to resolve an address, then EFI_TIMEOUT is
+  returned. If the Callback Protocol does not return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE,
+  then EFI_ABORTED is returned.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  IpAddr            Pointer to the IP address that is used to resolve a MAC address.
+  @param[in]  MacAddr           If not NULL, a pointer to the MAC address that was resolved with the
+                                ARP protocol.
+
+  @retval EFI_SUCCESS           The IP or MAC address was resolved.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+  @retval EFI_DEVICE_ERROR      The network device encountered an error during this operation.
+  @retval EFI_ICMP_ERROR        An error occur with the ICMP packet message.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcArp (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN EFI_IP_ADDRESS                   *IpAddr,
+  IN EFI_MAC_ADDRESS                  *MacAddr OPTIONAL
+  )
+{
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  EFI_EVENT               ResolvedEvent;
+  EFI_STATUS              Status;
+  EFI_MAC_ADDRESS         TempMac;
+  EFI_MAC_ADDRESS         ZeroMac;
+  BOOLEAN                 IsResolved;
+
+  if (This == NULL || IpAddr == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private       = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode          = Private->PxeBc.Mode;
+  ResolvedEvent = NULL;
+  Status        = EFI_SUCCESS;
+  IsResolved    = FALSE;
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (Mode->UsingIpv6) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Station address should be ready before do arp.
+  //
+  if (!Private->IsAddressOk) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Mode->IcmpErrorReceived = FALSE;
+  ZeroMem (&TempMac, sizeof (EFI_MAC_ADDRESS));
+  ZeroMem (&ZeroMac, sizeof (EFI_MAC_ADDRESS));
+
+  if (!Mode->AutoArp) {
+    //
+    // If AutoArp is FALSE, only search in the current Arp cache.
+    //
+    PxeBcArpCacheUpdate (NULL, Private);
+    if (!PxeBcCheckArpCache (Mode, &IpAddr->v4, &TempMac)) {
+      Status = EFI_DEVICE_ERROR;
+      goto ON_EXIT;
+    }
+  } else {
+    Status = gBS->CreateEvent (
+                    EVT_NOTIFY_SIGNAL,
+                    TPL_NOTIFY,
+                    PxeBcCommonNotify,
+                    &IsResolved,
+                    &ResolvedEvent
+                    );
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    //
+    // If AutoArp is TRUE, try to send Arp request on initiative.
+    //
+    Status = Private->Arp->Request (Private->Arp, &IpAddr->v4, ResolvedEvent, &TempMac);
+    if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {
+      goto ON_EXIT;
+    }
+
+    while (!IsResolved) {
+      if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {
+        break;
+      }
+    }
+    if (CompareMem (&TempMac, &ZeroMac, sizeof (EFI_MAC_ADDRESS)) != 0) {
+      Status = EFI_SUCCESS;
+    } else {
+      Status = EFI_TIMEOUT;
+    }
+  }
+
+  //
+  // Copy the Mac address to user if needed.
+  //
+  if (MacAddr != NULL && !EFI_ERROR (Status)) {
+    CopyMem (MacAddr, &TempMac, sizeof (EFI_MAC_ADDRESS));
+  }
+
+ON_EXIT:
+  if (ResolvedEvent != NULL) {
+    gBS->CloseEvent (ResolvedEvent);
+  }
+  return Status;
+}
+
+
+/**
+  Updates the parameters that affect the operation of the PXE Base Code Protocol.
+
+  This function sets parameters that affect the operation of the PXE Base Code Protocol.
+  The parameter specified by NewAutoArp is used to control the generation of ARP
+  protocol packets. If NewAutoArp is TRUE, then ARP Protocol packets will be generated
+  as required by the PXE Base Code Protocol. If NewAutoArp is FALSE, then no ARP
+  Protocol packets will be generated. In this case, the only mappings that are
+  available are those stored in the ArpCache of the EFI_PXE_BASE_CODE_MODE structure.
+  If there are not enough mappings in the ArpCache to perform a PXE Base Code Protocol
+  service, then the service will fail. This function updates the AutoArp field of
+  the EFI_PXE_BASE_CODE_MODE structure to NewAutoArp.
+  The SetParameters() call must be invoked after a Callback Protocol is installed
+  to enable the use of callbacks.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  NewAutoArp        If not NULL, a pointer to a value that specifies whether to replace the
+                                current value of AutoARP.
+  @param[in]  NewSendGUID       If not NULL, a pointer to a value that specifies whether to replace the
+                                current value of SendGUID.
+  @param[in]  NewTTL            If not NULL, a pointer to be used in place of the current value of TTL,
+                                the "time to live" field of the IP header.
+  @param[in]  NewToS            If not NULL, a pointer to be used in place of the current value of ToS,
+                                the "type of service" field of the IP header.
+  @param[in]  NewMakeCallback   If not NULL, a pointer to a value that specifies whether to replace the
+                                current value of the MakeCallback field of the Mode structure.
+
+  @retval EFI_SUCCESS           The new parameters values were updated.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetParameters (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN BOOLEAN                          *NewAutoArp         OPTIONAL,
+  IN BOOLEAN                          *NewSendGUID        OPTIONAL,
+  IN UINT8                            *NewTTL             OPTIONAL,
+  IN UINT8                            *NewToS             OPTIONAL,
+  IN BOOLEAN                          *NewMakeCallback    OPTIONAL
+  )
+{
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  EFI_GUID                SystemGuid;
+  EFI_STATUS              Status;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode    = Private->PxeBc.Mode;
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (NewMakeCallback != NULL) {
+    if (*NewMakeCallback) {
+      //
+      // Update the previous PxeBcCallback protocol.
+      //
+      Status = gBS->HandleProtocol (
+                      Private->Controller,
+                      &gEfiPxeBaseCodeCallbackProtocolGuid,
+                      (VOID **) &Private->PxeBcCallback
+                      );
+
+      if (EFI_ERROR (Status) || (Private->PxeBcCallback->Callback == NULL)) {
+        return EFI_INVALID_PARAMETER;
+      }
+    } else {
+      Private->PxeBcCallback = NULL;
+    }
+    Mode->MakeCallbacks = *NewMakeCallback;
+  }
+
+  if (NewSendGUID != NULL) {
+    if (*NewSendGUID && EFI_ERROR (NetLibGetSystemGuid (&SystemGuid))) {
+      return EFI_INVALID_PARAMETER;
+    }
+    Mode->SendGUID = *NewSendGUID;
+  }
+
+  if (NewAutoArp != NULL) {
+    Mode->AutoArp = *NewAutoArp;
+  }
+
+  if (NewTTL != NULL) {
+    Mode->TTL = *NewTTL;
+  }
+
+  if (NewToS != NULL) {
+    Mode->ToS = *NewToS;
+  }
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Updates the station IP address and/or subnet mask values of a network device.
+
+  This function updates the station IP address and/or subnet mask values of a network
+  device. The NewStationIp field is used to modify the network device's current IP address.
+  If NewStationIP is NULL, then the current IP address will not be modified. Otherwise,
+  this function updates the StationIp field of the EFI_PXE_BASE_CODE_MODE structure
+  with NewStationIp. The NewSubnetMask field is used to modify the network device's current subnet
+  mask. If NewSubnetMask is NULL, then the current subnet mask will not be modified.
+  Otherwise, this function updates the SubnetMask field of the EFI_PXE_BASE_CODE_MODE
+  structure with NewSubnetMask.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  NewStationIp      Pointer to the new IP address to be used by the network device.
+  @param[in]  NewSubnetMask     Pointer to the new subnet mask to be used by the network device.
+
+  @retval EFI_SUCCESS           The new station IP address and/or subnet mask were updated.
+  @retval EFI_NOT_STARTED       The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetStationIP (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN EFI_IP_ADDRESS                   *NewStationIp    OPTIONAL,
+  IN EFI_IP_ADDRESS                   *NewSubnetMask   OPTIONAL
+  )
+{
+  EFI_STATUS              Status;
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  EFI_ARP_CONFIG_DATA     ArpConfigData;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (NewStationIp != NULL &&
+      (!NetIp4IsUnicast (NTOHL (NewStationIp->Addr[0]), 0) &&
+       !NetIp6IsValidUnicast (&NewStationIp->v6))) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode    = Private->PxeBc.Mode;
+  Status  = EFI_SUCCESS;
+
+  if (!Mode->UsingIpv6 &&
+      NewSubnetMask != NULL &&
+      !IP4_IS_VALID_NETMASK (NTOHL (NewSubnetMask->Addr[0]))) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (Mode->UsingIpv6 && NewStationIp != NULL) {
+    //
+    // Set the IPv6 address by Ip6Config protocol.
+    //
+    Status = PxeBcRegisterIp6Address (Private, &NewStationIp->v6);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+  } else if (!Mode->UsingIpv6 && NewStationIp != NULL) {
+    //
+    // Configure the corresponding ARP with the IPv4 address.
+    //
+    ZeroMem (&ArpConfigData, sizeof (EFI_ARP_CONFIG_DATA));
+
+    ArpConfigData.SwAddressType   = 0x0800;
+    ArpConfigData.SwAddressLength = (UINT8) sizeof (EFI_IPv4_ADDRESS);
+    ArpConfigData.StationAddress  = &NewStationIp->v4;
+
+    Private->Arp->Configure (Private->Arp, NULL);
+    Private->Arp->Configure (Private->Arp, &ArpConfigData);
+
+    if (NewSubnetMask != NULL) {
+      Mode->RouteTableEntries                = 1;
+      Mode->RouteTable[0].IpAddr.Addr[0]     = NewStationIp->Addr[0] & NewSubnetMask->Addr[0];
+      Mode->RouteTable[0].SubnetMask.Addr[0] = NewSubnetMask->Addr[0];
+      Mode->RouteTable[0].GwAddr.Addr[0]     = 0;
+    }
+
+    Private->IsAddressOk = TRUE;
+  }
+
+  if (NewStationIp != NULL) {
+    CopyMem (&Mode->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
+    CopyMem (&Private->StationIp, NewStationIp, sizeof (EFI_IP_ADDRESS));
+  }
+
+  if (!Mode->UsingIpv6 && NewSubnetMask != NULL) {
+    CopyMem (&Mode->SubnetMask, NewSubnetMask, sizeof (EFI_IP_ADDRESS));
+    CopyMem (&Private->SubnetMask ,NewSubnetMask, sizeof (EFI_IP_ADDRESS));
+  }
+
+  Status = PxeBcFlushStationIp (Private, NewStationIp, NewSubnetMask);
+ON_EXIT:
+  return Status;
+}
+
+
+/**
+  Updates the contents of the cached DHCP and Discover packets.
+
+  The pointers to the new packets are used to update the contents of the cached
+  packets in the EFI_PXE_BASE_CODE_MODE structure.
+
+  @param[in]  This                   Pointer to the EFI_PXE_BASE_CODE_PROTOCOL instance.
+  @param[in]  NewDhcpDiscoverValid   Pointer to a value that will replace the current
+                                     DhcpDiscoverValid field.
+  @param[in]  NewDhcpAckReceived     Pointer to a value that will replace the current
+                                     DhcpAckReceived field.
+  @param[in]  NewProxyOfferReceived  Pointer to a value that will replace the current
+                                     ProxyOfferReceived field.
+  @param[in]  NewPxeDiscoverValid    Pointer to a value that will replace the current
+                                     ProxyOfferReceived field.
+  @param[in]  NewPxeReplyReceived    Pointer to a value that will replace the current
+                                     PxeReplyReceived field.
+  @param[in]  NewPxeBisReplyReceived Pointer to a value that will replace the current
+                                     PxeBisReplyReceived field.
+  @param[in]  NewDhcpDiscover        Pointer to the new cached DHCP Discover packet contents.
+  @param[in]  NewDhcpAck             Pointer to the new cached DHCP Ack packet contents.
+  @param[in]  NewProxyOffer          Pointer to the new cached Proxy Offer packet contents.
+  @param[in]  NewPxeDiscover         Pointer to the new cached PXE Discover packet contents.
+  @param[in]  NewPxeReply            Pointer to the new cached PXE Reply packet contents.
+  @param[in]  NewPxeBisReply         Pointer to the new cached PXE BIS Reply packet contents.
+
+  @retval EFI_SUCCESS            The cached packet contents were updated.
+  @retval EFI_NOT_STARTED        The PXE Base Code Protocol is in the stopped state.
+  @retval EFI_INVALID_PARAMETER  This is NULL or does not point to a valid
+                                 EFI_PXE_BASE_CODE_PROTOCOL structure.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeBcSetPackets (
+  IN EFI_PXE_BASE_CODE_PROTOCOL       *This,
+  IN BOOLEAN                          *NewDhcpDiscoverValid      OPTIONAL,
+  IN BOOLEAN                          *NewDhcpAckReceived        OPTIONAL,
+  IN BOOLEAN                          *NewProxyOfferReceived     OPTIONAL,
+  IN BOOLEAN                          *NewPxeDiscoverValid       OPTIONAL,
+  IN BOOLEAN                          *NewPxeReplyReceived       OPTIONAL,
+  IN BOOLEAN                          *NewPxeBisReplyReceived    OPTIONAL,
+  IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpDiscover           OPTIONAL,
+  IN EFI_PXE_BASE_CODE_PACKET         *NewDhcpAck                OPTIONAL,
+  IN EFI_PXE_BASE_CODE_PACKET         *NewProxyOffer             OPTIONAL,
+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeDiscover            OPTIONAL,
+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeReply               OPTIONAL,
+  IN EFI_PXE_BASE_CODE_PACKET         *NewPxeBisReply            OPTIONAL
+  )
+{
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+
+  if (This == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  Private = PXEBC_PRIVATE_DATA_FROM_PXEBC (This);
+  Mode    = Private->PxeBc.Mode;
+
+  if (!Mode->Started) {
+    return EFI_NOT_STARTED;
+  }
+
+  if (NewDhcpDiscoverValid != NULL) {
+    Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;
+  }
+
+  if (NewDhcpAckReceived != NULL) {
+    Mode->DhcpAckReceived = *NewDhcpAckReceived;
+  }
+
+  if (NewProxyOfferReceived != NULL) {
+    Mode->ProxyOfferReceived = *NewProxyOfferReceived;
+  }
+
+  if (NewPxeDiscoverValid != NULL) {
+    Mode->PxeDiscoverValid = *NewPxeDiscoverValid;
+  }
+
+  if (NewPxeReplyReceived != NULL) {
+    Mode->PxeReplyReceived = *NewPxeReplyReceived;
+  }
+
+  if (NewPxeBisReplyReceived != NULL) {
+    Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;
+  }
+
+  if (NewDhcpDiscover != NULL) {
+    CopyMem (&Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
+  }
+
+  if (NewDhcpAck != NULL) {
+    CopyMem (&Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));
+  }
+
+  if (NewProxyOffer != NULL) {
+    CopyMem (&Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));
+  }
+
+  if (NewPxeDiscover != NULL) {
+    CopyMem (&Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
+  }
+
+  if (NewPxeReply != NULL) {
+    CopyMem (&Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
+  }
+
+  if (NewPxeBisReply != NULL) {
+    CopyMem (&Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
+  }
+
+  return EFI_SUCCESS;
+}
+
+EFI_PXE_BASE_CODE_PROTOCOL  gPxeBcProtocolTemplate = {
+  EFI_PXE_BASE_CODE_PROTOCOL_REVISION,
+  EfiPxeBcStart,
+  EfiPxeBcStop,
+  EfiPxeBcDhcp,
+  EfiPxeBcDiscover,
+  EfiPxeBcMtftp,
+  EfiPxeBcUdpWrite,
+  EfiPxeBcUdpRead,
+  EfiPxeBcSetIpFilter,
+  EfiPxeBcArp,
+  EfiPxeBcSetParameters,
+  EfiPxeBcSetStationIP,
+  EfiPxeBcSetPackets,
+  NULL
+};
+
+
+/**
+  Callback function that is invoked when the PXE Base Code Protocol is about to transmit, has
+  received, or is waiting to receive a packet.
+
+  This function is invoked when the PXE Base Code Protocol is about to transmit, has received,
+  or is waiting to receive a packet. Parameters Function and Received specify the type of event.
+  Parameters PacketLen and Packet specify the packet that generated the event. If these fields
+  are zero and NULL respectively, then this is a status update callback. If the operation specified
+  by Function is to continue, then CALLBACK_STATUS_CONTINUE should be returned. If the operation
+  specified by Function should be aborted, then CALLBACK_STATUS_ABORT should be returned. Due to
+  the polling nature of UEFI device drivers, a callback function should not execute for more than 5 ms.
+  The SetParameters() function must be called after a Callback Protocol is installed to enable the
+  use of callbacks.
+
+  @param[in]  This              Pointer to the EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL instance.
+  @param[in]  Function          The PXE Base Code Protocol function that is waiting for an event.
+  @param[in]  Received          TRUE if the callback is being invoked due to a receive event. FALSE if
+                                the callback is being invoked due to a transmit event.
+  @param[in]  PacketLength      The length, in bytes, of Packet. This field will have a value of zero if
+                                this is a wait for receive event.
+  @param[in]  PacketPtr         If Received is TRUE, a pointer to the packet that was just received;
+                                otherwise a pointer to the packet that is about to be transmitted.
+
+  @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE If Function specifies a continue operation.
+  @retval EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT    If Function specifies an abort operation.
+
+**/
+EFI_PXE_BASE_CODE_CALLBACK_STATUS
+EFIAPI
+EfiPxeLoadFileCallback (
+  IN EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  *This,
+  IN EFI_PXE_BASE_CODE_FUNCTION           Function,
+  IN BOOLEAN                              Received,
+  IN UINT32                               PacketLength,
+  IN EFI_PXE_BASE_CODE_PACKET             *PacketPtr     OPTIONAL
+  )
+{
+  EFI_INPUT_KEY       Key;
+  EFI_STATUS          Status;
+
+  //
+  // Catch Ctrl-C or ESC to abort.
+  //
+  Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
+
+  if (!EFI_ERROR (Status)) {
+
+    if (Key.ScanCode == SCAN_ESC || Key.UnicodeChar == (0x1F & 'c')) {
+
+      return EFI_PXE_BASE_CODE_CALLBACK_STATUS_ABORT;
+    }
+  }
+  //
+  // No print if receive packet
+  //
+  if (Received) {
+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+  }
+  //
+  // Print only for three functions
+  //
+  switch (Function) {
+
+  case EFI_PXE_BASE_CODE_FUNCTION_MTFTP:
+    //
+    // Print only for open MTFTP packets, not every MTFTP packets
+    //
+    if (PacketLength != 0 && PacketPtr != NULL) {
+      if (PacketPtr->Raw[0x1C] != 0x00 || PacketPtr->Raw[0x1D] != 0x01) {
+        return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+      }
+    }
+    break;
+
+  case EFI_PXE_BASE_CODE_FUNCTION_DHCP:
+  case EFI_PXE_BASE_CODE_FUNCTION_DISCOVER:
+    break;
+
+  default:
+    return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+  }
+
+  if (PacketLength != 0 && PacketPtr != NULL) {
+    //
+    // Print '.' when transmit a packet
+    //
+    AsciiPrint (".");
+  }
+
+  return EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE;
+}
+
+EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL gPxeBcCallBackTemplate = {
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL_REVISION,
+  EfiPxeLoadFileCallback
+};
+
+
+/**
+  Causes the driver to load a specified file.
+
+  @param[in]      This                Protocol instance pointer.
+  @param[in]      FilePath            The device specific path of the file to load.
+  @param[in]      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 an exact file
+                                      to be loaded.
+  @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 no 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.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiPxeLoadFile (
+  IN     EFI_LOAD_FILE_PROTOCOL           *This,
+  IN     EFI_DEVICE_PATH_PROTOCOL         *FilePath,
+  IN     BOOLEAN                          BootPolicy,
+  IN OUT UINTN                            *BufferSize,
+  IN     VOID                             *Buffer       OPTIONAL
+  )
+{
+  PXEBC_PRIVATE_DATA          *Private;
+  PXEBC_VIRTUAL_NIC           *VirtualNic;
+  EFI_PXE_BASE_CODE_PROTOCOL  *PxeBc;
+  BOOLEAN                     UsingIpv6;
+  EFI_STATUS                  Status;
+  BOOLEAN                     MediaPresent;
+
+  if (FilePath == NULL || !IsDevicePathEnd (FilePath)) {
+    return EFI_INVALID_PARAMETER;
+  }
+  
+  VirtualNic = PXEBC_VIRTUAL_NIC_FROM_LOADFILE (This);
+  Private    = VirtualNic->Private;
+  PxeBc      = &Private->PxeBc;
+  UsingIpv6  = FALSE;
+  Status     = EFI_DEVICE_ERROR;
+
+  if (This == NULL || BufferSize == NULL) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Only support BootPolicy
+  //
+  if (!BootPolicy) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Check media status before PXE start
+  //
+  MediaPresent = TRUE;
+  NetLibDetectMedia (Private->Controller, &MediaPresent);
+  if (!MediaPresent) {
+    return EFI_NO_MEDIA;
+  }
+
+  //
+  // Check whether the virtual nic is using IPv6 or not.
+  //
+  if (VirtualNic == Private->Ip6Nic) {
+    UsingIpv6 = TRUE;
+  }
+
+  //
+  // Start Pxe Base Code to initialize PXE boot.
+  //
+  Status = PxeBc->Start (PxeBc, UsingIpv6);
+  if (Status == EFI_ALREADY_STARTED && UsingIpv6 != PxeBc->Mode->UsingIpv6) {
+    //
+    // PxeBc protocol has already been started but not on the required IP version, restart it.
+    //
+    Status = PxeBc->Stop (PxeBc);
+    if (!EFI_ERROR (Status)) {
+      Status = PxeBc->Start (PxeBc, UsingIpv6);
+    }
+  }
+  if (Status == EFI_SUCCESS || Status == EFI_ALREADY_STARTED) {
+    Status = PxeBcLoadBootFile (Private, BufferSize, Buffer);
+  }
+
+  if (Status != EFI_SUCCESS &&
+      Status != EFI_UNSUPPORTED &&
+      Status != EFI_BUFFER_TOO_SMALL) {
+    //
+    // There are three cases, which needn't stop pxebc here.
+    //   1. success to download file.
+    //   2. success to get file size.
+    //   3. unsupported.
+    //
+    PxeBc->Stop (PxeBc);
+  } else {
+    //
+    // The DHCP4 can have only one configured child instance so we need to stop
+    // reset the DHCP4 child before we return. Otherwise these programs which 
+    // also need to use DHCP4 will be impacted.
+    //
+    if (!PxeBc->Mode->UsingIpv6) {
+      Private->Dhcp4->Stop (Private->Dhcp4);
+      Private->Dhcp4->Configure (Private->Dhcp4, NULL);
+    }
+  }
+
+  return Status;
+}
+
+EFI_LOAD_FILE_PROTOCOL  gLoadFileProtocolTemplate = { EfiPxeLoadFile };
+
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
new file mode 100644
index 0000000..b7d0921
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcImpl.h
@@ -0,0 +1,225 @@
+/** @file
+  This EFI_PXE_BASE_CODE_PROTOCOL and EFI_LOAD_FILE_PROTOCOL.
+  interfaces declaration.
+
+  Copyright (c) 2007 - 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.
+
+**/
+
+#ifndef __EFI_PXEBC_IMPL_H__
+#define __EFI_PXEBC_IMPL_H__
+
+#include <Uefi.h>
+
+#include <Guid/SmBios.h>
+#include <IndustryStandard/SmBios.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/Arp.h>
+#include <Protocol/Ip4.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Ip6.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/Udp4.h>
+#include <Protocol/Udp6.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Mtftp4.h>
+#include <Protocol/Mtftp6.h>
+#include <Protocol/PxeBaseCode.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/PxeBaseCodeCallBack.h>
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/DriverBinding.h>
+#include <Protocol/AdapterInformation.h>
+
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseLib.h>
+#include <Library/NetLib.h>
+#include <Library/DpcLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PcdLib.h>
+
+typedef struct _PXEBC_PRIVATE_DATA  PXEBC_PRIVATE_DATA;
+typedef struct _PXEBC_PRIVATE_PROTOCOL PXEBC_PRIVATE_PROTOCOL;
+typedef struct _PXEBC_VIRTUAL_NIC   PXEBC_VIRTUAL_NIC;
+
+#include "PxeBcDriver.h"
+#include "PxeBcDhcp4.h"
+#include "PxeBcDhcp6.h"
+#include "PxeBcMtftp.h"
+#include "PxeBcBoot.h"
+#include "PxeBcSupport.h"
+
+#define PXEBC_DEFAULT_HOPLIMIT        64
+#define PXEBC_DEFAULT_LIFETIME        50000    // 50 ms, unit is microsecond
+#define PXEBC_UDP_TIMEOUT             30000000 // 3 seconds, unit is 100nanosecond
+#define PXEBC_DAD_ADDITIONAL_DELAY    30000000 // 3 seconds
+#define PXEBC_MTFTP_TIMEOUT           4
+#define PXEBC_MTFTP_RETRIES           6
+#define PXEBC_DHCP_RETRIES            4        // refers to mPxeDhcpTimeout, also by PXE2.1 spec.
+#define PXEBC_MENU_MAX_NUM            24
+#define PXEBC_OFFER_MAX_NUM           16
+
+#define PXEBC_PRIVATE_DATA_SIGNATURE          SIGNATURE_32 ('P', 'X', 'E', 'P')
+#define PXEBC_VIRTUAL_NIC_SIGNATURE           SIGNATURE_32 ('P', 'X', 'E', 'V')
+#define PXEBC_PRIVATE_DATA_FROM_PXEBC(a)      CR (a, PXEBC_PRIVATE_DATA, PxeBc, PXEBC_PRIVATE_DATA_SIGNATURE)
+#define PXEBC_PRIVATE_DATA_FROM_ID(a)         CR (a, PXEBC_PRIVATE_DATA, Id, PXEBC_PRIVATE_DATA_SIGNATURE)
+#define PXEBC_VIRTUAL_NIC_FROM_LOADFILE(a)    CR (a, PXEBC_VIRTUAL_NIC, LoadFile, PXEBC_VIRTUAL_NIC_SIGNATURE)
+
+typedef union {
+  PXEBC_DHCP4_PACKET_CACHE            Dhcp4;
+  PXEBC_DHCP6_PACKET_CACHE            Dhcp6;
+} PXEBC_DHCP_PACKET_CACHE;
+
+struct _PXEBC_PRIVATE_PROTOCOL {
+  UINT64                                    Reserved;
+};
+
+struct _PXEBC_VIRTUAL_NIC {
+  UINT32                                    Signature;
+  EFI_HANDLE                                Controller;
+  EFI_LOAD_FILE_PROTOCOL                    LoadFile;
+  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;
+  PXEBC_PRIVATE_DATA                        *Private;
+};
+
+struct _PXEBC_PRIVATE_DATA {
+  UINT32                                    Signature;
+  EFI_HANDLE                                Controller;
+  EFI_HANDLE                                Image;
+
+  PXEBC_PRIVATE_PROTOCOL                    Id;
+  EFI_SIMPLE_NETWORK_PROTOCOL               *Snp; 
+
+  PXEBC_VIRTUAL_NIC                         *Ip4Nic;
+  PXEBC_VIRTUAL_NIC                         *Ip6Nic;
+
+  EFI_HANDLE                                ArpChild;
+  EFI_HANDLE                                Ip4Child;
+  EFI_HANDLE                                Dhcp4Child;
+  EFI_HANDLE                                Mtftp4Child;
+  EFI_HANDLE                                Udp4ReadChild;
+  EFI_HANDLE                                Udp4WriteChild;
+
+  EFI_ARP_PROTOCOL                          *Arp;
+  EFI_IP4_PROTOCOL                          *Ip4;
+  EFI_IP4_CONFIG2_PROTOCOL                  *Ip4Config2;
+  EFI_DHCP4_PROTOCOL                        *Dhcp4;
+  EFI_MTFTP4_PROTOCOL                       *Mtftp4;
+  EFI_UDP4_PROTOCOL                         *Udp4Read;
+  EFI_UDP4_PROTOCOL                         *Udp4Write;
+
+  EFI_HANDLE                                Ip6Child;
+  EFI_HANDLE                                Dhcp6Child;
+  EFI_HANDLE                                Mtftp6Child;
+  EFI_HANDLE                                Udp6ReadChild;
+  EFI_HANDLE                                Udp6WriteChild;
+
+  EFI_IP6_PROTOCOL                          *Ip6;
+  EFI_IP6_CONFIG_PROTOCOL                   *Ip6Cfg;
+  EFI_DHCP6_PROTOCOL                        *Dhcp6;
+  EFI_MTFTP6_PROTOCOL                       *Mtftp6;
+  EFI_UDP6_PROTOCOL                         *Udp6Read;
+  EFI_UDP6_PROTOCOL                         *Udp6Write;
+
+  EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+  EFI_PXE_BASE_CODE_PROTOCOL                PxeBc;
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL       LoadFileCallback;
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL       *PxeBcCallback;
+  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;
+
+  EFI_PXE_BASE_CODE_MODE                    Mode;
+  EFI_PXE_BASE_CODE_FUNCTION                Function;
+  UINT32                                    Ip6Policy;
+  UINT32                                    SolicitTimes;
+  UINT64                                    ElapsedTime;
+
+  EFI_UDP4_CONFIG_DATA                      Udp4CfgData;
+  EFI_UDP6_CONFIG_DATA                      Udp6CfgData;
+  EFI_IP4_CONFIG_DATA                       Ip4CfgData;
+  EFI_IP6_CONFIG_DATA                       Ip6CfgData;
+
+  EFI_EVENT                                 UdpTimeOutEvent;
+  EFI_EVENT                                 ArpUpdateEvent;
+  EFI_IP4_COMPLETION_TOKEN                  IcmpToken;
+  EFI_IP6_COMPLETION_TOKEN                  Icmp6Token;
+
+  BOOLEAN                                   IsAddressOk;
+  BOOLEAN                                   IsOfferSorted;
+  BOOLEAN                                   IsProxyRecved;
+  BOOLEAN                                   IsDoDiscover;
+
+  EFI_IP_ADDRESS                            TmpStationIp;
+  EFI_IP_ADDRESS                            StationIp;
+  EFI_IP_ADDRESS                            SubnetMask;
+  EFI_IP_ADDRESS                            GatewayIp;
+  EFI_IP_ADDRESS                            ServerIp;
+  UINT16                                    CurSrcPort;
+  UINT32                                    IaId;
+
+  UINT32                                    Ip4MaxPacketSize;
+  UINT32                                    Ip6MaxPacketSize;
+  UINT8                                     *BootFileName;
+  UINTN                                     BootFileSize;
+  UINTN                                     BlockSize;
+
+  PXEBC_DHCP_PACKET_CACHE                   ProxyOffer;
+  PXEBC_DHCP_PACKET_CACHE                   DhcpAck;
+  PXEBC_DHCP_PACKET_CACHE                   PxeReply;
+  EFI_DHCP6_PACKET                          *Dhcp6Request;
+  EFI_DHCP4_PACKET                          SeedPacket;
+
+  //
+  // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.
+  //
+  // It supposed that
+  //
+  //   OfferNum:    8
+  //   OfferBuffer: [ProxyBinl, ProxyBinl, DhcpOnly, ProxyPxe10, DhcpOnly, DhcpPxe10, DhcpBinl, ProxyBinl]
+  //   (OfferBuffer is 0-based.)
+  //
+  // And assume that (DhcpPxe10 is the first priority actually.)
+  //
+  //   SelectIndex:     2
+  //   SelectProxyType: PXEBC_OFFER_TYPE_PROXY_BINL
+  //   (SelectIndex is 1-based, and 0 means no one is selected.)
+  //
+  // So it should be
+  //
+  //                 DhcpOnly  DhcpPxe10  DhcpWfm11a  DhcpBinl  ProxyPxe10  ProxyWfm11a  ProxyBinl  Bootp
+  //   OfferCount:  [    2(n),      1(n),       0(n),     1(n),       1(1),        0(1),      3(n),  1(1)]
+  //
+  //   OfferIndex: {[       2,         5,          0,        6,          3,           0,        *0,     0]
+  //                [       4,         0,          0,        0,          0,           0,         1,     0]
+  //                [       0,         0,          0,        0,          0,           0,         7,     0]
+  //                ...                                                                                  ]}
+  //   (OfferIndex is 0-based.)
+  //
+  //
+  UINT32                                    SelectIndex;
+  UINT32                                    SelectProxyType;
+  PXEBC_DHCP_PACKET_CACHE                   OfferBuffer[PXEBC_OFFER_MAX_NUM];
+  UINT32                                    OfferNum;
+  UINT32                                    OfferCount[PxeOfferTypeMax];
+  UINT32                                    OfferIndex[PxeOfferTypeMax][PXEBC_OFFER_MAX_NUM];
+};
+
+extern EFI_PXE_BASE_CODE_PROTOCOL           gPxeBcProtocolTemplate;
+extern EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL  gPxeBcCallBackTemplate;
+extern EFI_LOAD_FILE_PROTOCOL               gLoadFileProtocolTemplate;
+
+#endif
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
new file mode 100644
index 0000000..61c8f84
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.c
@@ -0,0 +1,1113 @@
+/** @file
+  Functions implementation related with Mtftp for UefiPxeBc Driver.
+
+  Copyright (c) 2007 - 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+CHAR8 *mMtftpOptions[PXE_MTFTP_OPTION_MAXIMUM_INDEX] = {
+  "blksize",
+  "timeout",
+  "tsize",
+  "multicast"
+};
+
+
+/**
+  This is a callback function when packets are received or transmitted in Mtftp driver.
+
+  A callback function that is provided by the caller to intercept
+  the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP6_OPCODE_DATA8 packets processed in the
+  EFI_MTFTP6_PROTOCOL.ReadFile() function, and alternatively to intercept
+  EFI_MTFTP6_OPCODE_OACK or EFI_MTFTP6_OPCODE_ERROR packets during a call to
+  EFI_MTFTP6_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().
+
+  @param[in]  This           Pointer to EFI_MTFTP6_PROTOCOL.
+  @param[in]  Token          Pointer to EFI_MTFTP6_TOKEN.
+  @param[in]  PacketLen      Length of EFI_MTFTP6_PACKET.
+  @param[in]  Packet         Pointer to EFI_MTFTP6_PACKET to be checked.
+
+  @retval EFI_SUCCESS    The current operation succeeded.
+  @retval EFI_ABORTED    Abort the current transfer process.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcMtftp6CheckPacket (
+  IN EFI_MTFTP6_PROTOCOL              *This,
+  IN EFI_MTFTP6_TOKEN                 *Token,
+  IN UINT16                           PacketLen,
+  IN EFI_MTFTP6_PACKET                *Packet
+  )
+{
+  PXEBC_PRIVATE_DATA                  *Private;
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+  EFI_STATUS                          Status;
+
+  Private   = (PXEBC_PRIVATE_DATA *) Token->Context;
+  Callback  = Private->PxeBcCallback;
+  Status    = EFI_SUCCESS;
+
+  if (Packet->OpCode == EFI_MTFTP6_OPCODE_ERROR) {
+    //
+    // Store the tftp error message into mode data and set the received flag.
+    //
+    Private->Mode.TftpErrorReceived   = TRUE;
+    Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+    AsciiStrnCpyS (
+      Private->Mode.TftpError.ErrorString,
+      PXE_MTFTP_ERROR_STRING_LENGTH,
+      (CHAR8 *) Packet->Error.ErrorMessage,
+      PXE_MTFTP_ERROR_STRING_LENGTH - 1
+      );
+    Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0';
+  }
+
+  if (Callback != NULL) {
+    //
+    // Callback to user if has when received any tftp packet.
+    //
+    Status = Callback->Callback (
+                        Callback,
+                        Private->Function,
+                        TRUE,
+                        PacketLen,
+                        (EFI_PXE_BASE_CODE_PACKET *) Packet
+                        );
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+      //
+      // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+      //
+      Status = EFI_ABORTED;
+    } else {
+      //
+      // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+      //
+      Status = EFI_SUCCESS;
+    }
+  }
+
+  return Status;
+}
+
+
+/**
+  This function is to get the size of a file using Tftp.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to EFI_MTFTP6_CONFIG_DATA.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in, out] BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Sucessfully obtained the size of file.
+  @retval EFI_NOT_FOUND      Parse the tftp ptions failed.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Has not obtained the size of the file.
+
+**/
+EFI_STATUS
+PxeBcMtftp6GetFileSize (
+  IN     PXEBC_PRIVATE_DATA           *Private,
+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,
+  IN     UINT8                        *Filename,
+  IN     UINTN                        *BlockSize,
+  IN OUT UINT64                       *BufferSize
+  )
+{
+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;
+  EFI_MTFTP6_OPTION                   ReqOpt[2];
+  EFI_MTFTP6_PACKET                   *Packet;
+  EFI_MTFTP6_OPTION                   *Option;
+  UINT32                              PktLen;
+  UINT8                               OptBuf[128];
+  UINT32                              OptCnt;
+  EFI_STATUS                          Status;
+
+  *BufferSize               = 0;
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp6                    = Private->Mtftp6;
+  Packet                    = NULL;
+  Option                    = NULL;
+  PktLen                    = 0;
+  OptCnt                    = 1;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status = Mtftp6->Configure (Mtftp6, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Build the required options for get info.
+  //
+  ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];
+  PxeBcUintnToAscDec (0, OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+  ReqOpt[0].ValueStr  = OptBuf;
+
+  if (BlockSize != NULL) {
+    ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[1].ValueStr  = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1));
+    OptCnt++;
+  }
+
+  Status = Mtftp6->GetInfo (
+                     Mtftp6,
+                     NULL,
+                     Filename,
+                     NULL,
+                     (UINT8) OptCnt,
+                     ReqOpt,
+                     &PktLen,
+                     &Packet
+                     );
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_TFTP_ERROR) {
+      //
+      // Store the tftp error message into mode data and set the received flag.
+      //
+      Private->Mode.TftpErrorReceived   = TRUE;
+      Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+      AsciiStrnCpyS (
+        Private->Mode.TftpError.ErrorString,
+        PXE_MTFTP_ERROR_STRING_LENGTH,
+        (CHAR8 *) Packet->Error.ErrorMessage,
+        PXE_MTFTP_ERROR_STRING_LENGTH - 1
+        );
+      Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0';
+    }
+    goto ON_ERROR;
+  }
+
+  //
+  // Parse the options in the reply packet.
+  //
+  OptCnt = 0;
+  Status = Mtftp6->ParseOptions (
+                     Mtftp6,
+                     PktLen,
+                     Packet,
+                     (UINT32 *) &OptCnt,
+                     &Option
+                     );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Parse out the value of "tsize" option.
+  //
+  Status = EFI_NOT_FOUND;
+  while (OptCnt != 0) {
+    if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {
+      *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));
+      Status      = EFI_SUCCESS;
+    }
+    OptCnt--;
+  }
+  FreePool (Option);
+
+ON_ERROR:
+  if (Packet != NULL) {
+    FreePool (Packet);
+  }
+  Mtftp6->Configure (Mtftp6, NULL);
+
+  return Status;
+}
+
+
+/**
+  This function is to get data of a file using Tftp.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to EFI_MTFTP6_CONFIG_DATA.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in]      BufferPtr      Pointer to buffer.
+  @param[in, out] BufferSize     Pointer to buffer size.
+  @param[in]      DontUseBuffer  Indicates whether with a receive buffer.
+
+  @retval EFI_SUCCESS        Successfully read the data from the special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp6ReadFile (
+  IN    PXEBC_PRIVATE_DATA            *Private,
+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,
+  IN     UINT8                        *Filename,
+  IN     UINTN                        *BlockSize,
+  IN     UINT8                        *BufferPtr,
+  IN OUT UINT64                       *BufferSize,
+  IN     BOOLEAN                      DontUseBuffer
+  )
+{
+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;
+  EFI_MTFTP6_TOKEN                    Token;
+  EFI_MTFTP6_OPTION                   ReqOpt[1];
+  UINT32                              OptCnt;
+  UINT8                               OptBuf[128];
+  EFI_STATUS                          Status;
+
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp6                    = Private->Mtftp6;
+  OptCnt                    = 0;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status = Mtftp6->Configure (Mtftp6, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (BlockSize != NULL) {
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[0].ValueStr  = OptBuf;
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+    OptCnt++;
+  }
+
+  Token.Event         = NULL;
+  Token.OverrideData  = NULL;
+  Token.Filename      = Filename;
+  Token.ModeStr       = NULL;
+  Token.OptionCount   = OptCnt;
+  Token.OptionList    = ReqOpt;
+  Token.Context       = Private;
+
+  if (DontUseBuffer) {
+    Token.BufferSize  = 0;
+    Token.Buffer      = NULL;
+  } else {
+    Token.BufferSize  = *BufferSize;
+    Token.Buffer      = BufferPtr;
+  }
+
+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;
+  Token.TimeoutCallback = NULL;
+  Token.PacketNeeded    = NULL;
+
+  Status = Mtftp6->ReadFile (Mtftp6, &Token);
+  //
+  // Get the real size of received buffer.
+  //
+  *BufferSize = Token.BufferSize;
+
+  Mtftp6->Configure (Mtftp6, NULL);
+
+  return Status;
+}
+
+
+/**
+  This function is used to write the data of a file using Tftp.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to EFI_MTFTP6_CONFIG_DATA.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       Overwrite      Indicate whether with overwrite attribute.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Successfully wrote the data into a special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval other              Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp6WriteFile (
+  IN     PXEBC_PRIVATE_DATA           *Private,
+  IN     EFI_MTFTP6_CONFIG_DATA       *Config,
+  IN     UINT8                        *Filename,
+  IN     BOOLEAN                      Overwrite,
+  IN     UINTN                        *BlockSize,
+  IN     UINT8                        *BufferPtr,
+  IN OUT UINT64                       *BufferSize
+  )
+{
+  EFI_MTFTP6_PROTOCOL                 *Mtftp6;
+  EFI_MTFTP6_TOKEN                    Token;
+  EFI_MTFTP6_OPTION                   ReqOpt[1];
+  UINT32                              OptCnt;
+  UINT8                               OptBuf[128];
+  EFI_STATUS                          Status;
+
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp6                    = Private->Mtftp6;
+  OptCnt                    = 0;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status = Mtftp6->Configure (Mtftp6, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (BlockSize != NULL) {
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[0].ValueStr  = OptBuf;
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+    OptCnt++;
+  }
+
+  Token.Event           = NULL;
+  Token.OverrideData    = NULL;
+  Token.Filename        = Filename;
+  Token.ModeStr         = NULL;
+  Token.OptionCount     = OptCnt;
+  Token.OptionList      = ReqOpt;
+  Token.BufferSize      = *BufferSize;
+  Token.Buffer          = BufferPtr;
+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;
+  Token.TimeoutCallback = NULL;
+  Token.PacketNeeded    = NULL;
+
+  Status = Mtftp6->WriteFile (Mtftp6, &Token);
+  //
+  // Get the real size of transmitted buffer.
+  //
+  *BufferSize = Token.BufferSize;
+
+  Mtftp6->Configure (Mtftp6, NULL);
+
+  return Status;
+}
+
+
+/**
+  This function is to read the data (file) from a directory using Tftp.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to EFI_MTFTP6_CONFIG_DATA.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+  @param[in]       DontUseBuffer  Indicates whether to use a receive buffer.
+
+  @retval EFI_SUCCESS        Successfully obtained the data from the file included in directory.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Operation failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp6ReadDirectory (
+  IN     PXEBC_PRIVATE_DATA            *Private,
+  IN     EFI_MTFTP6_CONFIG_DATA        *Config,
+  IN     UINT8                         *Filename,
+  IN     UINTN                         *BlockSize,
+  IN     UINT8                         *BufferPtr,
+  IN OUT UINT64                        *BufferSize,
+  IN     BOOLEAN                       DontUseBuffer
+  )
+{
+  EFI_MTFTP6_PROTOCOL                  *Mtftp6;
+  EFI_MTFTP6_TOKEN                     Token;
+  EFI_MTFTP6_OPTION                    ReqOpt[1];
+  UINT32                               OptCnt;
+  UINT8                                OptBuf[128];
+  EFI_STATUS                           Status;
+
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp6                    = Private->Mtftp6;
+  OptCnt                    = 0;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status = Mtftp6->Configure (Mtftp6, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (BlockSize != NULL) {
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[0].ValueStr  = OptBuf;
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+    OptCnt++;
+  }
+
+  Token.Event         = NULL;
+  Token.OverrideData  = NULL;
+  Token.Filename      = Filename;
+  Token.ModeStr       = NULL;
+  Token.OptionCount   = OptCnt;
+  Token.OptionList    = ReqOpt;
+  Token.Context       = Private;
+
+  if (DontUseBuffer) {
+    Token.BufferSize  = 0;
+    Token.Buffer      = NULL;
+  } else {
+    Token.BufferSize  = *BufferSize;
+    Token.Buffer      = BufferPtr;
+  }
+
+  Token.CheckPacket     = PxeBcMtftp6CheckPacket;
+  Token.TimeoutCallback = NULL;
+  Token.PacketNeeded    = NULL;
+
+  Status = Mtftp6->ReadDirectory (Mtftp6, &Token);
+  //
+  // Get the real size of received buffer.
+  //
+  *BufferSize = Token.BufferSize;
+
+  Mtftp6->Configure (Mtftp6, NULL);
+
+  return Status;
+}
+
+
+/**
+  This is a callback function when packets are received or transmitted in Mtftp driver.
+
+  A callback function that is provided by the caller to intercept
+  the EFI_MTFTP6_OPCODE_DATA or EFI_MTFTP4_OPCODE_DATA8 packets processed in the
+  EFI_MTFTP4_PROTOCOL.ReadFile() function, and alternatively to intercept
+  EFI_MTFTP4_OPCODE_OACK or EFI_MTFTP4_OPCODE_ERROR packets during a call to
+  EFI_MTFTP4_PROTOCOL.ReadFile(), WriteFile() or ReadDirectory().
+
+  @param[in]  This           Pointer to EFI_MTFTP4_PROTOCOL.
+  @param[in]  Token          Pointer to EFI_MTFTP4_TOKEN.
+  @param[in]  PacketLen      Length of EFI_MTFTP4_PACKET.
+  @param[in]  Packet         Pointer to EFI_MTFTP4_PACKET to be checked.
+
+  @retval EFI_SUCCESS    The current operation succeeeded.
+  @retval EFI_ABORTED    Abort the current transfer process.
+
+**/
+EFI_STATUS
+EFIAPI
+PxeBcMtftp4CheckPacket (
+  IN EFI_MTFTP4_PROTOCOL        *This,
+  IN EFI_MTFTP4_TOKEN           *Token,
+  IN UINT16                     PacketLen,
+  IN EFI_MTFTP4_PACKET          *Packet
+  )
+{
+  PXEBC_PRIVATE_DATA                  *Private;
+  EFI_PXE_BASE_CODE_CALLBACK_PROTOCOL *Callback;
+  EFI_STATUS                          Status;
+
+  Private   = (PXEBC_PRIVATE_DATA *) Token->Context;
+  Callback  = Private->PxeBcCallback;
+  Status    = EFI_SUCCESS;
+
+  if (Packet->OpCode == EFI_MTFTP4_OPCODE_ERROR) {
+    //
+    // Store the tftp error message into mode data and set the received flag.
+    //
+    Private->Mode.TftpErrorReceived   = TRUE;
+    Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+    AsciiStrnCpyS (
+      Private->Mode.TftpError.ErrorString,
+      PXE_MTFTP_ERROR_STRING_LENGTH,
+      (CHAR8 *) Packet->Error.ErrorMessage,
+      PXE_MTFTP_ERROR_STRING_LENGTH - 1
+      );
+    Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0';
+  }
+
+  if (Callback != NULL) {
+    //
+    // Callback to user if has when received any tftp packet.
+    //
+    Status = Callback->Callback (
+                        Callback,
+                        Private->Function,
+                        TRUE,
+                        PacketLen,
+                        (EFI_PXE_BASE_CODE_PACKET *) Packet
+                        );
+    if (Status != EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE) {
+      //
+      // User wants to abort current process if not EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+      //
+      Status = EFI_ABORTED;
+    } else {
+      //
+      // User wants to continue current process if EFI_PXE_BASE_CODE_CALLBACK_STATUS_CONTINUE.
+      //
+      Status = EFI_SUCCESS;
+    }
+  }
+
+  return Status;
+}
+
+
+/**
+  This function is to get size of a file using Tftp.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to EFI_MTFTP4_CONFIG_DATA.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in, out] BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Successfully obtained the size of file.
+  @retval EFI_NOT_FOUND      Parse the tftp options failed.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Did not obtain the size of the file.
+
+**/
+EFI_STATUS
+PxeBcMtftp4GetFileSize (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,
+  IN     UINT8                      *Filename,
+  IN     UINTN                      *BlockSize,
+  IN OUT UINT64                     *BufferSize
+  )
+{
+  EFI_MTFTP4_PROTOCOL *Mtftp4;
+  EFI_MTFTP4_OPTION   ReqOpt[2];
+  EFI_MTFTP4_PACKET   *Packet;
+  EFI_MTFTP4_OPTION   *Option;
+  UINT32              PktLen;
+  UINT8               OptBuf[128];
+  UINT32              OptCnt;
+  EFI_STATUS          Status;
+
+  *BufferSize               = 0;
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp4                    = Private->Mtftp4;
+  Packet                    = NULL;
+  Option                    = NULL;
+  PktLen                    = 0;
+  OptCnt                    = 1;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status = Mtftp4->Configure (Mtftp4, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Build the required options for get info.
+  //
+  ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_TSIZE_INDEX];
+  PxeBcUintnToAscDec (0, OptBuf, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+  ReqOpt[0].ValueStr  = OptBuf;
+
+  if (BlockSize != NULL) {
+    ReqOpt[1].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[1].ValueStr  = (UINT8 *) (ReqOpt[0].ValueStr + AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1);
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[1].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX - (AsciiStrLen ((CHAR8 *) ReqOpt[0].ValueStr) + 1));
+    OptCnt++;
+  }
+
+  Status = Mtftp4->GetInfo (
+                     Mtftp4,
+                     NULL,
+                     Filename,
+                     NULL,
+                     (UINT8) OptCnt,
+                     ReqOpt,
+                     &PktLen,
+                     &Packet
+                     );
+  if (EFI_ERROR (Status)) {
+    if (Status == EFI_TFTP_ERROR) {
+      //
+      // Store the tftp error message into mode data and set the received flag.
+      //
+      Private->Mode.TftpErrorReceived   = TRUE;
+      Private->Mode.TftpError.ErrorCode = (UINT8) Packet->Error.ErrorCode;
+      AsciiStrnCpyS (
+        Private->Mode.TftpError.ErrorString,
+        PXE_MTFTP_ERROR_STRING_LENGTH,
+        (CHAR8 *) Packet->Error.ErrorMessage,
+        PXE_MTFTP_ERROR_STRING_LENGTH - 1
+        );
+      Private->Mode.TftpError.ErrorString[PXE_MTFTP_ERROR_STRING_LENGTH - 1] = '\0';
+    }
+    goto ON_ERROR;
+  }
+
+  //
+  // Parse the options in the reply packet.
+  //
+  OptCnt = 0;
+  Status = Mtftp4->ParseOptions (
+                     Mtftp4,
+                     PktLen,
+                     Packet,
+                     (UINT32 *) &OptCnt,
+                     &Option
+                     );
+  if (EFI_ERROR (Status)) {
+    goto ON_ERROR;
+  }
+
+  //
+  // Parse out the value of "tsize" option.
+  //
+  Status = EFI_NOT_FOUND;
+  while (OptCnt != 0) {
+    if (AsciiStrnCmp ((CHAR8 *) Option[OptCnt - 1].OptionStr, "tsize", 5) == 0) {
+      *BufferSize = AsciiStrDecimalToUint64 ((CHAR8 *) (Option[OptCnt - 1].ValueStr));
+      Status      = EFI_SUCCESS;
+    }
+    OptCnt--;
+  }
+  FreePool (Option);
+
+ON_ERROR:
+  if (Packet != NULL) {
+    FreePool (Packet);
+  }
+  Mtftp4->Configure (Mtftp4, NULL);
+
+  return Status;
+}
+
+
+/**
+  This function is to read the data of a file using Tftp.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to EFI_MTFTP4_CONFIG_DATA.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in]      BufferPtr      Pointer to buffer.
+  @param[in, out] BufferSize     Pointer to buffer size.
+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.
+
+  @retval EFI_SUCCESS        Successfully read the data from the special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp4ReadFile (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,
+  IN     UINT8                      *Filename,
+  IN     UINTN                      *BlockSize,
+  IN     UINT8                      *BufferPtr,
+  IN OUT UINT64                     *BufferSize,
+  IN     BOOLEAN                    DontUseBuffer
+  )
+{
+  EFI_MTFTP4_PROTOCOL *Mtftp4;
+  EFI_MTFTP4_TOKEN    Token;
+  EFI_MTFTP4_OPTION   ReqOpt[1];
+  UINT32              OptCnt;
+  UINT8               OptBuf[128];
+  EFI_STATUS          Status;
+
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp4                    = Private->Mtftp4;
+  OptCnt                    = 0;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status = Mtftp4->Configure (Mtftp4, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (BlockSize != NULL) {
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[0].ValueStr  = OptBuf;
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+    OptCnt++;
+  }
+
+  Token.Event         = NULL;
+  Token.OverrideData  = NULL;
+  Token.Filename      = Filename;
+  Token.ModeStr       = NULL;
+  Token.OptionCount   = OptCnt;
+  Token.OptionList    = ReqOpt;
+  Token.Context       = Private;
+
+  if (DontUseBuffer) {
+    Token.BufferSize  = 0;
+    Token.Buffer      = NULL;
+  } else {
+    Token.BufferSize  = *BufferSize;
+    Token.Buffer      = BufferPtr;
+  }
+
+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;
+  Token.TimeoutCallback = NULL;
+  Token.PacketNeeded    = NULL;
+
+  Status = Mtftp4->ReadFile (Mtftp4, &Token);
+  //
+  // Get the real size of received buffer.
+  //
+  *BufferSize = Token.BufferSize;
+
+  Mtftp4->Configure (Mtftp4, NULL);
+
+  return Status;
+}
+
+
+/**
+  This function is to write the data of a file using Tftp.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to EFI_MTFTP4_CONFIG_DATA.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       Overwrite      Indicates whether to use the overwrite attribute.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Successfully write the data  into the special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval other              Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp4WriteFile (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     EFI_MTFTP4_CONFIG_DATA     *Config,
+  IN     UINT8                      *Filename,
+  IN     BOOLEAN                    Overwrite,
+  IN     UINTN                      *BlockSize,
+  IN     UINT8                      *BufferPtr,
+  IN OUT UINT64                     *BufferSize
+  )
+{
+  EFI_MTFTP4_PROTOCOL *Mtftp4;
+  EFI_MTFTP4_TOKEN    Token;
+  EFI_MTFTP4_OPTION   ReqOpt[1];
+  UINT32              OptCnt;
+  UINT8               OptBuf[128];
+  EFI_STATUS          Status;
+
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp4                    = Private->Mtftp4;
+  OptCnt                    = 0;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status  = Mtftp4->Configure (Mtftp4, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (BlockSize != NULL) {
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[0].ValueStr  = OptBuf;
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+    OptCnt++;
+  }
+
+  Token.Event           = NULL;
+  Token.OverrideData    = NULL;
+  Token.Filename        = Filename;
+  Token.ModeStr         = NULL;
+  Token.OptionCount     = OptCnt;
+  Token.OptionList      = ReqOpt;
+  Token.BufferSize      = *BufferSize;
+  Token.Buffer          = BufferPtr;
+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;
+  Token.TimeoutCallback = NULL;
+  Token.PacketNeeded    = NULL;
+
+  Status = Mtftp4->WriteFile (Mtftp4, &Token);
+  //
+  // Get the real size of transmitted buffer.
+  //
+  *BufferSize = Token.BufferSize;
+
+  Mtftp4->Configure (Mtftp4, NULL);
+
+  return Status;
+}
+
+
+/**
+  This function is to get data (file) from a directory using Tftp.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to EFI_MTFTP4_CONFIG_DATA.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+  @param[in]       DontUseBuffer  Indicates whether to use a receive buffer.
+
+  @retval EFI_SUCCES         Successfully obtained the data from the file included in the directory.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Operation failed.
+
+**/
+EFI_STATUS
+PxeBcMtftp4ReadDirectory (
+  IN     PXEBC_PRIVATE_DATA            *Private,
+  IN     EFI_MTFTP4_CONFIG_DATA        *Config,
+  IN     UINT8                         *Filename,
+  IN     UINTN                         *BlockSize,
+  IN     UINT8                         *BufferPtr,
+  IN OUT UINT64                        *BufferSize,
+  IN     BOOLEAN                       DontUseBuffer
+  )
+{
+  EFI_MTFTP4_PROTOCOL *Mtftp4;
+  EFI_MTFTP4_TOKEN    Token;
+  EFI_MTFTP4_OPTION   ReqOpt[1];
+  UINT32              OptCnt;
+  UINT8               OptBuf[128];
+  EFI_STATUS          Status;
+
+  Status                    = EFI_DEVICE_ERROR;
+  Mtftp4                    = Private->Mtftp4;
+  OptCnt                    = 0;
+  Config->InitialServerPort = PXEBC_BS_DOWNLOAD_PORT;
+
+  Status = Mtftp4->Configure (Mtftp4, Config);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (BlockSize != NULL) {
+    ReqOpt[0].OptionStr = (UINT8 *) mMtftpOptions[PXE_MTFTP_OPTION_BLKSIZE_INDEX];
+    ReqOpt[0].ValueStr  = OptBuf;
+    PxeBcUintnToAscDec (*BlockSize, ReqOpt[0].ValueStr, PXE_MTFTP_OPTBUF_MAXNUM_INDEX);
+    OptCnt++;
+  }
+
+  Token.Event         = NULL;
+  Token.OverrideData  = NULL;
+  Token.Filename      = Filename;
+  Token.ModeStr       = NULL;
+  Token.OptionCount   = OptCnt;
+  Token.OptionList    = ReqOpt;
+  Token.Context       = Private;
+
+  if (DontUseBuffer) {
+    Token.BufferSize  = 0;
+    Token.Buffer      = NULL;
+  } else {
+    Token.BufferSize  = *BufferSize;
+    Token.Buffer      = BufferPtr;
+  }
+
+  Token.CheckPacket     = PxeBcMtftp4CheckPacket;
+  Token.TimeoutCallback = NULL;
+  Token.PacketNeeded    = NULL;
+
+  Status = Mtftp4->ReadDirectory (Mtftp4, &Token);
+  //
+  // Get the real size of received buffer.
+  //
+  *BufferSize = Token.BufferSize;
+
+  Mtftp4->Configure (Mtftp4, NULL);
+
+  return Status;
+}
+
+
+/**
+  This function is wrapper to get the file size using TFTP.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to configure data.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in, out] BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Successfully obtained the size of file.
+  @retval EFI_NOT_FOUND      Parse the tftp options failed.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Did not obtain the size of the file.
+
+**/
+EFI_STATUS
+PxeBcTftpGetFileSize (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     VOID                       *Config,
+  IN     UINT8                      *Filename,
+  IN     UINTN                      *BlockSize,
+  IN OUT UINT64                     *BufferSize
+  )
+{
+  if (Private->PxeBc.Mode->UsingIpv6) {
+    return PxeBcMtftp6GetFileSize (
+             Private,
+             (EFI_MTFTP6_CONFIG_DATA *) Config,
+             Filename,
+             BlockSize,
+             BufferSize
+             );
+  } else {
+    return PxeBcMtftp4GetFileSize (
+             Private,
+             (EFI_MTFTP4_CONFIG_DATA *) Config,
+             Filename,
+             BlockSize,
+             BufferSize
+             );
+  }
+}
+
+
+/**
+  This function is a wrapper to get file using TFTP.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to config data.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in]      BufferPtr      Pointer to buffer.
+  @param[in, out] BufferSize     Pointer to buffer size.
+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.
+
+  @retval EFI_SUCCESS        Sucessfully read the data from the special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadFile (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     VOID                       *Config,
+  IN     UINT8                      *Filename,
+  IN     UINTN                      *BlockSize,
+  IN     UINT8                      *BufferPtr,
+  IN OUT UINT64                     *BufferSize,
+  IN     BOOLEAN                    DontUseBuffer
+  )
+{
+  if (Private->PxeBc.Mode->UsingIpv6) {
+    return PxeBcMtftp6ReadFile (
+             Private,
+             (EFI_MTFTP6_CONFIG_DATA *) Config,
+             Filename,
+             BlockSize,
+             BufferPtr,
+             BufferSize,
+             DontUseBuffer
+             );
+  } else {
+    return PxeBcMtftp4ReadFile (
+             Private,
+             (EFI_MTFTP4_CONFIG_DATA *) Config,
+             Filename,
+             BlockSize,
+             BufferPtr,
+             BufferSize,
+             DontUseBuffer
+             );
+  }
+}
+
+
+/**
+  This function is a wrapper to write file using TFTP.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to config data.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       Overwrite      Indicate whether with overwrite attribute.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Successfully wrote the data into a special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval other              Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpWriteFile (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     VOID                       *Config,
+  IN     UINT8                      *Filename,
+  IN     BOOLEAN                    Overwrite,
+  IN     UINTN                      *BlockSize,
+  IN     UINT8                      *BufferPtr,
+  IN OUT UINT64                     *BufferSize
+  )
+{
+  if (Private->PxeBc.Mode->UsingIpv6) {
+    return PxeBcMtftp6WriteFile (
+             Private,
+             (EFI_MTFTP6_CONFIG_DATA *) Config,
+             Filename,
+             Overwrite,
+             BlockSize,
+             BufferPtr,
+             BufferSize
+             );
+  } else {
+    return PxeBcMtftp4WriteFile (
+             Private,
+             (EFI_MTFTP4_CONFIG_DATA *) Config,
+             Filename,
+             Overwrite,
+             BlockSize,
+             BufferPtr,
+             BufferSize
+             );
+  }
+}
+
+
+/**
+  This function is a wrapper to get the data (file) from a directory using TFTP.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to config data.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+  @param[in]       DontUseBuffer  Indicatse whether to use a receive buffer.
+
+  @retval EFI_SUCCES         Successfully obtained the data from the file included in the directory.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Operation failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadDirectory (
+  IN     PXEBC_PRIVATE_DATA            *Private,
+  IN     VOID                          *Config,
+  IN     UINT8                         *Filename,
+  IN     UINTN                         *BlockSize,
+  IN     UINT8                         *BufferPtr,
+  IN OUT UINT64                        *BufferSize,
+  IN     BOOLEAN                       DontUseBuffer
+  )
+{
+  if (Private->PxeBc.Mode->UsingIpv6) {
+    return PxeBcMtftp6ReadDirectory (
+             Private,
+             (EFI_MTFTP6_CONFIG_DATA *) Config,
+             Filename,
+             BlockSize,
+             BufferPtr,
+             BufferSize,
+             DontUseBuffer
+             );
+  } else {
+    return PxeBcMtftp4ReadDirectory (
+             Private,
+             (EFI_MTFTP4_CONFIG_DATA *) Config,
+             Filename,
+             BlockSize,
+             BufferPtr,
+             BufferSize,
+             DontUseBuffer
+             );
+  }
+}
+
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
new file mode 100644
index 0000000..2935b0a
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcMtftp.h
@@ -0,0 +1,137 @@
+/** @file
+  Functions declaration related with Mtftp for UefiPxeBc Driver.
+
+  Copyright (c) 2007 - 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.
+
+**/
+
+#ifndef __EFI_PXEBC_MTFTP_H__
+#define __EFI_PXEBC_MTFTP_H__
+
+#define PXE_MTFTP_OPTION_BLKSIZE_INDEX     0
+#define PXE_MTFTP_OPTION_TIMEOUT_INDEX     1
+#define PXE_MTFTP_OPTION_TSIZE_INDEX       2
+#define PXE_MTFTP_OPTION_MULTICAST_INDEX   3
+#define PXE_MTFTP_OPTION_MAXIMUM_INDEX     4
+#define PXE_MTFTP_OPTBUF_MAXNUM_INDEX      128
+
+#define PXE_MTFTP_ERROR_STRING_LENGTH      127   // refer to definition of struct EFI_PXE_BASE_CODE_TFTP_ERROR.
+#define PXE_MTFTP_DEFAULT_BLOCK_SIZE       512   // refer to rfc-1350.
+
+
+/**
+  This function is wrapper to get the file size using TFTP.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to configure data.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in, out] BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Successfully obtained the size of file.
+  @retval EFI_NOT_FOUND      Parse the tftp ptions failed.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Did not obtain the size of the file.
+
+**/
+EFI_STATUS
+PxeBcTftpGetFileSize (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     VOID                       *Config,
+  IN     UINT8                      *Filename,
+  IN     UINTN                      *BlockSize,
+  IN OUT UINT64                     *BufferSize
+  );
+
+
+/**
+  This function is a wrapper to get a file using TFTP.
+
+  @param[in]      Private        Pointer to PxeBc private data.
+  @param[in]      Config         Pointer to config data.
+  @param[in]      Filename       Pointer to boot file name.
+  @param[in]      BlockSize      Pointer to required block size.
+  @param[in]      BufferPtr      Pointer to buffer.
+  @param[in, out] BufferSize     Pointer to buffer size.
+  @param[in]      DontUseBuffer  Indicates whether to use a receive buffer.
+
+  @retval EFI_SUCCESS        Successfully read the data from the special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Read data from file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadFile (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     VOID                       *Config,
+  IN     UINT8                      *Filename,
+  IN     UINTN                      *BlockSize,
+  IN     UINT8                      *BufferPtr,
+  IN OUT UINT64                     *BufferSize,
+  IN     BOOLEAN                    DontUseBuffer
+  );
+
+
+/**
+  This function is a wrapper to put file with TFTP.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to config data.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       Overwrite      Indicates whether to use an overwrite attribute.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+
+  @retval EFI_SUCCESS        Successfully wrote the data into the special file.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval other              Write data into file failed.
+
+**/
+EFI_STATUS
+PxeBcTftpWriteFile (
+  IN     PXEBC_PRIVATE_DATA         *Private,
+  IN     VOID                       *Config,
+  IN     UINT8                      *Filename,
+  IN     BOOLEAN                    Overwrite,
+  IN     UINTN                      *BlockSize,
+  IN     UINT8                      *BufferPtr,
+  IN OUT UINT64                     *BufferSize
+  );
+
+
+/**
+  This function is a wrapper to get the data (file) from a directory using TFTP.
+
+  @param[in]       Private        Pointer to PxeBc private data.
+  @param[in]       Config         Pointer to config data.
+  @param[in]       Filename       Pointer to boot file name.
+  @param[in]       BlockSize      Pointer to required block size.
+  @param[in]       BufferPtr      Pointer to buffer.
+  @param[in, out]  BufferSize     Pointer to buffer size.
+  @param[in]       DontUseBuffer  Indicates whether with a receive buffer.
+
+  @retval EFI_SUCCES         Successfully obtained the data from the file included in directory.
+  @retval EFI_DEVICE_ERROR   The network device encountered an error during this operation.
+  @retval Others             Operation failed.
+
+**/
+EFI_STATUS
+PxeBcTftpReadDirectory (
+  IN     PXEBC_PRIVATE_DATA            *Private,
+  IN     VOID                          *Config,
+  IN     UINT8                         *Filename,
+  IN     UINTN                         *BlockSize,
+  IN     UINT8                         *BufferPtr,
+  IN OUT UINT64                        *BufferSize,
+  IN     BOOLEAN                       DontUseBuffer
+  );
+#endif
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
new file mode 100644
index 0000000..e8d64a3
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.c
@@ -0,0 +1,1513 @@
+/** @file
+  Support functions implementation for UefiPxeBc Driver.
+
+  Copyright (c) 2007 - 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.
+
+**/
+
+#include "PxeBcImpl.h"
+
+
+/**
+  Flush the previous configration using the new station Ip address.
+
+  @param[in]   Private        The pointer to the PxeBc private data.
+  @param[in]   StationIp      The pointer to the station Ip address.
+  @param[in]   SubnetMask     The pointer to the subnet mask address for v4.
+
+  @retval EFI_SUCCESS         Successfully flushed the previous configuration.
+  @retval Others              Failed to flush using the new station Ip.
+
+**/
+EFI_STATUS
+PxeBcFlushStationIp (
+  PXEBC_PRIVATE_DATA       *Private,
+  EFI_IP_ADDRESS           *StationIp,
+  EFI_IP_ADDRESS           *SubnetMask     OPTIONAL
+  )
+{
+  EFI_PXE_BASE_CODE_MODE   *Mode;
+  EFI_STATUS               Status;
+
+  ASSERT (StationIp != NULL);
+
+  Mode   = Private->PxeBc.Mode;
+  Status = EFI_SUCCESS;
+
+  if (Mode->UsingIpv6) {
+
+    CopyMem (&Private->Udp6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+    CopyMem (&Private->Ip6CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+
+    //
+    // Reconfigure the Ip6 instance to capture background ICMP6 packets with new station Ip address.
+    //
+    Private->Ip6->Cancel (Private->Ip6, &Private->Icmp6Token);
+    Private->Ip6->Configure (Private->Ip6, NULL);
+
+    Status = Private->Ip6->Configure (Private->Ip6, &Private->Ip6CfgData);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    Status = Private->Ip6->Receive (Private->Ip6, &Private->Icmp6Token);
+  } else {
+    ASSERT (SubnetMask != NULL);
+    CopyMem (&Private->Udp4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));
+    CopyMem (&Private->Udp4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+    CopyMem (&Private->Ip4CfgData.StationAddress, StationIp, sizeof (EFI_IPv4_ADDRESS));
+    CopyMem (&Private->Ip4CfgData.SubnetMask, SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+
+    //
+    // Reconfigure the Ip4 instance to capture background ICMP packets with new station Ip address.
+    //
+    Private->Ip4->Cancel (Private->Ip4, &Private->IcmpToken);
+    Private->Ip4->Configure (Private->Ip4, NULL);
+
+    Status = Private->Ip4->Configure (Private->Ip4, &Private->Ip4CfgData);
+    if (EFI_ERROR (Status)) {
+      goto ON_EXIT;
+    }
+
+    Status = Private->Ip4->Receive (Private->Ip4, &Private->IcmpToken);
+  }
+
+ON_EXIT:
+  return Status;
+}
+
+
+/**
+  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
+PxeBcCommonNotify (
+  IN EFI_EVENT           Event,
+  IN VOID                *Context
+  )
+{
+  *((BOOLEAN *) Context) = TRUE;
+}
+
+
+/**
+  Do arp resolution from arp cache in PxeBcMode.
+
+  @param  Mode           The pointer to EFI_PXE_BASE_CODE_MODE.
+  @param  Ip4Addr        The Ip4 address for resolution.
+  @param  MacAddress     The resoluted MAC address if the resolution is successful.
+                         The value is undefined if the resolution fails.
+
+  @retval TRUE           Found an matched entry.
+  @retval FALSE          Did not find a matched entry.
+
+**/
+BOOLEAN
+PxeBcCheckArpCache (
+  IN  EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN  EFI_IPv4_ADDRESS          *Ip4Addr,
+  OUT EFI_MAC_ADDRESS           *MacAddress
+  )
+{
+  UINT32       Index;
+
+  ASSERT (!Mode->UsingIpv6);
+
+  //
+  // Check whether the current Arp cache in mode data contains this information or not.
+  //
+  for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {
+    if (EFI_IP4_EQUAL (&Mode->ArpCache[Index].IpAddr.v4, Ip4Addr)) {
+      CopyMem (
+        MacAddress,
+        &Mode->ArpCache[Index].MacAddr,
+        sizeof (EFI_MAC_ADDRESS)
+        );
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+
+/**
+  Update the arp cache periodically.
+
+  @param  Event              The pointer to EFI_PXE_BC_PROTOCOL.
+  @param  Context            Context of the timer event.
+
+**/
+VOID
+EFIAPI
+PxeBcArpCacheUpdate (
+  IN EFI_EVENT            Event,
+  IN VOID                 *Context
+  )
+{
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  EFI_ARP_FIND_DATA       *ArpEntry;
+  UINT32                  EntryLength;
+  UINT32                  EntryCount;
+  UINT32                  Index;
+  EFI_STATUS              Status;
+
+  Private = (PXEBC_PRIVATE_DATA *) Context;
+  Mode    = Private->PxeBc.Mode;
+
+  ASSERT (!Mode->UsingIpv6);
+
+  //
+  // Get the current Arp cache from Arp driver.
+  //
+  Status = Private->Arp->Find (
+                           Private->Arp,
+                           TRUE,
+                           NULL,
+                           &EntryLength,
+                           &EntryCount,
+                           &ArpEntry,
+                           TRUE
+                           );
+  if (EFI_ERROR (Status)) {
+    return;
+  }
+
+  //
+  // Update the Arp cache in mode data.
+  //
+  Mode->ArpCacheEntries = MIN (EntryCount, EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES);
+
+  for (Index = 0; Index < Mode->ArpCacheEntries; Index++) {
+    CopyMem (
+      &Mode->ArpCache[Index].IpAddr,
+      ArpEntry + 1,
+      ArpEntry->SwAddressLength
+      );
+    CopyMem (
+      &Mode->ArpCache[Index].MacAddr,
+      (UINT8 *) (ArpEntry + 1) + ArpEntry->SwAddressLength,
+      ArpEntry->HwAddressLength
+      );
+    ArpEntry = (EFI_ARP_FIND_DATA *) ((UINT8 *) ArpEntry + EntryLength);
+  }
+}
+
+
+/**
+  Notify function to handle the received ICMP message in DPC.
+
+  @param  Context               The PXEBC private data.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorDpcHandle (
+  IN VOID                      *Context
+  )
+{
+  EFI_STATUS                   Status;
+  EFI_IP4_RECEIVE_DATA         *RxData;
+  EFI_IP4_PROTOCOL             *Ip4;
+  PXEBC_PRIVATE_DATA           *Private;
+  EFI_PXE_BASE_CODE_MODE       *Mode;
+  UINT8                        Type;
+  UINTN                        Index;
+  UINT32                       CopiedLen;
+  UINT8                        *IcmpError;
+
+  Private = (PXEBC_PRIVATE_DATA *) Context;
+  Mode    = &Private->Mode;
+  Status  = Private->IcmpToken.Status;
+  RxData  = Private->IcmpToken.Packet.RxData;
+  Ip4     = Private->Ip4;
+
+  ASSERT (!Mode->UsingIpv6);
+
+  if (Status == EFI_ABORTED) {
+    //
+    // It's triggered by user cancellation.
+    //
+    return;
+  }
+
+  if (RxData == NULL) {
+    goto ON_EXIT;
+  }
+
+  if (Status != EFI_ICMP_ERROR) {
+    //
+    // The return status should be recognized as EFI_ICMP_ERROR.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  if (EFI_IP4 (RxData->Header->SourceAddress) != 0 &&
+      !NetIp4IsUnicast (EFI_NTOHL (RxData->Header->SourceAddress), 0)) {
+    //
+    // The source address of the received packet should be a valid unicast address.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  if (!EFI_IP4_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v4)) {
+    //
+    // The destination address of the received packet should be equal to the host address.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  if (RxData->Header->Protocol != EFI_IP_PROTO_ICMP) {
+    //
+    // The protocol value in the header of the receveid packet should be EFI_IP_PROTO_ICMP.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);
+
+  if (Type != ICMP_DEST_UNREACHABLE &&
+      Type != ICMP_SOURCE_QUENCH &&
+      Type != ICMP_REDIRECT &&
+      Type != ICMP_TIME_EXCEEDED &&
+      Type != ICMP_PARAMETER_PROBLEM) {
+    //
+    // The type of the receveid ICMP message should be ICMP_ERROR_MESSAGE.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  //
+  // Copy the right ICMP error message into mode data.
+  //
+  CopiedLen = 0;
+  IcmpError = (UINT8 *) &Mode->IcmpError;
+
+  for (Index = 0; Index < RxData->FragmentCount; Index++) {
+    CopiedLen += RxData->FragmentTable[Index].FragmentLength;
+    if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {
+      CopyMem (
+        IcmpError,
+        RxData->FragmentTable[Index].FragmentBuffer,
+        RxData->FragmentTable[Index].FragmentLength
+        );
+    } else {
+      CopyMem (
+        IcmpError,
+        RxData->FragmentTable[Index].FragmentBuffer,
+        CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+        );
+    }
+    IcmpError += CopiedLen;
+  }
+
+ON_EXIT:
+  Private->IcmpToken.Status = EFI_NOT_READY;
+  Ip4->Receive (Ip4, &Private->IcmpToken);
+}
+
+
+/**
+  Callback function to update the latest ICMP6 error message.
+
+  @param  Event                 The event signalled.
+  @param  Context               The context passed in using the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorUpdate (
+  IN EFI_EVENT            Event,
+  IN VOID                 *Context
+  )
+{
+  QueueDpc (TPL_CALLBACK, PxeBcIcmpErrorDpcHandle, Context);
+}
+
+
+/**
+  Notify function to handle the received ICMP6 message in DPC.
+
+  @param  Context               The PXEBC private data.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorDpcHandle (
+  IN VOID                 *Context
+  )
+{
+  PXEBC_PRIVATE_DATA      *Private;
+  EFI_IP6_RECEIVE_DATA    *RxData;
+  EFI_IP6_PROTOCOL        *Ip6;
+  EFI_PXE_BASE_CODE_MODE  *Mode;
+  EFI_STATUS              Status;
+  UINTN                   Index;
+  UINT8                   Type;
+  UINT32                  CopiedLen;
+  UINT8                   *Icmp6Error;
+
+  Private = (PXEBC_PRIVATE_DATA *) Context;
+  Mode    = &Private->Mode;
+  Status  = Private->Icmp6Token.Status;
+  RxData  = Private->Icmp6Token.Packet.RxData;
+  Ip6     = Private->Ip6;
+
+  ASSERT (Mode->UsingIpv6);
+
+  if (Status == EFI_ABORTED) {
+    //
+    // It's triggered by user cancellation.
+    //
+    return;
+  }
+
+  if (RxData == NULL) {
+    goto ON_EXIT;
+  }
+
+  if (Status != EFI_ICMP_ERROR) {
+    //
+    // The return status should be recognized as EFI_ICMP_ERROR.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  if (!NetIp6IsValidUnicast (&RxData->Header->SourceAddress)) {
+    //
+    // The source address of the received packet should be a valid unicast address.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  if (!NetIp6IsUnspecifiedAddr (&Mode->StationIp.v6) &&
+      !EFI_IP6_EQUAL (&RxData->Header->DestinationAddress, &Mode->StationIp.v6)) {
+    //
+    // The destination address of the received packet should be equal to the host address.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  if (RxData->Header->NextHeader != IP6_ICMP) {
+    //
+    // The nextheader in the header of the receveid packet should be IP6_ICMP.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  Type = *((UINT8 *) RxData->FragmentTable[0].FragmentBuffer);
+
+  if (Type != ICMP_V6_DEST_UNREACHABLE &&
+      Type != ICMP_V6_PACKET_TOO_BIG &&
+      Type != ICMP_V6_PACKET_TOO_BIG &&
+      Type != ICMP_V6_PARAMETER_PROBLEM) {
+    //
+    // The type of the receveid packet should be an ICMP6 error message.
+    //
+    gBS->SignalEvent (RxData->RecycleSignal);
+    goto ON_EXIT;
+  }
+
+  //
+  // Copy the right ICMP6 error message into mode data.
+  //
+  CopiedLen  = 0;
+  Icmp6Error = (UINT8 *) &Mode->IcmpError;
+
+  for (Index = 0; Index < RxData->FragmentCount; Index++) {
+    CopiedLen += RxData->FragmentTable[Index].FragmentLength;
+    if (CopiedLen <= sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)) {
+      CopyMem (
+        Icmp6Error,
+        RxData->FragmentTable[Index].FragmentBuffer,
+        RxData->FragmentTable[Index].FragmentLength
+        );
+    } else {
+      CopyMem (
+        Icmp6Error,
+        RxData->FragmentTable[Index].FragmentBuffer,
+        CopiedLen - sizeof (EFI_PXE_BASE_CODE_ICMP_ERROR)
+        );
+    }
+    Icmp6Error += CopiedLen;
+  }
+
+ON_EXIT:
+  Private->Icmp6Token.Status = EFI_NOT_READY;
+  Ip6->Receive (Ip6, &Private->Icmp6Token);
+}
+
+
+/**
+  Callback function to update the latest ICMP6 error message.
+
+  @param  Event                 The event signalled.
+  @param  Context               The context passed in using the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorUpdate (
+  IN EFI_EVENT               Event,
+  IN VOID                    *Context
+  )
+{
+  QueueDpc (TPL_CALLBACK, PxeBcIcmp6ErrorDpcHandle, Context);
+}
+
+
+/**
+  This function is to configure a UDPv4 instance for UdpWrite.
+
+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.
+  @param[in]       StationIp            The pointer to the station address.
+  @param[in]       SubnetMask           The pointer to the subnet mask.
+  @param[in]       Gateway              The pointer to the gateway address.
+  @param[in, out]  SrcPort              The pointer to the source port.
+  @param[in]       DoNotFragment        If TRUE, fragment is not enabled.
+                                        Otherwise, fragment is enabled.
+
+  @retval          EFI_SUCCESS          Successfully configured this instance.
+  @retval          Others               Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp4Write (
+  IN     EFI_UDP4_PROTOCOL  *Udp4,
+  IN     EFI_IPv4_ADDRESS   *StationIp,
+  IN     EFI_IPv4_ADDRESS   *SubnetMask,
+  IN     EFI_IPv4_ADDRESS   *Gateway,
+  IN OUT UINT16             *SrcPort,
+  IN     BOOLEAN            DoNotFragment
+  )
+{
+  EFI_UDP4_CONFIG_DATA  Udp4CfgData;
+  EFI_STATUS            Status;
+
+  ZeroMem (&Udp4CfgData, sizeof (Udp4CfgData));
+
+  Udp4CfgData.TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;
+  Udp4CfgData.ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;
+  Udp4CfgData.TypeOfService      = DEFAULT_ToS;
+  Udp4CfgData.TimeToLive         = DEFAULT_TTL;
+  Udp4CfgData.AllowDuplicatePort = TRUE;
+  Udp4CfgData.DoNotFragment      = DoNotFragment;
+
+  CopyMem (&Udp4CfgData.StationAddress, StationIp, sizeof (*StationIp));
+  CopyMem (&Udp4CfgData.SubnetMask, SubnetMask, sizeof (*SubnetMask));
+
+  Udp4CfgData.StationPort = *SrcPort;
+
+  //
+  // Reset the UDPv4 instance.
+  //
+  Udp4->Configure (Udp4, NULL);
+
+  Status = Udp4->Configure (Udp4, &Udp4CfgData);
+  if (!EFI_ERROR (Status) && !EFI_IP4_EQUAL (Gateway, &mZeroIp4Addr)) {
+    //
+    // The basic configuration is OK, need to add the default route entry
+    //
+    Status = Udp4->Routes (Udp4, FALSE, &mZeroIp4Addr, &mZeroIp4Addr, Gateway);
+    if (EFI_ERROR (Status)) {
+      Udp4->Configure (Udp4, NULL);
+    }
+  }
+
+  if (!EFI_ERROR (Status) && *SrcPort == 0) {
+    Udp4->GetModeData (Udp4, &Udp4CfgData, NULL, NULL, NULL);
+    *SrcPort = Udp4CfgData.StationPort;
+  }
+
+  return Status;
+}
+
+
+/**
+  This function is to configure a UDPv6 instance for UdpWrite.
+
+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.
+  @param[in]       StationIp            The pointer to the station address.
+  @param[in, out]  SrcPort              The pointer to the source port.
+
+  @retval          EFI_SUCCESS          Successfully configured this instance.
+  @retval          Others               Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp6Write (
+  IN     EFI_UDP6_PROTOCOL  *Udp6,
+  IN     EFI_IPv6_ADDRESS   *StationIp,
+  IN OUT UINT16             *SrcPort
+  )
+{
+  EFI_UDP6_CONFIG_DATA  CfgData;
+  EFI_STATUS            Status;
+
+  ZeroMem (&CfgData, sizeof (EFI_UDP6_CONFIG_DATA));
+
+  CfgData.ReceiveTimeout     = PXEBC_DEFAULT_LIFETIME;
+  CfgData.TransmitTimeout    = PXEBC_DEFAULT_LIFETIME;
+  CfgData.HopLimit           = PXEBC_DEFAULT_HOPLIMIT;
+  CfgData.AllowDuplicatePort = TRUE;
+  CfgData.StationPort        = *SrcPort;
+
+  CopyMem (&CfgData.StationAddress, StationIp, sizeof (EFI_IPv6_ADDRESS));
+
+  //
+  // Reset the UDPv6 instance.
+  //
+  Udp6->Configure (Udp6, NULL);
+
+  Status = Udp6->Configure (Udp6, &CfgData);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  if (!EFI_ERROR (Status) && *SrcPort == 0) {
+    Udp6->GetModeData (Udp6, &CfgData, NULL, NULL, NULL);
+    *SrcPort = CfgData.StationPort;
+  }
+
+  return Status;
+}
+
+
+/**
+  This function is to configure a UDPv4 instance for UdpWrite.
+
+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.
+  @param[in]       Session              The pointer to the UDP4 session data.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       Gateway              The pointer to the gateway address.
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be
+                                        prefixed to the data at BufferPtr.
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.
+  @param[in]       BufferPtr            A pointer to the data to be written.
+
+  @retval          EFI_SUCCESS          Successfully send out data using Udp4Write.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Write (
+  IN EFI_UDP4_PROTOCOL       *Udp4,
+  IN EFI_UDP4_SESSION_DATA   *Session,
+  IN EFI_EVENT               TimeoutEvent,
+  IN EFI_IPv4_ADDRESS        *Gateway      OPTIONAL,
+  IN UINTN                   *HeaderSize   OPTIONAL,
+  IN VOID                    *HeaderPtr    OPTIONAL,
+  IN UINTN                   *BufferSize,
+  IN VOID                    *BufferPtr
+  )
+{
+  EFI_UDP4_COMPLETION_TOKEN Token;
+  EFI_UDP4_TRANSMIT_DATA    *TxData;
+  UINT32                    TxLength;
+  UINT32                    FragCount;
+  UINT32                    DataLength;
+  BOOLEAN                   IsDone;
+  EFI_STATUS                Status;
+
+  //
+  // Arrange one fragment buffer for data, and another fragment buffer for header if has.
+  //
+  FragCount = (HeaderSize != NULL) ? 2 : 1;
+  TxLength  = sizeof (EFI_UDP4_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP4_FRAGMENT_DATA);
+  TxData    = (EFI_UDP4_TRANSMIT_DATA *) AllocateZeroPool (TxLength);
+  if (TxData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  TxData->FragmentCount                               = FragCount;
+  TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;
+  TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;
+  DataLength                                          = (UINT32) *BufferSize;
+
+  if (HeaderSize != NULL) {
+    TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;
+    TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;
+    DataLength                             += (UINT32) *HeaderSize;
+  }
+
+  if (Gateway != NULL) {
+    TxData->GatewayAddress  = Gateway;
+  }
+
+  TxData->UdpSessionData  = Session;
+  TxData->DataLength      = DataLength;
+  Token.Packet.TxData     = TxData;
+  Token.Status            = EFI_NOT_READY;
+  IsDone                  = FALSE;
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  PxeBcCommonNotify,
+                  &IsDone,
+                  &Token.Event
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  Status = Udp4->Transmit (Udp4, &Token);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+  //
+  while (!IsDone &&
+         Token.Status == EFI_NOT_READY &&
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+    Udp4->Poll (Udp4);
+  }
+
+  Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;
+
+ON_EXIT:
+  if (Token.Event != NULL) {
+    gBS->CloseEvent (Token.Event);
+  }
+  FreePool (TxData);
+
+  return Status;
+}
+
+
+/**
+  This function is to configure a UDPv4 instance for UdpWrite.
+
+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.
+  @param[in]       Session              The pointer to the UDP6 session data.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be
+                                        prefixed to the data at BufferPtr.
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.
+  @param[in]       BufferPtr            A pointer to the data to be written.
+
+  @retval          EFI_SUCCESS          Successfully sent out data using Udp6Write.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Write (
+  IN EFI_UDP6_PROTOCOL       *Udp6,
+  IN EFI_UDP6_SESSION_DATA   *Session,
+  IN EFI_EVENT               TimeoutEvent,
+  IN UINTN                   *HeaderSize   OPTIONAL,
+  IN VOID                    *HeaderPtr    OPTIONAL,
+  IN UINTN                   *BufferSize,
+  IN VOID                    *BufferPtr
+  )
+{
+  EFI_UDP6_COMPLETION_TOKEN Token;
+  EFI_UDP6_TRANSMIT_DATA    *TxData;
+  UINT32                    TxLength;
+  UINT32                    FragCount;
+  UINT32                    DataLength;
+  BOOLEAN                   IsDone;
+  EFI_STATUS                Status;
+
+  //
+  // Arrange one fragment buffer for data, and another fragment buffer for header if has.
+  //
+  FragCount = (HeaderSize != NULL) ? 2 : 1;
+  TxLength  = sizeof (EFI_UDP6_TRANSMIT_DATA) + (FragCount - 1) * sizeof (EFI_UDP6_FRAGMENT_DATA);
+  TxData    = (EFI_UDP6_TRANSMIT_DATA *) AllocateZeroPool (TxLength);
+  if (TxData == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  TxData->FragmentCount                               = FragCount;
+  TxData->FragmentTable[FragCount - 1].FragmentLength = (UINT32) *BufferSize;
+  TxData->FragmentTable[FragCount - 1].FragmentBuffer = BufferPtr;
+  DataLength                                          = (UINT32) *BufferSize;
+
+  if (HeaderSize != NULL) {
+    TxData->FragmentTable[0].FragmentLength = (UINT32) *HeaderSize;
+    TxData->FragmentTable[0].FragmentBuffer = HeaderPtr;
+    DataLength                             += (UINT32) *HeaderSize;
+  }
+
+  TxData->UdpSessionData  = Session;
+  TxData->DataLength      = DataLength;
+  Token.Packet.TxData     = TxData;
+  Token.Status            = EFI_NOT_READY;
+  IsDone                  = FALSE;
+
+  Status = gBS->CreateEvent (
+                  EVT_NOTIFY_SIGNAL,
+                  TPL_NOTIFY,
+                  PxeBcCommonNotify,
+                  &IsDone,
+                  &Token.Event
+                  );
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  Status = Udp6->Transmit (Udp6, &Token);
+  if (EFI_ERROR (Status)) {
+    goto ON_EXIT;
+  }
+
+  //
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+  //
+  while (!IsDone &&
+         Token.Status == EFI_NOT_READY &&
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+    Udp6->Poll (Udp6);
+  }
+
+  Status = (Token.Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token.Status;
+
+ON_EXIT:
+  if (Token.Event != NULL) {
+    gBS->CloseEvent (Token.Event);
+  }
+  FreePool (TxData);
+
+  return Status;
+}
+
+
+/**
+  Check the received packet using the Ip filter.
+
+  @param[in]  Mode                The pointer to the mode data of PxeBc.
+  @param[in]  Session             The pointer to the current UDPv4 session.
+  @param[in]  OpFlags             Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Passed the Ip filter successfully.
+  @retval     FALSE               Failed to pass the Ip filter.
+
+**/
+BOOLEAN
+PxeBcCheckByIpFilter (
+  IN EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN VOID                      *Session,
+  IN UINT16                    OpFlags
+  )
+{
+  EFI_IP_ADDRESS               DestinationIp;
+  UINTN                        Index;
+
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_FILTER) == 0) {
+    return TRUE;
+  }
+
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS) != 0) {
+    return TRUE;
+  }
+
+  //
+  // Convert the destination address in session data to host order.
+  //
+  if (Mode->UsingIpv6) {
+    CopyMem (
+      &DestinationIp,
+      &((EFI_UDP6_SESSION_DATA *) Session)->DestinationAddress,
+      sizeof (EFI_IPv6_ADDRESS)
+      );
+    NTOHLLL (&DestinationIp.v6);
+  } else {
+    ZeroMem (&DestinationIp, sizeof (EFI_IP_ADDRESS));
+    CopyMem (
+      &DestinationIp,
+      &((EFI_UDP4_SESSION_DATA *) Session)->DestinationAddress,
+      sizeof (EFI_IPv4_ADDRESS)
+      );
+    EFI_NTOHL (DestinationIp);
+  }
+
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST) != 0 &&
+      (IP4_IS_MULTICAST (DestinationIp.Addr[0]) ||
+       IP6_IS_MULTICAST (&DestinationIp))) {
+    return TRUE;
+  }
+
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_BROADCAST) != 0 &&
+      IP4_IS_LOCAL_BROADCAST (DestinationIp.Addr[0])) {
+    ASSERT (!Mode->UsingIpv6);
+    return TRUE;
+  }
+
+  if ((Mode->IpFilter.Filters & EFI_PXE_BASE_CODE_IP_FILTER_STATION_IP) != 0 &&
+      (EFI_IP4_EQUAL (&Mode->StationIp.v4, &DestinationIp) ||
+       EFI_IP6_EQUAL (&Mode->StationIp.v6, &DestinationIp))) {
+    //
+    // Matched if the dest address is equal to the station address.
+    //
+    return TRUE;
+  }
+
+  for (Index = 0; Index < Mode->IpFilter.IpCnt; Index++) {
+    ASSERT (Index < EFI_PXE_BASE_CODE_MAX_IPCNT);
+    if (EFI_IP4_EQUAL (&Mode->IpFilter.IpList[Index].v4, &DestinationIp) ||
+        EFI_IP6_EQUAL (&Mode->IpFilter.IpList[Index].v6, &DestinationIp)) {
+      //
+      // Matched if the dest address is equal to any of address in the filter list.
+      //
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+
+/**
+  Filter the received packet using the destination Ip.
+
+  @param[in]       Mode           The pointer to the mode data of PxeBc.
+  @param[in]       Session        The pointer to the current UDPv4 session.
+  @param[in, out]  DestIp         The pointer to the destination Ip address.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Passed the IPv4 filter successfully.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestIp (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT EFI_IP_ADDRESS            *DestIp,
+  IN     UINT16                    OpFlags
+  )
+{
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_IP) != 0) {
+    //
+    // Copy the destination address from the received packet if accept any.
+    //
+    if (DestIp != NULL) {
+      if (Mode->UsingIpv6) {
+        CopyMem (
+          DestIp,
+          &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress,
+          sizeof (EFI_IPv6_ADDRESS)
+          );
+      } else {
+        ZeroMem (DestIp, sizeof (EFI_IP_ADDRESS));
+        CopyMem (
+          DestIp,
+          &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress,
+          sizeof (EFI_IPv4_ADDRESS)
+          );
+      }
+
+    }
+    return TRUE;
+  } else if (DestIp != NULL &&
+             (EFI_IP4_EQUAL (DestIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||
+              EFI_IP6_EQUAL (DestIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress))) {
+    //
+    // The destination address in the received packet is matched if present.
+    //
+    return TRUE;
+  } else if (EFI_IP4_EQUAL (&Mode->StationIp, &((EFI_UDP4_SESSION_DATA *)Session)->DestinationAddress) ||
+             EFI_IP6_EQUAL (&Mode->StationIp, &((EFI_UDP6_SESSION_DATA *)Session)->DestinationAddress)) {
+    //
+    // The destination address in the received packet is equal to the host address.
+    //
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+/**
+  Check the received packet using the destination port.
+
+  @param[in]       Mode           The pointer to the mode data of PxeBc.
+  @param[in]       Session        The pointer to the current UDPv4 session.
+  @param[in, out]  DestPort       The pointer to the destination port.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Passed the IPv4 filter successfully.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestPort (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT UINT16                    *DestPort,
+  IN     UINT16                    OpFlags
+  )
+{
+  UINT16       Port;
+
+  if (Mode->UsingIpv6) {
+    Port = ((EFI_UDP6_SESSION_DATA *) Session)->DestinationPort;
+  } else {
+    Port = ((EFI_UDP4_SESSION_DATA *) Session)->DestinationPort;
+  }
+
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_DEST_PORT) != 0) {
+    //
+    // Return the destination port in the received packet if accept any.
+    //
+    if (DestPort != NULL) {
+      *DestPort = Port;
+    }
+    return TRUE;
+  } else if (DestPort != NULL && *DestPort == Port) {
+    //
+    // The destination port in the received packet is matched if present.
+    //
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+/**
+  Filter the received packet using the source Ip.
+
+  @param[in]       Mode           The pointer to the mode data of PxeBc.
+  @param[in]       Session        The pointer to the current UDPv4 session.
+  @param[in, out]  SrcIp          The pointer to the source Ip address.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Passed the IPv4 filter successfully.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcIp (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT EFI_IP_ADDRESS            *SrcIp,
+  IN     UINT16                    OpFlags
+  )
+{
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_IP) != 0) {
+    //
+    // Copy the source address from the received packet if accept any.
+    //
+    if (SrcIp != NULL) {
+      if (Mode->UsingIpv6) {
+        CopyMem (
+          SrcIp,
+          &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress,
+          sizeof (EFI_IPv6_ADDRESS)
+          );
+      } else {
+        ZeroMem (SrcIp, sizeof (EFI_IP_ADDRESS));
+        CopyMem (
+          SrcIp,
+          &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress,
+          sizeof (EFI_IPv4_ADDRESS)
+          );
+      }
+
+    }
+    return TRUE;
+  } else if (SrcIp != NULL &&
+             (EFI_IP4_EQUAL (SrcIp, &((EFI_UDP4_SESSION_DATA *)Session)->SourceAddress) ||
+              EFI_IP6_EQUAL (SrcIp, &((EFI_UDP6_SESSION_DATA *)Session)->SourceAddress))) {
+    //
+    // The source address in the received packet is matched if present.
+    //
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+/**
+  Filter the received packet using the source port.
+
+  @param[in]       Mode           The pointer to the mode data of PxeBc.
+  @param[in]       Session        The pointer to the current UDPv4 session.
+  @param[in, out]  SrcPort        The pointer to the source port.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Passed the IPv4 filter successfully.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcPort (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT UINT16                    *SrcPort,
+  IN     UINT16                    OpFlags
+  )
+{
+  UINT16       Port;
+
+  if (Mode->UsingIpv6) {
+    Port = ((EFI_UDP6_SESSION_DATA *) Session)->SourcePort;
+  } else {
+    Port = ((EFI_UDP4_SESSION_DATA *) Session)->SourcePort;
+  }
+
+  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_ANY_SRC_PORT) != 0) {
+    //
+    // Return the source port in the received packet if accept any.
+    //
+    if (SrcPort != NULL) {
+      *SrcPort = Port;
+    }
+    return TRUE;
+  } else if (SrcPort != NULL && *SrcPort == Port) {
+    //
+    // The source port in the received packet is matched if present.
+    //
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+
+/**
+  This function is to receive packet using Udp4Read.
+
+  @param[in]       Udp4                 The pointer to EFI_UDP4_PROTOCOL.
+  @param[in]       Token                The pointer to EFI_UDP4_COMPLETION_TOKEN.
+  @param[in]       Mode                 The pointer to EFI_PXE_BASE_CODE_MODE.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       OpFlags              The UDP operation flags.
+  @param[in]       IsDone               The pointer to the IsDone flag.
+  @param[out]      IsMatched            The pointer to the IsMatched flag.
+  @param[in, out]  DestIp               The pointer to the destination address.
+  @param[in, out]  DestPort             The pointer to the destination port.
+  @param[in, out]  SrcIp                The pointer to the source address.
+  @param[in, out]  SrcPort              The pointer to the source port.
+
+  @retval          EFI_SUCCESS          Successfully read the data using Udp4.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Read (
+  IN     EFI_UDP4_PROTOCOL            *Udp4,
+  IN     EFI_UDP4_COMPLETION_TOKEN    *Token,
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,
+  IN     EFI_EVENT                    TimeoutEvent,
+  IN     UINT16                       OpFlags,
+  IN     BOOLEAN                      *IsDone,
+     OUT BOOLEAN                      *IsMatched,
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL
+  )
+{
+  EFI_UDP4_RECEIVE_DATA     *RxData;
+  EFI_UDP4_SESSION_DATA     *Session;
+  EFI_STATUS                Status;
+
+  Token->Status = EFI_NOT_READY;
+  *IsDone       = FALSE;
+
+  Status = Udp4->Receive (Udp4, Token);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+  //
+  while (!(*IsDone) &&
+         Token->Status == EFI_NOT_READY &&
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+    //
+    // Poll the token utill reply/ICMPv6 error message received or timeout.
+    //
+    Udp4->Poll (Udp4);
+    if (Token->Status == EFI_ICMP_ERROR ||
+        Token->Status == EFI_NETWORK_UNREACHABLE ||
+        Token->Status == EFI_HOST_UNREACHABLE ||
+        Token->Status == EFI_PROTOCOL_UNREACHABLE ||
+        Token->Status == EFI_PORT_UNREACHABLE) {
+      break;
+    }
+  }
+
+  Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // check whether this packet matches the filters
+    //
+    RxData    = Token->Packet.RxData;
+    Session   = &RxData->UdpSession;
+
+    *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);
+    }
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);
+    }
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);
+    }
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);
+    }
+
+    if (!(*IsMatched)) {
+      //
+      // Recycle the receiving buffer if not matched.
+      //
+      gBS->SignalEvent (RxData->RecycleSignal);
+    }
+  }
+
+  return Status;
+}
+
+
+/**
+  This function is to receive packets using Udp6Read.
+
+  @param[in]       Udp6                 The pointer to EFI_UDP6_PROTOCOL.
+  @param[in]       Token                The pointer to EFI_UDP6_COMPLETION_TOKEN.
+  @param[in]       Mode                 The pointer to EFI_PXE_BASE_CODE_MODE.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       OpFlags              The UDP operation flags.
+  @param[in]       IsDone               The pointer to the IsDone flag.
+  @param[out]      IsMatched            The pointer to the IsMatched flag.
+  @param[in, out]  DestIp               The pointer to the destination address.
+  @param[in, out]  DestPort             The pointer to the destination port.
+  @param[in, out]  SrcIp                The pointer to the source address.
+  @param[in, out]  SrcPort              The pointer to the source port.
+
+  @retval          EFI_SUCCESS          Successfully read data using Udp6.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Read (
+  IN     EFI_UDP6_PROTOCOL            *Udp6,
+  IN     EFI_UDP6_COMPLETION_TOKEN    *Token,
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,
+  IN     EFI_EVENT                    TimeoutEvent,
+  IN     UINT16                       OpFlags,
+  IN     BOOLEAN                      *IsDone,
+     OUT BOOLEAN                      *IsMatched,
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL
+  )
+{
+  EFI_UDP6_RECEIVE_DATA     *RxData;
+  EFI_UDP6_SESSION_DATA     *Session;
+  EFI_STATUS                Status;
+
+  Token->Status = EFI_NOT_READY;
+  *IsDone       = FALSE;
+
+  Status = Udp6->Receive (Udp6, Token);
+  if (EFI_ERROR (Status)) {
+    return Status;
+  }
+
+  //
+  // Poll the UDPv6 read instance if no packet received and no timeout triggered.
+  //
+  while (!(*IsDone) &&
+         Token->Status == EFI_NOT_READY &&
+         EFI_ERROR (gBS->CheckEvent (TimeoutEvent))) {
+    //
+    // Poll the token utill reply/ICMPv6 error message received or timeout.
+    //
+    Udp6->Poll (Udp6);
+    if (Token->Status == EFI_ICMP_ERROR ||
+        Token->Status == EFI_NETWORK_UNREACHABLE ||
+        Token->Status == EFI_HOST_UNREACHABLE ||
+        Token->Status == EFI_PROTOCOL_UNREACHABLE ||
+        Token->Status == EFI_PORT_UNREACHABLE) {
+      break;
+    }
+  }
+
+  Status = (Token->Status == EFI_NOT_READY) ? EFI_TIMEOUT : Token->Status;
+
+  if (!EFI_ERROR (Status)) {
+    //
+    // check whether this packet matches the filters
+    //
+    RxData    = Token->Packet.RxData;
+    Session   = &RxData->UdpSession;
+
+    *IsMatched = PxeBcCheckByIpFilter (Mode, Session, OpFlags);
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcCheckByDestIp (Mode, Session, DestIp, OpFlags);
+    }
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcCheckByDestPort (Mode, Session, DestPort, OpFlags);
+    }
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcFilterBySrcIp (Mode, Session, SrcIp, OpFlags);
+    }
+
+    if (*IsMatched) {
+      *IsMatched = PxeBcFilterBySrcPort (Mode, Session, SrcPort, OpFlags);
+    }
+
+    if (!(*IsMatched)) {
+      //
+      // Recycle the receiving buffer if not matched.
+      //
+      gBS->SignalEvent (RxData->RecycleSignal);
+    }
+  }
+
+  return Status;
+}
+
+
+/**
+  This function is to display the IPv4 address.
+
+  @param[in]  Ip        The pointer to the IPv4 address.
+
+**/
+VOID
+PxeBcShowIp4Addr (
+  IN EFI_IPv4_ADDRESS   *Ip
+  )
+{
+  UINTN                 Index;
+
+  for (Index = 0; Index < 4; Index++) {
+    AsciiPrint ("%d", Ip->Addr[Index]);
+    if (Index < 3) {
+      AsciiPrint (".");
+    }
+  }
+}
+
+
+/**
+  This function is to display the IPv6 address.
+
+  @param[in]  Ip        The pointer to the IPv6 address.
+
+**/
+VOID
+PxeBcShowIp6Addr (
+  IN EFI_IPv6_ADDRESS   *Ip
+  )
+{
+  UINTN                 Index;
+
+  for (Index = 0; Index < 16; Index++) {
+
+    if (Ip->Addr[Index] != 0) {
+      AsciiPrint ("%x", Ip->Addr[Index]);
+    }
+    Index++;
+    if (Index > 15) {
+      return;
+    }
+    if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {
+      AsciiPrint ("0");
+    }
+    AsciiPrint ("%x", Ip->Addr[Index]);
+    if (Index < 15) {
+      AsciiPrint (":");
+    }
+  }
+}
+
+
+/**
+  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
+PxeBcUintnToAscDecWithFormat (
+  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 convert a UINTN to a ASCII string, and return the
+  actual length of the buffer.
+
+  @param[in]  Number         Numeric value to be converted.
+  @param[in]  Buffer         The pointer to the buffer for ASCII string.
+  @param[in]  BufferSize     The maxsize of the buffer.
+
+  @return     Length         The actual length of the ASCII string.
+
+**/
+UINTN
+PxeBcUintnToAscDec (
+  IN UINTN               Number,
+  IN UINT8               *Buffer,
+  IN UINTN               BufferSize
+  )
+{
+  UINTN           Index;
+  UINTN           Length;
+  CHAR8           TempStr[64];
+
+  Index           = 63;
+  TempStr[Index]  = 0;
+
+  do {
+    Index--;
+    TempStr[Index] = (CHAR8) ('0' + (Number % 10));
+    Number         = (UINTN) (Number / 10);
+  } while (Number != 0);
+
+  AsciiStrCpyS ((CHAR8 *) Buffer, BufferSize, &TempStr[Index]);
+
+  Length = AsciiStrLen ((CHAR8 *) Buffer);
+
+  return Length;
+}
+
+
+/**
+  This function is to convert unicode hex number to a UINT8.
+
+  @param[out]  Digit                   The converted UINT8 for output.
+  @param[in]   Char                    The unicode hex number to be converted.
+
+  @retval      EFI_SUCCESS             Successfully converted the unicode hex.
+  @retval      EFI_INVALID_PARAMETER   Failed to convert the unicode hex.
+
+**/
+EFI_STATUS
+PxeBcUniHexToUint8 (
+  OUT UINT8                *Digit,
+  IN  CHAR16               Char
+  )
+{
+  if ((Char >= L'0') && (Char <= L'9')) {
+    *Digit = (UINT8) (Char - L'0');
+    return EFI_SUCCESS;
+  }
+
+  if ((Char >= L'A') && (Char <= L'F')) {
+    *Digit = (UINT8) (Char - L'A' + 0x0A);
+    return EFI_SUCCESS;
+  }
+
+  if ((Char >= L'a') && (Char <= L'f')) {
+    *Digit = (UINT8) (Char - L'a' + 0x0A);
+    return EFI_SUCCESS;
+  }
+
+  return EFI_INVALID_PARAMETER;
+}
+
+/**
+  Calculate the elapsed time.
+
+  @param[in]      Private      The pointer to PXE private data
+
+**/
+VOID
+CalcElapsedTime (
+  IN     PXEBC_PRIVATE_DATA     *Private
+  )
+{
+  EFI_TIME          Time;
+  UINT64            CurrentStamp;
+  UINT64            ElapsedTimeValue;
+
+  //
+  // Generate a time stamp of the centiseconds from 1900/1/1, assume 30day/month.
+  //
+  ZeroMem (&Time, sizeof (EFI_TIME));
+  gRT->GetTime (&Time, NULL);
+  CurrentStamp = (UINT64)
+    (
+      ((((((Time.Year - 1900) * 360 +
+       (Time.Month - 1)) * 30 +
+       (Time.Day - 1)) * 24 + Time.Hour) * 60 +
+       Time.Minute) * 60 + Time.Second) * 100
+       + DivU64x32(Time.Nanosecond, 10000000)
+    );
+
+  //
+  // Sentinel value of 0 means that this is the first DHCP packet that we are
+  // sending and that we need to initialize the value.  First DHCP Solicit
+  // gets 0 elapsed-time.  Otherwise, calculate based on StartTime.
+  //
+  if (Private->ElapsedTime == 0) {
+    Private->ElapsedTime = CurrentStamp;
+  } else {
+    ElapsedTimeValue = CurrentStamp - Private->ElapsedTime;
+
+    //
+    // If elapsed time cannot fit in two bytes, set it to 0xffff.
+    //
+    if (ElapsedTimeValue > 0xffff) {
+      ElapsedTimeValue = 0xffff;
+    }
+    //
+    // Save the elapsed time
+    //
+    Private->ElapsedTime = ElapsedTimeValue;
+  }
+}
+
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
new file mode 100644
index 0000000..deb55e6
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/PxeBcSupport.h
@@ -0,0 +1,515 @@
+/** @file
+  Support functions declaration for UefiPxeBc Driver.
+
+  Copyright (c) 2007 - 2014, 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.
+
+**/
+
+#ifndef __EFI_PXEBC_SUPPORT_H__
+#define __EFI_PXEBC_SUPPORT_H__
+
+
+#define ICMP_DEST_UNREACHABLE      3
+#define ICMP_SOURCE_QUENCH         4
+#define ICMP_REDIRECT              5
+#define ICMP_ECHO_REQUEST          8
+#define ICMP_TIME_EXCEEDED         11
+#define ICMP_PARAMETER_PROBLEM     12
+
+
+
+/**
+  Flush the previous configration using the new station Ip address.
+
+  @param[in]   Private        Pointer to PxeBc private data.
+  @param[in]   StationIp      Pointer to the station Ip address.
+  @param[in]   SubnetMask     Pointer to the subnet mask address for v4.
+
+  @retval EFI_SUCCESS         Successfully flushed the previous config.
+  @retval Others              Failed to flush using the new station Ip.
+
+**/
+EFI_STATUS
+PxeBcFlushStationIp (
+  PXEBC_PRIVATE_DATA       *Private,
+  EFI_IP_ADDRESS           *StationIp,
+  EFI_IP_ADDRESS           *SubnetMask     OPTIONAL
+  );
+
+
+/**
+  Notify callback function when an event is triggered.
+
+  @param[in]  Event           The triggered event.
+  @param[in]  Context         The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+PxeBcCommonNotify (
+  IN EFI_EVENT           Event,
+  IN VOID                *Context
+  );
+
+
+/**
+  Perform arp resolution from the arp cache in PxeBcMode.
+
+  @param  Mode           Pointer to EFI_PXE_BASE_CODE_MODE.
+  @param  Ip4Addr        The Ip4 address for resolution.
+  @param  MacAddress     The resoluted MAC address if the resolution is successful.
+                         The value is undefined if resolution fails.
+
+  @retval TRUE           Found a matched entry.
+  @retval FALSE          Did not find a matched entry.
+
+**/
+BOOLEAN
+PxeBcCheckArpCache (
+  IN  EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN  EFI_IPv4_ADDRESS          *Ip4Addr,
+  OUT EFI_MAC_ADDRESS           *MacAddress
+  );
+
+
+/**
+  Update arp cache periodically.
+
+  @param  Event              Pointer to EFI_PXE_BC_PROTOCOL.
+  @param  Context            Context of the timer event.
+
+**/
+VOID
+EFIAPI
+PxeBcArpCacheUpdate (
+  IN EFI_EVENT    Event,
+  IN VOID         *Context
+  );
+
+
+/**
+  xxx
+
+  @param  Event                 The event signaled.
+  @param  Context               The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmpErrorUpdate (
+  IN EFI_EVENT             Event,
+  IN VOID                  *Context
+  );
+
+
+/**
+  xxx
+
+  @param  Event                 The event signaled.
+  @param  Context               The context passed in by the event notifier.
+
+**/
+VOID
+EFIAPI
+PxeBcIcmp6ErrorUpdate (
+  IN EFI_EVENT             Event,
+  IN VOID                  *Context
+  );
+
+
+/**
+  This function is to configure a UDPv4 instance for UdpWrite.
+
+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.
+  @param[in]       StationIp            Pointer to the station address.
+  @param[in]       SubnetMask           Pointer to the subnet mask.
+  @param[in]       Gateway              Pointer to the gateway address.
+  @param[in, out]  SrcPort              Pointer to the source port.
+  @param[in]       DoNotFragment        The flag of DoNotFragment bit in the IPv4
+                                        packet.
+
+  @retval          EFI_SUCCESS          Successfully configured this instance.
+  @retval          Others               Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp4Write (
+  IN     EFI_UDP4_PROTOCOL  *Udp4,
+  IN     EFI_IPv4_ADDRESS   *StationIp,
+  IN     EFI_IPv4_ADDRESS   *SubnetMask,
+  IN     EFI_IPv4_ADDRESS   *Gateway,
+  IN OUT UINT16             *SrcPort,
+  IN     BOOLEAN            DoNotFragment
+  );
+
+
+/**
+  This function is to configure a UDPv6 instance for UdpWrite.
+
+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.
+  @param[in]       StationIp            Pointer to the station address.
+  @param[in, out]  SrcPort              Pointer to the source port.
+
+  @retval          EFI_SUCCESS          Successfuly configured this instance.
+  @retval          Others               Failed to configure this instance.
+
+**/
+EFI_STATUS
+PxeBcConfigUdp6Write (
+  IN     EFI_UDP6_PROTOCOL  *Udp6,
+  IN     EFI_IPv6_ADDRESS   *StationIp,
+  IN OUT UINT16             *SrcPort
+  );
+
+/**
+  This function is to configure a UDPv4 instance for UdpWrite.
+
+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.
+  @param[in]       Session              Pointer to the UDP4 session data.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       Gateway              Pointer to the gateway address.
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be
+                                        prefixed to the data at BufferPtr.
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.
+  @param[in]       BufferPtr            A pointer to the data to be written.
+
+  @retval          EFI_SUCCESS          Successfully sent out data with Udp4Write.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Write (
+  IN EFI_UDP4_PROTOCOL       *Udp4,
+  IN EFI_UDP4_SESSION_DATA   *Session,
+  IN EFI_EVENT               TimeoutEvent,
+  IN EFI_IPv4_ADDRESS        *Gateway      OPTIONAL,
+  IN UINTN                   *HeaderSize   OPTIONAL,
+  IN VOID                    *HeaderPtr    OPTIONAL,
+  IN UINTN                   *BufferSize,
+  IN VOID                    *BufferPtr
+  );
+
+
+/**
+  This function is to configure a UDPv6 instance for UdpWrite.
+
+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.
+  @param[in]       Session              Pointer to the UDP6 session data.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       HeaderSize           An optional field which may be set to the length of a header
+                                        at HeaderPtr to be prefixed to the data at BufferPtr.
+  @param[in]       HeaderPtr            If HeaderSize is not NULL, a pointer to a header to be
+                                        prefixed to the data at BufferPtr.
+  @param[in]       BufferSize           A pointer to the size of the data at BufferPtr.
+  @param[in]       BufferPtr            A pointer to the data to be written.
+
+  @retval          EFI_SUCCESS          Successfully to send out data with Udp6Write.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Write (
+  IN EFI_UDP6_PROTOCOL       *Udp6,
+  IN EFI_UDP6_SESSION_DATA   *Session,
+  IN EFI_EVENT               TimeoutEvent,
+  IN UINTN                   *HeaderSize   OPTIONAL,
+  IN VOID                    *HeaderPtr    OPTIONAL,
+  IN UINTN                   *BufferSize,
+  IN VOID                    *BufferPtr
+  );
+
+
+/**
+  Check the received packet with the Ip filter.
+
+  @param[in]  Mode                Pointer to mode data of PxeBc.
+  @param[in]  Session             Pointer to the current UDPv4 session.
+  @param[in]  OpFlags             Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Succesfully passed the Ip filter.
+  @retval     FALSE               Failed to pass the Ip filter.
+
+**/
+BOOLEAN
+PxeBcCheckByIpFilter (
+  IN EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN VOID                      *Session,
+  IN UINT16                    OpFlags
+  );
+
+
+/**
+  Filter the received packet with the destination Ip.
+
+  @param[in]       Mode           Pointer to mode data of PxeBc.
+  @param[in]       Session        Pointer to the current UDPv4 session.
+  @param[in, out]  DestIp         Pointer to the dest Ip address.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Succesfully passed the IPv4 filter.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestIp (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT EFI_IP_ADDRESS            *DestIp,
+  IN     UINT16                    OpFlags
+  );
+
+
+/**
+  Check the received packet with the destination port.
+
+  @param[in]       Mode           Pointer to mode data of PxeBc.
+  @param[in]       Session        Pointer to the current UDPv4 session.
+  @param[in, out]  DestPort       Pointer to the destination port.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Succesfully passed the IPv4 filter.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcCheckByDestPort (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT UINT16                    *DestPort,
+  IN     UINT16                    OpFlags
+  );
+
+
+/**
+  Filter the received packet with the source Ip.
+
+  @param[in]       Mode           Pointer to mode data of PxeBc.
+  @param[in]       Session        Pointer to the current UDPv4 session.
+  @param[in, out]  SrcIp          Pointer to the source Ip address.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Succesfully passed the IPv4 filter.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcIp (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT EFI_IP_ADDRESS            *SrcIp,
+  IN     UINT16                    OpFlags
+  );
+
+
+/**
+  Filter the received packet with the source port.
+
+  @param[in]       Mode           Pointer to mode data of PxeBc.
+  @param[in]       Session        Pointer to the current UDPv4 session.
+  @param[in, out]  SrcPort        Pointer to the source port.
+  @param[in]       OpFlags        Operation flag for UdpRead/UdpWrite.
+
+  @retval     TRUE                Succesfully passed the IPv4 filter.
+  @retval     FALSE               Failed to pass the IPv4 filter.
+
+**/
+BOOLEAN
+PxeBcFilterBySrcPort (
+  IN     EFI_PXE_BASE_CODE_MODE    *Mode,
+  IN     VOID                      *Session,
+  IN OUT UINT16                    *SrcPort,
+  IN     UINT16                    OpFlags
+  );
+
+
+/**
+  This function is to receive packet with Udp4Read.
+
+  @param[in]       Udp4                 Pointer to EFI_UDP4_PROTOCOL.
+  @param[in]       Token                Pointer to EFI_UDP4_COMPLETION_TOKEN.
+  @param[in]       Mode                 Pointer to EFI_PXE_BASE_CODE_MODE.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       OpFlags              The UDP operation flags.
+  @param[in]       IsDone               Pointer to IsDone flag.
+  @param[out]      IsMatched            Pointer to IsMatched flag.
+  @param[in, out]  DestIp               Pointer to destination address.
+  @param[in, out]  DestPort             Pointer to destination port.
+  @param[in, out]  SrcIp                Pointer to source address.
+  @param[in, out]  SrcPort              Pointer to source port.
+
+  @retval          EFI_SUCCESS          Successfully read data with Udp4.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp4Read (
+  IN     EFI_UDP4_PROTOCOL            *Udp4,
+  IN     EFI_UDP4_COMPLETION_TOKEN    *Token,
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,
+  IN     EFI_EVENT                    TimeoutEvent,
+  IN     UINT16                       OpFlags,
+  IN     BOOLEAN                      *IsDone,
+     OUT BOOLEAN                      *IsMatched,
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL
+  );
+
+
+/**
+  This function is to receive packet with Udp6Read.
+
+  @param[in]       Udp6                 Pointer to EFI_UDP6_PROTOCOL.
+  @param[in]       Token                Pointer to EFI_UDP6_COMPLETION_TOKEN.
+  @param[in]       Mode                 Pointer to EFI_PXE_BASE_CODE_MODE.
+  @param[in]       TimeoutEvent         The event for timeout.
+  @param[in]       OpFlags              The UDP operation flags.
+  @param[in]       IsDone               Pointer to IsDone flag.
+  @param[out]      IsMatched            Pointer to IsMatched flag.
+  @param[in, out]  DestIp               Pointer to destination address.
+  @param[in, out]  DestPort             Pointer to destination port.
+  @param[in, out]  SrcIp                Pointer to source address.
+  @param[in, out]  SrcPort              Pointer to source port.
+
+  @retval          EFI_SUCCESS          Successfully read data with Udp6.
+  @retval          Others               Failed to send out data.
+
+**/
+EFI_STATUS
+PxeBcUdp6Read (
+  IN     EFI_UDP6_PROTOCOL            *Udp6,
+  IN     EFI_UDP6_COMPLETION_TOKEN    *Token,
+  IN     EFI_PXE_BASE_CODE_MODE       *Mode,
+  IN     EFI_EVENT                    TimeoutEvent,
+  IN     UINT16                       OpFlags,
+  IN     BOOLEAN                      *IsDone,
+     OUT BOOLEAN                      *IsMatched,
+  IN OUT EFI_IP_ADDRESS               *DestIp      OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *DestPort    OPTIONAL,
+  IN OUT EFI_IP_ADDRESS               *SrcIp       OPTIONAL,
+  IN OUT EFI_PXE_BASE_CODE_UDP_PORT   *SrcPort     OPTIONAL
+  );
+
+
+/**
+  This function is to display the IPv4 address.
+
+  @param[in]  Ip        Pointer to the IPv4 address.
+
+**/
+VOID
+PxeBcShowIp4Addr (
+  IN EFI_IPv4_ADDRESS   *Ip
+  );
+
+
+/**
+  This function is to display the IPv6 address.
+
+  @param[in]  Ip        Pointer to the IPv6 address.
+
+**/
+VOID
+PxeBcShowIp6Addr (
+  IN EFI_IPv6_ADDRESS   *Ip
+  );
+
+
+/**
+  This function is to convert UINTN to ASCII string with required format.
+
+  @param[in]  Number         Numeric value to be converted.
+  @param[in]  Buffer         Pointer to the buffer for ASCII string.
+  @param[in]  Length         Length of the required format.
+
+**/
+VOID
+PxeBcUintnToAscDecWithFormat (
+  IN UINTN                       Number,
+  IN UINT8                       *Buffer,
+  IN INTN                        Length
+  );
+
+
+/**
+  This function is to convert a UINTN to a ASCII string, and return the
+  actual length of the buffer.
+
+  @param[in]  Number         Numeric value to be converted.
+  @param[in]  Buffer         Pointer to the buffer for ASCII string.
+  @param[in]  BufferSize     The maxsize of the buffer.
+  
+  @return     Length         The actual length of the ASCII string.
+
+**/
+UINTN
+PxeBcUintnToAscDec (
+  IN UINTN               Number,
+  IN UINT8               *Buffer,
+  IN UINTN               BufferSize
+  );
+
+/**
+  This function is to convert unicode hex number to a UINT8.
+
+  @param[out]  Digit                   The converted UINT8 for output.
+  @param[in]   Char                    The unicode hex number to be converted.
+
+  @retval      EFI_SUCCESS             Successfully converted the unicode hex.
+  @retval      EFI_INVALID_PARAMETER   Failed to convert the unicode hex.
+
+**/
+EFI_STATUS
+PxeBcUniHexToUint8 (
+  OUT UINT8                *Digit,
+  IN  CHAR16               Char
+  );
+
+/**
+  Calculate the elapsed time.
+
+  @param[in]      Private      The pointer to PXE private data
+
+**/
+VOID
+CalcElapsedTime (
+  IN     PXEBC_PRIVATE_DATA     *Private
+  );
+
+/**
+  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.
+
+**/
+EFI_HANDLE
+PxeBcGetNicByIp4Children (
+  IN EFI_HANDLE                 ControllerHandle
+  );
+
+/**
+  Get the Nic handle using any child handle in the IPv6 stack.
+
+  @param[in]  ControllerHandle    Pointer to child handle over IPv6.
+
+  @return NicHandle               The pointer to the Nic handle.
+
+**/
+EFI_HANDLE
+PxeBcGetNicByIp6Children (
+  IN EFI_HANDLE                  ControllerHandle
+  );
+#endif
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
new file mode 100644
index 0000000..b6bd49f
--- /dev/null
+++ b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
@@ -0,0 +1,109 @@
+## @file
+#  Access PXE-compatible devices for network access and network booting.
+#  
+#  This driver provides PXE Base Code Protocol which is used to accessing
+#  PXE-compatible device for network access or booting. It could work together
+#  with an IPv4 stack, an IPv6 stack or both.
+#
+#
+#  Copyright (c) 2007 - 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                      = UefiPxeBcDxe
+  FILE_GUID                      = B95E9FDA-26DE-48d2-8807-1F9107AC5E3A
+  MODULE_TYPE                    = UEFI_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = PxeBcDriverEntryPoint
+  UNLOAD_IMAGE                   = NetLibDefaultUnload
+  MODULE_UNI_FILE                = UefiPxeBcDxe.uni
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+#  VALID_ARCHITECTURES           = IA32 X64 IPF
+#
+
+[Sources]
+  ComponentName.c
+  PxeBcDriver.c
+  PxeBcDriver.h
+  PxeBcImpl.c
+  PxeBcImpl.h
+  PxeBcBoot.c
+  PxeBcBoot.h
+  PxeBcDhcp6.c
+  PxeBcDhcp6.h
+  PxeBcDhcp4.c
+  PxeBcDhcp4.h
+  PxeBcMtftp.c
+  PxeBcMtftp.h
+  PxeBcSupport.c
+  PxeBcSupport.h
+
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+
+
+[LibraryClasses]
+  BaseLib
+  UefiLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+  BaseMemoryLib
+  MemoryAllocationLib
+  DebugLib
+  NetLib
+  DpcLib
+  DevicePathLib
+  PcdLib
+
+[Protocols]
+  ## TO_START
+  ## SOMETIMES_CONSUMES
+  gEfiDevicePathProtocolGuid                           
+  gEfiNetworkInterfaceIdentifierProtocolGuid_31        ## SOMETIMES_CONSUMES
+  gEfiArpServiceBindingProtocolGuid                    ## TO_START
+  gEfiArpProtocolGuid                                  ## TO_START
+  gEfiIp4ServiceBindingProtocolGuid                    ## TO_START
+  gEfiIp4ProtocolGuid                                  ## TO_START
+  gEfiIp4Config2ProtocolGuid                           ## TO_START
+  gEfiIp6ServiceBindingProtocolGuid                    ## TO_START
+  gEfiIp6ProtocolGuid                                  ## TO_START
+  gEfiIp6ConfigProtocolGuid                            ## TO_START
+  gEfiUdp4ServiceBindingProtocolGuid                   ## TO_START
+  gEfiUdp4ProtocolGuid                                 ## TO_START
+  gEfiMtftp4ServiceBindingProtocolGuid                 ## TO_START
+  gEfiMtftp4ProtocolGuid                               ## TO_START
+  gEfiDhcp4ServiceBindingProtocolGuid                  ## TO_START
+  gEfiDhcp4ProtocolGuid                                ## TO_START
+  gEfiUdp6ServiceBindingProtocolGuid                   ## TO_START
+  gEfiUdp6ProtocolGuid                                 ## TO_START
+  gEfiMtftp6ServiceBindingProtocolGuid                 ## TO_START
+  gEfiMtftp6ProtocolGuid                               ## TO_START
+  gEfiDhcp6ServiceBindingProtocolGuid                  ## TO_START
+  gEfiDhcp6ProtocolGuid                                ## TO_START
+  gEfiPxeBaseCodeCallbackProtocolGuid                  ## SOMETIMES_PRODUCES
+  gEfiPxeBaseCodeProtocolGuid                          ## BY_START
+  gEfiLoadFileProtocolGuid                             ## BY_START
+  gEfiAdapterInformationProtocolGuid                   ## SOMETIMES_CONSUMES
+
+[Guids]
+  gEfiAdapterInfoUndiIpv6SupportGuid                   ## SOMETIMES_CONSUMES ## GUID
+
+[Pcd]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdTftpBlockSize      ## SOMETIMES_CONSUMES
+[UserExtensions.TianoCore."ExtraFiles"]
+  UefiPxeBcDxeExtra.uni
diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.uni
new file mode 100644
index 0000000000000000000000000000000000000000..37ca0cb0362e12966bc4df850eb5b1450601f811
GIT binary patch
literal 2416
zcmd6oUuzRV6vgke;CC3>7nL?q5d{%3wW}^_Q__^`lO#==EKRZ{*=_BYSAS<FyOV^9
z6$Hz&f9B4ebI(2Z&g{=0O>3Ite#GVMjh$I$6C2unyN6YCEj#5J+Jej3p?$H$cCq9(
zwF~Q6!HT|L<7|N6CF9`lK+A2;su}jeuKehnac>Q?$Nb<1uRi{{$Di5A8a1cLt!rbV
ziPwNQ8L@{x-UZLxGZCZ6+u0tm7ucM=#Y3~oN!|swvYKGI^0=ow-4eg_tHe@%>ltM5
zAJy#sMx)y*6ja>Q!&wR&=UJEV7Q3jic#g=gAPcDyRl6c5S(U|%XNm%s_KdFucGc$G
zb~a|8ugyBJRaY?*(oMCIz87|lUj~mce$F0%{gC%ld(8EMuifS73Go#(g;4=tRY3@I
zF!T(4mNVO6rp3HGBDQKgXPs(0_kA$H^A+{(*y}p?s6p(U&?_S!V+u;ujX}4n(p9(5
z$SBm?(f?wlco|<4Vn_U*XXCJj-7u^Br^46j(0@Jn_VAnVtaegKrka(?6_XHa3Q}aL
zj+Lh>n)z?d=Ip$<ZqJTvpYf+PE|KeMw}+h>v(3Dw0jt_+dKW(HzLj-c)kUgmN1b60
z6%yy~_-t}o*Cl9qjaPrUJ_jM33eq@oRUZ}f#7loVsl$f&Ey%cpk<Oh4-*Y;yxuN|6
zOCQexJ+~gc0m_&=cClND*tHLwc-qw+YukICE&I$kMOBBcdUvs=^x83BiP!qr4tOWn
zrAk1Ebx7&$1dk(++~!L?t>+eR;R<z&_nseXw6ntLgn6O1eMDy`cy&Cp1MDfN!f5;G
z9Y*3Vn__nHm5-tZuXp$=`d8i`nGtjC#bg;JWRY<4S2z(pMT<i^OELFX6pR(qpQY)!
z%cgs~?jN0-4i?hy8%S+1jzLQpwa7ipv{0*y-|wz%zjK6DvV(NyIo}n#V2P<Ty&M0q
qty+iMX`l`|B71e$VO_PGbojFd@x=T8|H`}ikJSJ3-~St9)qerEOoM#@

literal 0
HcmV?d00001

diff --git a/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni b/Platform/BroxtonPlatformPkg/Common/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxeExtra.uni
new file mode 100644
index 0000000000000000000000000000000000000000..2877631fc42de8bed1ea2fce88842ac260adee72
GIT binary patch
literal 1332
zcmZvcTW=FV42AuS#D7?&FM!&F+Y>^Fge<V4O`<dr<*C|Sny!>~qfICcKOXpwXD>~K
zs?69P&+(aKcmMwBSjQrMFZhe>qg`2MYg^h=dqPxaQ#?gSc8}Gwx%J(O?9_6+l>E$o
zbG9J++;&#r-`dKy_$ij+pUA!CZ0T!-UhUz0V|%~5vTHIUdj|S*_Sg2(UfCP0liGW0
z$Jj}UxyDw7!ZyC9+|RsXo0BdKl_6dMTg(c$Z21*-=iJ-&p4W}-L;d!K-2_INzh^^!
z4obnxZa}Htw<BZe{v6ghXB$7;ms&(b#7gxttTkv2eo9ogrQ{sz)|pAS3SYMuWraIz
zbt$zOtM2{ehE5CYib<$-<E$$bF;iw%>mwy{`rF*=(ZfFe=>eC9>o3=0&MJ0?ug%~f
zL~#gusn!->ub#p4VPB-G*k2J39TMkzavpO!)Fo)SCu>&T&r1cwX!k%@&C!ldyz^Pk
zv4XbBFI?$1uP^9l=aKeXA~TpTn7PC1ZBVZHwd*>KHWT}5F}z01(a^rIcI^y5LDL9T
zwNHp9OxhGHcBQ8_V2_DM!~#Oog@nnD$vAi9A(kduYl~gDLT$14+?RJ;bBpXd=RzHN
zMfqJSiP?a70;({EUVV%w?ux1Age>K#YRLLbu5SKd|Hg@!>pw9rtAtm?c2VOr=m}bk
zm@C!n|5rbKg*wh%njY1Yqpyj+Ez<6?RCff*xPHMU*h5gK;K$?)V0nfWIoc6D)c?SW
gzNf63I!(d}`QoQ)E$r~6Q~xgfZr}G-%}9Cw09297Y5)KL

literal 0
HcmV?d00001

diff --git a/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc b/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc
index 0d2d0db..53658cf 100644
--- a/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc
+++ b/Platform/BroxtonPlatformPkg/PlatformDsc/Components.dsc
@@ -1,7 +1,7 @@
 ## @file
 #  Platform Components Description.
 #
-#  Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2016 - 2017, 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
@@ -325,7 +325,7 @@
      NetworkPkg/Mtftp6Dxe/Mtftp6Dxe.inf
   !endif
   !if $(NETWORK_IP6_ENABLE) == TRUE
-     NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
+     $(PLATFORM_PACKAGE_COMMON)/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
   !else
      MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf {
        <LibraryClasses>
diff --git a/Platform/BroxtonPlatformPkg/PlatformPkg.fdf b/Platform/BroxtonPlatformPkg/PlatformPkg.fdf
index 93f0422..0d9a087 100644
--- a/Platform/BroxtonPlatformPkg/PlatformPkg.fdf
+++ b/Platform/BroxtonPlatformPkg/PlatformPkg.fdf
@@ -1,7 +1,7 @@
 ## @file
 #  FDF file of Platform.
 #
-#  Copyright (c) 2008 - 2016, Intel Corporation. All rights reserved.<BR>
+#  Copyright (c) 2008 - 2017, 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
@@ -681,7 +681,7 @@ APRIORI DXE {
   !endif
   !if $(NETWORK_IP6_ENABLE) == TRUE
   INF  NetworkPkg/TcpDxe/TcpDxe.inf
-  INF  NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
+  INF $(PLATFORM_PACKAGE_COMMON)/SampleCode/NetworkPkg/UefiPxeBcDxe/UefiPxeBcDxe.inf
   !else
   INF MdeModulePkg/Universal/Network/UefiPxeBcDxe/UefiPxeBcDxe.inf
   INF MdeModulePkg/Universal/Network/Tcp4Dxe/Tcp4Dxe.inf
diff --git a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Sdio/Dxe/MMC/MmcMediaDeviceDxe/MMCSDBlockIo.c b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Sdio/Dxe/MMC/MmcMediaDeviceDxe/MMCSDBlockIo.c
index f89911c..5c91190 100644
--- a/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Sdio/Dxe/MMC/MmcMediaDeviceDxe/MMCSDBlockIo.c
+++ b/Silicon/BroxtonSoC/BroxtonSiPkg/SouthCluster/Sdio/Dxe/MMC/MmcMediaDeviceDxe/MMCSDBlockIo.c
@@ -1,7 +1,7 @@
 /** @file
   Block I/O protocol for MMC/SD device.
 
-  Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR>
+  Copyright (c) 2012 - 2017, 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
@@ -585,7 +585,8 @@ MMCSDBlockIoInit (
   GP_CHUNK_SIZE = 0;
   if (((ExtCsd->PARTITIONING_SUPPORT & BIT0) == BIT0) &&
       ((ExtCsd->PARTITION_SETTING_COMPLETED & BIT0) == BIT0)) {
-    GP_CHUNK_SIZE = MultU64x32 (ExtCsd->HC_WP_GRP_SIZE * ExtCsd->HC_ERASE_GRP_SIZE, SIZE_512KB);
+    GP_CHUNK_SIZE = MultU64x32 (ExtCsd->HC_WP_GRP_SIZE, ExtCsd->HC_ERASE_GRP_SIZE);
+    GP_CHUNK_SIZE = MultU64x32 (GP_CHUNK_SIZE, SIZE_512KB);
   }
 
   for (Loop = 0; Loop < MAX_NUMBER_OF_PARTITIONS; Partition++, Loop++) {
-- 
2.7.0.windows.1




^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2017-01-09  2:05 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-01-09  1:39 [Patch][edk2-platforms/devel-MinnowBoard3] Fix build error lushifex
2017-01-09  2:05 ` Wei, David

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox