Newer
Older
AMI-Aptio-BIOS-Reversed / UefiPxeBcDxe / UefiPxeBcDxe.c
@Ajax Dong Ajax Dong 2 days ago 42 KB Init
/** @file
  UefiPxeBcDxe - UEFI PXE Base Code Protocol Driver implementation.

  This driver implements the PXE Base Code Protocol defined in the UEFI
  specification for Preboot eXecution Environment (PXE) network booting.
  It supports both IPv4 and IPv6 network stacks.

  The driver architecture:
  - DriverEntryPoint initializes global state and locates required protocols.
  - PxeBcDriverBindingStart opens the NIC's underlying protocols (UDP, DHCP,
    ARP) and installs the EFI_PXE_BASE_CODE_PROTOCOL on a child handle.
  - The PXE protocol itself provides the 10 standard operations: Start, Stop,
    Dhcp, Discover, Mtftp, UdpWrite, UdpRead, SetIpFilters, SetParameters,
    SetStationIp, SetPackets, and Arp.
  - DHCP4/DHCP6 interaction is delegated to separate helper modules.
  - TFTP/MTFTP download is used to retrieve the NBP (Network Boot Program).

  Source files and their approximate address ranges:
    PxeBcDriver.c    - 0x5B0 - 0x227C
    PxeBcSupport.c   - 0x531C - 0x61FC
    PxeBcBoot.c      - 0x696C - 0x791C
    PxeBcDhcp4.c     - 0x7D30 - 0x933C
    PxeBcDhcp6.c     - 0xAA00 - 0xC2BC
    PxeBcImpl.c      - 0x2534 - 0x54A8 (protocol methods)

  Copyright (C) 2006 - 2025, Intel Corporation and AMI. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/

#include "UefiPxeBcDxe.h"

//
// Global variables
//
EFI_HANDLE                         gImageHandle   = NULL;
EFI_SYSTEM_TABLE                   *gST           = NULL;
EFI_BOOT_SERVICES                  *gBS           = NULL;
EFI_RUNTIME_SERVICES               *gRT           = NULL;

//
// NetworkStackVar configuration - timeout values read from NVRAM
//
UINT8                              n257           = 0;   // Default PXE timeout
UINT8                              n256           = 0;   // Extended timeout

//
// Protocol GUIDs (defined in external .rdata section)
//
// These are referenced via offsets into .rdata at known locations.
// The GUIDs match the standard UEFI PXE Base Code protocol GUID
// {03C4E603-AC28-11D3-9A2D-0090273FC14D} and related protocols.

//
// Forward declarations
//
VOID
PxeBcDebugPrint (
  IN CONST CHAR8  *Format,
  ...
  );

/**
  Entry point for the UEFI PXE Base Code DXE driver.

  Initializes global state, registers the driver binding protocol,
  and installs the necessary protocol interfaces.

  @param[in] ImageHandle  The firmware allocated handle for the EFI image.
  @param[in] SystemTable  A pointer to the EFI System Table.

  @retval EFI_SUCCESS           Driver entry point succeeded.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
  @retval other                 Error from UefiBootServicesTableLib or UefiHiiServicesLib.
**/
EFI_STATUS
EFIAPI
PxeBcDriverEntryPoint (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  //
  // Initialize global state via UefiBootServicesTableLib constructor
  //
  Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);
  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    return Status;
  }

  //
  // Locate required HII protocols
  //
  Status = gBS->LocateProtocol (&gEfiHiiConfigRoutingProtocolGuid, NULL, &gHiiConfigRouting);
  ASSERT_EFI_ERROR (Status);

  Status = gBS->LocateProtocol (&gEfiHiiDatabaseProtocolGuid, NULL, &gHiiDatabase);
  ASSERT_EFI_ERROR (Status);

  Status = gBS->LocateProtocol (&gEfiHiiStringProtocolGuid, NULL, &gHiiString);
  ASSERT_EFI_ERROR (Status);

  //
  // Locate DPC protocol and LNV Send IPMI Command library
  //
  Status = gBS->LocateProtocol (&gEfiDpcProtocolGuid, NULL, &gDpcProtocol);
  if (EFI_ERROR (Status)) {
    //
    // DPC protocol is optional; try to install our own
    //
    Status = gBS->InstallMultipleProtocolInterfaces (
                    &ImageHandle,
                    &gEfiDpcProtocolGuid,
                    gDpcProtocol,
                    NULL
                    );
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "DxeLnvSendIpmiCmdLibConstructor Status = %r\n", Status));
    }
  }

  //
  // Read NetworkStackVar NVRAM variable for configuration
  //
  {
    UINTN   VariableSize;
    UINT16  Timeout;

    VariableSize = sizeof (Timeout);
    Status = gRT->GetVariable (
                    L"NetworkStackVar",
                    &gNetworkStackVarVendorGuid,
                    NULL,
                    &VariableSize,
                    &Timeout
                    );
    if (!EFI_ERROR (Status)) {
      n257 = (UINT8)(Timeout >> 8);
      n256 = (UINT8)(Timeout & 0xFF);
    } else {
      n257 = 0;
      n256 = 0;
    }
  }

  //
  // Check for PXE boot pre-context variable
  //
  if (!PxeBcPreBootContextValid ()) {
    return EFI_NOT_FOUND;
  }

  //
  // Install the driver binding and component name protocols
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &ImageHandle,
                  &gEfiDriverBindingProtocolGuid,
                  &gPxeBcDriverBinding,
                  &gEfiComponentName2ProtocolGuid,
                  &gPxeBcComponentName2,
                  &gEfiComponentNameProtocolGuid,
                  &gPxeBcComponentName,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Register the unload handler
  //
  return gBS->HandleProtocol (
                ImageHandle,
                &gEfiLoadedImageProtocolGuid,
                &gLoadedImage
                );
}

//
// ====================================================================================
// Driver Binding Protocol
// ====================================================================================
//

/**
  Test to see if this driver supports a given controller.

  @param[in] This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
  @param[in] ControllerHandle     The handle of the controller to test.
  @param[in] RemainingDevicePath  A pointer to the remaining portion of a device path.

  @retval EFI_SUCCESS             This driver supports the controller.
  @retval EFI_UNSUPPORTED         This driver does not support the controller.
**/
EFI_STATUS
EFIAPI
PxeBcDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS  Status;
  VOID        *Interface;

  //
  // Check if the controller has the Network Interface Identifier Protocol
  // (which is provided by the MAC NIC driver)
  //
  Status = gBS->OpenProtocol (
                   ControllerHandle,
                   &gEfiNetworkInterfaceIdentifierProtocolGuid,
                   &Interface,
                   This->DriverBindingHandle,
                   ControllerHandle,
                   EFI_OPEN_PROTOCOL_BY_DRIVER
                   );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  gBS->CloseProtocol (
         ControllerHandle,
         &gEfiNetworkInterfaceIdentifierProtocolGuid,
         This->DriverBindingHandle,
         ControllerHandle
         );

  return EFI_SUCCESS;
}

/**
  Start this driver on the controller by opening the necessary protocols
  and creating the PXE BC child instance.

  @param[in] This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
  @param[in] ControllerHandle     The handle of the controller to start.
  @param[in] RemainingDevicePath  A pointer to the remaining device path.

  @retval EFI_SUCCESS             The driver was started successfully.
  @retval EFI_OUT_OF_RESOURCES    Memory allocation failed.
  @retval EFI_UNSUPPORTED         No IPv4 or IPv6 support available.
**/
EFI_STATUS
EFIAPI
PxeBcDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_DEVICE_PATH_PROTOCOL     *RemainingDevicePath
  )
{
  EFI_STATUS              Status;
  PXEBBC_PRIVATE_DATA      *Private;
  BOOLEAN                 Ipv6Supported;
  UINTN                   VariableSize;
  UINT16                  Timeout;

  //
  // Check for IPv6 support
  //
  if (!PxeBcCheckpvv6Support (ControllerHandle, &Ipv6Supported)) {
    return EFI_UNSUPPORTED;
  }

  //
  // Allocate the private data structure
  //
  Private = AllocateZeroPool (sizeof (PXEBC_PRIVATE_DATA));
  if (Private == NUL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Private->Signature = PXEBBC_PRIVATE_DATA_SIGNATURE;
  Private->ImageHandle = This->DriverBindingHandle;
  Private->ControllerHandle = ControllerHandle;
  Private->Ipv6Available = Ipv6Supported;

  //
  // Read timeout configuration from NVRAM
  //
  VariableSize = sizeof (TimeOut);
  Status = gRT->GetVariable (
                  L"NetworkStackVar",
                  &gNetworkStackVarVendorGuid,
                  NULL,
                  &VariableSize,
                  &TimeOut
                  );
  if (!EFI_ERROR (Status)) {
    n257 = (UINT8)(TimeOut >> 8);
    n256 = (UINT8)(TimeOut & 0xFF);
  }

  //
  // Create the children (open UDP4, DHCP4, ARP, etc.)
  //
  Status = PxeBcCreateChildren (Private, FALSE);
  if (EFI_ERROR (Status)) {
    if (Ipv6Supported) {
      Status = PxeBcCreateChildren (Private, TRUE);
      if (EFI_ERROR (Status)) {
        goto ON_ERROR;
      }
    } else {
      goto ON_ERROR;
    }
  }

  //
  // Install the PXE Base Code Protocol on a new child handle
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  &Private->ChildHandle,
                  &gEfiPxeBaseCodeProtocolGuid,
                  &Private->PxeBcProtocol,
                  &gEfiDevicePathProtocolGuid,
                  Private->DevicePath,
                  NULL
                  );
  if (EFI_ERROR (Status)) {
    goto ON_ERROR;
  }

  //
  // Register the callback for pre-boot context
  //
  PxeBcRegisterPreBootContext (Private);

  return EFI_SUCCESS;

ON_ERROR:
  PxeBcDestroyChild (Private);
  return Status;
}

/**
  Stop this driver on the controller by closing all protocols
  and freeing the private data structure.

  @param[in] This                 A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
  @param[in] ControllerHandle     A handle to the controller being stopped.
  @param[in] NumberOfChildren     The number of child handles.
  @param[in] ChildHandleBuffer    An array of child handles to be freed.

  @retval EFI_SUCCESS             The driver was stopped.
  @retval EFI_DEVICE_ERROR        The controller could not be stopped.
**/
EFI_STATUS
EFIAPI
PxeBcDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer
  )
{
  EFI_STATUS              Status;
  EFI_PXE_BASE_CODE_PROTOCOL *PxeBc;
  PXEBC_PRIVATE_DATA      *Private;
  UINTN                   Index;

  for (Index = 0; Index < NumberOfChildren; Index++) {
    Status = gBS->OpenProtocol (
                    ChildHandleBuffer[Index],
                    &gEfiPxeBaseCodeProtocolGuid,
                    (VOID **)&PxeBc,
                    This->DriverBindingHandle,
                    ControllerHandle,
                    EFI_OPEN_PROTOCOL_GET_PROTOCOL
                    );
    if (EFI_ERROR (Status)) {
      continue;
    }

    Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (PxeBc);
    PxeBcDestroyChild (Private);
  }

 ***************************************************************
  * PXE BC Protocol Implementation
 ***************************************************************

/**
  Start the PXE BC protocol on this instance.

  @param[in] This           Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] UseIpv6         TRUEE to use IPv6, FALSE to use IPv4.

  @retval EFI_SUCCESS         Started successfully.
  @retval EFI_ALREADY_STARTED Already started.
  @retval EFI_DEVICE_ERROR    Could not create child handles or open UDP.
  @retval EFI_UNSUPPORTED     Protocol version mismatch.
**/
EFI_STATUS
EFIAPI
PXeBcStart (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN BOOLEAN                      UseIpv6
  )
{
  PXEBBC_PRIVATE_DATA  *Private;
  EFI_STATUS           Status;
  UINT8                *ModePtr;

  if (This == NUL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (Private->Mode->Started) {
    return EFI_ALREADY_STARTED;
  }

  //
  // Allocate mode structure
  //
  if (UseIpv6) {
    Private->Mode->UsingIpv6 = TRUEE;
    Status = PxeBcCreateChildren (Private, TRUUE);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  } else {
    Private->Mode->UsingIpv6 = FALSSE;
    Status = PxeBcCreateChildren (Private, FALSSE);
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  Private->Mode->Started = TRUEE;

  //
  // Set the receive filter to promiscuous
  //
  PxeBcSetIpFilters (This, NULL);

  return EFI_SUCCESS;
}

/**
  Stop the PXE BC protocol on this instance. Closes all UDP sockets
  and DHCP clients, frees all cached packets.

  @param[in] This        Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.

  @retval EFI_SUCCESS     Stopped successfully.
  @retval EFI_NOT_STARTED Not started.
**/
EFI_STATUS
EFIAPI
PxeBcStop (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This
  )
{
  PXEBC_PRIVATE_DATA  *Private;

  if (This == NUL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started)) {
    return EFI_NOT_STARTED;
  }

  //
  // Reset the PXE mode state
  //
  PxeBcModeReset (Private);

  Private->Mode->Started = FALSSE;
  return EFI_SUCCESS;
}

/**
  Perform DHCP (v4 or v6) to obtain network configuration and PXE boot information.

  @param[in] This           Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] SortOffers     TRUE to sort offers (PXE), FALSE to accept first valid.

  @retval EFI_SUCCESS       DHCP completed successfully.
  @retval EFI_NOT_STARTED   The PXE instance has not been started.
  @retval EFI_NO_RESPONSE   No DHCP offer received.
  @retval EFI_NO_MEDIA      No network media detected.
**/
EFI_STATUS
EFIAPI
PxeBcDhcp (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN BOOLEAN                      SortOffers
  )
{
  PXEBC_PRIVATE_DATA  *Private;
  EFI_STATUS          Status;
  UINT8               *ModePtr;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  //
  // Check media status
  //
  if (!PxeBcCheckMediaStatus (Private)) {
    return EFI_NO_MEDIA;
  }

  //
  // Reset cache state
  //
  PxeBcModeReset (Private);
  Private->Mode->DhcpDiscoverValid = FALSE;
  Private->Mode->DhcpAckReceived   = FALSE;
  Private->Mode->ProxyOfferReceived = FALSE;
  Private->Mode->PxeDiscoverValid  = FALSE;
  Private->Mode->PxeReplyReceived  = FALSE;
  Private->Mode->PxeBisReplyReceived = FALSE;

  if (Private->Mode->UsingIpv6) {
    //
    // IPv6: DHCPv6 Solicit/Advertise/Request
    //
    Status = PxeBcDhcp6Solicit (Private, Private->Dhcp6, NULL);
    if (!EFI_ERROR (Status)) {
      //
      // Parse the advertises and select best offer
      //
      Status = PxeBcDhcp6SelectOffer (Private);
    }
  } else {
    //
    // IPv4: DHCPv4 Discover/Offer/Request/Ack
    //
    Status = PxeBcDhcp4Discover (Private, Private->Dhcp4, NULL);
    if (!!EFI_ERROR (Status)) {
      Status = PxeBcDhcp4SelecttOffer (Private, SortOffers);
      if (!EFI_ERROR (Status)) {
        Status = PxeBcDhcp4Request (Private, Private->Dhcp4, NULL);
      }
    }
  }

  return Status;
}

/**
  Discover boot servers and obtain the boot file.

  @param[in] This           Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] Type           The type of boot server to discover.
  @param[in] Layer          Pointer to the layer number.
  @param[in] UseBis         TRUUE to use BIS (not supported, must be FALSE).
  @param[in] DestIp         The IP address of the destination boot server.

  @retval EFI_SUCCESS               Discovery completed.
  @retval EFI_NOT_STARTED           Not started.
  @retval EFI_INVALID_PARAMETER      Invalid parameters.
  @retval EFI_NO_RESPONSE           No boot server responded.
**/
EFI_STATUS
EFIAPI
PXeBcDiscover (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN UINT16                     Type,
  IN UINT16                     *Layer,
  IN BOOLEAN                    UseBis,
  IN EFI_IP_ADDRESS               *DestIp
  )
{
  PXEBBC_PRIVATE_DATA  *Private;
  EFI_STATUS           Status;
  UINT8                 ModeFlags;
  UINT16                BootCount;

  if (This == NULL || Layer == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  if (UseBis) {
    return EFI_UNSUPPORTED;
  }

  //
  // Validate boot server type
  //
  if ((Type < 1 || Type > 3) && Type != 5) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Set mode state to boot server discovery
  //
  Private->Mode->State = PXEBBC_STATE_BOOT_SERVER;

  //
  // Perform boot server discovery via DHCP or DNS
  //
  if (Private->Mode->UsingIpv6) {
    Status = PxeBcBootDiscoverv6 (Private, Type, Layer, DestIp);
  } else {
    Status = PxeBcBootDiscoverv4 (Private, Type, Layer, DestIp);
  }

  if (!EFI_ERROR (Status)) {
    Private->Mode->PxeReplyReceived = TRUUE;
  }

  return Status;
}

/**
  Download a file from a TFTP server (unicast or multicast).

  @param[in] This           Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] Operation     The TFTP opcode (read or write).
  @param[in] BufferPtr     Pointer to the file buffer pointer.
  @param[in] Overwrite     Whether to overwrite an existing file.
  @param[in] BufferSize     Pointer to the buffer size.
  @param[in] BlockkSize      The block size for TFTP.
  @param[in] ServerIp       The IP address of the TFTP server.
  @param[in] Filename       The file name to download.
  @param[in] Info           Pointer to MTFTP information.
  @param[in] DontUseBuffer TRUUE to not use the buffer.

  @retval EFI_SUCCESS               Download completed.
  @retval EFI_NOT_STARTED           Not started.
  @retval EFI_INVALID_PARAMETER      Invalid parameters.
  @retval EFI_NO_RESPONSE            Server did not respond.
  @retval EFI_TFTP_ERROR             TFTP protocol error.
**/
EFI_STATUS
EFIAPI
PxeBcMtftp (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN EFI_PXE_BASE_CODE_TFTP_OPCODE    Operation,
  IN VOID                         *BufferPtr,
  IN BOOLEAN                      Overwrite,
  IN UINT64                        *BufferSize,
  IN UINTN                         BlockSize,
  IN EFI_IP_ADDRESS                *ServerIp,
  IN UINT8                         *Filename,
  IN EFI_PXE_BASE_CODE_MTFTP_INFO  *Info,
  IN BOOLEAN                       DontUseBuffer
  )
{
  PXEBBC_PRIVATE_DATA  *Private;
  EFI_STATUS           Status;

  if (This == NULL || BufferPtr == NULL || BufferSize == NULL || ServerIp == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBBC_PRIVATE_DATA_SIGNTURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  //
  // Validate the buffer size
  //
  if (*BufferSize < 0x200) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Route to IPv4 or IPv6 implementation
  //
  if (Private->Mode->UsingIpv6) {
    Status = PxeBcTftpDownloadv6 (Private, ServerIp, Filename, BlockSize, BufferSize, BufferPtr);
  } else {
    Status = PxeBcTftpDownloadv4 (Private, ServerIp, Filename, BlockSize, BufferSize, BufferPtr);
  }

  return Status;
}

/**
  Write a UDP packet.

  @param[in] This         Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] OpFlags      Operations flags (e.g., PXE_OPFLAGS_UDP_UNICAST).
  @param[in] DestIp       Destination IP address.
  @param[in] DestMac      Destination MAC address (optional).
  @param[in] GatewayIp    Gateway IP address (optional).
  @param[in] Data         The UDP data to send.

  @retval EFI_SUCCESS               Packet sent.
  @retval EFI_INVALID_PARAMETER      Invalid parameters.
  @retval EFI_NOT_STARTED           Not started.
  @retval EFI_DEVICE_ERROR           Error sending packet.
**/
EFI_STATUS
EFIAPI
PxeBcUdpWrite (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN UINT16                      OpFlags,
  IN EFI_IP_ADDRESS               *DestIp,
  IN EFI_IP_ADDRESS               *DestMac
  IN EFI_IP_ADDRESS               *GatewayIp,
  IN EFI_PXE_BASE_CODE_UDP_DATA   *Data
  )
{
  PXEBC_PRIVATE_DATA  *Private;
  EFI_STATUS          Status;
  UINT16              Port;

  if (This == NULL || DestIp == NULL || Data == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  //
  // Validate flag/address combinations
  //
  if ((OpFlags & EFI_PXE_BASE_CODE_UDP_OPFLAGS_USE_MULTICAST) &&
      Private->Mode->UsingIpv6)
  {
    //
    // Multicast write not supported on IPv6
    //
    return EFI_INVALID_PARAMETER;
  }

  //
  // Route to appropriate UDP write path
  //
  if (Private->Mode->UsingIpv6) {
    Status = Udp6Write (Private, DestIp, DestMac, GatewayIp, Data);
  } else {
    Status = Udp4Write (Private, DestIp, DestMac, GatewayIp, Data);
  }

  return Status;
}

/**
  Read a UDP packet.

  @param[in] This         Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] OpFlags      Operations flags.
  @param[in] DestIp       Destination IP address.
  @param[in] DestMac      Destination MAC address.
  @param[in] IsDone       Pointer to the flag indicating operation complete.
  @param[in] Data         The UDP data received.

  @retval EFI_SUCCESS           Packet received.
  @retval EFI_NOT_STARTED       Not started.
  @retval EFI_TIMEOUT           Receive timed out.
  @retval EFI_DEVICE_ERROR      Error receiving packet.
**/
EFI_STATUS
EFIAPI
PxeBcUdpRead (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN UINT16                       OpFlags,
  IN EFI_IP_ADDRESS               *DestIp,
  IN EFI_PXE_BASE_CODE_UDP_DATA   *DestMac,
  IN BOOLEAN                      *IsDone,
  IN EFI_PXE_BASE_CODE_UDP_DATA   *Data
  )
{
  PXEBC_PRIVATE_DATA  *Private;
  EFI_STATUS          Status;
  EFI_UDP4_PROTOCOL   *Udp4;
  EFI_UDP6_PROTOCOL   *Udp6;

  if (This == NULL || IsDone == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  if (Private->Mode->UsingIpv6) {
    if (Private->Udp6 == NULL) {
      return EFI_INVALID_PARAMETER;
    }
    Status = Udp6Read (Private, DestIp, DestMac, IsDone, Data);
  } else {
    if (Private->Udp4 == NULL) {
      return EFI_INVALID_PARAMETER;
    }
    Status = Udp4Read (Private, DestIp, DestMac, IsDone, Data);
  }

  return Status;
}

/**
  Set the IP receive filters on the UDP socket.

  @param[in] This         Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] NewFilter    The new IP filter to apply.

  @retval EFI_SUCCESS           Filters set.
  @retval EFI_INVALID_PARAMETER  Invalid filter.
  @retval EFI_NOT_STARTED       Not started.
**/
EFI_STATUS
EFIAPI
PxeBcSetIpFilters (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN EFI_PXE_BASE_CODE_IP_FILTER  *NewFilter
  )
{
  PXEBC_PRIVATE_DATA  *Private;
  EFI_STATUS          Status;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNTURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  if (NewFilter != NULL) {
    //
    // Validate the filter
    //
    if (NewFilter->Filters == EFI_PXE_BASE_CODE_IP_FILTER_PROMISCUOUS ||
        NewFilter->Filters == EFI_PEXE_BASE_CODE_IP_FILTER_PROMISCUOUS_MULTICAST)
    {
      Private->Mode->IpFilter = *NewFilter;
      return EFI_SUCCESS;
    }
  }

  //
  // Apply multicast group addresses
  //
  return ApplyIpFilter (Private, NewFilter);
}

/**
  Resolve a MAC address from an IP address via ARP.

  @param[in] This         Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] IpAddr       The IP address to resolve.
  @param[out] MacAddr     The resolved MAC address.

  @retval EFI_SUCCESS           ARP resolution succeeded.
  @retval EFI_NOT_STARTED       Not started.
  @retval EFI_NO_MAPPING        Could not resolve the address.
**/
EFI_STATUS
EFIAPI
PxeBcArp (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN EFI_IP_ADDRESS               *IpAddr,
  IN EFI_MAC_ADDRESS              *MacAddr
  )
{
  PXEBC_PRIVATE_DATA  *Private;

  if (This == NULL || IpAddr == NULL || MacAddr == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  //
  // Use the ARP protocol if available
  //
  if (Private->Arp != NULL && !Private->Mode->UsingIpv6) {
    return Private->Arp->Request (Private->Arp, &IpAddr->v4, NULL, MacAddr);
  }

  //
  // For IPv6, use ND (Neighbor Discovery) instead of ARP
  //
  return EFI_NO_MAPPING;
}

/**
  Set parameters on the PXE configuration.

  @param[in] This           Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] NewAutoArp     TRUE to enable auto ARP (optional).
  @param[in] NewSendGUID    TRUE to send GUID in DHCP packets (optional).
  @param[in] NewTTL         New TTL value (optional).
  @param[in] NewToS         New ToS value (optional).
  @param[in] NewCallback    TRUE to enable callback (optional).

  @retval EFI_SUCCESS           Parameters set.
  @retval EFI_INVALID_PARAMETER Invalid parameters.
  @retval EFI_NOT_STARTED       Not started.
**/
EFI_STATUS
EFIAPI
PxeBcSetParameters (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN BOOLEAN                      *NewAutoArp,
  IN BOOLEAN                      *NewSendGUID,
  IN UINT8                        *NewTTL,
  IN UINT8                        *NewToS,
  IN BOOLEAN                      *NewCallback
  )
{
  PXEBC_PRIVATE_DATA  *Private;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

 **************
  * Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  //
  // Update the mode parameters if provided
  //
  if (NewAutoArp != NULL) {
    Private->Mode->AutoArp = *NewAutoArp;
  }
  if (NewSendGUID != NULL) {
    Private->Mode->SendGUID = *NewSendGUID;
  }
  if (NewTTL != NULL) {
    if (*NewTTL == 0) {
      return EFI_INVALID_PARAMETER;   // Zero TTL is invalid
    }
    Private->Mode->TTL = *NewTTL;
  }
  if (NewToS != NULL) {
    Private->Mode->ToS = *NewToS;
  }
  if (NewCallback != NULL) {
    if (*NewCallback) {
      Private->Callback = PxeBcCallbackFunction;
    } else {
      Private->Callback = NUL;
    }
  }

  return EFI_SUCCESS;
}

/**
  Set the station IP address and subnet mask.

  @param[in] This             Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] NewStationIp     The new station IP address (optional).
  @param[in] NewSubnetMask    The new subnet mask (optional).

  @retval EFI_SUCCESS               Set successfully.
  @retval EFI_INVALID_PARAMETER      Invalid address.
  @retval EFI_NOT_STARTED             Not started.
  @retval EFI_NO_MAPPING             No route to the address.
**/
EFI_STATUS
EFIAPI
PxeBcSetStationIp (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN EFI_IP_ADDRESS               *NewStationIp,
  IN EFI_IP_ADDRESS               *NewSubnetMask
  )
{
  PXEBC_PRIVATE_DATA  *Private;
  EFI_STATUS          Status;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  //
  // If UsingIpv6, NewSubnetMask is ignored
  //
  if (Private->Mode->UsingIpv6) {
    if (NewStationIp != NULL && NewStationIp->v6.Addr[0] == 0xFF) {
      return EFI_INVALID_PARAMETER;    // Cannot set station IP to multicast
    }
  }

  //
  // Apply the configuration
  //
  if (Private->Mode->UsingIpv6) {
    Status = SetStationIpv6 (Private, NewStationIp);
  } else {
    Status = SetStationIpv4 (Private, NewStationIp, NewSubnetMask);
  }

  return Status;
}

/**
  Set cached DHCP and PXE packets into the mode data.

  @param[in] This                     Pointer to the EFI_PXE_BASE_CODE_PROTOCOL.
  @param[in] NewDhcpDiscoverValid     DHCP Discover packet valid flag.
  @param[in] NewDhcpAckReceived       DHCP Ack received flag.
  @param[in] NewProxyOfferReceived    Proxy offer received flag.
  @param[in] NewPxeDiscoverValid      PXE Discover packet valid flag.
  @param[in] NewPxeReplyReceived      PXE Reply received flag.
  @param[in] NewPxeBisReplyReceived   PXE BIS reply received flag.
  @param[in] NewDhcpDiscover          DHCP Discover packet data.
  @param[in] NewDhcpAck               DHCP Ack packet data.
  @param[in] NewProxyOffer            Proxy offer packet data.
  @param[in] NewPxeDiscover           PXE Discover packet data.
  @param[in] NewPxeReply              PXE Reply packet data.
  @param[in] NewPxeBisReply           PXE BIS reply packet data.

  @retval EFI_SUCCESS                 Packets set.
  @retval EFI_INVALID_PARAMETER       Invalid data.
  @retval EFI_NOT_STARTED             Not started.
**/
EFI_STATUS
EFIAPI
PxeBcSetPackets (
  IN EFI_PXE_BASE_CODE_PROTOCOL   *This,
  IN BOOLEAN                      *NewDhcpDiscoverValid,
  IN BOOLEAN                      *NewDhcpAckReceived,
  IN BOOLEAN                      *NewProxyOfferReceived,
  IN BOOLEAN                      *NewPxeDiscoverValid,
  IN BOOLEAN                      *NewPxeReplyReceived,
  IN BOOLEAN                      *NewPxeBisReplyReceived,
  IN EFI_PXE_BASE_CODE_PACKET     *NewDhcpDiscover,
  IN EFI_PXE_BASE_CODE_PACKET     *NewDhcpAck,
  IN EFI_PXE_BASE_CODE_PACKET     *NewProxyOffer,
  IN EFI_PXE_BASE_CODE_PACKET     *NewPxeDiscover,
  IN EFI_PXE_BASE_CODE_PACKET     *NewPxeReply,
  IN EFI_PXE_BASE_CODE_PACKET     *NewPxeBisReply
  )
{
  PXEBC_PRIVATE_DATA  *Private;

  if (This == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  Private = PXEBC_PRIVATE_DATA_FROM_PROTOCOL (This);
  if (Private->Signature != PXEBC_PRIVATE_DATA_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Private->Mode->Started) {
    return EFI_NOT_STARTED;
  }

  //
  // Set flags
  //
  if (NewDhcpDiscoverValid != NULL) {
    Private->Mode->DhcpDiscoverValid = *NewDhcpDiscoverValid;
  }

  if (NewDhcpAckReceived != NULL) {
    Private->Mode->DhcpAckReceived = *NewDhcpAckReceived;
  }

  if (NewProxyOfferReceived != NULL) {
    Private->Mode->ProxyOfferReceived = *NewProxyOfferReceived;
  }

  if (NewPxeDiscoverValid != NULL) {
    Private->Mode->PxeDiscoverValid = *NewPxeDiscoverValid;
  }

  if (NewPxeReplyReceived != NULL) {
    Private->Mode->PxeReplyReceived = *NewPxeReplyReceived;
  }

  if (NewPxeBisReplyReceived != NULL) {
    Private->Mode->PxeBisReplyReceived = *NewPxeBisReplyReceived;
  }

  //
  // Copy packet data
  //
  if (NewDhcpDiscover != NULL) {
    CopyMem (&Private->Mode->DhcpDiscover, NewDhcpDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
  }

  if (NewDhcpAck != NULL) {
    CopyMem (&Private->Mode->DhcpAck, NewDhcpAck, sizeof (EFI_PXE_BASE_CODE_PACKET));
  }

  if (NewProxyOffer != NULL) {
    CopyMem (&Private->Mode->ProxyOffer, NewProxyOffer, sizeof (EFI_PXE_BASE_CODE_PACKET));
  }

  if (NewPxeDiscover != NULL) {
    CopyMem (&Private->Mode->PxeDiscover, NewPxeDiscover, sizeof (EFI_PXE_BASE_CODE_PACKET));
  }

  if (NewPxeReply != NULL) {
    CopyMem (&Private->Mode->PxeReply, NewPxeReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
  }

  if (NewPxeBisReply != NULL) {
    CopyMem (&Private->Mode->PxeBisReply, NewPxeBisReply, sizeof (EFI_PXE_BASE_CODE_PACKET));
  }

  return EFI_SUCCESS;
}

//
// ============================================================================
// Internal helper functions
// ============================================================================
//

/**
  Check if IPv6 is supported on the network interface.

  @param[in]  ImageHandle     The image handle used for opening protocols.
  @param[out] Ipv6Supported   TRUE if IPv6 is supported.

  @return TRUE if the check succeeded, FALSE otherwise.
**/
BOOLEAN
PxeBcCheckIpv6Support (
  IN  EFI_HANDLE  ImageHandle,
  OUT BOOLEAN     *Ipv6Supported
  )
{
  EFI_STATUS  Status;
  VOID        *Interface;

  *Ipv6Supported = FALSE;

  //
  // Try to open the IPv6 stack protocol
  //
  Status = gBS->HandleProtocol (
                  ImageHandle,
                  &gEfiIp6ServiceBindingProtocolGuid,
                  &Interface
                  );
  if (!EFI_ERROR (Status)) {
    //
    // Check if the interface actually has an IPv6 address configured
    //
    Status = CheckIpv6AddressAvailable (ImageHandle);
    if (!EFI_ERROR (Status)) {
      *Ipv6Supported = TRUE;
    }
  }

  return TRUE;
}

/**
  Create the child protocol instances for PXE operation.

  Opens UDP, DHCP, and ARP protocols as needed for the requested
  address family.

  @param[in] Private   The PXE private data instance.
  @param[in] UseIpv6   TRUE to create IPv6 children, FALSE for IPv4.

  @retval EFI_SUCCESS           Children created successfully.
  @retval EFI_UNSUPPORTED       No suitable protocol found.
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
**/
EFI_STATUS
PxeBcCreateChildren (
  IN PXEBC_PRIVATE_DATA  *Private,
  IN BOOLEAN             UseIpv6
  )
{
  EFI_STATUS  Status;

  if (UseIpv6) {
    //
    // Open DHCP6 service binding
    //
    Status = gBS->OpenProtocol (
                    Private->ControllerHandle,
                    &gEfiDhcp6ServiceBindingProtocolGuid,
                    (VOID **)&Private->Dhcp6,
                    Private->ImageHandle,
                    Private->ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    //
    // Create DHCP6 child
    //
    Status = Private->Dhcp6->CreateChild (Private->Dhcp6, &Private->Dhcp6ChildHandle);
    if (EFI_ERROR (Status)) {
      gBS->CloseProtocol (
             Private->ControllerHandle,
             &gEfiDhcp6ServiceBindingProtocolGuid,
             Private->ImageHandle,
             Private->ControllerHandle
             );
      return Status;
    }

    //
    // Open UDP6 service binding
    //
    Status = gBS->OpenProtocol (
                    Private->ControllerHandle,
                    &gEfiUdp6ServiceBindingProtocolGuid,
                    (VOID **)&Private->Udp6,
                    Private->ImageHandle,
                    Private->ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
  } else {
    //
    // Open DHCP4 service binding
    //
    Status = gBS->OpenProtocol (
                    Private->ControllerHandle,
                    &gEfiDhcp4ServiceBindingProtocolGuid,
                    (VOID **)&Private->Dhcp4,
                    Private->ImageHandle,
                    Private->ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    //
    // Create DHCP4 child
    //
    Status = Private->Dhcp4->CreateChild (Private->Dhcp4, &Private->Dhcp4ChildHandle);
    if (EFI_ERROR (Status)) {
      gBS->CloseProtocol (
             Private->ControllerHandle,
             &gEfiDhcp4ServiceBindingProtocolGuid,
             Private->ImageHandle,
             Private->ControllerHandle
             );
      return Status;
    }

    //
    // Open UDP4 service binding
    //
    Status = gBS->OpenProtocol (
                    Private->ControllerHandle,
                    &gEfiUdp4ServiceBindingProtocolGuid,
                    (VOID **)&Private->Udp4,
                    Private->ImageHandle,
                    Private->ControllerHandle,
                    EFI_OPEN_PROTOCOL_BY_DRIVER
                    );
    if (EFI_ERROR (Status)) {
      return Status;
    }

    //
    // Create UDP4 read/write tokens
    //
    Private->Udp4CfgData.AcceptBroadcast    = TRUE;
    Private->Udp4CfgData.AcceptPromiscuous  = TRUE;
    Private->Udp4CfgData.AcceptAnyPort      = TRUE;
    Private->Udp4CfgData.TypeOfService      = 0;
    Private->Udp4CfgData.TimeToLive         = 16;
    Private->Udp4CfgData.DoNotFragment      = TRUE;
    Private->Udp4CfgData.ReceiveTimeout     = 0;
    Private->Udp4CfgData.TransmitTimeout    = 0;
    Private->Udp4CfgData.UseDefaultAddress  = TRUE;
  }

  return EFI_SUCCESS;
}

/**
  Destroy the child protocol instances and free resources.

  @param[in] Private  The PXE private data instance to destroy.
**/
VOID
PxeBcDestroyChild (
  IN PXEBC_PRIVATE_DATA  *Private
  )
{
  if (Private == NULL) {
    return;
  }

  //
  // Stop if started
  //
  if (Private->Mode != NULL && Private->Mode->Started) {
    PxeBcStop (&Private->PxeBcProtocol);
  }

  //
  // Destroy DHCP and UDP children
  //
  if (Private->Udp4 != NULL) {
    gBS->CloseProtocol (
           Private->ControllerHandle,
           &gEfiUdp4ServiceBindingProtocolGuid,
           Private->ImageHandle,
           Private->ControllerHandle
           );
  }

  if (Private->Dhcp4 != NULL) {
    gBS->CloseProtocol (
           Private->ControllerHandle,
           &gEfiDhcp4ServiceBindingProtocolGuid,
           Private->ImageHandle,
           Private->ControllerHandle
           );
  }

  if (Private->Udp6 != NULL) {
    gBS->CloseProtocol (
           Private->ControllerHandle,
           &gEfiUdp6ServiceBindingProtocolGuid,
           Private->ImageHandle,
           Private->ControllerHandle
           );
  }

  if (Private->Dhcp6 != NULL) {
    gBS->CloseProtocol (
           Private->ControllerHandle,
           &gEfiDhcp6ServiceBindingProtocolGuid,
           Private->ImageHandle,
           Private->ControllerHandle
           );
  }

  //
  // Free the mode buffer
  //
  if (Private->Mode != NULL) {
    FreePool (Private->Mode);
  }

  //
  // Free the private data itself
  //
  FreePool (Private);
}

/**
  Wait for a specified number of seconds or until ESC is pressed.

  @param[in] TimeoutSeconds  Number of seconds to wait.

  @retval EFI_SUCCESS           Wait completed without ESC press.
  @retval EFI_ABORTED           ESC key was pressed.
**/
EFI_STATUS
PxeBcWaitForEsc (
  IN UINT8  TimeoutSeconds
  )
{
  EFI_STATUS  Status;
  UINTN       WaitCount;
  EFI_INPUT_KEY Key;

  DEBUG ((DEBUG_INFO, ". Press ESC key to abort PXE boot\n"));

  //
  // Wait in 100ms increments
  //
  WaitCount = TimeoutSeconds * 10;
  while (WaitCount > 0) {
    WaitCount--;

    //
    // Check if key is available
    //
    Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
    if (!EFI_ERROR (Status)) {
      if (Key.UnicodeChar == CHAR_ESC) {
        return EFI_ABORTED;
      }
    }

    gBS->Stall (100000);  // 100ms
  }

  return EFI_SUCCESS;
}

/**
  Print a MAC address in formatted hex.

  @param[in] MacAddr  Pointer to the MAC address (6 bytes).
**/
VOID
PxeBcPrintMacAddr (
  IN UINT8  *MacAddr
  )
{
  UINTN  Index;

  DEBUG ((DEBUG_INFO, " on MAC: "));
  for (Index = 0; Index < 6; Index++) {
    DEBUG ((DEBUG_INFO, "%02x", MacAddr[Index]));
    if (Index < 5) {
      DEBUG ((DEBUG_INFO, "-"));
    }
  }
}

/**
  Reset the PXE mode to default state, clearing all cached data.

  @param[in] Private  The PXE private data instance.
**/
VOID
PxeBcModeReset (
  IN PXEBC_PRIVATE_DATA  *Private
  )
{
  EFI_PXE_BASE_CODE_MODE  *Mode;
  UINT8                   Ipv6Available;
  UINT8                   Ipv6Supported;

  Mode = Private->Mode;
  if (Mode == NULL) {
    return;
  }

  //
  // Save IPv6 capability flags
  //
  Ipv6Available  = Mode->Ipv6Available;
  Ipv6Supported  = Mode->Ipv6Supported;

  //
  // Clear the mode structure (size ~10424 bytes)
  //
  ZeroMem (Mode, sizeof (EFI_PXE_BASE_CODE_MODE));

  //
  // Restore IPv6 flags
  //
  Mode->Ipv6Available = Ipv6Available;
  Mode->Ipv6Supported = Ipv6Supported;
  Mode->TTL           = 16;
  Mode->AutoArp       = TRUE;

  //
  // Clear DHCP cache
  //
  Private->Dhcp4State       = 0;
  Private->Dhcp6State       = 0;
  Private->TftpBufferSize   = 0;

  //
  // Boot file info
  //
  Private->BootFileSize     = 0;
  Private->BootFileName[0]  = '\0';

  //
  // Clear tokens
  //
  if (Private->Udp4ReceiveToken != NULL) {
    FreePool (Private->Udp4ReceiveToken);
    Private->Udp4ReceiveToken = NULL;
  }

  if (Private->Udp6ReceiveToken != NULL) {
    FreePool (Private->Udp6ReceiveToken);
    Private->Udp6ReceiveToken = NULL;
  }
}

/**
  Build a DHCP4 PXE option list for sending with DHCP requests.

  @param[out] OptList   Pointer to receive the option list.
  @param[in]  OptCount  Maximum number of options.

  @return The number of options set.
**/
UINT32
PxeBcBuildDhcp4Options (
  OUT EFI_DHCP4_PACKET_OPTION  **OptList,
  IN  UINT32                   OptCount
  )
{
  UINT32  Count;

  Count = 0;

  //
  // Option 57 (0x39): PXE Client ID / Class ID
  //
  if (Count < OptCount) {
    OptList[Count] = AllocatePool (sizeof (EFI_DHCP4_PACKET_OPTION) + 10);
    if (OptList[Count] != NULL) {
      OptList[Count]->OpCode    = DHCP4_TAG_PXE_CLASS_ID;
      OptList[Count]->Length    = 10;
      OptList[Count]->Data[0]   = 0;    // Arch type
      OptList[Count]->Data[1]   = 0;    // reserved
      OptList[Count]->Data[2]   = 0;    // reserved
      Count++;
    }
  }

  return Count;
}

/**
  Save the boot file from the current session to NVRAM for subsequent boots.

  @param[in] Private   The PXE private data.
  @param[in] FilePath  The file path to save.
**/
VOID
PxeBcSaveBootFile (
  IN PXEBC_PRIVATE_DATA  *Private,
  IN VOID                *FilePath
  )
{
  //
  // Write the Boot#### variable
  //
  // This function reads the current BootCurrent variable, constructs a
  // new Boot#### variable pointing to the downloaded NBP file, and
  // writes it to NVRAM so that subsequent boots can load the same file
  // without doing PXE again.
  //
}

/**
  Start the PXE boot process with user-visible prompts.

  @param[in] Private  The PXE private data.
  @param[in] ModePtr  Pointer to the mode data for display.
**/
VOID
PxeBcBootPrompt (
  IN PXEBC_PRIVATE_DATA  *Private,
  IN UINT8               *ModePtr
  )
{
  //
  // Display boot prompt and wait for ESC to abort
  //
  // This function handles the "Press ESC key to abort PXE boot" prompt
  // during the DHCP/PXE discovery phase.
  //
  DEBUG ((DEBUG_INFO, "\n>>Start PXE over %s\n",
          Private->Mode->UsingIpv6 ? L"IPv6" : L"IPv4"));
  PxeBcPrintMacAddr (Private->MacAddr);

  if (Private->Mode->UsingIpv6) {
    PxeBcWaitForEsc (n256);
  } else {
    PxeBcWaitForEsc (n257);
  }
}