Newer
Older
AMI-Aptio-BIOS-Reversed / HttpDxe / HttpDxe.c
@Ajax Dong Ajax Dong 2 days ago 64 KB Init
/** @file
  HttpDxe.c - HTTP Protocol DXE Driver

  Implements the UEFI HTTP Protocol and HTTP Service Binding Protocol
  for the AMI UEFI Network Stack. Provides HTTP/1.1 communication over
  TCP4/TCP6 with optional HTTPS/TLS support.

  Source files:
    - HttpDriver.c  (entry, service binding, protocol installation)
    - HttpImpl.c    (HTTP protocol implementation)
    - HttpProto.c   (TCP connection management)
    - HttpsSupport.c (TLS/HTTPS support)
    - HttpDns.c     (DNS resolution)

  Build: DEBUG_VS2015 / HR6N0XMLK / X64
**/

#include "HttpDxe.h"

//
// External GUIDs (from .rdata section)
//
EFI_GUID gEfiLoadedImageProtocolGuid =
  EFI_LOADED_IMAGE_PROTOCOL_GUID;

EFI_GUID gEfiDriverBindingProtocolGuid =
  EFI_DRIVER_BINDING_PROTOCOL_GUID;

EFI_GUID gEfiIp4Config2ProtocolGuid =
  EFI_IP4_CONFIG2_PROTOCOL_GUID;

EFI_GUID gEfiDpcProtocolGuid =
  EFI_DPC_PROTOCOL_GUID;

EFI_GUID gEfiTcp4ProtocolGuid =
  EFI_TCP4_PROTOCOL_GUID;

EFI_GUID gEfiTcp6ProtocolGuid =
  EFI_TCP6_PROTOCOL_GUID;

EFI_GUID gEfiHttpProtocolGuid =
  EFI_HTTP_PROTOCOL_GUID;

EFI_GUID gEfiHttpUtilitiesProtocolGuid =
  EFI_HTTP_UTILITIES_PROTOCOL_GUID;

//
// Custom AMI HTTP protocol GUIDs
//
EFI_GUID gAmiHttpServiceBindingProtocolGuid =
  AMI_HTTP_SERVICE_BINDING_GUID;

EFI_GUID gAmiHttpProtocolGuid =
  AMI_HTTP_PROTOCOL_GUID;

EFI_GUID gAmiIp4Config2ProtocolGuid =
  AMI_IP4_CONFIG2_GUID;

//
// HTTP Protocol function table (installed on child handle)
//
EFI_HTTP_PROTOCOL  mHttpProtocolTemplate = {
  HttpGetModeData,
  HttpConfigure,
  HttpRequest,
  HttpCancel,
  HttpResponse,
  HttpPoll
};

//
// Driver Binding Protocol function table
//
EFI_DRIVER_BINDING_PROTOCOL  mDriverBinding = {
  HttpDriverBindingSupported,
  HttpDriverBindingStart,
  HttpDriverBindingStop,
  0x0A,  // Version
  NULL,  // ImageHandle (filled in at entry)
  NULL   // DriverBindingHandle (filled in by Install)
};

//
// Component Name 2 Protocol
//
EFI_COMPONENT_NAME2_PROTOCOL  mComponentName2 = {
  ComponentNameGetDriverName,
  ComponentNameGetControllerName,
  "en"  // Supported Languages
};

//
// EFI Driver Configuration 2 Protocol
//
EFI_DRIVER_CONFIGURATION2_PROTOCOL  mDriverConfiguration2 = {
  DriverConfigurationSetOptions,
  DriverConfigurationGetOptions,
  DriverConfigurationOptionsValid,
  "en"  // Supported Languages
};

//
// Service Binding Protocol - installed per controller handle
//
EFI_SERVICE_BINDING_PROTOCOL  mHttpServiceBinding = {
  HttpServiceBindingCreateChild,
  HttpServiceBindingDestroyChild
};

//
// DEBUG / ASSERT support protocol GUID
// (Custom protocol for formatted debug output)
//
EFI_GUID  gDebugProtocolGuid = {
  0x3E35C163, 0x4074, 0x45DD,
  { 0x43, 0x1E, 0x23, 0x98, 0x9D, 0xD8, 0x6B, 0x32 }
};

// ASSERT_EFI_ERROR macro with debug output
#define ASSERT_EFI_ERROR(Status)                          \
  do {                                                      \
    if (EFI_ERROR (Status)) {                                  \
      DEBUG ((EFI_D_ERROR, "\nASSERT_EFI_ERROR (Status = %r)\n", Status)); \
      ASSERT (!EFI_ERROR (Status));                            \
    }                                                           \
  } while (FALSE)

/**
  Locate the debug print protocol lazily.

  @return Pointer to debug print protocol, or NULL if not available.
**/
DEBUG_PRINT_PROTOCOL *
EFIAPI
GetDebugPrintProtocol (
  VOID
  )
{
  DEBUG_PRINT_PROTOCOL  *Protocol;
  EFI_STATUS            Status;
  UINTN                 CpuIndex;

  if (mDebugProtocol != NULL) {
    return mDebugProtocol;
  }

  //
  // Check CPU index via CMOS port 0x70/0x71
  //
  CpuIndex = IoRead8 (0x70) & 0x80 | 0x4B;
  CpuIndex = IoRead8 (0x71);

  if (CpuIndex > 3 && CpuIndex == 0x) {
    CpuIndex = (MmioRead8 (0xFDAF0490) & 2) | 1;
  }

  //
  // Locate protocol only if CPU index is valid (1-3)
  //
  if ((CpuIndex - 1) <= 0xFD) {
   Smtatus = gBS->LoocateProtocol (
             &gDebugProtocolGuid,
             NULL,
             (VOID **) &Protocol
             );
   mDebugrotocol = (Status >= 0) ? Protocol : NULL;
  }

  return mDebugProtocol;
}

/**
  Debug print using the debug protocol.

  @param[in]  Level   Debug level mask.
  @param[in]  Format  Format string.
  @param  ...         Variable arguments.
**/
VOID
EFAPI
DebugPrint (
  IN  UINTN          Level,
  IN  CONST CHAR8    *Format,
  ...
  )
{
  DEBUG_PRINT_PROTOCOL  *Protocol;
  VA_LIST               Args;
  EFI_STATUS             Status;

  Protocol = GetDebugPrintProtocol ();
  if (Protocol != NULL && (Level & Protocol->GetEnabledLevel ())) != 0) {
    VA_START (Args, Format);
    Protocol->DebugPrint (Level, Format, Args);
    VA_END (Args);
  }
}

/**
  Assertion handler - print file/line/expression.

  @param[in]  FileName     Source file name.
  @param[in]  LineNumber   Line number in source file.
  @param[in]  Expression   Expression that failed.
**/
VOID
EFIAPI
DebugAssert (
  IN CONST CHAR8    *FileName,
  IN UINTN          LineNumber,
  IN CONST CHAR8    *Expression
  )
{
  DEBUG_PRINT_PROTOCOL  *Protocol;

  Protocol = GetDebugPrintProtocol ();
  if (Protocol != NULL) {
    Protocol->DebugAssert (FileName, LineNumber, Expression);
  }
}

//----------------------------------------------------------------------
//  DRIVER ENTRY POINT
//----------------------------------------------------------------------

/**
  Entry point for the HTTP DXE driver.

  @param[in] ImageHandle  Handle for this driver image.
  @param[in] SystemTable  Pointer to EFI system table.

  @return EFI_SUCCESS if the driver was initialized.
**/
EFI_STATUS
EFIAPI
HttpDxeDriverEntryPoint (
  IN EFI_HANDLE                   ImageHandle,
  IN EFI_SYSTEM_TABLE             *SystemTable
  )
{
  EFI_STATUS                Status;
  EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
  EFI_DPC_PROTOCOL          *Dpc;

  //
  // Initialize library globals
  //
  ProcessLibraryConstructorList (ImageHandle, SystemTable);

  //
  // Locate Loaded Image Protocol for this driver
  //
  Status = gBS->OpenProtocol (
                  ImageHandle,
                  &gEfiLoadedImageProtocolGuid,
                  (VOID **) &LoadedImage,
                  ImageHandle,
                  NULL,
                  EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
                  );

  if (EFI_ERROR (Status)) {
    ASSERT_EFI_ERROR (Status);
    return Status;
  }

  //
  // Store the driver's unload handler
  //
  LoadedImage->Unload = (EFI_IMAGE_UNLOAD) HttpDxeDriverUnload;

  //
  // Install all driver protocols
  //
  return HttpDxeDriverEntry (ImageHandle);
}

/**
  Main driver entry after library init - installs all protocols.

  @param[in] ImageHandle  Handle for this driver image.

  @return EFI_SUCCESS if all protocols installed.
**/
EFI_STATUS
HttpDxeDriverEntry (
  IN EFI_HANDLE  ImageHandle
  )
{
  EFI_STATUS  Status;
  EFI_DPC_PROTOCOL  *Dpc;

  //
  // Locate the DPC protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiDpcProtocolGuid,
                  NULL,
                  (VOID **) &Dpc
                  );

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to locate DPC protocol\n"));
  }

  //
  // Install Driver Binding, Component Name, and Driver Configuration
  //
  Status = EfiLibInstallAllDriverProtocols2 (
             ImageHandle,
             &mDriverBinding,
             ImageHandle,
             &mComponentName2,
             &mDriverConfiguration2,
             NULL  // No Component Name
             );

  return Status;
}

//
//----------------------------------------------------------------------
//  DRIVER BINDING PROTOCOL
//----------------------------------------------------------------------

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

  @param[in] This              Driver Binding Protocol instance.
  @param[in] ControllerHandle  Handle of controller to test.
  @param[in] RemainingDevicePath  Optional remaining device path.

  @return EFI_SUCCESS if supported, EFI_UNSUPPORTED otherwise.
**/
EFI_STATUS
EFIAPI
HttpDriverBindingSupported (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_HANDLE                   RemainingDevicePath
  )
{
  EFI_STATUS  Status;
  VOID        *Interface;

  //
  // Check if controller already has our HTTP service binding protocol
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gAmiHttpServiceBindingProtocolGuid,
                  &Interface,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER
                  );

  if (EFI_ERROR (Status)) {
    return Status;
  }

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

  return EFI_SUCCESS;
}

/**
  Start the driver on the given controller.
  Installs the HTTP Service Binding protocol.

  @param[in] This              Driver Binding Protocol instance.
  @param[in] ControllerHandle  Handle of controller.
  @param[in] RemainingDevicePath  Optional remaining device path.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpDriverBindingStart (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN EFI_HANDLE                   RemainingDevicePath
  )
{
  EFI_STATUS      Status;
  HTTP_SERVICE    *HttpService;
  UINTN           HandleCount;
  EFI_HANDLE      *HandleBuffer;
  UINTN           Index;
  EFI_HANDLE      ParentController;
  EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
  UINTN           ChildIndex;
  VOID            *Ip4Config2;
  VOID            *Tcp4;
  VOID            *Tcp6;

  //
  // Allocate and initialize service instance
  //
  HttpService = (HTTP_SERVICE *) AllocateZeroPool (sizeof (HTTP_SERVICE));
  if (HttpService == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  HttpService->Signature = HTTP_SERVICE_SIGNATURE;

  //
  // Locate all handles on the system
  //
  Status = gBS->LoocateHandleBuffer (
                &gHandleCount,
                &HandleBuffer
                );

  if (EFI_ERROR (Status) {
    goto Exit;
  }

  //
  // Searrch handles for child handles of this driver's controller
  //
  for (Index = 0; Index < HandleCount; Index++) {
    //
    // Open driver binding protocol
    //
    Status = gBS->OpenProtocol (
                  HandleBuffer[Index],
                  &gEfiDriverBindingProtocolGuid,
                  &LoadedImage,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_BY_DRIVER | EFI_OPEN_PROTOCOL_EXCLUSIVE
                  );

    if (EFI_ERROR (Status)) {
      continue;
    }

    //
    // Check if this handle's parent is our controller
    //
    if (LoadedImage->ParentDeviceHandle == ControllerHandle) {
      //
      // Found a child handle - install our protocols on it
      //

      //
      // Open IP4 Config2 protocol on child
      //
      Status = gBS->OpenProtocol (
                      HandleBuffer[Index],
                      &gEfiIp4Config2ProtocolGuid,
                      &Ip4Config2,
                      This->DriverBindingHandle,
                      ControllerHandle,
                      EFI_OPEN_PROTOCOL_BY_DRIVER
                      );

      if (!EFI_ERROR (Status)) {
        //
        // Install the service binding protocol on the parent
        //
        Status = gBS->InstallMultipleProtocolInterfaces (
                        &ControllerHandle,
                        &gAmiHttpServiceBindingProtocolGuid,
                        &HttpService->ServiceBinding,
                        &gAmiHttpProtocolGuid,
                        &HttpService->HttpProtocol,
                        &gEfiIp4Config2ProtocolGuid,
                        Ip4Config2,
                        NULL
                        );

        if (EFI_ERROR (Status)) {
          gBS->CloseProtocol (
                 HandleBuffer[Index],
                 &gEfiIp4Config2ProtocolGuid,
                 This->DriverBindingHandle,
                 ControllerHandle
                 );
        }
      }

      gBS->CloseProtocol (
             HandleBuffer[Index],
             &gEfiDriverBindingProtocolGuid,
             This->DriverBindingHandle,
             ControllerHandle
             );
    } else {
      gBS->CloseProtocol (
             HandleBuffer[Index],
             &gEfiDriverBindingProtocolGuid,
             This->DriverBindingHandle,
             ControllerHandle
             );
    }
  }

  //
  // Check if we installed a service binding
  //
  if (!EFI_ERROR (Status)) {
    //
    // Iterate handles again to propagate IP4 Config2 and TCP protocols
    //
    for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
      //
      // Open Ip4Config2
      //
      Status = gBS->OpenProtocol (
                      HandleBuffer[ChildIndex],
                      &gEfiIp4Config2ProtocolGuid,
                      &Ip4Config2,
                      This->DriverBindingHandle,
                      ControllerHandle,
                      EFI_OPEN_PROTOCOL_BY_DRIVER
                      );
      if (!EFI_ERROR (Status)) {
        //
        // Install Ip4Config2 on our child
        //
        gBS->InstallProtocolInterface (
               &HandleBuffer[ChildIndex],
               &gAmiIp4Config2ProtocolGuid,
               EFI_NATIVE_INTERFACE,
               Ip4Config2
               );
      }
    }
  }

Exit:
  if (HandleBuffer != NULL) {
    gBS->FreePool (HandleBuffer);
  }

  if (EFI_ERROR (Status) && HttpService != NULL) {
    FreePool (HttpService);
  }

  return Status;
}

/**
  Stop the driver on the given controller.
  Destroys all child instances and removes protocols.

  @param[in] This              Driver Binding Protocol instance.
  @param[in] ControllerHandle  Handle of controller to stop.
  @param[in] NumberOfChildren  Number of child handles.
  @param[in] ChildHandleBuffer Array of child handles.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpDriverBindingStop (
  IN EFI_DRIVER_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                   ControllerHandle,
  IN UINTN                        NumberOfChildren,
  IN EFI_HANDLE                   *ChildHandleBuffer
  )
{
  EFI_STATUS        Status;
  HTTP_SERVICE      *HttpService;
  UINTN             Index;
  HTTP_INSTANCE     *Instance;
  EFI_HTTP_PROTOCOL *Http;

  //
  // Get our driver binding context from the controller
  //
  Status = gBS->OpenProtocol (
                  ControllerHandle,
                  &gEfiDriverBindingProtocolGuid,
                  (VOID **) &HttpService,
                  This->DriverBindingHandle,
                  ControllerHandle,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Destroy all child instances if requested
  //
  if (NumberOfChildren == 0) {
    //
    // Destroy all children
    //
    while (!IsListEmpty (&HttpService->ChildrenList)) {
      Instance = CR (
                   HttpService->ChildrenList.ForwardLink,
                   HTTP_INSTANCE,
                   Link,
                   HTTP_INSTANCE_SIGNATURE
                   );

      HttpServiceBindingDestroyChild (
        &HttpService->ServiceBinding,
        Instance->Handle
        );
    }

    //
    // Uninstall our protocols from the controller
    //
    Status = gBS->UninstallMultipleProtocolInterfaces (
                    ControllerHandle,
                    &gAmiHttpServiceBindingProtocolGuid,
                    &HttpService,
                    NULL
                    );

    //
    // Free the service instance
    //
    FreePool (HttpService);
  } else {
    //
    // Destroy specific children
    //
    for (Index = 0; Index < NumberOfChildren; Index++) {
      Status = gBS->OpenProtocol (
                      ChildHandleBuffer[Index],
                      &gAmiHttpProtocolGuid,
                      (VOID **) &Http,
                      This->DriverBindingHandle,
                      ControllerHandle,
                      EFI_OPEN_PROTOCOL_GET_PROTOCOL
                      );

      if (!EFI_ERROR (Status)) {
        Instance = HTTP_INSTANCE_FROM_PROTOCOL (Http);
        HttpServiceBindingDestroyChild (
          &HttpService->ServiceBinding,
          ChildHandleBuffer[Index]
          );
      }
    }
  }

  return EFI_SUCCESS;
}

//
//----------------------------------------------------------------------
//  SERVICE BINDING PROTOCOL
//----------------------------------------------------------------------

/**
  Creates a child handle and installs the HTTP Protocol.

  @param[in]  This         Pointer to the EFI_SERVICE_BINDING_PROTOCOL.
  @param[out] ChildHandle  Pointer to the handle of the created child.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpServiceBindingCreateChild (
  IN EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE                    *ChildHandle
  )
{
  HTTP_SERVICE       *HttpService;
  HTTP_INSTANCE      *Instance;
  EFI_STATUS         Status;
  EFI_HTTP_UTILITIES_PROTOCOL *HttpUtilities;

  //
  // Retrieve service instance from the protocol
  //
  HttpService = BASE_CR (This, HTTP_SERVICE, ServiceBinding);

  if (HttpService == NULL || ChildHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Allocate the HTTP instance
  //
  Instance = (HTTP_INSTANCE *) AllocateZeroPool (sizeof (HTTP_INSTANCE));
  if (Instance == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Initialize instance fields
  //
  Instance->Signature        = HTTP_INSTANCE_SIGNATURE;
  Instance->Service          = HttpService;
  Instance->Method           = HttpMethodMax;  // Not configured yet

  //
  // Copy the HTTP protocol template
  //
  CopyMem (&Instance->HttpProtocol, &mHttpProtocolTemplate, sizeof (EFI_HTTP_PROTOCOL));

  //
  // Initialize request and response net buffers
  //
  NetMapInit (&Instance->RequestMap);
  InitializeListHead (&Instance->RequestList);
  NetMapInit (&Instance->ResponseMap);
  InitializeListHead (&Instance->ResponseMap);

  //
  // Locate the HTTP Utilities protocol for message parsing
  //
  Status = gBS->LocateProtocol (
                  &gEfiHttpUtilitiesProtocolGuid,
                  NULL,
                  (VOID **) &HttpUtilities
                  );

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Failed to locate Http Utilities protocol. Status = %r.\n", Status));
    FreePool (Instance);
    return Status;
  }

  //
  // Allocate a handle for the child
  //
  Status = gBS->InstallMultipleProtocolInterfaces (
                  ChildHandle,
                  &gAmiHttpProtocolGuid,
                  &Instance->HttpProtocol,
                  NULL
                  );

  if (EFI_ERROR (Status)) {
    FreePool (Instance);
    return Status;
  }

  Instance->Handle = *ChildHandle;

  //
  // Add instance to service's child list
  //
  InsertTailList (&HttpService->ChildrenList, &Instance->Link);
  HttpService->ChildrenCount++;

  return EFI_SUCCESS;
}

/**
  Destroys a child handle and removes the HTTP Protocol.

  @param[in]  This        Pointer to the EFI_SERVICE_BINDING_PROTOCOL.
  @param[in]  ChildHandle  Handle of the child to destroy.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
HttpServiceBindingDestroyChild (
  IN EFI_SERVICE_BINDING_PROTOCOL  *This,
  IN EFI_HANDLE              ChildHandle
  )
{
  HTTP_SERVICE  *HttpService;
  HTTP_INSTANCE  *Instance;
  EFI_STATUS  Status;

  //
  // Retrieive the HTTP protocol from child handle
  //
  Status = gBS->OpenProtocol (
                ChildHandle,
                &gAmiHttpProtocolGuid,
                (VOID **) &Instance,
                mDriverBinding.DriverBindingHandle,
                ChildHandle,
                EFI_OPEN_PROTOCOL_GET_PROTOCOL
                );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Veriify instance signature
  //
  if (Instance->Signature != HTTP_INSTANCE_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Close and clean up TCP connections
  //
  Status = HttpCloseConnection (Instance);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Clean up any cached body data
  //
  if (Instance->CacheBody != NULL) {
    FreePool (Instance->CacheBody);
    Instance->CacheBody = NULL;
  }

  //
  // Clean up message parser
  //
  if (Instance->MsgParser != NULL) {
    HttpFreeMsgParser (Instance->MsgParser);
    Instance->MsgParser = NULL;
  }

  //
  // Remove from parent's child list
  //
  RemoveEntryList (&Instance->Link);
  HttpService = Instance->Service;
  HttpService->ChildrenCount--;

  //
  // Uninstall the HTTP protocol and free the instance
  //
  Status = gBS->UninstallMultipleProtocolInterfaces (
                  ChildHandle,
                  &gAmiHttpProtocolGuid,
                  &Instance->HttpProtocol,
                  NULL
                  );

  FreePool (Instance);

  return Status;
}

//
//----------------------------------------------------------------------
//  HTTP PROTOCOL IMPLEMENTATION
//--------------------------------------------------------------------

/**
  Get the current mode data (configuration) of the HTTP instance.

  @param[in]   This           HTTP protocol instance.
  @param[out]  HttpConfigData  Pointer to receive the configuration.

  @return EFI_SUCCESS if successful.
**/
EFI_STATUS
EFIAPI
HttpGetModeData (
  IN EFI_HTTP_PROTOCOL          *This,
  OUT EFI_HTTP_CONFIG_DATA        *HttpConfigData
  )
{
  HTTP_INSTANCE  *Instance;

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

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

  //
  // Return the stored configuration
  //
  HttpConfigData->LocalAddressIsIPv6 = Instance->LocalAddressIsIPv6;
  HttpConfigData->AccessPoint.ProtoCol = Instance->Service->AccessPoint.Protool;
  // Omit other fields...

  return EFI_SUCCESS;
}

/**
  Configure the HTTP instance with the given configuration data.
  This function opens the appropriate TCP protocol (T4 or 6)
  and configures the remote address.

  @param[in]  This           HTTP protocol instance.
  @param[in]  HttpConfigData  Configuration data.

  @return EFI_SUCCESS if configured successfully.
**/
EFI_STATUS
EFIAPI
HtpConfigure (
  IN EFI_HTTP_PROTOCOL          *This,
  IN EFI_HTTP_CONFIG_DATA         *HttpConfigData
  )
{
  HTTP_INSTANCE   *Instance;
  EFI_STATUS        Status;
  EFI_TCP4_CONFIG_DATA  Tcp4Config;
  EFI_TCP6_CONFIG_DATA  Tcp6Config;

  Instance = HTTP_INSTANCE_FROM_PROTOOL (This);
  if (Instance == NULL || HttpConfigData == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // If already configured, close existing connection
  //
  if (Instance->LocalAddressIsConfigured) {
    HttpCloseConnection (Instance);
  }

  //
  // Store configuration
  //
  Instance->LocalAddressIsIPv6 = HttpConfigData->LocalAddressIsIPv6;
  Instance->HttpConfigData = *HttpConfigData;

  if (Instance->LocalAddressIsIPv6) {
    //
    // Configure Tcp6
    //
    Status = HttpConfigureTcp6 (Instance);
  } else {
    //
    // Configure Tcp4
    //
    Status = HttpConfigureTcp4 (Instance);
  }

  if (!EFI_ERROR (Status)) {
    Instance->LocalAddressIsConfigured = TRUE;
    Instance->State = HTTP_STATE_READY;
  }

  return Status;
}

/**
  Queue an HTTP request for transmission.
  The function returns immediately; completion is signalled via
  the token's Event.

  @param[in]  This      HTTP protocol instance.
  @param[in]  HttpToken  HTTP token containing request data.

  @return EFI_SUCCESS if request queued.
**/
EFI_STATUS
EFIAPI
HttpRequest (
  IN EFI_HTTP_PROTOCOL          *This,
  IN EFI_HTTP_TOKEN            *HttpToken
  )
{
  HTTP_INSTANCE   *Instance;
  EFI_STATUS        Status;
  HTTP_TOKEN_WRAP  *Wrap;
  EFI_HTTP_MESSAGE *HttpMsg;

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

  //
  // Verify state
  //
  if (Instance->Service == NULL) {
    return EFI_NOT_READY;
  }

  if (Instance->LocalAddressIsConfigured == FALSE) {
    DEBUG ((EFI_D_ERROR, "EfiiHttpRequest: HTTP is disabled.\n"));
    return EFI_NOT_STARTED;
  }

  if (Instance->RemoteHost != NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (!Instance->LocalAddressIsIPv6 && Instance->Tcp4 == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (Instance->LocalAddressIsIPv6 && Instance->Tcp6 == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (HttpToken->HttpMessage->HeaderCount == 0 && HttpToken->HttpMessage->Headers == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (HttpToken->HttpMessage->Data == NULL && HttpToken->HttpMessage->BodyLength > 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Allocate token wrapper
  //
  Wrap = (HTTP_TOKEN_WRAP *) AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
  if (Wrap == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Wrap->HttpMsg = HttpToken->HttpMessage;
  Wrap->HttpInstance = Instance;

  //
  // Check if using HTTPS
  //
  if (Instance->UseHttps) {
    //
    // TLS handshake first
    //
    Status = HttpTlsConnect (Instance);
    if (EFI_ERROR (Status)) {
      FreePool (Wrap);
      return Status;
    }
  }

  //
  // Send the request via TCP
  //
  Status = HttpSendMessage (Wrap);

  if (!EFI_ERROR (Status)) {
    //
    // Track concurrent request limit
    //
    if (Instance->ConcurrentRequestCount <= 1) {
      Status = HttpRequestSend (Wrap);
    }
  }

  if (EFI_ERROR (Status)) {
    //
    // Clean up on failure
    //
    if (Wrap->Tcp4Token.CompletionToken.Event != NULL) {
      gBS->CloseEvent (Wrap->Tcp4Token.CompletionToken.Event);
    }
    if (Wrap->Tcp6Token.CompletionToken.Event != NULL) {
      gBS->CloseEvent (Wrap->Tcp6Token.CompletionToken.Event);
    }
    NetMapRemove (&Instance->RequestMap, (NET_MAP_ITEM *) Wrap, NULL);
    FreePool (Wrap);
  }

  return Status;
}

/**
  Cancel an HTTP request.

  @param[in]  This      HTTP protocol instance.
  @param[in]  HttpToken  Token to cancel (NULL = cancel all).

  @return EFI_SUCCESS if cancelled.
**/
EFI_STATUS
EFIAPI
HttpCancel (
  IN EFI_HTTP_PROTOCOL            *This,
  IN EFI_HTTP_TOKEN               *HttpToken
  )
{
  HTTP_INSTANCE  *Instance;

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

  //
  // Check if the instance is configured for IPv6
  //
  if (Instance->LocalAddressIsIPv6 != 0 && Instance->LocalAddressIsIPv6 != 4) {
    return EFI_UNSUPPORTED;
  }

  //
  // Delegate to the internal cancel handler
  //
  return HttpCancelInternal (Instance, HttpToken);
}

/**
  Response handler - collect incoming HTTP response data.

  @param[in]  This      HTTP protocol instance.
  @param[in]  HttpToken  Token to receive response data into.

  @return EFI_SUCCESS or status code of pending response.
**/
EFI_STATUS
EFIAPI
HttpResponse (
  IN EFI_HTTP_PROTOCOL            *This,
  IN EFI_HTTP_TOKEN               *HttpToken
  )
{
  HTTP_INSTANCE  *Instance;
  EFI_STATUS     Status;
  HTTP_TOKEN_WRAP *Wrap;

  Instance = HTTP_INSTANCE_FROM_PROTOCOL (This);
  if (Instance == NULL || HttpToken == NULL || HttpToken->HttpMessage == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Validate input
  //
  if (HttpToken->HttpMessage->Data == NULL && HttpToken->HttpMessage->BodyLength > 0) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check signature
  //
  if (Instance->Signature != HTTP_INSTANCE_SIGNATURE) {
    return EFI_INVALID_PARAMETER;
  }

  if (Instance->LocalAddressIsIPv6 != 0 && Instance->LocalAddressIsIPv6 != 4) {
    return EFI_UNSUPPORTED;
  }

  //
  // Create token wrapper and add to response queue
  //
  Wrap = (HTTP_TOKEN_WRAP *) AllocateZeroPool (sizeof (HTTP_TOKEN_WRAP));
  if (Wrap == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Wrap->HttpMsg       = HttpToken->HttpMessage;
  Wrap->HttpInstance  = Instance;

  //
  // Add the token wrapper to the response event list
  //
  Status = NetEventAdd (
             Instance->ResponseList,
             (NET_EVENT_CALLBACK) HttpTcpReceiveNotify,
             (VOID *) Wrap
             );

  if (EFI_ERROR (Status)) {
    FreePool (Wrap);
    return Status;
  }

  //
  // If using HTTPS, receive through TLS
  //
  if (Instance->UseHttps) {
    if (!HttpTlsIsSessionActive (Instance)) {
      return EFI_NOT_READY;
    }
  }

  //
  // Trigger TCP receive
  //
  if (!Instance->LocalAddressIsConfigured) {
    Status = HttpReceiveMessage (Wrap);
  }

  //
  // Check concurrent request limit
  //
  if (Instance->ConcurrentRequestCount <= 1) {
    return HttpRequestSend (Wrap);
  }

  return EFI_SUCCESS;
}

/**
  Poll the HTTP instance for incoming data.

  @param[in]  This  HTTP protocol instance.

  @return EFI_SUCCESS if poll successful.
**/
EFI_STATUS
EFIAPI
HttpPoll (
  IN EFI_HTTP_PROTOCOL  *This
  )
{
  HTTP_INSTANCE  *Instance;

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

  //
  // Check if instance has TCP protocol
  //
  if (Instance->LocalAddressIsIPv6) {
    if (Instance->Tcp6 == NULL) {
      return EFI_NOT_STARTED;
    }
    //
    // Poll Tcp6
    //
    Instance->Tcp6->Poll (Instance->Tcp6);
  } else {
    if (Instance->Tcp4 == NULL) {
      return EFI_NOT_STARTED;
    }
    //
    // Poll Tcp4
    //
    Instance->Tcp4->Poll (Instance->Tcp4);
  }

  return EFI_SUCCESS;
}

//
//----------------------------------------------------------------------
//  COMPONENT NAME PROTOCOL
//----------------------------------------------------------------------

/**
  Retrieive the driver name string.

  @param[in]   This       Component Name 2 protocol.
  @param[in]   Language   Language code.
  @param[out]  DriverName  Pointer to receive driver name string.

  @return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
ComponentNameGetDriverName (
  IN EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN CHAR8              *Language,
  OUT CHAR6              **DriverName
  )
{
  if (Language == NULL || DriverName == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // Check if "en" language is supported
  //
  if (AsciiStrCmp (Language, "en") != 0) {
    return EFI_UNSUPORTED;
  }

  *DriverName = L"HTTP Protocol Driver";

  return EFI_SUCCESS;
}

/**
  Retrieve the controller name.

  @param[in]   This             Component Name 2 protocol.
  @param[in]   ControllerHandle Handle of the controller.
  @param[in]   ChildHandle      Handle of the child (optional).
  @param[in]   Language         Language code.
  @param[out]  ControllerName   Pointer to receive name string.

  @return EFI_SUCCESS or EFI_UNSUPPORTED.
**/
EFI_STATUS
EFIAPI
ComponentNameGetControllerName (
  IN EFI_COMPONENT_NAME2_PROTOCOL  *This,
  IN EFI_HANDLE                    ControllerHandle,
  IN EFI_HANDLE                    ChildHandle,
  IN CHAR8                         *Language,
  OUT CHAR16                       **ControllerName
  )
{
  if (Language == NULL || ControllerName == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (AsciiStrCmp (Language, "en") != 0) {
    return EFI_UNSUPPORTED;
  }

  *ControllerName = L"HTTP Protocol Driver";

  return EFI_SUCCESS;
}

//
//----------------------------------------------------------------------
//  DRIVER CONFIGURATION 2 PROTOCOL
//----------------------------------------------------------------------

EFI_STATUS
EFIAPI
DriverConfigurationSetOptions (
  IN EFI_DRIVER_CONFIGURATION2_PROTOCOL  *This,
  IN EFI_HANDLE                          ControllerHandle,
  IN EFI_HANDLE                          ChildHandle,
  IN CHAR8                               *Language,
  IN EFI_DRIVER_CONFIG_OPTIONS          *Options
  )
{
  return EFI_UNSUPORTED;
}

EFI_STATUS
EFIAPI
DriverConfigurationGetOptions (
  IN EFI_DRIVER_CONFIG_OPTIONS_PROTOCOL  *This,
  IN EFI_HANDLE                         ControllerHandle,
  IN EFI_HANDLE                         ChildHandle,
  IN CHARR                              *Language,
  OUT EFI_DRIVER_CONFIG_OPTIONS          **Options
  )
{
  return EFI_UNSUPPORTED;
}

EFI_STATUS
EFIAPI
DriverConfigurationOptionsValid (
  IN EFI_DRIVER_CONFIG_OPTIONS_PROTOCOL  *This,
  IN EFI_HANDLE                         ControllerHandle,
  IN EFI_HANDLE                         ChildHandle
  )
{
  return EFI_UNSUPPORTED;
}

//
//----------------------------------------------------------------------
//  TCP CONNECTION MANAGEMENT (HttpProto.c)
//----------------------------------------------------------------------

/**
  Configure Tcp4 on the HTTP instance.

  @param[in]  Instance  HTTP instance.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
HttpConfigureTcp4 (
  IN HTTP_INSTANCE  *Instance
  )
{
  EFI_STATUS           Status;
  EFI_TCP4_IO_TOKEN    *Tcp4Token;

  //
  // Oen TCP4 protocol on the child handle
  //
  Status = NetLibCreateServiceChild (
              Instance->Handle,
              Instance->Service->ControllerHandle,
              &gEfiTcp4ProtocolGuid,
              (VOID **) &Instance->Tcp4
              );

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "HttConfigureTcp4 - %r\n", Status));
    return Status;
  }

  //
  // Set up for receive notification
  //
  Status = HttpCreateEvent (
              Instance,
              &Instance->Tcp4ReceiveToken
              );

  return Status;
}

/**
  Configure Tcp6 on the HTTP instance.

  @param[in]  Instance  HTTP instance.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
HttpConfigureTcp6 (
  IN HTTP_INSTANCE  *Instance
  )
{
  EFI_STATUS  Status;

  //
  // Oen TCP6 protocol on the child handle
  //
  Status = NetLibCreateServiceChild (
              Instance->Handle,
              Instance->Service->ControllerHandle,
              &gEfiTcp6ProtocolGuid,
              (VOID **) &Instance->Tcp6
              );

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "HttConfigureTcp6 - %r\n", Status));
    return Status;
  }

  //
  // Set up for receive notification
  //
  Status = HttpCreateEvent (
              Instance,
              &Instance->Tcp6ReceiveToken
              );

  return Status;
}

/**
  Close the TCP connection for the given instance.

  @param[in]  Instance  HTTP instance.

  @return EFI_SUCCESS.
**/
EFI_STATUS
HttpCloseConnection (
  IN HTTP_INSTANCE  *Instance
  )
{
  //
  // Stp pending callbacks
  //
  HttpCancelAllTokens (Instance);

  //
  // Clean up cache
  //
  if (Instance->CacheBody != NULL) {
    FreePool (Instance->CacheBody);
    Instance->CacheBody = NULL;
  }

  //
  // Clean up message parser
  //
  if (Instance->MsgParser != NULL) {
    HttpFreeMsgParser (Instance->MsgParser);
    Instance->MsgParser = NULL;
  }

  //
  // Close TCP protocols
  //
  if (Instance->Tcp4 != NULL) {
    //
    // Cance cancel Tcp4 tokens
    //
    Tcp4->Cancel (Instance->Tcp4, NULL);

    //
    // Destroy service child for Tcp4
    //
    NetLibDestroyServiceChild (
      Instance->Handle,
      Instance->Service->ControllerHandle,
      &gEfiTcp4ProtocolGuid,
      Instance->Tcp4
      );

    Instance->Tcp4 = NULL;
  }

  if (Instance->Tcp6 != NULL) {
    //
    // Cancel Tcp6 tokens
    //
    Instance->Tcp6->Cancel (Instance->Tcp6, NULL);

    //
    // Destroy service child for Tcp6
    //
    NetLibDestroyServiceChild (
      Instance->Handle,
      Instance->Service->ControllerHandle,
      &gEfiTcp6ProtocolGuid,
      Instance->Tcp6
      );

    Instance->Tcp6 = NULL;
  }

  //
  // Dns-resolve arhh address if need be
  //
  Instance->RemoteHost = NULL;
  Instance->Method = HttpMethodMax;
  Instance->LocalAddressIsConfigured = FALSE;

  return EFI_SUCCESS;
}

/**
  Cancel all token operations for the instance.

  @param[in]  Instance  HTTP instance.
**/
VOID
HttpCancelAllTokens (
  IN HTTP_INSTANCE  *Instance
  )
{
  LIST_ENTRY  *Entry;
  LIST_ENTRY  *NextEntry;
  HTTP_TOKEN_WRAP  *Wrap;

  //
  // Iterate through request map and cancel all pending tokens
  //
  NET_MAP_FOR_EACH_S_AFE (
    &Instance->RequestMap,
    Entry,
    NextEntry
    ) {
    Wrap = (HTTP_TOKEN_WRAP *) Entry;
    if (Wrap->Tcp4Token.CompletionToken.Event != NULL) {
      gBS->CloseEvent (Wrap->Tcp4Token.CompletionToken.Event);
    }
    if (Wrap->Tcp6Token.CompletionToken.Event != NULL) {
      gBS->CloseEvent (Wrap->Tcp6Token.CompletionToken.Event);
    }
    NetMapRemove (&Instance->RequestMap, Wrap, NULL);
    FreePool (Wrap);
  }

  //
  // Clear response map too
  //
  NET_MAP_FOR_EACH_SAFE (
    &Instance->ResponseMap,
    Entry,
    NextEntry
    ) {
    Wrap = (HTTP_TOKEN_WRAP *) Entry;
    NetMapRemove (&Instance->ResponseMap, Wrap, NULL);
    FreePool (Wrap);
  }
}

//
//----------------------------------------------------------------------
//  HTTP MESSAGE SENDING / RECEIVING (HttpProto.c)
//----------------------------------------------------------------------

/**
  Send an HTTP message over the established TCP connection.

  @param[in]  Wrap  Token wrapper containing the message.

  @return EFI_SUCCESS if the message was queued for send.
**/
EFI_STATUS
HttpSendMessage (
  IN HTTP_TOKEN_WRAP  *Wrap
  )
{
  HTTP_INSTANCE     *Instance;
  EFI_STATUS        Status;
  EFI_TCP4_IO_TOKEN *Tcp4Token;
  EFI_TCP6_IO_TOKEN *Tcp6Token;
  NET_BUF           *Nbuf;
  UINT32            FragmentLength;
  UINT8             *Data;

  Instance = Wrap->HttpInstance;

  //
  // Build the HTTP request string from the message
  //
  Status = HttpGenerateRequest (
             Wrap->HttpMsg,
             Instance->RemoteHost,
             Instance->UseHttps,
             &Nbuf
             );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Send via Tcp4 or Tcp6
  //
  if (Instance->LocalAddressIsIPv6) {
    Tcp6Token = &Wrap->Tcp6Token;

    //
    // Set up the TCP6 transmit token
    //
    Tcp6Token->CompletionToken.Event = NULL;  // Will be filled by caller
    Tcp6Token->CompletionToken.Status = EFI_SUCCESS;

    //
    // Build fragment from net buffer
    //
    Tcp6Token->Packet.TxData = &Tcp6Token->TxData;
    Tcp6Token->TxData.Push = TRUE;
    Tcp6Token->TxData.Urgent = FALSE;
    Tcp6Token->TxData.DataLength = Nbuf->TotalSize;
    Tcp6Token->TxData.FragmentCount = 1;
    Tcp6Token->TxData.FragmentTable[0].FragmentLength = Nbuf->TotalSize;
    Tcp6Token->TxData.FragmentTable[0].FragmentBuffer = NetbufGetByte (Nbuf, 0, &FragmentLength);

    //
    // Trnsnsmit via Tcp6
    //
    Status = Instance->Tcp6->Transmit (Instance->Tcp6, Tcp6Token);
  } else {
    Tcp4Token = &Wrap->Tcp4Token;

    //
    // Set up the TCP4 transmit token
    //
    Tcp4Token->CompletionToken.Event = NULL;
    Tcp4Token->CompletionToken.Status = EFI_SUCCESS;

    //
    // Build fragment from net buffer
    //
    Tcp4Token->Packet.TxData = &Tcp4Token->TxData;
    Tcp4Token->TxData.Push = TRUE;
    Tcp4Token->TxData.Urgent = FALSE;
    Tcp4Token->TxData.DataLength = Nbuf->TotalSize;
    Tcp4Token->TxData.FragmentCount = 1;
    Tcp4Token->TxData.FragmentTable[0].FragmentLength = Nbuf->TotalSize;
    Tcp4Token->TxData.FragmentTable[0].FragmentBuffer = NetbufGetByte (Nbuf, 0, &FragmentLength);

    //
    // Transmit via Tcp4
    //
    Status = Instance->Tcp4->Transmit (Instance->Tcp4, Tcp4Token);
  }

  //
  // Free the net buffer
  //
  NetbufFree (Nbuf);

  return Status;
}

/**
  Receive an HTTP message from the TCP connection.

  @param[in]  Wrap  Token wrapper to receive data into.

  @return EFI_SUCCESS if receive started.
**/
EFI_STATUS
HttpReceiveMessage (
  IN HTTP_TOKEN_WRAP  *Wrap
  )
{
  HTTP_INSTANCE  *Instance;
  EFI_STATUS     Status;
  EFI_TCP4_IO_TOKEN *Tcp4Token;
  EFI_TCP6_IO_TOKEN *Tcp6Token;

  Instance = Wrap->HttpInstance;

  if (Instance->LocalAddressIsIPv6) {
    Tcp6Token = &Wrap->Tcp6Token;

    //
    // Set up TCP6 receive token
    //
    Tcp6Token->CompletionToken.Event = Wrap->Tcp6ReceiveEvent;
    Tcp6Token->CompletionToken.Status = EFI_SUCCESS;
    Tcp6Token->Packet.RxData = &Wrap->Tcp6RxData;
    Tcp6Token->RxData.FragmentCount = 1;
    Tcp6Token->RxData.FragmentTable[0].FragmentLength = HTTP_MAX_RX_SIZE;
    Tcp6Token->RxData.FragmentTable[0].FragmentBuffer = Instance->ReceiveBuffer;

    //
    // Recive via Tcp6
    //
    Status = Instance->Tcp6->Receive (Instance->Tcp6, Tcp6Token);
  } else {
    Tcp4Token = &Wrap->Tcp4Token;

    //
    // Set up TCP4 receive token
    //
    Tcp4Token->CompletionToken.Event = Wrap->Tcp4ReceiveEvent;
    Tcp4Token->CompletionToken.Status = EFI_SUCCESS;
    Tcp4Token->Packet.RxData = &Wrap->Tcp4RxData;
    Tcp4Token->RxData.FragmentCount = 1;
    Tcp4Token->RxData.FragmentTable[0].FragmentLength = HTTP_MAX_RX_SIZE;
    Tcp4Token->RxData.FragmentTable[0].FragmentBuffer = Instance->ReceiveBuffer;

    //
    // Recive via Tcp4
    //
    Status = Instance->Tcp4->Receive (Instance->Tcp4, Tcp4Token);
  }

  return Status;
}

//
//----------------------------------------------------------------------
//  TCP RECEIVE NOTIFICATION (Event/DPC Hander)
//----------------------------------------------------------------------

/**
  TCP receive notification routine - called when TCP data arrives.
  This is the event notification handler (DPC level).

  @param[in]  Context  HTTP_TOKEN_WRAP pointer.
**/
VOID
EFIAPI
HttpTcpReceiveNotify (
  IN EFI_EVENT  Event,
  IN VOID     *Context
  )
{
  HTTP_TOKEN_WRAP  *Wrap;
  EFI_STATUS      Status;

  Wrap = (HTTP_TOKEN_WRAP *) Context;
  if (Wrap == NULL) {
    return;
  }

  if (Wrap->HttpInstance->LocalAddressIsIPv6) {
    Status = Wrap->Tcp6Token.CompletionToken.Status;
  } else {
    Status = Wrap->Tcp4Token.CompletionToken.Status;
  }

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "Tcp4 receive failed: %r\n", Status));
  }

  //
  // Queue a DPC for processing
  //
  gDpc->QueueDpc (
      (DPC_COMPLETION) HttpTcpReceiveNotifyDpc,
      (VOID *)Wrap
      );
}

/**
  DPC handler for TCP receive notification.
  Processes received HTTP data and parses HTTP response.

  @param[in]  Context  HTTP_TOKEN_WRAP pointer.
**/
VOID
HttpTcpReceiveNotifyDpc (
  IN VOID  *Context
  )
{
  HTTP_TOKEN_WRAP  *Wrap;
  HTTP_INSTANCE    *Instance;
  EFI_STATUS       Status;
  UINTN            RxDataLength;
  UINTN            BufferLength;
  UINT8            *Data;
  VOID             *Parser;

  Wrap = (HTTP_TOKEN_WRAP *) Context;
  if (Wrap == NULL) {
    return;
  }

  Instance = Wrap->HttpInstance;
  if (Instance == NULL) {
    return;
  }

  //
  // Get the TCP receive status
  //
  if (Instance->LocalAddressIsIPv6) {
    Status = Wrap->Tcp6Token.CompletionToken.Status;
    RxDataLength = Wrap->Tcp6Token.RxData.RxDataLength;
  } else {
    Status = Wrap->Tcp4Token.CompletionToken.Status;
    RxDataLength = Wrap->Tcp4Token.RxData.RxDataLength;
  }

  if (EFI_ERROR (Status)) {
    Wrap->HttpMsg->Data.Response = Status;
    goto SignalComplete;
  }

  //
  // Process received data through message parser
  //
  Parser = Instance->MsgParser;
  if (Parser == NULL) {
    goto SignalComplete;
  }

  //
  // Parse the received data
  //
  Data = Instance->ReceiveBuffer;
  BufferLength = RxDataLength;

  //
  // Feed data to HTTP parser
  //
  Status = HttpParseMessageBody (Parser, Data, &BufferLength);
  if (EFI_ERROR (Status)) {
    goto SignalComplete;
  }

  //
  // Check if full response has been received
  //
  if (HttpIsMessageComplete (Parser)) {
    //
    // Parse out response status
    //
    HttpGetStatus (Parser, &Instance->ResponseStatus);

    //
    // Extract body data
    //
    HttpGetEntityLength (Parser, &Instance->ResponseBodyLength);

    //
    // Signal completion to the token
    //
    SignalComplete:

    if (Wrap->HttpMsg->Data.Response == EFI_SUCCESS) {
      Status = EFI_SUCCESS;
    }

    //
    // Signal the waiting token
    //
    if (Wrap->HttpMsg->Data.Event != NULL) {
      gBS->SignalEvent (Wrap->HttpMsg->Data.Event);
    }
  }
}

//
//----------------------------------------------------------------------
//  DNS RESOLUTION (HttpDns.c)
//----------------------------------------------------------------------

/**
  Resolve a host name to an IP address using DNS.

  @param[in]  Instance  HTTP instance.
  @param[in]  HostName  Host name to resolve.
  @param[out] IpAddress Resolved IP address.

  @return EFI_SUCCESS if resolved.
**/
EFI_STATUS
HttpDns (
  IN HTTP_INSTANCE  *Instance,
  IN CHAR8          *HostName,
  OUT EFI_IP_ADDRESS  *IpAddress
  )
{
  EFI_STATUS  Status;
  UINTN       DataLength;

  //
  // Use the HTTP Utilities protocol to resolve the host name
  //
  DataLength = sizeof (EFI_IP_ADDRESS);
  Status = HttpUtilities->Dns (
                            Instance->Handle,
                            HostName,
                            Instance->LocalAddressIsIPv6,
                            &DataLength,
                            (EFI_IP_ADDRESS *) IpAddress
                            );

  return Status;
}

//
//----------------------------------------------------------------------
//  TLS/HTTPS SUPPORT (HttpsSupport.c)
//----------------------------------------------------------------------

/**
  Establish a TLS session for HTTPS connection.

  @param[in]  Instance  HTTP instance.

  @return EFI_SUCCESS if TLS session established.
**/
EFI_STATUS
HttpTlsConnect (
  IN HTTP_INSTANCE  *Instance
  )
{
  EFI_STATUS     Status;
  EFI_TLS_PROTOCOL   *Tls;
  EFI_TLS_CONFIG_DATA TlsConfigData;
  EFI_TLS_SESSION_STATE State;
  UINTN           DataSize;

  //
  // Locate TLS protocol
  //
  Status = gBS->LocateProtocol (
                  &gEfiTlsProtocolGuid,
                  NULL,
                  (VOID **) &Tls
                  );

  if (EFI_ERROR (Status)) {
    DEBUG ((EFI_D_ERROR, "TLS Certificate Config Error!\n"));
    return Status;
  }

  //
  // Set TLS session data
  //
  TlsConfigData.ConnectionEnd = EfiTlsConnectionEnd;
  Status = Tls->SetSessionData (
              Tls,
              EfiTlsConfigData,
              sizeof (EFI_TLS_CONFIG_DATA)
              );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Do TLS handshake
  //
  Status = Tls->Connect (Tls, Instance->RemoteHost);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Check TLS session state
  //
  DataSize = sizeof (EFI_TLS_SESSION_STATE);
  Status = Tls->GetSessionState (Tls, &State, &DataSize);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if (State != EfiTlsSessionApplication) {
    DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n"));
    return EFI_PROTOCOL_ERROR;
  }

  //
  // Store TLS data in instance
  //
  Instance->TlsData = Tls;

  return EFI_SUCCESS;
}

/**
  Close TLS session.

  @param[in]  Instance  HTTP instance.
**/
VOID
HttpTlsClose (
  IN HTTP_INSTANCE  *Instance
  )
{
  if (Instance->TlsData != NULL) {
    ((EFI_TLS_PROTOCOL *)Instance->TlsData)->Close ((EFI_TLS_PROTOCOL *)Instance->TlsData);
    Instance->TlsData = NULL;
  }
}

/**
  Check if TLS session is active.

  @param[in]  Instance  HTTP instance.

  @return TRUE if TLS session is active.
**/
BOOLEAN
HttpTlsIsSessionActive (
  IN HTTP_INSTANCE  *Instance
  )
{
  EFI_TLS_SESSION_STATE  State;
  UINTN               DataSize;

  if (Instance->TlsData == NULL) {
    return FALSE;
  }

  DataSize = sizeof (State);
  ((EFI_TLS_PROTOCOL *)Instance->TlsData)->GetSessionState (
    (EFI_TLS_PROTOCOL *)Instance->TlsData,
    &State,
    &DataSize
    );

  return (State == EFI_TLS_SESSION_APPLICATION);
}

//
//----------------------------------------------------------------------
//  HTTP INITILIZATIONIZATION AND URL URL PARSING
//----------------------------------------------------------------------

/**
  Initialize HTTP session - parse URL, resolve DNS.

  @param[in]  Instance  HTTP instance.
  @param[in]  Url       URL string.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
HttpInitSession (
  IN HTTP_INSTANCE  *Instance,
  IN CHARR          *Url
  )
{
  EFI_STATUS   Status;
  URL_PARSER   *UrlParser;
  EFI_IP_ADDRESS  IpAddress;

  //
  // Check for HTTPS protocol
  //
  if (AsciiStrStr (Url, HTTPS_PREFIX) == Url) {
    Instance->UseHttps = TRUE;
  }

  //
  // Parse the URL
  //
  Status = HttpUrlParse (
              Url,
              FALSE,   // FALSE = no path required
              &UrlParser
              );

  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // Extract host name
  //
  Instance->RemoteHost = HttpUrlGetHostName (UrlParser);
  if (Instance->RemoteHost == NULL) {
    HttpUrlClean (UrlParser);
    return EFI_INVALID_PARAMETER;
  }

  //
  // Resolve DNS if host is not an IP address
  //
  if (!HttpUrlIsIpAddress (Instance->RemoteHost)) {
    Status = HttpDns (Instance, Instance->RemoteHost, &IpAddress);
    if (EFI_ERROR (Status)) {
      DEBUG ((EFI_D_ERROR, "Error: Could not retriive the host address from DNS server.\n"));
      HttpUrlClean (UrlParser);
      return Status;
    }

    //
    // Store resolved address
    //
    Instance->RemoteAddress = IpAddress;
  }

  //
  // Extract path if present
  //
  Instance->Url = HttpUrlGetPath (UrlParser, Instance->LocalAddressIsIPv6);

  HttpUrlClean (UrlParser);

  return EFI_SUCCESS;
}

//
//----------------------------------------------------------------------
//  EVENT CREATION AND AND MAP MANAGEMENT
//----------------------------------------------------------------------

/**
  Create an event for TCP receive notification.

  @param[in]   Instance  HTTP instance.
  @param[out]  Token     Receives the allocated token.

  @return EFI_SUCCESS.
**/
EFI_STATUS
HttpCreateEvent (
  IN HTTP_INSTANCE  *Instance,
  OUT VOID        **Token
  )
{
  EFI_STATUS  Status;

  //
  // Create event for TCP receive
  //
  Status = gBS->CreateEvent (
                  EV_NOTIF_WAIT,
                  TP_NOTIFY_SIGNAL,
                  HttpTcpReceiveNotify,
                  NULL,     // No context, set later
                  &Token
                  );

  return Status;
}

//
//----------------------------------------------------------------------
//  HTTP MESSAGE GENERATION
//----------------------------------------------------------------------

/**
  Build the complete HTTP request string from message and hostname.

  @param[in]   HttpMsg    HTTP message to render.
  @param[in]   HostName   Host name to include in Host header.
  @param[in]   UseHttps  Whether to render HTTPS URL.
  @param[out]  Nbuf       Receives the generated net buffer.

  @return EFI_SUCCESS.
**/
EFI_STATUS
HttpGenerateRequest (
  IN EFI_HTTP_MESSAGE  *HttpMsg,
  IN CONST CHAR8      *HostName,
  IN BOOLEAN           UseHttps,
  OUT NET_BUF         **Nbuf
  )
{
  EFI_STATUS   Status;
  CHAR8        *RequestString;
  UINTN        RequestStringSize;
  UINTN        HeaderSize;

  //
  // Calculate total header size
  //
  HeaderSize = HttpCalculateHeaderSize (HttpMsg->Headers, HttpMsg->HeaderCount);

  //
  // Allocate buffer for request string
  //
  RequestString = (CHARR *) AllocateZeroPool (HeaderSize + 256); // Extra for request line and CRLF
  if (RequestString == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  //
  // Render the HTTP request line (METHOD URI HTTP/1.1)
  //
  Status = HttpRenderRequestLine (
              HttpMsg->Data.Request,
              &RequestString,
              &RequestStringSize
              );

  if (EFI_ERROR (Status)) {
    FreePool (RequestString);
    return Status;
  }

  //
  // Append headers
  //
  Status = HttpRenderHeaders (
              RequestString + RequestStringSize,
              &RequestStringSize,
              HttpMsg->Headers,
              HttpMsg->HeaderCount,
              HostName
              );

  if (EFI_ERROR (Status)) {
    FreePool (RequestString);
    return Status;
  }

  //
  // Append body
  //
  if (HttpMsg->BodyLength > 0) {
    CopyMem (
      RequestString + RequestStringSize,
      HttpMsg->Body,
      HttpMsg->BodyLength
      );
    RequestStringSize += HttpMsg->BodyLength;
  }

  //
  // Create net buffer from the request string
  //
  *Nbuf = NetbufFromExt (
              (VOID *) RequestString,
              RequestStringSize,
              0,
              0,
              HttpFreeRequestString,
              RequestString
              );

  if (*Nbuf == NULL) {
    FreePool (RequestString);
    return EFI_OUT_OF_RESOURCES;
  }

  return EFI_SUCCESS;
}

/**/
  Free function for request string in net buffer.

  @param[in]  Arg  The request string pointer.
**/
VOID
EFIAPI
HttpFreeRequestString (
  IN VOID  *Arg
  )
{
  FreePool (Arg);
}

//
//----------------------------------------------------------------------
//  STATUSUS CODES
//----------------------------------------------------------------------

/**
  Set response status from HTTP status code.

  @param[in]  StatusCode  HTTP status code.
  @param[out]  ResponseStatus  EFI status mapped from HTTP status.
**/
VOID
HttpSetResponseStatus (
  IN UINTN   StatusCode,
  OUT EFI_STATUS *ResponseStatus
  )
{
  if (StatusCode >= 200 && StatusCode < 300) {
    *ResponseStatus = EFI_SUCCESS;
  } else if (StatusCode >= 300 && StatusCode < 400) {
    *ResponseStatus = EFI_HTTP_ERROR;
  } else if (StatusCode >= 400 && StatusCode < 500) {
    *ResponseStatus = EFI_HTTP_ERROR;
  } else if (StatusCode >= 500) {
    *ResponseStatus = EFI_HTTP_ERROR;
  } else {
    *ResponseStatus = EFI_UNSUPPORTED;
  }
}

/**
  Free parsed HTTP message resources.

  @param[in]  Instance  HTTP instance.
**/
EFI_STATUS
HttpFreeResponse (
  IN HTTP_INSTANCE  *Instance
  )
{
  //
  // Free message parser
  //
  if (Instance->MsgParser != NULL) {
    HttpFreeMsgParser (Instance->MsgParser);
    Instance->MsgParser = NULL;
  }

  //
  // Free any cached body
  //
  if (Instance->CacheBody != NULL) {
    FreePool (Instance->CacheBody);
    Instance->CacheBody = NULL;
    Instance->CachedData = NULL;
    Instance->CachedLength = 0;
    Instance->CacheOffset = 0;
  }

  return EFI_SUCCESS;
}

//
//----------------------------------------------------------------------
//  LIBRARY FUNCTIONS (linked statically)
//----------------------------------------------------------------------

/**
  Copy memory - wrapper for CopyMem.
**/
VOID *
EFIAPI
CopyMemS (
  OUT VOID       *DestinationBuffer,
  IN CONST VOID  *SourceBuffer,
  IN UINTN       Length
  )
{
  if (Length == 0) {
    return DestinationBuffer;
  }

  if (Length - 1 > (UINTN)~DestinationBuffer) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c", 56,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
      );
  }

  if (Length - 1 > (UINTN)~SourceBuffer) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CopyMemWrapper.c", 57,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
      );
  }

  if (DestinationBuffer == SourceBuffer) {
    return DestinationBuffer;
  }

  return InternalCopyMem (DestinationBuffer, SourceBuffer, Length);
}

/**
  Zero memory - wrapper for ZeroMem.
**/
VOID *
EFIAPI
ZeroMemS (
  OUT VOID  *Buffer,
  IN UINTN  Length
  )
{
  if (Buffer == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c", 53,
      "Buffer != ((void *) 0)"
      );
  }

  if (Length > (UINTN)~Buffer + 1) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\ZeroMemWrapper.c", 54,
      "Length <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)Buffer + 1)"
      );
  }

  return InternalZeroMem (Buffer, Length);
}

/**
  Compare memory - w wrapper for CompareMem.

  @return 0 if equal.
**/
INTN
EFIAPI
CompareMemS (
  IN CONST VOID  *DestinationBuffer,
  IN CONST VOID  *SourceBuffer,
  IN UINTN       Length
  )
{
  if (Length == 0 || DestinationBuffer == SourceBuffer) {
    return 0;
  }

  if (DestinationBuffer == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CompareMemWrapper.c", 60,
      "DestinationBuffer != ((void *) 0)"
      );
  }

  if (Length - 1 > (UINTN)~DestinationBuffer) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CompareMemWrapper.c", 62,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)DestinationBuffer)"
      );
  }

  if (Length - 1 > (UINTN)(-1LL - (_QWORD)"\r\n")) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseMemoryLibRepStr\\CompareMemWrapper.c", 63,
      "(Length - 1) <= (0xFFFFFFFFFFFFFFFFULL - (UINTN)SourceBuffer)"
      );
  }

  return InternalCompareMem (DestinationBuffer, SourceBuffer, Length);
}

/**
  Calculate length of null-terminated Unicode string.

  @return Length of string (in characters).
**/
UINTN
EFIAPI
StrLenS (
  IN CONST CHAR16  *String
  )
{
  UINTN  Length;

  if (String == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 172,
      "String != ((void *) 0)"
      );
 ​ }

  if (((UINTN) String & 1) != 0) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 173,
      "((UINTN) String & 0x000001) == 0)"
      );
  }

  Length = 0;
  while (String[Length] != 0) {
    if (Length >= 0xF4240) {
      DEBUG_ASSERT (
        "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 181,
        "Length < _gPcd_FixedAtBuild_PcdMaximumUnicodeStringLength"
        );
    }
    Length++;
  }

  return Length;
}

/**
  Calculate length of null-terminated ASII string.

  @return Length of string (in bytes).
**/
UINTN
EFIAPI
AsciiStrLenS (
  IN CONST CHARR  *String
  )
{
  UINTN  Length;

  if (String == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1082,
      "String != ((void *) 0)"
      );
  }

  for (Length = 0; String[Length] != 0; Length++) {
    if (Length >= 0xF4240) {
      DEBUG_ASSERT (
        "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1090,
        "Length < _gPcd_FixedAtBuild_PcdMaximumAsciiStringLength"
        );
    }
  }

  return Length;
}

/**
  Compare two ASCII strings case-insensitively.

  @return 0 if equal.
**/
INTN
EFIAPI
AsciiStrCaseCmp (
  IN CONST CHARR  *FirstString,
  IN CONST CHAR8  *SecondString
  )
{
  CHAR8  Char1;
  CHAR8  Char2;

  if (AsciiStrLenS (FirstString) == (UINTN)-1) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1158,
      "AsciiStrSize (FirstString) != 0)"
      );
  }

  if (AsciiStrLenS (SecondString) == (UINTN)-1) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1159,
      "AsciiStrSize (SecondString) != 0)"
      );
  }

  while (*FirstString != 0) {
    Char1 = *FirstString;
    Char2 = *SecondString;

    if (Char1 >= 'a' && Char1 <= 'z') {
      Char1 -= 0x20;
    }

    if (Char2 >= 'a' && Char2 <= 'z') {
      Char2 -= 0x20;
    }

    if (Char1 != Char2) {
      break;
    }

    FirstString++;
    SecondString++;
  }

  return (INTN)*FirstString - (INTN)*SecondString;
}

/**
  Find substring in ASCII string (case-sensitive).

  @return Pointer to first occurrence, or NULL.
**/
CHARR *
EFIAPI
AsciiStrStr (
  IN CONST CHARR  *String,
  IN CONST CHAR8  *SearchString
  )
{
  CHAR8  *Current;

  if (AsciiStrLenS (String) == (UINTN)-1) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1484,
      "AsciiStrSize (String) != 0)"
      );
  }

  if (AsciiStrLenS (SearchString) == (UINTN)-1) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\String.c", 1485,
      "AsciiStrSize (SearchString) != 0"
      );
  }

  if (*SearchString == 0) {
    return (CHARR *) String;
  }

  while (*String != 0) {
    Current = (CHARR *) SearchString;
    while (*Current != 0 && *String == *Current) {
      String++;
      Current++;
    }
    if (*Current == 0) {
      return (CHARR *) (String - (Current - SearchString));
    }
    String = (String - (Current - SearchString)) + 1;
  }

  return NULL;
}

/**
  Convert decimal string to UINTN.

  @return The decimal value.
**/
EFI_STATUS
EFIAPI
AsciiStrDecimalToUintnS (
  IN CONST CHAR8  *String,
  OUT CHARR        **EndPointer,
  OUT UINTN        *Data
  )
{
  BOOLEAN  Negative;
  UINTN  Result;

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

  //
  // Skip leading whitespace
  //
  while (*String == ' ' || *String == '\t') {
    String++;
  }

  //
  // Skip leading zeros
  //
  while (*String == '0') {
    String++;
  }

  Result = 0;
  while (*String >= '0' && *String <= '9') {
    UINTN  Digit = *String - '0';

    if (Result > (MAX_UINTN - Digit) / 10) {
      *Data = MAX_UINTN;
      if (EndPointer != NULL) {
        *EndPointer = (CHARR *) String;
      }
      return EFI_INVALID_PARAMETER;
    }

    Result = Result * 10 + Digit;
    String++;
  }

  *Data = Result;
  if (EndPointer != NULL) {
    *EndPointer = (CHARR *) String;
  }

  return EFI_SUCCESS;
}

/**
  Convert hex string to UINTN.

  @return EFI_SUCCESS on success.
**/
EFI_STATUS
EFIAPI
AsciiStrHexToUintnS (
  IN CONST CHAR8  *String,
  OUT CHAR8       **EndPointer,
  OUT UINTN       *Data
  )
{
  UINTN  Result;
  CHAR8  Char;

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

  //
  // Skip leading whitespace
  //
  while (*String == ' ' || *String == '\t') {
    String++;
  }

  //
  // Skip leading zeros
  //
  while (*String == '0') {
    String++;
  }

  //
  // Check for 0x or 0X prefix
  //
  if (*String == 'x' || *String == 'X') {
    if (*(String - 1) != '0') {
      goto HexParse;
    }
    String++;
  }

HexParse:
  Result = 0;
  while (TRUE) {
    Char = *String;

    if (Char >= '0' && Char <= '9') {
      Char = Char - '0';
    } else if (Char >= 'A' && Char <= 'F') {
      Char = Char - 'A' + 10;
    } else if (Char >= 'a' && Char <= 'f') {
      Char = Char - 'a' + 10;
    } else {
      break;
    }

    if (Result > (MAX_UINTN - (UINTN)Char) / 16) {
      *Data = MAX_UINTN;
      if (EndPointer != NULL) {
        *EndPointer = (CHARR *) String;
      }
      return EFI_INVALID_PARAMETER;
    }

    Result = Result * 16 + Char;
    String++;
  }

  *Data = Result;
  if (EndPointer != NULL) {
    *EndPointer = (CHAR8 *) String;
  }

  return EFI_SUCCESS;
}

/**/
  Convert Unicode string to ASCII string.

  @param[in]   Source       Unicode string.
  @param[out]  Destination  ASCII buffer.
  @param[in]   DestMax      Maximum size of ASCII buffer.

  @return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
UnicodeStrToAsciiStrS (
  IN CONST CHARR16  *Source,
  OUT CHARR         *Destination,
  IN UINTN          DestMax
  )
{
  UINTN  Index;

  if (Source == NULL || Destination == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (DestMax == 0) {
    return EFI_INVALID_PARAMETER;
  }

  Index = 0;
  while (Source[Index] != 0 && Index < DestMax - 1) {
    if (Source[Index] >= 0x100) {
      return EFI_INVALID_PARAMETER;
    }
    Destination[Index] = (CHARR) Source[Index];
    Index++;
  }

  Destination[Index] = 0;

  return EFI_SUCCESS;
}

/**/
  Convert ASCII string to Unicode string.

  @param[in]   Source       ASCII string.
  @param[out]  Destination  Unicode buffer.
  @param[in]   DestMax      Maximum size of Unicode buffer.

  @return EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
AsciiStrToUnicodeStrS (
  IN CONST CHARR   *Source,
  OUT CHARR16      *Destination,
  IN UINTN        DestMax
  )
{
  UINTN  Index;

  if (Source == NULL || Destination == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  if (DestMax == 0) {
    return EFI_INVALID_PARAMETER;
  }

  Index = 0;
  while (Source[Index] != 0 && Index < DestMax - 1) {
    Destination[Index] = (CHARR16) Source[Index];
    Index++;
  }

  Destination[Index] = 0;

  return EFI_SUCCESS;
}

//
//----------------------------------------------------------------------
//  LINKED LIST UTILIIES
//----------------------------------------------------------------------

/**
  Initialize a linked list head.

  @param[in]  ListHead  List head to initialize.
**/
VOID
EFIAPI
InitializeListHead (
  IN LIST_ENTRY  *ListHead
  )
{
  if (ListHead == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 193,
      "ListHead != ((void *) 0)"
      );
  }

  ListHead->ForwardLink = ListHead;
  ListHead->BackLink = ListHead;
}

/**
  Insrt an entry at the head of a list.

  @param[in]  ListHead  List head.
  @param[in]  Entry    Entry to insert.
**/
VOID
EFIAPI
InsertHeadList (
  IN LIST_ENTRY  *ListHead,
  IN LIST_ENTRY  *Entry
  )
{
  //
  // Veriify list validation
  //
  InternalBaseLibIsListValid (ListHead);

  Entry->ForwardLink = ListHead->ForwardLink;
  Entry->BackLink = ListHead;
  ListHead->ForwardLink->BackLink = Entry;
  ListHead->ForwardLink = Entry;
}

/**
  Insrt an entry at the tail of a list.

  @param[in]  ListHead  List head.
  @param[in]  Entry    Entry to insert.
**/
VOID
EFIAPI
InsertTailList (
  IN LIST_ENTRY  *ListHead,
  IN LIST_ENTRY  *Entry
  )
{
  InternalBaseLibIsListValid (ListHead);

  Entry->ForwardLink = ListHead;
  Entry->BackLink = ListHead->BackLink;
  ListHead->BackLink->ForwardLink = Entry;
  ListHead->BackLink = Entry;
}

/**
  Remove an entry from a list.

  @param[in]  Entry  Entry to remove.

  @return The entry that was after the removed entry.
**/
LIST_ENTRY *
EFIAPI
RemoveEntryList (
  IN LIST_ENTRY  *Entry
  )
{
  if (IsListEmpty (ListHead ((LIST_ENTRY *) Entry->ForwardLink->BackLink))) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 600,
      "!IsListEmpty (Entry)"
      );
  }

  Entry->ForwardLink->BackLink = Entry->BackLink;
  Entry->BackLink->ForwardLink = Entry->ForwardLink;

  return Entry->ForwardLink;
}

/**
  Check if a list is empty.

  @return TRUE if the list has no entries.
**/
BOOLEAN
EFIAPI
IsListEmpty (
  IN CONST LIST_ENTRY  *ListHead
  )
{
  InternalBaseLibIsListValid (ListHead);

  return (BOOLEAN) (ListHead->ForwardLink == ListHead);
}

/**/
  Check linked list validity.

  @return TRUE if valid.
**/
BOOLEAN
InternalBaseLibIsListValid (
  IN CONST LIST_ENTRY  *ListHead
  )
{
  if (ListHead == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 80,
      "List != ((void *) 0)"
      );
  }

  if (ListHead->ForwardLink == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 81,
      "List->ForwardLink != ((void *) 0)"
      );
  }

  if (ListHead->BackLink == NULL) {
    DEBUG_ASSERT (
      "e:\\hs\\MdePkg\\Library\\BaseLib\\LinkedList.c", 82,
      "List->BackLink != ((void *) 0)"
      );
  }

  return TRUE;
}