summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe')
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.c1306
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.h137
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.c177
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.h93
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c700
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.h73
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h44
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni21
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr49
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c912
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h253
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c1032
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h169
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.c1332
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.h525
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.inf101
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.uni18
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni14
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.c767
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.h48
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.c779
-rw-r--r--src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.h215
22 files changed, 8765 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.c
new file mode 100644
index 00000000..b59af6d5
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.c
@@ -0,0 +1,1306 @@
+/** @file
+ Implementation of the boot file download function.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+
+/**
+ Update the device path node to include the boot resource information.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Device patch successfully updated.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootUpdateDevicePath (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_DEV_PATH *Node;
+ EFI_DEVICE_PATH_PROTOCOL *TmpIpDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TmpDnsDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
+ UINTN Length;
+ EFI_STATUS Status;
+
+ TmpIpDevicePath = NULL;
+ TmpDnsDevicePath = NULL;
+
+ //
+ // Update the IP node with DHCP assigned information.
+ //
+ if (!Private->UsingIpv6) {
+ Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
+ if (Node == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv4.Header.SubType = MSG_IPv4_DP;
+ SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
+ CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
+ Node->Ipv4.RemotePort = Private->Port;
+ Node->Ipv4.Protocol = EFI_IP_PROTO_TCP;
+ Node->Ipv4.StaticIpAddress = FALSE;
+ CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ } else {
+ Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
+ if (Node == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv6.Header.SubType = MSG_IPv6_DP;
+ SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
+ Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH;
+ Node->Ipv6.RemotePort = Private->Port;
+ Node->Ipv6.Protocol = EFI_IP_PROTO_TCP;
+ Node->Ipv6.IpAddressOrigin = 0;
+ CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS));
+ CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ TmpIpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ if (TmpIpDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ //
+ // Update the DNS node with DNS server IP list if existed.
+ //
+ if (Private->DnsServerIp != NULL) {
+ Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6) + Private->DnsServerCount * sizeof (EFI_IP_ADDRESS);
+ Node = AllocatePool (Length);
+ if (Node == NULL) {
+ FreePool (TmpIpDevicePath);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_DNS_DP;
+ SetDevicePathNodeLength (Node, Length);
+ Node->Dns.IsIPv6 = Private->UsingIpv6 ? 0x01 : 0x00;
+ CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6), Private->DnsServerIp, Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
+
+ TmpDnsDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ FreePool (TmpIpDevicePath);
+ TmpIpDevicePath = NULL;
+ if (TmpDnsDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ }
+
+ //
+ // Update the URI node with the boot file URI.
+ //
+ Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);
+ Node = AllocatePool (Length);
+ if (Node == NULL) {
+ if (TmpIpDevicePath != NULL) {
+ FreePool (TmpIpDevicePath);
+ }
+ if (TmpDnsDevicePath != NULL) {
+ FreePool (TmpDnsDevicePath);
+ }
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_URI_DP;
+ SetDevicePathNodeLength (Node, Length);
+ CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));
+
+ if (TmpDnsDevicePath != NULL) {
+ NewDevicePath = AppendDevicePathNode (TmpDnsDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (TmpDnsDevicePath);
+ } else {
+ ASSERT (TmpIpDevicePath != NULL);
+ NewDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (TmpIpDevicePath);
+ }
+ FreePool (Node);
+ if (NewDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!Private->UsingIpv6) {
+ //
+ // Reinstall the device path protocol of the child handle.
+ //
+ Status = gBS->ReinstallProtocolInterface (
+ Private->Ip4Nic->Controller,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip4Nic->DevicePath,
+ NewDevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FreePool (Private->Ip4Nic->DevicePath);
+ Private->Ip4Nic->DevicePath = NewDevicePath;
+ } else {
+ //
+ // Reinstall the device path protocol of the child handle.
+ //
+ Status = gBS->ReinstallProtocolInterface (
+ Private->Ip6Nic->Controller,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip6Nic->DevicePath,
+ NewDevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ FreePool (Private->Ip6Nic->DevicePath);
+ Private->Ip6Nic->DevicePath = NewDevicePath;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse the boot file URI information from the selected Dhcp4 offer packet.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.
+ @retval Others Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+HttpBootDhcp4ExtractUriInfo (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer;
+ HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer;
+ UINT32 SelectIndex;
+ UINT32 ProxyIndex;
+ UINT32 DnsServerIndex;
+ EFI_DHCP4_PACKET_OPTION *Option;
+ EFI_STATUS Status;
+
+ ASSERT (Private != NULL);
+ ASSERT (Private->SelectIndex != 0);
+ SelectIndex = Private->SelectIndex - 1;
+ ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
+
+ DnsServerIndex = 0;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // SelectOffer contains the IP address configuration and name server configuration.
+ // HttpOffer contains the boot file URL.
+ //
+ SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;
+ if (Private->FilePathUri == NULL) {
+ //
+ // In Corporate environment, we need a HttpOffer.
+ //
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
+ HttpOffer = SelectOffer;
+ } else {
+ ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+ HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
+ }
+ Private->BootFileUriParser = HttpOffer->UriParser;
+ Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
+ } else {
+ //
+ // In Home environment the BootFileUri comes from the FilePath.
+ //
+ Private->BootFileUriParser = Private->FilePathUriParser;
+ Private->BootFileUri = Private->FilePathUri;
+ }
+
+ //
+ // Check the URI scheme.
+ //
+ Status = HttpBootCheckUriScheme (Private->BootFileUri);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status));
+ if (Status == EFI_INVALID_PARAMETER) {
+ AsciiPrint ("\n Error: Invalid URI address.\n");
+ } else if (Status == EFI_ACCESS_DENIED) {
+ AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
+ }
+ return Status;
+ }
+
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
+ Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
+ ASSERT (Option != NULL);
+
+ //
+ // Record the Dns Server address list.
+ //
+ Private->DnsServerCount = (Option->Length) / sizeof (EFI_IPv4_ADDRESS);
+
+ Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
+ if (Private->DnsServerIp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {
+ CopyMem (&(Private->DnsServerIp[DnsServerIndex].v4), &(((EFI_IPv4_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv4_ADDRESS));
+ }
+
+ //
+ // Configure the default DNS server if server assigned.
+ //
+ Status = HttpBootRegisterIp4Dns (
+ Private,
+ Option->Length,
+ Option->Data
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Private->DnsServerIp);
+ Private->DnsServerIp = NULL;
+ return Status;
+ }
+ }
+
+ //
+ // Extract the port from URL, and use default HTTP port 80 if not provided.
+ //
+ Status = HttpUrlGetPort (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &Private->Port
+ );
+ if (EFI_ERROR (Status) || Private->Port == 0) {
+ Private->Port = 80;
+ }
+
+ //
+ // All boot informations are valid here.
+ //
+
+ //
+ // Update the device path to include the boot resource information.
+ //
+ Status = HttpBootUpdateDevicePath (Private);
+ if (EFI_ERROR (Status) && Private->DnsServerIp != NULL) {
+ FreePool (Private->DnsServerIp);
+ Private->DnsServerIp = NULL;
+ }
+
+ return Status;
+}
+
+/**
+ Parse the boot file URI information from the selected Dhcp6 offer packet.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Successfully parsed out all the boot information.
+ @retval Others Failed to parse out the boot information.
+
+**/
+EFI_STATUS
+HttpBootDhcp6ExtractUriInfo (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ HTTP_BOOT_DHCP6_PACKET_CACHE *SelectOffer;
+ HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer;
+ UINT32 SelectIndex;
+ UINT32 ProxyIndex;
+ UINT32 DnsServerIndex;
+ EFI_DHCP6_PACKET_OPTION *Option;
+ EFI_IPv6_ADDRESS IpAddr;
+ CHAR8 *HostName;
+ UINTN HostNameSize;
+ CHAR16 *HostNameStr;
+ EFI_STATUS Status;
+
+ ASSERT (Private != NULL);
+ ASSERT (Private->SelectIndex != 0);
+ SelectIndex = Private->SelectIndex - 1;
+ ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
+
+ DnsServerIndex = 0;
+
+ Status = EFI_SUCCESS;
+ HostName = NULL;
+ //
+ // SelectOffer contains the IP address configuration and name server configuration.
+ // HttpOffer contains the boot file URL.
+ //
+ SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6;
+ if (Private->FilePathUri == NULL) {
+ //
+ // In Corporate environment, we need a HttpOffer.
+ //
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
+ HttpOffer = SelectOffer;
+ } else {
+ ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
+ ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
+ HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;
+ }
+ Private->BootFileUriParser = HttpOffer->UriParser;
+ Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;
+ } else {
+ //
+ // In Home environment the BootFileUri comes from the FilePath.
+ //
+ Private->BootFileUriParser = Private->FilePathUriParser;
+ Private->BootFileUri = Private->FilePathUri;
+ }
+
+ //
+ // Check the URI scheme.
+ //
+ Status = HttpBootCheckUriScheme (Private->BootFileUri);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status));
+ if (Status == EFI_INVALID_PARAMETER) {
+ AsciiPrint ("\n Error: Invalid URI address.\n");
+ } else if (Status == EFI_ACCESS_DENIED) {
+ AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
+ }
+ return Status;
+ }
+
+ //
+ // Set the Local station address to IP layer.
+ //
+ Status = HttpBootSetIp6Address (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Register the IPv6 gateway address to the network device.
+ //
+ Status = HttpBootSetIp6Gateway (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
+ (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
+ Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];
+ ASSERT (Option != NULL);
+
+ //
+ // Record the Dns Server address list.
+ //
+ Private->DnsServerCount = HTONS (Option->OpLen) / sizeof (EFI_IPv6_ADDRESS);
+
+ Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
+ if (Private->DnsServerIp == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {
+ CopyMem (&(Private->DnsServerIp[DnsServerIndex].v6), &(((EFI_IPv6_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv6_ADDRESS));
+ }
+
+ //
+ // Configure the default DNS server if server assigned.
+ //
+ Status = HttpBootSetIp6Dns (
+ Private,
+ HTONS (Option->OpLen),
+ Option->Data
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+ }
+
+ //
+ // Extract the HTTP server Ip from URL. This is used to Check route table
+ // whether can send message to HTTP Server Ip through the GateWay.
+ //
+ Status = HttpUrlGetIp6 (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &IpAddr
+ );
+
+ if (EFI_ERROR (Status)) {
+ //
+ // The Http server address is expressed by Name Ip, so perform DNS resolution
+ //
+ Status = HttpUrlGetHostName (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &HostName
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ HostNameSize = AsciiStrSize (HostName);
+ HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
+ if (HostNameStr == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
+
+ if (HostName != NULL) {
+ FreePool (HostName);
+ }
+
+ Status = HttpBootDns (Private, HostNameStr, &IpAddr);
+ FreePool (HostNameStr);
+ if (EFI_ERROR (Status)) {
+ AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n");
+ goto Error;
+ }
+ }
+
+ CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));
+
+ //
+ // Extract the port from URL, and use default HTTP port 80 if not provided.
+ //
+ Status = HttpUrlGetPort (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &Private->Port
+ );
+ if (EFI_ERROR (Status) || Private->Port == 0) {
+ Private->Port = 80;
+ }
+
+ //
+ // All boot informations are valid here.
+ //
+
+ //
+ // Update the device path to include the boot resource information.
+ //
+ Status = HttpBootUpdateDevicePath (Private);
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ return Status;
+
+Error:
+ if (Private->DnsServerIp != NULL) {
+ FreePool (Private->DnsServerIp);
+ Private->DnsServerIp = NULL;
+ }
+
+ return Status;
+}
+
+
+/**
+ Discover all the boot information for boot file.
+
+ @param[in, out] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Successfully obtained all the boot information .
+ @retval Others Failed to retrieve the boot information.
+
+**/
+EFI_STATUS
+HttpBootDiscoverBootInfo (
+ IN OUT HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
+ // other Http boot information.
+ //
+ Status = HttpBootDhcp (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (!Private->UsingIpv6) {
+ Status = HttpBootDhcp4ExtractUriInfo (Private);
+ } else {
+ Status = HttpBootDhcp6ExtractUriInfo (Private);
+ }
+
+ return Status;
+}
+
+/**
+ HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
+
+ @param[in] EventType Indicate the Event type that occurs in the current callback.
+ @param[in] Message HTTP message which will be send to, or just received from HTTP server.
+ @param[in] Context The Callback Context pointer.
+
+ @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
+ @retval Others Tells the HttpIo to abort the current HTTP process.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootHttpIoCallback (
+ IN HTTP_IO_CALLBACK_EVENT EventType,
+ IN EFI_HTTP_MESSAGE *Message,
+ IN VOID *Context
+ )
+{
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ EFI_STATUS Status;
+ Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
+ if (Private->HttpBootCallback != NULL) {
+ Status = Private->HttpBootCallback->Callback (
+ Private->HttpBootCallback,
+ EventType == HttpIoRequest ? HttpBootHttpRequest : HttpBootHttpResponse,
+ EventType == HttpIoRequest ? FALSE : TRUE,
+ sizeof (EFI_HTTP_MESSAGE),
+ (VOID *) Message
+ );
+ return Status;
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ Create a HttpIo instance for the file download.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Successfully created.
+ @retval Others Failed to create HttpIo.
+
+**/
+EFI_STATUS
+HttpBootCreateHttpIo (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ HTTP_IO_CONFIG_DATA ConfigData;
+ EFI_STATUS Status;
+ EFI_HANDLE ImageHandle;
+
+ ASSERT (Private != NULL);
+
+ ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));
+ if (!Private->UsingIpv6) {
+ ConfigData.Config4.HttpVersion = HttpVersion11;
+ ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
+ IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);
+ IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);
+ ImageHandle = Private->Ip4Nic->ImageHandle;
+ } else {
+ ConfigData.Config6.HttpVersion = HttpVersion11;
+ ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
+ IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);
+ ImageHandle = Private->Ip6Nic->ImageHandle;
+ }
+
+ Status = HttpIoCreateIo (
+ ImageHandle,
+ Private->Controller,
+ Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,
+ &ConfigData,
+ HttpBootHttpIoCallback,
+ (VOID *) Private,
+ &Private->HttpIo
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Private->HttpCreated = TRUE;
+ return EFI_SUCCESS;
+}
+
+/**
+ Release all the resource of a cache item.
+
+ @param[in] Cache The pointer to the cache item.
+
+**/
+VOID
+HttpBootFreeCache (
+ IN HTTP_BOOT_CACHE_CONTENT *Cache
+ )
+{
+ UINTN Index;
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ HTTP_BOOT_ENTITY_DATA *EntityData;
+
+ if (Cache != NULL) {
+ //
+ // Free the request data
+ //
+ if (Cache->RequestData != NULL) {
+ if (Cache->RequestData->Url != NULL) {
+ FreePool (Cache->RequestData->Url);
+ }
+ FreePool (Cache->RequestData);
+ }
+
+ //
+ // Free the response header
+ //
+ if (Cache->ResponseData != NULL) {
+ if (Cache->ResponseData->Headers != NULL) {
+ for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {
+ FreePool (Cache->ResponseData->Headers[Index].FieldName);
+ FreePool (Cache->ResponseData->Headers[Index].FieldValue);
+ }
+ FreePool (Cache->ResponseData->Headers);
+ }
+ }
+
+ //
+ // Free the response body
+ //
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {
+ EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);
+ if (EntityData->Block != NULL) {
+ FreePool (EntityData->Block);
+ }
+ RemoveEntryList (&EntityData->Link);
+ FreePool (EntityData);
+ }
+
+ FreePool (Cache);
+ }
+}
+
+/**
+ Clean up all cached data.
+
+ @param[in] Private The pointer to the driver's private data.
+
+**/
+VOID
+HttpBootFreeCacheList (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *NextEntry;
+ HTTP_BOOT_CACHE_CONTENT *Cache;
+
+ NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {
+ Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
+ RemoveEntryList (&Cache->Link);
+ HttpBootFreeCache (Cache);
+ }
+}
+
+/**
+ Get the file content from cached data.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] Uri Uri of the file to be retrieved from cache.
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
+ then the size of the requested file is returned in
+ BufferSize.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS Successfully created.
+ @retval Others Failed to create HttpIo.
+
+**/
+EFI_STATUS
+HttpBootGetFileFromCache (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN CHAR16 *Uri,
+ IN OUT UINTN *BufferSize,
+ OUT UINT8 *Buffer,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ LIST_ENTRY *Entry;
+ LIST_ENTRY *Entry2;
+ HTTP_BOOT_CACHE_CONTENT *Cache;
+ HTTP_BOOT_ENTITY_DATA *EntityData;
+ UINTN CopyedSize;
+
+ if (Uri == NULL || BufferSize == NULL || Buffer == NULL || ImageType == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NET_LIST_FOR_EACH (Entry, &Private->CacheList) {
+ Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
+ //
+ // Compare the URI to see whether we already have a cache for this file.
+ //
+ if ((Cache->RequestData != NULL) &&
+ (Cache->RequestData->Url != NULL) &&
+ (StrCmp (Uri, Cache->RequestData->Url) == 0)) {
+ //
+ // Hit in cache, record image type.
+ //
+ *ImageType = Cache->ImageType;
+
+ //
+ // Check buffer size.
+ //
+ if (*BufferSize < Cache->EntityLength) {
+ *BufferSize = Cache->EntityLength;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Fill data to buffer.
+ //
+ CopyedSize = 0;
+ NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
+ EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
+ if (*BufferSize > CopyedSize) {
+ CopyMem (
+ Buffer + CopyedSize,
+ EntityData->DataStart,
+ MIN (EntityData->DataLength, *BufferSize - CopyedSize)
+ );
+ CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
+ }
+ }
+ *BufferSize = CopyedSize;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+/**
+ A callback function to intercept events during message parser.
+
+ This function will be invoked during HttpParseMessageBody() with various events type. An error
+ return status of the callback function will cause the HttpParseMessageBody() aborted.
+
+ @param[in] EventType Event type of this callback call.
+ @param[in] Data A pointer to data buffer.
+ @param[in] Length Length in bytes of the Data.
+ @param[in] Context Callback context set by HttpInitMsgParser().
+
+ @retval EFI_SUCCESS Continue to parser the message body.
+ @retval Others Abort the parse.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootGetBootFileCallback (
+ IN HTTP_BODY_PARSE_EVENT EventType,
+ IN CHAR8 *Data,
+ IN UINTN Length,
+ IN VOID *Context
+ )
+{
+ HTTP_BOOT_CALLBACK_DATA *CallbackData;
+ HTTP_BOOT_ENTITY_DATA *NewEntityData;
+ EFI_STATUS Status;
+ EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;
+
+ //
+ // We only care about the entity data.
+ //
+ if (EventType != BodyParseEventOnData) {
+ return EFI_SUCCESS;
+ }
+
+ CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
+ HttpBootCallback = CallbackData->Private->HttpBootCallback;
+ if (HttpBootCallback != NULL) {
+ Status = HttpBootCallback->Callback (
+ HttpBootCallback,
+ HttpBootHttpEntityBody,
+ TRUE,
+ (UINT32)Length,
+ Data
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ //
+ // Copy data if caller has provided a buffer.
+ //
+ if (CallbackData->BufferSize > CallbackData->CopyedSize) {
+ CopyMem (
+ CallbackData->Buffer + CallbackData->CopyedSize,
+ Data,
+ MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
+ );
+ CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
+ }
+
+ //
+ // The caller doesn't provide a buffer, save the block into cache list.
+ //
+ if (CallbackData->Cache != NULL) {
+ NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
+ if (NewEntityData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ if (CallbackData->NewBlock) {
+ NewEntityData->Block = CallbackData->Block;
+ CallbackData->Block = NULL;
+ }
+ NewEntityData->DataLength = Length;
+ NewEntityData->DataStart = (UINT8*) Data;
+ InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ This function download the boot file by using UEFI HTTP protocol.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] HeaderOnly Only request the response header, it could save a lot of time if
+ the caller only want to know the size of the requested file.
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
+ then the size of the requested file is returned in
+ BufferSize.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
+ BufferSize has been updated with the size needed to complete
+ the request.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootGetBootFile (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN BOOLEAN HeaderOnly,
+ IN OUT UINTN *BufferSize,
+ OUT UINT8 *Buffer,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_STATUS_CODE StatusCode;
+ CHAR8 *HostName;
+ EFI_HTTP_REQUEST_DATA *RequestData;
+ HTTP_IO_RESPONSE_DATA *ResponseData;
+ HTTP_IO_RESPONSE_DATA ResponseBody;
+ HTTP_IO *HttpIo;
+ HTTP_IO_HEADER *HttpIoHeader;
+ VOID *Parser;
+ HTTP_BOOT_CALLBACK_DATA Context;
+ UINTN ContentLength;
+ HTTP_BOOT_CACHE_CONTENT *Cache;
+ UINT8 *Block;
+ UINTN UrlSize;
+ CHAR16 *Url;
+ BOOLEAN IdentityMode;
+ UINTN ReceivedSize;
+
+ ASSERT (Private != NULL);
+ ASSERT (Private->HttpCreated);
+
+ if (BufferSize == NULL || ImageType == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*BufferSize != 0 && Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // First, check whether we already cached the requested Uri.
+ //
+ UrlSize = AsciiStrSize (Private->BootFileUri);
+ Url = AllocatePool (UrlSize * sizeof (CHAR16));
+ if (Url == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);
+ if (!HeaderOnly && Buffer != NULL) {
+ Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType);
+ if (Status != EFI_NOT_FOUND) {
+ FreePool (Url);
+ return Status;
+ }
+ }
+
+ //
+ // Not found in cache, try to download it through HTTP.
+ //
+
+ //
+ // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
+ //
+ Cache = NULL;
+ if ((!HeaderOnly) && (*BufferSize == 0)) {
+ Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
+ if (Cache == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR_1;
+ }
+ Cache->ImageType = ImageTypeMax;
+ InitializeListHead (&Cache->EntityDataList);
+ }
+
+ //
+ // 2. Send HTTP request message.
+ //
+
+ //
+ // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
+ // Host
+ // Accept
+ // User-Agent
+ //
+ HttpIoHeader = HttpIoCreateHeader (3);
+ if (HttpIoHeader == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR_2;
+ }
+
+ //
+ // Add HTTP header field 1: Host
+ //
+ HostName = NULL;
+ Status = HttpUrlGetHostName (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ &HostName
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_3;
+ }
+ Status = HttpIoSetHeader (
+ HttpIoHeader,
+ HTTP_HEADER_HOST,
+ HostName
+ );
+ FreePool (HostName);
+ if (EFI_ERROR (Status)) {
+ goto ERROR_3;
+ }
+
+ //
+ // Add HTTP header field 2: Accept
+ //
+ Status = HttpIoSetHeader (
+ HttpIoHeader,
+ HTTP_HEADER_ACCEPT,
+ "*/*"
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_3;
+ }
+
+ //
+ // Add HTTP header field 3: User-Agent
+ //
+ Status = HttpIoSetHeader (
+ HttpIoHeader,
+ HTTP_HEADER_USER_AGENT,
+ HTTP_USER_AGENT_EFI_HTTP_BOOT
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_3;
+ }
+
+ //
+ // 2.2 Build the rest of HTTP request info.
+ //
+ RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
+ if (RequestData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR_3;
+ }
+ RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
+ RequestData->Url = Url;
+
+ //
+ // 2.3 Record the request info in a temp cache item.
+ //
+ if (Cache != NULL) {
+ Cache->RequestData = RequestData;
+ }
+
+ //
+ // 2.4 Send out the request to HTTP server.
+ //
+ HttpIo = &Private->HttpIo;
+ Status = HttpIoSendRequest (
+ HttpIo,
+ RequestData,
+ HttpIoHeader->HeaderCount,
+ HttpIoHeader->Headers,
+ 0,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_4;
+ }
+
+ //
+ // 3. Receive HTTP response message.
+ //
+
+ //
+ // 3.1 First step, use zero BodyLength to only receive the response headers.
+ //
+ ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA));
+ if (ResponseData == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR_4;
+ }
+ Status = HttpIoRecvResponse (
+ &Private->HttpIo,
+ TRUE,
+ ResponseData
+ );
+ if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {
+ if (EFI_ERROR (ResponseData->Status)) {
+ StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
+ HttpBootPrintErrorMessage (StatusCode);
+ Status = ResponseData->Status;
+ }
+ goto ERROR_5;
+ }
+
+ //
+ // Check the image type according to server's response.
+ //
+ Status = HttpBootCheckImageType (
+ Private->BootFileUri,
+ Private->BootFileUriParser,
+ ResponseData->HeaderCount,
+ ResponseData->Headers,
+ ImageType
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_5;
+ }
+
+ //
+ // 3.2 Cache the response header.
+ //
+ if (Cache != NULL) {
+ Cache->ResponseData = ResponseData;
+ Cache->ImageType = *ImageType;
+ }
+
+ //
+ // 3.3 Init a message-body parser from the header information.
+ //
+ Parser = NULL;
+ Context.NewBlock = FALSE;
+ Context.Block = NULL;
+ Context.CopyedSize = 0;
+ Context.Buffer = Buffer;
+ Context.BufferSize = *BufferSize;
+ Context.Cache = Cache;
+ Context.Private = Private;
+ Status = HttpInitMsgParser (
+ HeaderOnly ? HttpMethodHead : HttpMethodGet,
+ ResponseData->Response.StatusCode,
+ ResponseData->HeaderCount,
+ ResponseData->Headers,
+ HttpBootGetBootFileCallback,
+ (VOID*) &Context,
+ &Parser
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_6;
+ }
+
+ //
+ // 3.4 Continue to receive and parse message-body if needed.
+ //
+ Block = NULL;
+ if (!HeaderOnly) {
+ //
+ // 3.4.1, check whether we are in identity transfer-coding.
+ //
+ ContentLength = 0;
+ Status = HttpGetEntityLength (Parser, &ContentLength);
+ if (!EFI_ERROR (Status)) {
+ IdentityMode = TRUE;
+ } else {
+ IdentityMode = FALSE;
+ }
+
+ //
+ // 3.4.2, start the message-body download, the identity and chunked transfer-coding
+ // is handled in different path here.
+ //
+ ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));
+ if (IdentityMode) {
+ //
+ // In identity transfer-coding there is no need to parse the message body,
+ // just download the message body to the user provided buffer directly.
+ //
+ ReceivedSize = 0;
+ while (ReceivedSize < ContentLength) {
+ ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize;
+ ResponseBody.BodyLength = *BufferSize - ReceivedSize;
+ Status = HttpIoRecvResponse (
+ &Private->HttpIo,
+ FALSE,
+ &ResponseBody
+ );
+ if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
+ if (EFI_ERROR (ResponseBody.Status)) {
+ Status = ResponseBody.Status;
+ }
+ goto ERROR_6;
+ }
+ ReceivedSize += ResponseBody.BodyLength;
+ if (Private->HttpBootCallback != NULL) {
+ Status = Private->HttpBootCallback->Callback (
+ Private->HttpBootCallback,
+ HttpBootHttpEntityBody,
+ TRUE,
+ (UINT32)ResponseBody.BodyLength,
+ ResponseBody.Body
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_6;
+ }
+ }
+ }
+ } else {
+ //
+ // In "chunked" transfer-coding mode, so we need to parse the received
+ // data to get the real entity content.
+ //
+ Block = NULL;
+ while (!HttpIsMessageComplete (Parser)) {
+ //
+ // Allocate a buffer in Block to hold the message-body.
+ // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
+ // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
+ // every HttpIoRecvResponse().
+ //
+ if (Block == NULL || Context.BufferSize == 0) {
+ Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
+ if (Block == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ERROR_6;
+ }
+ Context.NewBlock = TRUE;
+ Context.Block = Block;
+ } else {
+ Context.NewBlock = FALSE;
+ }
+
+ ResponseBody.Body = (CHAR8*) Block;
+ ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
+ Status = HttpIoRecvResponse (
+ &Private->HttpIo,
+ FALSE,
+ &ResponseBody
+ );
+ if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
+ if (EFI_ERROR (ResponseBody.Status)) {
+ Status = ResponseBody.Status;
+ }
+ goto ERROR_6;
+ }
+
+ //
+ // Parse the new received block of the message-body, the block will be saved in cache.
+ //
+ Status = HttpParseMessageBody (
+ Parser,
+ ResponseBody.BodyLength,
+ ResponseBody.Body
+ );
+ if (EFI_ERROR (Status)) {
+ goto ERROR_6;
+ }
+ }
+ }
+ }
+
+ //
+ // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
+ //
+ Status = HttpGetEntityLength (Parser, &ContentLength);
+ if (EFI_ERROR (Status)) {
+ goto ERROR_6;
+ }
+
+ if (*BufferSize < ContentLength) {
+ Status = EFI_BUFFER_TOO_SMALL;
+ } else {
+ Status = EFI_SUCCESS;
+ }
+ *BufferSize = ContentLength;
+
+ //
+ // 4. Save the cache item to driver's cache list and return.
+ //
+ if (Cache != NULL) {
+ Cache->EntityLength = ContentLength;
+ InsertTailList (&Private->CacheList, &Cache->Link);
+ }
+
+ if (Parser != NULL) {
+ HttpFreeMsgParser (Parser);
+ }
+
+ return Status;
+
+ERROR_6:
+ if (Parser != NULL) {
+ HttpFreeMsgParser (Parser);
+ }
+ if (Context.Block != NULL) {
+ FreePool (Context.Block);
+ }
+ HttpBootFreeCache (Cache);
+
+ERROR_5:
+ if (ResponseData != NULL) {
+ FreePool (ResponseData);
+ }
+ERROR_4:
+ if (RequestData != NULL) {
+ FreePool (RequestData);
+ }
+ERROR_3:
+ HttpIoFreeHeader (HttpIoHeader);
+ERROR_2:
+ if (Cache != NULL) {
+ FreePool (Cache);
+ }
+ERROR_1:
+ if (Url != NULL) {
+ FreePool (Url);
+ }
+
+ return Status;
+}
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.h
new file mode 100644
index 00000000..b0ca80e9
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootClient.h
@@ -0,0 +1,137 @@
+/** @file
+ Declaration of the boot file download function.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_HTTP_BOOT_HTTP_H__
+#define __EFI_HTTP_BOOT_HTTP_H__
+
+#define HTTP_BOOT_REQUEST_TIMEOUT 5000 // 5 seconds in uints of millisecond.
+#define HTTP_BOOT_RESPONSE_TIMEOUT 5000 // 5 seconds in uints of millisecond.
+#define HTTP_BOOT_BLOCK_SIZE 1500
+
+
+
+#define HTTP_USER_AGENT_EFI_HTTP_BOOT "UefiHttpBoot/1.0"
+
+//
+// Record the data length and start address of a data block.
+//
+typedef struct {
+ LIST_ENTRY Link; // Link to the EntityDataList in HTTP_BOOT_CACHE_CONTENT
+ UINT8 *Block; // If NULL, the data is in previous data block.
+ UINT8 *DataStart; // Point to somewhere in the Block
+ UINTN DataLength;
+} HTTP_BOOT_ENTITY_DATA;
+
+//
+// Structure for a cache item
+//
+typedef struct {
+ LIST_ENTRY Link; // Link to the CacheList in driver's private data.
+ EFI_HTTP_REQUEST_DATA *RequestData;
+ HTTP_IO_RESPONSE_DATA *ResponseData; // Not include any message-body data.
+ HTTP_BOOT_IMAGE_TYPE ImageType;
+ UINTN EntityLength;
+ LIST_ENTRY EntityDataList; // Entity data (message-body)
+} HTTP_BOOT_CACHE_CONTENT;
+
+//
+// Callback data for HTTP_BODY_PARSER_CALLBACK()
+//
+typedef struct {
+ EFI_STATUS Status;
+ //
+ // Cache info.
+ //
+ HTTP_BOOT_CACHE_CONTENT *Cache;
+ BOOLEAN NewBlock;
+ UINT8 *Block;
+
+ //
+ // Caller provided buffer to load the file in.
+ //
+ UINTN CopyedSize;
+ UINTN BufferSize;
+ UINT8 *Buffer;
+
+ HTTP_BOOT_PRIVATE_DATA *Private;
+} HTTP_BOOT_CALLBACK_DATA;
+
+/**
+ Discover all the boot information for boot file.
+
+ @param[in, out] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Successfully obtained all the boot information .
+ @retval Others Failed to retrieve the boot information.
+
+**/
+EFI_STATUS
+HttpBootDiscoverBootInfo (
+ IN OUT HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ Create a HttpIo instance for the file download.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Successfully created.
+ @retval Others Failed to create HttpIo.
+
+**/
+EFI_STATUS
+HttpBootCreateHttpIo (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ This function download the boot file by using UEFI HTTP protocol.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] HeaderOnly Only request the response header, it could save a lot of time if
+ the caller only want to know the size of the requested file.
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
+ then the size of the requested file is returned in
+ BufferSize.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
+ BufferSize has been updated with the size needed to complete
+ the request.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootGetBootFile (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN BOOLEAN HeaderOnly,
+ IN OUT UINTN *BufferSize,
+ OUT UINT8 *Buffer,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ );
+
+/**
+ Clean up all cached data.
+
+ @param[in] Private The pointer to the driver's private data.
+
+**/
+VOID
+HttpBootFreeCacheList (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.c
new file mode 100644
index 00000000..7eaec8ea
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.c
@@ -0,0 +1,177 @@
+/** @file
+ Implementation of EFI_COMPONENT_NAME_PROTOCOL and EFI_COMPONENT_NAME2_PROTOCOL protocol.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+
+///
+/// Component Name Protocol instance
+///
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_COMPONENT_NAME_PROTOCOL gHttpBootDxeComponentName = {
+ (EFI_COMPONENT_NAME_GET_DRIVER_NAME) HttpBootDxeComponentNameGetDriverName,
+ (EFI_COMPONENT_NAME_GET_CONTROLLER_NAME)HttpBootDxeComponentNameGetControllerName,
+ "eng"
+};
+
+///
+/// Component Name 2 Protocol instance
+///
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_COMPONENT_NAME2_PROTOCOL gHttpBootDxeComponentName2 = {
+ HttpBootDxeComponentNameGetDriverName,
+ HttpBootDxeComponentNameGetControllerName,
+ "en"
+};
+
+///
+/// Table of driver names
+///
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_UNICODE_STRING_TABLE mHttpBootDxeDriverNameTable[] = {
+ { "eng;en", (CHAR16 *)L"UEFI HTTP Boot Driver" },
+ { NULL, NULL }
+};
+
+///
+/// Table of controller names
+///
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_UNICODE_STRING_TABLE mHttpBootDxeControllerNameTable[] = {
+ { "eng;en", (CHAR16 *)L"UEFI Http Boot Controller" },
+ { NULL, NULL }
+};
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the EFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param Language A pointer to a three-character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ @param DriverName A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ )
+{
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mHttpBootDxeDriverNameTable,
+ DriverName,
+ (BOOLEAN) (This != &gHttpBootDxeComponentName2)
+ );
+}
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param ControllerHandle The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ @param ChildHandle The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ @param Language A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ @param ControllerName A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language, from the point of view of the driver specified
+ by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE NicHandle;
+ UINT32 *Id;
+
+ if (ControllerHandle == NULL || ChildHandle != NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ NicHandle = HttpBootGetNicByIp4Children (ControllerHandle);
+ if (NicHandle == NULL) {
+ NicHandle = HttpBootGetNicByIp6Children(ControllerHandle);
+ if (NicHandle == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+ }
+
+ //
+ // Try to retrieve the private data by caller ID GUID.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &Id,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ return LookupUnicodeString2 (
+ Language,
+ This->SupportedLanguages,
+ mHttpBootDxeControllerNameTable,
+ ControllerName,
+ (BOOLEAN)(This != &gHttpBootDxeComponentName2)
+ );
+
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.h
new file mode 100644
index 00000000..a02fc147
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootComponentName.h
@@ -0,0 +1,93 @@
+/** @file
+ Declaration of HTTP boot driver's EFI_COMPONENT_NAME_PROTOCOL and
+ EFI_COMPONENT_NAME2_PROTOCOL function.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_UEFI_HTTP_BOOT_COM_NAME_H__
+#define __EFI_UEFI_HTTP_BOOT_COM_NAME_H__
+
+/**
+ Retrieves a Unicode string that is the user-readable name of the EFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param Language A pointer to a three-character ISO 639-2 language identifier.
+ This is the language of the driver name that that the caller
+ is requesting, and it must match one of the languages specified
+ in SupportedLanguages. The number of languages supported by a
+ driver is up to the driver writer.
+ @param DriverName A pointer to the Unicode string to return. This Unicode string
+ is the name of the driver specified by This in the language
+ specified by Language.
+
+ @retval EFI_SUCCESS The Unicode string for the Driver specified by This
+ and the language specified by Language was returned
+ in DriverName.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER DriverName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetDriverName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN CHAR8 *Language,
+ OUT CHAR16 **DriverName
+ );
+
+/**
+ Retrieves a Unicode string that is the user readable name of the controller
+ that is being managed by an EFI Driver.
+
+ @param This A pointer to the EFI_COMPONENT_NAME_PROTOCOL instance.
+ @param ControllerHandle The handle of a controller that the driver specified by
+ This is managing. This handle specifies the controller
+ whose name is to be returned.
+ @param ChildHandle The handle of the child controller to retrieve the name
+ of. This is an optional parameter that may be NULL. It
+ will be NULL for device drivers. It will also be NULL
+ for a bus drivers that wish to retrieve the name of the
+ bus controller. It will not be NULL for a bus driver
+ that wishes to retrieve the name of a child controller.
+ @param Language A pointer to a three character ISO 639-2 language
+ identifier. This is the language of the controller name
+ that the caller is requesting, and it must match one
+ of the languages specified in SupportedLanguages. The
+ number of languages supported by a driver is up to the
+ driver writer.
+ @param ControllerName A pointer to the Unicode string to return. This Unicode
+ string is the name of the controller specified by
+ ControllerHandle and ChildHandle in the language specified
+ by Language, from the point of view of the driver specified
+ by This.
+
+ @retval EFI_SUCCESS The Unicode string for the user-readable name in the
+ language specified by Language for the driver
+ specified by This was returned in DriverName.
+ @retval EFI_INVALID_PARAMETER ControllerHandle is NULL.
+ @retval EFI_INVALID_PARAMETER ChildHandle is not NULL and it is not a valid EFI_HANDLE.
+ @retval EFI_INVALID_PARAMETER Language is NULL.
+ @retval EFI_INVALID_PARAMETER ControllerName is NULL.
+ @retval EFI_UNSUPPORTED The driver specified by This is not currently managing
+ the controller specified by ControllerHandle and
+ ChildHandle.
+ @retval EFI_UNSUPPORTED The driver specified by This does not support the
+ language specified by Language.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeComponentNameGetControllerName (
+ IN EFI_COMPONENT_NAME2_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle OPTIONAL,
+ IN CHAR8 *Language,
+ OUT CHAR16 **ControllerName
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c
new file mode 100644
index 00000000..3814d196
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.c
@@ -0,0 +1,700 @@
+/** @file
+ Helper functions for configuring or getting the parameters relating to HTTP Boot.
+
+Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+#include <Library/UefiBootManagerLib.h>
+
+CHAR16 mHttpBootConfigStorageName[] = L"HTTP_BOOT_CONFIG_IFR_NVDATA";
+
+/**
+ Add new boot option for HTTP boot.
+
+ @param[in] Private Pointer to the driver private data.
+ @param[in] UsingIpv6 Set to TRUE if creating boot option for IPv6.
+ @param[in] Description The description text of the boot option.
+ @param[in] Uri The URI string of the boot file.
+
+ @retval EFI_SUCCESS The boot option is created successfully.
+ @retval Others Failed to create new boot option.
+
+**/
+EFI_STATUS
+HttpBootAddBootOption (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN BOOLEAN UsingIpv6,
+ IN CHAR16 *Description,
+ IN CHAR16 *Uri
+ )
+{
+ EFI_DEV_PATH *Node;
+ EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
+ UINTN Length;
+ CHAR8 AsciiUri[URI_STR_MAX_SIZE];
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
+
+ NewDevicePath = NULL;
+ Node = NULL;
+ TmpDevicePath = NULL;
+
+ if (StrLen (Description) == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert the scheme to all lower case.
+ //
+ for (Index = 0; Index < StrLen (Uri); Index++) {
+ if (Uri[Index] == L':') {
+ break;
+ }
+ if (Uri[Index] >= L'A' && Uri[Index] <= L'Z') {
+ Uri[Index] -= (CHAR16)(L'A' - L'a');
+ }
+ }
+
+ //
+ // Only accept empty URI, or http and https URI.
+ //
+ if ((StrLen (Uri) != 0) && (StrnCmp (Uri, L"http://", 7) != 0) && (StrnCmp (Uri, L"https://", 8) != 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Create a new device path by appending the IP node and URI node to
+ // the driver's parent device path
+ //
+ if (!UsingIpv6) {
+ Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv4.Header.SubType = MSG_IPv4_DP;
+ SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
+ } else {
+ Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+ Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv6.Header.SubType = MSG_IPv6_DP;
+ SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
+ }
+ TmpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ if (TmpDevicePath == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ //
+ // Update the URI node with the input boot file URI.
+ //
+ UnicodeStrToAsciiStrS (Uri, AsciiUri, sizeof (AsciiUri));
+ Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (AsciiUri);
+ Node = AllocatePool (Length);
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ FreePool (TmpDevicePath);
+ goto ON_EXIT;
+ }
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_URI_DP;
+ SetDevicePathNodeLength (Node, Length);
+ CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), AsciiUri, AsciiStrSize (AsciiUri));
+ NewDevicePath = AppendDevicePathNode (TmpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ FreePool (TmpDevicePath);
+ if (NewDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_EXIT;
+ }
+
+ //
+ // Add a new load option.
+ //
+ Status = EfiBootManagerInitializeLoadOption (
+ &NewOption,
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ LOAD_OPTION_ACTIVE,
+ Description,
+ NewDevicePath,
+ NULL,
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1);
+ EfiBootManagerFreeLoadOption (&NewOption);
+
+ON_EXIT:
+
+ if (NewDevicePath != NULL) {
+ FreePool (NewDevicePath);
+ }
+
+ return Status;
+}
+
+/**
+
+ This function allows the caller to request the current
+ configuration for one or more named elements. The resulting
+ string is in <ConfigAltResp> format. Also, any and all alternative
+ configuration strings shall be appended to the end of the
+ current configuration string. If they are, they must appear
+ after the current configuration. They must contain the same
+ routing (GUID, NAME, PATH) as the current configuration string.
+ They must have an additional description indicating the type of
+ alternative configuration the string represents,
+ "ALTCFG=<StringToken>". That <StringToken> (when
+ converted from Hex UNICODE to binary) is a reference to a
+ string in the associated string pack.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+
+ @param[in] Request A null-terminated Unicode string in
+ <ConfigRequest> format. Note that this
+ includes the routing information as well as
+ the configurable name / value pairs. It is
+ invalid for this string to be in
+ <MultiConfigRequest> format.
+
+ @param[out] Progress On return, points to a character in the
+ Request string. Points to the string's null
+ terminator if request was successful. Points
+ to the most recent "&" before the first
+ failing name / value pair (or the beginning
+ of the string if the failure is in the first
+ name / value pair) if the request was not successful.
+
+ @param[out] Results A null-terminated Unicode string in
+ <ConfigAltResp> format which has all values
+ filled in for the names in the Request string.
+ String to be allocated by the called function.
+
+ @retval EFI_SUCCESS The Results string is filled with the
+ values corresponding to all requested
+ names.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+
+ @retval EFI_INVALID_PARAMETER For example, passing in a NULL
+ for the Request parameter
+ would result in this type of
+ error. In this case, the
+ Progress parameter would be
+ set to NULL.
+
+ @retval EFI_NOT_FOUND Routing data doesn't match any
+ known driver. Progress set to the
+ first character in the routing header.
+ Note: There is no requirement that the
+ driver validate the routing data. It
+ must skip the <ConfigHdr> in order to
+ process the names.
+
+ @retval EFI_INVALID_PARAMETER Illegal syntax. Progress set
+ to most recent "&" before the
+ error or the beginning of the
+ string.
+
+ @retval EFI_INVALID_PARAMETER Unknown name. Progress points
+ to the & before the name in
+ question.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootFormExtractConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Request,
+ OUT EFI_STRING *Progress,
+ OUT EFI_STRING *Results
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ EFI_STRING ConfigRequestHdr;
+ EFI_STRING ConfigRequest;
+ BOOLEAN AllocatedRequest;
+ UINTN Size;
+
+ if (Progress == NULL || Results == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Progress = Request;
+ if ((Request != NULL) && !HiiIsConfigHdrMatch (Request, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ ConfigRequestHdr = NULL;
+ ConfigRequest = NULL;
+ AllocatedRequest = FALSE;
+ Size = 0;
+
+ CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+ //
+ // Convert buffer data to <ConfigResp> by helper function BlockToConfig()
+ //
+ BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);
+ ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);
+ StrCpyS (CallbackInfo->HttpBootNvData.Description, DESCRIPTION_STR_MAX_SIZE / sizeof (CHAR16), HTTP_BOOT_DEFAULT_DESCRIPTION_STR);
+
+ ConfigRequest = Request;
+ if ((Request == NULL) || (StrStr (Request, L"OFFSET") == NULL)) {
+ //
+ // Request has no request element, construct full request string.
+ // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
+ // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
+ //
+ ConfigRequestHdr = HiiConstructConfigHdr (&gHttpBootConfigGuid, mHttpBootConfigStorageName, CallbackInfo->ChildHandle);
+ Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
+ ConfigRequest = AllocateZeroPool (Size);
+ if (ConfigRequest == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ AllocatedRequest = TRUE;
+ UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64)BufferSize);
+ FreePool (ConfigRequestHdr);
+ }
+
+ Status = gHiiConfigRouting->BlockToConfig (
+ gHiiConfigRouting,
+ ConfigRequest,
+ (UINT8 *) &CallbackInfo->HttpBootNvData,
+ BufferSize,
+ Results,
+ Progress
+ );
+
+ //
+ // Free the allocated config request string.
+ //
+ if (AllocatedRequest) {
+ FreePool (ConfigRequest);
+ ConfigRequest = NULL;
+ }
+ //
+ // Set Progress string to the original request string.
+ //
+ if (Request == NULL) {
+ *Progress = NULL;
+ } else if (StrStr (Request, L"OFFSET") == NULL) {
+ *Progress = Request + StrLen (Request);
+ }
+
+ return Status;
+}
+
+/**
+
+ This function applies changes in a driver's configuration.
+ Input is a Configuration, which has the routing data for this
+ driver followed by name / value configuration pairs. The driver
+ must apply those pairs to its configurable storage. If the
+ driver's configuration is stored in a linear block of data
+ and the driver's name / value pairs are in <BlockConfig>
+ format, it may use the ConfigToBlock helper function (above) to
+ simplify the job.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+
+ @param[in] Configuration A null-terminated Unicode string in
+ <ConfigString> format.
+
+ @param[out] Progress A pointer to a string filled in with the
+ offset of the most recent '&' before the
+ first failing name / value pair (or the
+ beginning of the string if the failure
+ is in the first name / value pair) or
+ the terminating NULL if all was
+ successful.
+
+ @retval EFI_SUCCESS The results have been distributed or are
+ awaiting distribution.
+
+ @retval EFI_OUT_OF_RESOURCES Not enough memory to store the
+ parts of the results that must be
+ stored awaiting possible future
+ protocols.
+
+ @retval EFI_INVALID_PARAMETERS Passing in a NULL for the
+ Results parameter would result
+ in this type of error.
+
+ @retval EFI_NOT_FOUND Target for the specified routing data
+ was not found.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootFormRouteConfig (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN CONST EFI_STRING Configuration,
+ OUT EFI_STRING *Progress
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+
+ if (Progress == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+ *Progress = Configuration;
+
+ if (Configuration == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check routing data in <ConfigHdr>.
+ // Note: there is no name for Name/Value storage, only GUID will be checked
+ //
+ if (!HiiIsConfigHdrMatch (Configuration, &gHttpBootConfigGuid, mHttpBootConfigStorageName)) {
+ return EFI_NOT_FOUND;
+ }
+
+ CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO (CallbackInfo);
+
+ BufferSize = sizeof (HTTP_BOOT_CONFIG_IFR_NVDATA);
+ ZeroMem (&CallbackInfo->HttpBootNvData, BufferSize);
+
+ Status = gHiiConfigRouting->ConfigToBlock (
+ gHiiConfigRouting,
+ Configuration,
+ (UINT8 *) &CallbackInfo->HttpBootNvData,
+ &BufferSize,
+ Progress
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Create a new boot option according to the configuration data.
+ //
+ HttpBootAddBootOption (
+ Private,
+ (CallbackInfo->HttpBootNvData.IpVersion == HTTP_BOOT_IP_VERSION_6) ? TRUE : FALSE,
+ CallbackInfo->HttpBootNvData.Description,
+ CallbackInfo->HttpBootNvData.Uri
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+
+ This function is called to provide results data to the driver.
+ This data consists of a unique key that is used to identify
+ which data is either being passed back or being asked for.
+
+ @param[in] This Points to the EFI_HII_CONFIG_ACCESS_PROTOCOL.
+ @param[in] Action Specifies the type of action taken by the browser.
+ @param[in] QuestionId A unique value which is sent to the original
+ exporting driver so that it can identify the type
+ of data to expect. The format of the data tends to
+ vary based on the opcode that generated the callback.
+ @param[in] Type The type of value for the question.
+ @param[in, out] Value A pointer to the data being sent to the original
+ exporting driver.
+ @param[out] ActionRequest On return, points to the action requested by the
+ callback function.
+
+ @retval EFI_SUCCESS The callback successfully handled the action.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the
+ variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be saved.
+ @retval EFI_UNSUPPORTED The specified Action is not supported by the
+ callback.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootFormCallback (
+ IN CONST EFI_HII_CONFIG_ACCESS_PROTOCOL *This,
+ IN EFI_BROWSER_ACTION Action,
+ IN EFI_QUESTION_ID QuestionId,
+ IN UINT8 Type,
+ IN OUT EFI_IFR_TYPE_VALUE *Value,
+ OUT EFI_BROWSER_ACTION_REQUEST *ActionRequest
+ )
+{
+ EFI_INPUT_KEY Key;
+ CHAR16 *Uri;
+ UINTN UriLen;
+ CHAR8 *AsciiUri;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ EFI_STATUS Status;
+
+ Uri = NULL;
+ UriLen = 0;
+ AsciiUri = NULL;
+ Status = EFI_SUCCESS;
+
+ if (This == NULL || Value == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CallbackInfo = HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS (This);
+
+ if (Action != EFI_BROWSER_ACTION_CHANGING) {
+ return EFI_UNSUPPORTED;
+ }
+
+ switch (QuestionId) {
+ case KEY_INITIATOR_URI:
+ //
+ // Get user input URI string
+ //
+ Uri = HiiGetString (CallbackInfo->RegisteredHandle, Value->string, NULL);
+ if(Uri == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The URI should be either an empty string (for corporate environment) ,or http(s) for home environment.
+ // Pop up a message box for the unsupported URI.
+ //
+ if (StrLen (Uri) != 0) {
+ UriLen = StrLen (Uri) + 1;
+ AsciiUri = AllocateZeroPool (UriLen);
+ if (AsciiUri == NULL) {
+ FreePool (Uri);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ UnicodeStrToAsciiStrS (Uri, AsciiUri, UriLen);
+
+ Status = HttpBootCheckUriScheme (AsciiUri);
+
+ if (Status == EFI_INVALID_PARAMETER) {
+
+ DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status));
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"ERROR: Unsupported URI!",
+ L"Only supports HTTP and HTTPS",
+ NULL
+ );
+ } else if (Status == EFI_ACCESS_DENIED) {
+
+ DEBUG ((EFI_D_ERROR, "HttpBootFormCallback: %r.\n", Status));
+
+ CreatePopUp (
+ EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE,
+ &Key,
+ L"ERROR: Unsupported URI!",
+ L"HTTP is disabled",
+ NULL
+ );
+ }
+ }
+
+ if (Uri != NULL) {
+ FreePool (Uri);
+ }
+
+ if (AsciiUri != NULL) {
+ FreePool (AsciiUri);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ return Status;
+}
+
+/**
+ Initialize the configuration form.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootConfigFormInit (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+ VENDOR_DEVICE_PATH VendorDeviceNode;
+ CHAR16 *MacString;
+ CHAR16 *OldMenuString;
+ CHAR16 MenuString[128];
+
+ CallbackInfo = &Private->CallbackInfo;
+
+ if (CallbackInfo->Initialized) {
+ return EFI_SUCCESS;
+ }
+
+ CallbackInfo->Signature = HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE;
+
+ //
+ // Construct device path node for EFI HII Config Access protocol,
+ // which consists of controller physical device path and one hardware
+ // vendor guid node.
+ //
+ ZeroMem (&VendorDeviceNode, sizeof (VENDOR_DEVICE_PATH));
+ VendorDeviceNode.Header.Type = HARDWARE_DEVICE_PATH;
+ VendorDeviceNode.Header.SubType = HW_VENDOR_DP;
+ CopyGuid (&VendorDeviceNode.Guid, &gEfiCallerIdGuid);
+ SetDevicePathNodeLength (&VendorDeviceNode.Header, sizeof (VENDOR_DEVICE_PATH));
+ CallbackInfo->HiiVendorDevicePath = AppendDevicePathNode (
+ Private->ParentDevicePath,
+ (EFI_DEVICE_PATH_PROTOCOL *) &VendorDeviceNode
+ );
+ if (CallbackInfo->HiiVendorDevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ CallbackInfo->ConfigAccess.ExtractConfig = HttpBootFormExtractConfig;
+ CallbackInfo->ConfigAccess.RouteConfig = HttpBootFormRouteConfig;
+ CallbackInfo->ConfigAccess.Callback = HttpBootFormCallback;
+
+ //
+ // Install Device Path Protocol and Config Access protocol to driver handle.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->ConfigAccess,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto Error;
+ }
+
+ //
+ // Publish our HII data.
+ //
+ CallbackInfo->RegisteredHandle = HiiAddPackages (
+ &gHttpBootConfigGuid,
+ CallbackInfo->ChildHandle,
+ HttpBootDxeStrings,
+ HttpBootConfigVfrBin,
+ NULL
+ );
+ if (CallbackInfo->RegisteredHandle == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto Error;
+ }
+
+ //
+ // Append MAC string in the menu help string
+ //
+ Status = NetLibGetMacString (Private->Controller, NULL, &MacString);
+ if (!EFI_ERROR (Status)) {
+ OldMenuString = HiiGetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP),
+ NULL
+ );
+ UnicodeSPrint (MenuString, 128, L"%s (MAC:%s)", OldMenuString, MacString);
+ HiiSetString (
+ CallbackInfo->RegisteredHandle,
+ STRING_TOKEN (STR_HTTP_BOOT_CONFIG_FORM_HELP),
+ MenuString,
+ NULL
+ );
+
+ FreePool (MacString);
+ FreePool (OldMenuString);
+
+ CallbackInfo->Initialized = TRUE;
+ return EFI_SUCCESS;
+ }
+
+Error:
+
+ HttpBootConfigFormUnload (Private);
+ return Status;
+}
+
+/**
+ Unload the configuration form, this includes: delete all the configuration
+ entries, uninstall the form callback protocol, and free the resources used.
+ The form will only be unload completely when both IP4 and IP6 stack are stopped.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is unloaded.
+ @retval Others Failed to unload the form.
+
+**/
+EFI_STATUS
+HttpBootConfigFormUnload (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ HTTP_BOOT_FORM_CALLBACK_INFO *CallbackInfo;
+
+ if (Private->Ip4Nic != NULL || Private->Ip6Nic != NULL) {
+ //
+ // Only unload the configuration form when both IP4 and IP6 stack are stopped.
+ //
+ return EFI_SUCCESS;
+ }
+
+ CallbackInfo = &Private->CallbackInfo;
+ if (CallbackInfo->ChildHandle != NULL) {
+ //
+ // Uninstall EFI_HII_CONFIG_ACCESS_PROTOCOL
+ //
+ gBS->UninstallMultipleProtocolInterfaces (
+ CallbackInfo->ChildHandle,
+ &gEfiDevicePathProtocolGuid,
+ CallbackInfo->HiiVendorDevicePath,
+ &gEfiHiiConfigAccessProtocolGuid,
+ &CallbackInfo->ConfigAccess,
+ NULL
+ );
+ CallbackInfo->ChildHandle = NULL;
+ }
+
+ if (CallbackInfo->HiiVendorDevicePath != NULL) {
+ FreePool (CallbackInfo->HiiVendorDevicePath);
+ CallbackInfo->HiiVendorDevicePath = NULL;
+ }
+
+ if (CallbackInfo->RegisteredHandle != NULL) {
+ //
+ // Remove HII package list
+ //
+ HiiRemovePackages (CallbackInfo->RegisteredHandle);
+ CallbackInfo->RegisteredHandle = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.h
new file mode 100644
index 00000000..c94ae8c0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfig.h
@@ -0,0 +1,73 @@
+/** @file
+ The header file of functions for configuring or getting the parameters
+ relating to HTTP Boot.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _HTTP_BOOT_CONFIG_H_
+#define _HTTP_BOOT_CONFIG_H_
+
+
+#include "HttpBootConfigNVDataStruc.h"
+
+typedef struct _HTTP_BOOT_FORM_CALLBACK_INFO HTTP_BOOT_FORM_CALLBACK_INFO;
+
+extern UINT8 HttpBootDxeStrings[];
+extern UINT8 HttpBootConfigVfrBin[];
+
+#pragma pack()
+
+#define HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE SIGNATURE_32 ('H', 'B', 'f', 'c')
+
+#define HTTP_BOOT_FORM_CALLBACK_INFO_FROM_CONFIG_ACCESS(Callback) \
+ CR ( \
+ Callback, \
+ HTTP_BOOT_FORM_CALLBACK_INFO, \
+ ConfigAccess, \
+ HTTP_BOOT_FORM_CALLBACK_INFO_SIGNATURE \
+ )
+
+struct _HTTP_BOOT_FORM_CALLBACK_INFO {
+ UINT32 Signature;
+ BOOLEAN Initialized;
+ EFI_HANDLE ChildHandle;
+ EFI_DEVICE_PATH_PROTOCOL *HiiVendorDevicePath;
+ EFI_HII_HANDLE RegisteredHandle;
+ EFI_HII_CONFIG_ACCESS_PROTOCOL ConfigAccess;
+ HTTP_BOOT_CONFIG_IFR_NVDATA HttpBootNvData;
+};
+
+/**
+ Initialize the configuration form.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is initialized.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootConfigFormInit (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ Unload the configuration form, this includes: delete all the configuration
+ entries, uninstall the form callback protocol, and free the resources used.
+ The form will only be unload completely when both IP4 and IP6 stack are stopped.
+
+ @param[in] Private Pointer to the driver private data.
+
+ @retval EFI_SUCCESS The configuration form is unloaded.
+ @retval Others Failed to unload the form.
+
+**/
+EFI_STATUS
+HttpBootConfigFormUnload (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h
new file mode 100644
index 00000000..19d7e452
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigNVDataStruc.h
@@ -0,0 +1,44 @@
+/** @file
+ Define NVData structures used by the HTTP Boot configuration component.
+
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _HTTP_BOOT_NVDATA_STRUC_H_
+#define _HTTP_BOOT_NVDATA_STRUC_H_
+
+#include <Guid/HttpBootConfigHii.h>
+
+#define HTTP_BOOT_IP_VERSION_4 0
+#define HTTP_BOOT_IP_VERSION_6 1
+
+//
+// Macros used for an IPv4 or an IPv6 address.
+//
+#define URI_STR_MIN_SIZE 0
+#define URI_STR_MAX_SIZE 255
+
+#define DESCRIPTION_STR_MIN_SIZE 6
+#define DESCRIPTION_STR_MAX_SIZE 75
+
+#define CONFIGURATION_VARSTORE_ID 0x1234
+
+#define FORMID_MAIN_FORM 1
+
+#define KEY_INITIATOR_URI 0x101
+
+#define HTTP_BOOT_DEFAULT_DESCRIPTION_STR L"UEFI HTTP"
+
+#pragma pack(1)
+typedef struct _HTTP_BOOT_CONFIG_IFR_NVDATA {
+ UINT8 IpVersion;
+ UINT8 Padding;
+ CHAR16 Description[DESCRIPTION_STR_MAX_SIZE];
+ CHAR16 Uri[URI_STR_MAX_SIZE];
+} HTTP_BOOT_CONFIG_IFR_NVDATA;
+#pragma pack()
+
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni
new file mode 100644
index 00000000..40abb13d
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigStrings.uni
@@ -0,0 +1,21 @@
+/** @file
+ String definitions for HTTP Boot configuration.
+
+ Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#langdef en-US "English"
+
+#string STR_HTTP_BOOT_CONFIG_FORM_TITLE #language en-US "HTTP Boot Configuration"
+#string STR_HTTP_BOOT_CONFIG_FORM_HELP #language en-US "Configure HTTP Boot parameters."
+#string STR_HTTP_BOOT_IP_VERSION_PROMPT #language en-US "Internet Protocol"
+#string STR_HTTP_BOOT_IP_VERSION_HELP #language en-US "Select the version of Internet Protocol."
+#string STR_HTTP_BOOT_IP_VERSION_4 #language en-US "IP4"
+#string STR_HTTP_BOOT_IP_VERSION_6 #language en-US "IP6"
+#string STR_BOOT_URI_PROMPT #language en-US "Boot URI"
+#string STR_BOOT_URI_HELP #language en-US "A new Boot Option will be created according to this Boot URI."
+#string STR_BOOT_DESCRIPTION_PROMPT #language en-US "Input the description"
+#string STR_NULL_STRING #language en-US ""
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr
new file mode 100644
index 00000000..0a59287c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootConfigVfr.vfr
@@ -0,0 +1,49 @@
+/** @file
+ VFR file used by the HTTP Boot configuration component.
+
+ Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootConfigNVDataStruc.h"
+
+
+formset
+ guid = HTTP_BOOT_CONFIG_GUID,
+ title = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_TITLE),
+ help = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_HELP),
+
+ varstore HTTP_BOOT_CONFIG_IFR_NVDATA,
+ name = HTTP_BOOT_CONFIG_IFR_NVDATA,
+ guid = HTTP_BOOT_CONFIG_GUID;
+
+ form formid = FORMID_MAIN_FORM,
+ title = STRING_TOKEN(STR_HTTP_BOOT_CONFIG_FORM_TITLE);
+
+ string varid = HTTP_BOOT_CONFIG_IFR_NVDATA.Description,
+ prompt = STRING_TOKEN(STR_BOOT_DESCRIPTION_PROMPT),
+ help = STRING_TOKEN(STR_NULL_STRING),
+ minsize = DESCRIPTION_STR_MIN_SIZE,
+ maxsize = DESCRIPTION_STR_MAX_SIZE,
+ endstring;
+
+ oneof varid = HTTP_BOOT_CONFIG_IFR_NVDATA.IpVersion,
+ prompt = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_PROMPT),
+ help = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_HELP),
+ option text = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_4), value = HTTP_BOOT_IP_VERSION_4, flags = DEFAULT;
+ option text = STRING_TOKEN(STR_HTTP_BOOT_IP_VERSION_6), value = HTTP_BOOT_IP_VERSION_6, flags = 0;
+ endoneof;
+
+ string varid = HTTP_BOOT_CONFIG_IFR_NVDATA.Uri,
+ prompt = STRING_TOKEN(STR_BOOT_URI_PROMPT),
+ help = STRING_TOKEN(STR_BOOT_URI_HELP),
+ flags = INTERACTIVE,
+ key = KEY_INITIATOR_URI,
+ minsize = URI_STR_MIN_SIZE,
+ maxsize = URI_STR_MAX_SIZE,
+ endstring;
+ endform;
+
+endformset;
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c
new file mode 100644
index 00000000..1d275ae0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.c
@@ -0,0 +1,912 @@
+/** @file
+ Functions implementation related with DHCPv4 for HTTP boot driver.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+
+//
+// This is a map from the interested DHCP4 option tags' index to the tag value.
+//
+UINT8 mInterestedDhcp4Tags[HTTP_BOOT_DHCP4_TAG_INDEX_MAX] = {
+ DHCP4_TAG_BOOTFILE_LEN,
+ DHCP4_TAG_OVERLOAD,
+ DHCP4_TAG_MSG_TYPE,
+ DHCP4_TAG_SERVER_ID,
+ DHCP4_TAG_VENDOR_CLASS_ID,
+ DHCP4_TAG_BOOTFILE,
+ DHCP4_TAG_DNS_SERVER
+};
+
+//
+// There are 4 times retries with the value of 4, 8, 16 and 32, refers to UEFI 2.5 spec.
+//
+UINT32 mHttpDhcpTimeout[4] = {4, 8, 16, 32};
+
+/**
+ Build the options buffer for the DHCPv4 request packet.
+
+ @param[in] Private Pointer to HTTP boot driver private data.
+ @param[out] OptList Pointer to the option pointer array.
+ @param[in] Buffer Pointer to the buffer to contain the option list.
+
+ @return Index The count of the built-in options.
+
+**/
+UINT32
+HttpBootBuildDhcp4Options (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ OUT EFI_DHCP4_PACKET_OPTION **OptList,
+ IN UINT8 *Buffer
+ )
+{
+ HTTP_BOOT_DHCP4_OPTION_ENTRY OptEnt;
+ UINT16 Value;
+ UINT32 Index;
+
+ Index = 0;
+ OptList[0] = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+
+ //
+ // Append parameter request list option.
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_PARA_LIST;
+ OptList[Index]->Length = 27;
+ OptEnt.Para = (HTTP_BOOT_DHCP4_OPTION_PARA *) OptList[Index]->Data;
+ OptEnt.Para->ParaList[0] = DHCP4_TAG_NETMASK;
+ OptEnt.Para->ParaList[1] = DHCP4_TAG_TIME_OFFSET;
+ OptEnt.Para->ParaList[2] = DHCP4_TAG_ROUTER;
+ OptEnt.Para->ParaList[3] = DHCP4_TAG_TIME_SERVER;
+ OptEnt.Para->ParaList[4] = DHCP4_TAG_NAME_SERVER;
+ OptEnt.Para->ParaList[5] = DHCP4_TAG_DNS_SERVER;
+ OptEnt.Para->ParaList[6] = DHCP4_TAG_HOSTNAME;
+ OptEnt.Para->ParaList[7] = DHCP4_TAG_BOOTFILE_LEN;
+ OptEnt.Para->ParaList[8] = DHCP4_TAG_DOMAINNAME;
+ OptEnt.Para->ParaList[9] = DHCP4_TAG_ROOTPATH;
+ OptEnt.Para->ParaList[10] = DHCP4_TAG_EXTEND_PATH;
+ OptEnt.Para->ParaList[11] = DHCP4_TAG_EMTU;
+ OptEnt.Para->ParaList[12] = DHCP4_TAG_TTL;
+ OptEnt.Para->ParaList[13] = DHCP4_TAG_BROADCAST;
+ OptEnt.Para->ParaList[14] = DHCP4_TAG_NIS_DOMAIN;
+ OptEnt.Para->ParaList[15] = DHCP4_TAG_NIS_SERVER;
+ OptEnt.Para->ParaList[16] = DHCP4_TAG_NTP_SERVER;
+ OptEnt.Para->ParaList[17] = DHCP4_TAG_VENDOR;
+ OptEnt.Para->ParaList[18] = DHCP4_TAG_REQUEST_IP;
+ OptEnt.Para->ParaList[19] = DHCP4_TAG_LEASE;
+ OptEnt.Para->ParaList[20] = DHCP4_TAG_SERVER_ID;
+ OptEnt.Para->ParaList[21] = DHCP4_TAG_T1;
+ OptEnt.Para->ParaList[22] = DHCP4_TAG_T2;
+ OptEnt.Para->ParaList[23] = DHCP4_TAG_VENDOR_CLASS_ID;
+ OptEnt.Para->ParaList[25] = DHCP4_TAG_BOOTFILE;
+ OptEnt.Para->ParaList[26] = DHCP4_TAG_UUID;
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append UUID/Guid-based client identifier option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_UUID;
+ OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UUID);
+ OptEnt.Uuid = (HTTP_BOOT_DHCP4_OPTION_UUID *) OptList[Index]->Data;
+ OptEnt.Uuid->Type = 0;
+ if (EFI_ERROR (NetLibGetSystemGuid ((EFI_GUID *) OptEnt.Uuid->Guid))) {
+ //
+ // Zero the Guid to indicate NOT programmable if failed to get system Guid.
+ //
+ ZeroMem (OptEnt.Uuid->Guid, sizeof (EFI_GUID));
+ }
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client network device interface option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_UNDI;
+ OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_UNDI);
+ OptEnt.Undi = (HTTP_BOOT_DHCP4_OPTION_UNDI *) OptList[Index]->Data;
+
+ if (Private->Nii != NULL) {
+ OptEnt.Undi->Type = Private->Nii->Type;
+ OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
+ OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
+ } else {
+ OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
+ OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
+ OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
+ }
+
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client system architecture option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_ARCH;
+ OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_ARCH);
+ OptEnt.Arch = (HTTP_BOOT_DHCP4_OPTION_ARCH *) OptList[Index]->Data;
+ Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE);
+ CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP_OPTION (OptList[Index - 1]);
+
+ //
+ // Append vendor class identify option
+ //
+ OptList[Index]->OpCode = DHCP4_TAG_VENDOR_CLASS_ID;
+ OptList[Index]->Length = (UINT8) sizeof (HTTP_BOOT_DHCP4_OPTION_CLID);
+ OptEnt.Clid = (HTTP_BOOT_DHCP4_OPTION_CLID *) OptList[Index]->Data;
+ CopyMem (
+ OptEnt.Clid,
+ DEFAULT_CLASS_ID_DATA,
+ sizeof (HTTP_BOOT_DHCP4_OPTION_CLID)
+ );
+ HttpBootUintnToAscDecWithFormat (
+ EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE,
+ OptEnt.Clid->ArchitectureType,
+ sizeof (OptEnt.Clid->ArchitectureType)
+ );
+
+ if (Private->Nii != NULL) {
+ CopyMem (OptEnt.Clid->InterfaceName, Private->Nii->StringId, sizeof (OptEnt.Clid->InterfaceName));
+ HttpBootUintnToAscDecWithFormat (Private->Nii->MajorVer, OptEnt.Clid->UndiMajor, sizeof (OptEnt.Clid->UndiMajor));
+ HttpBootUintnToAscDecWithFormat (Private->Nii->MinorVer, OptEnt.Clid->UndiMinor, sizeof (OptEnt.Clid->UndiMinor));
+ }
+
+ Index++;
+
+ return Index;
+}
+
+/**
+ Parse a certain dhcp4 option by OptTag in Buffer, and return with start pointer.
+
+ @param[in] Buffer Pointer to the option buffer.
+ @param[in] Length Length of the option buffer.
+ @param[in] OptTag Tag of the required option.
+
+ @retval NULL Failed to find the required option.
+ @retval Others The position of the required option.
+
+**/
+EFI_DHCP4_PACKET_OPTION *
+HttpBootParseDhcp4Options (
+ IN UINT8 *Buffer,
+ IN UINT32 Length,
+ IN UINT8 OptTag
+ )
+{
+ EFI_DHCP4_PACKET_OPTION *Option;
+ UINT32 Offset;
+
+ Option = (EFI_DHCP4_PACKET_OPTION *) Buffer;
+ Offset = 0;
+
+ while (Offset < Length && Option->OpCode != DHCP4_TAG_EOP) {
+
+ if (Option->OpCode == OptTag) {
+ //
+ // Found the required option.
+ //
+ return Option;
+ }
+
+ //
+ // Skip the current option to the next.
+ //
+ if (Option->OpCode == DHCP4_TAG_PAD) {
+ Offset++;
+ } else {
+ Offset += Option->Length + 2;
+ }
+
+ Option = (EFI_DHCP4_PACKET_OPTION *) (Buffer + Offset);
+ }
+
+ return NULL;
+}
+
+/**
+ Cache the DHCPv4 packet.
+
+ @param[in] Dst Pointer to the cache buffer for DHCPv4 packet.
+ @param[in] Src Pointer to the DHCPv4 packet to be cached.
+
+ @retval EFI_SUCCESS Packet is copied.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+HttpBootCacheDhcp4Packet (
+ IN EFI_DHCP4_PACKET *Dst,
+ IN EFI_DHCP4_PACKET *Src
+ )
+{
+ if (Dst->Size < Src->Length) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (&Dst->Dhcp4, &Src->Dhcp4, Src->Length);
+ Dst->Length = Src->Length;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Parse the cached DHCPv4 packet, including all the options.
+
+ @param[in] Cache4 Pointer to cached DHCPv4 packet.
+
+ @retval EFI_SUCCESS Parsed the DHCPv4 packet successfully.
+ @retval EFI_DEVICE_ERROR Failed to parse an invalid packet.
+
+**/
+EFI_STATUS
+HttpBootParseDhcp4Packet (
+ IN HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4
+ )
+{
+ EFI_DHCP4_PACKET *Offer;
+ EFI_DHCP4_PACKET_OPTION **Options;
+ UINTN Index;
+ EFI_DHCP4_PACKET_OPTION *Option;
+ BOOLEAN IsProxyOffer;
+ BOOLEAN IsHttpOffer;
+ BOOLEAN IsDnsOffer;
+ BOOLEAN IpExpressedUri;
+ UINT8 *Ptr8;
+ EFI_STATUS Status;
+ HTTP_BOOT_OFFER_TYPE OfferType;
+ EFI_IPv4_ADDRESS IpAddr;
+ BOOLEAN FileFieldOverloaded;
+
+ IsDnsOffer = FALSE;
+ IpExpressedUri = FALSE;
+ IsProxyOffer = FALSE;
+ IsHttpOffer = FALSE;
+ FileFieldOverloaded = FALSE;
+
+ ZeroMem (Cache4->OptList, sizeof (Cache4->OptList));
+
+ Offer = &Cache4->Packet.Offer;
+ Options = Cache4->OptList;
+
+ //
+ // Parse DHCPv4 options in this offer, and store the pointers.
+ // First, try to parse DHCPv4 options from the DHCP optional parameters field.
+ //
+ for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
+ Options[Index] = HttpBootParseDhcp4Options (
+ Offer->Dhcp4.Option,
+ GET_OPTION_BUFFER_LEN (Offer),
+ mInterestedDhcp4Tags[Index]
+ );
+ }
+ //
+ // Second, Check if bootfilename and serverhostname is overloaded to carry DHCP options refers to rfc-2132.
+ // If yes, try to parse options from the BootFileName field, then ServerName field.
+ //
+ Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD];
+ if (Option != NULL) {
+ if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_FILE) != 0) {
+ FileFieldOverloaded = TRUE;
+ for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
+ if (Options[Index] == NULL) {
+ Options[Index] = HttpBootParseDhcp4Options (
+ (UINT8 *) Offer->Dhcp4.Header.BootFileName,
+ sizeof (Offer->Dhcp4.Header.BootFileName),
+ mInterestedDhcp4Tags[Index]
+ );
+ }
+ }
+ }
+ if ((Option->Data[0] & HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME) != 0) {
+ for (Index = 0; Index < HTTP_BOOT_DHCP4_TAG_INDEX_MAX; Index++) {
+ if (Options[Index] == NULL) {
+ Options[Index] = HttpBootParseDhcp4Options (
+ (UINT8 *) Offer->Dhcp4.Header.ServerName,
+ sizeof (Offer->Dhcp4.Header.ServerName),
+ mInterestedDhcp4Tags[Index]
+ );
+ }
+ }
+ }
+ }
+
+ //
+ // The offer with "yiaddr" is a proxy offer.
+ //
+ if (Offer->Dhcp4.Header.YourAddr.Addr[0] == 0) {
+ IsProxyOffer = TRUE;
+ }
+
+ //
+ // The offer with "HTTPClient" is a Http offer.
+ //
+ Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID];
+ if ((Option != NULL) && (Option->Length >= 10) &&
+ (CompareMem (Option->Data, DEFAULT_CLASS_ID_DATA, 10) == 0)) {
+ IsHttpOffer = TRUE;
+ }
+
+ //
+ // The offer with Domain Server is a DNS offer.
+ //
+ Option = Options[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
+ if (Option != NULL) {
+ IsDnsOffer = TRUE;
+ }
+
+ //
+ // Parse boot file name:
+ // Boot URI information is provided thru 'file' field in DHCP Header or option 67.
+ // According to RFC 2132, boot file name should be read from DHCP option 67 (bootfile name) if present.
+ // Otherwise, read from boot file field in DHCP header.
+ //
+ if (Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] != NULL) {
+ //
+ // RFC 2132, Section 9.5 does not strictly state Bootfile name (option 67) is null
+ // terminated string. So force to append null terminated character at the end of string.
+ //
+ Ptr8 = (UINT8*)&Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data[0];
+ Ptr8 += Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Length;
+ if (*(Ptr8 - 1) != '\0') {
+ *Ptr8 = '\0';
+ }
+ } else if (!FileFieldOverloaded && Offer->Dhcp4.Header.BootFileName[0] != 0) {
+ //
+ // If the bootfile is not present and bootfilename is present in DHCPv4 packet, just parse it.
+ // Do not count dhcp option header here, or else will destroy the serverhostname.
+ //
+ Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] = (EFI_DHCP4_PACKET_OPTION *)
+ (&Offer->Dhcp4.Header.BootFileName[0] -
+ OFFSET_OF (EFI_DHCP4_PACKET_OPTION, Data[0]));
+ }
+
+ //
+ // Http offer must have a boot URI.
+ //
+ if (IsHttpOffer && Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE] == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Try to retrieve the IP of HTTP server from URI.
+ //
+ if (IsHttpOffer) {
+ Status = HttpParseUrl (
+ (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
+ (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data),
+ FALSE,
+ &Cache4->UriParser
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = HttpUrlGetIp4 (
+ (CHAR8*) Options[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data,
+ Cache4->UriParser,
+ &IpAddr
+ );
+ IpExpressedUri = !EFI_ERROR (Status);
+ }
+
+ //
+ // Determine offer type of the DHCPv4 packet.
+ //
+ if (IsHttpOffer) {
+ if (IpExpressedUri) {
+ if (IsProxyOffer) {
+ OfferType = HttpOfferTypeProxyIpUri;
+ } else {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : HttpOfferTypeDhcpIpUri;
+ }
+ } else {
+ if (!IsProxyOffer) {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;
+ } else {
+ OfferType = HttpOfferTypeProxyNameUri;
+ }
+ }
+
+ } else {
+ if (!IsProxyOffer) {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;
+ } else {
+ if (Cache4->UriParser != NULL) {
+ FreePool (Cache4->UriParser);
+ }
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ Cache4->OfferType = OfferType;
+ return EFI_SUCCESS;
+}
+
+/**
+ Cache all the received DHCPv4 offers, and set OfferIndex and OfferCount.
+
+ @param[in] Private Pointer to HTTP boot driver private data.
+ @param[in] RcvdOffer Pointer to the received offer packet.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval Others Operation failed.
+**/
+EFI_STATUS
+HttpBootCacheDhcp4Offer (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN EFI_DHCP4_PACKET *RcvdOffer
+ )
+{
+ HTTP_BOOT_DHCP4_PACKET_CACHE *Cache4;
+ EFI_DHCP4_PACKET *Offer;
+ HTTP_BOOT_OFFER_TYPE OfferType;
+ EFI_STATUS Status;
+
+ ASSERT (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM);
+ Cache4 = &Private->OfferBuffer[Private->OfferNum].Dhcp4;
+ Offer = &Cache4->Packet.Offer;
+
+ //
+ // Cache the content of DHCPv4 packet firstly.
+ //
+ Status = HttpBootCacheDhcp4Packet (Offer, RcvdOffer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Validate the DHCPv4 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (HttpBootParseDhcp4Packet (Cache4))) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
+ //
+ OfferType = Cache4->OfferType;
+ ASSERT (OfferType < HttpOfferTypeMax);
+ ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+ Private->OfferCount[OfferType]++;
+ Private->OfferNum++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType.
+
+ @param[in] Private Pointer to HTTP boot driver private data.
+
+**/
+VOID
+HttpBootSelectDhcpOffer (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ Private->SelectIndex = 0;
+ Private->SelectProxyType = HttpOfferTypeMax;
+
+ if (Private->FilePathUri != NULL) {
+ //
+ // We are in home environment, the URI is already specified.
+ // Just need to choose a DHCP offer.
+ // The offer with DNS server address takes priority here.
+ //
+ if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1;
+ }
+
+ } else {
+ //
+ // We are in corporate environment.
+ //
+ // Priority1: HttpOfferTypeDhcpIpUri or HttpOfferTypeDhcpIpUriDns
+ // Priority2: HttpOfferTypeDhcpNameUriDns
+ // Priority3: HttpOfferTypeDhcpOnly + HttpOfferTypeProxyIpUri
+ // Priority4: HttpOfferTypeDhcpDns + HttpOfferTypeProxyIpUri
+ // Priority5: HttpOfferTypeDhcpDns + HttpOfferTypeProxyNameUri
+ // Priority6: HttpOfferTypeDhcpDns + HttpOfferTypeDhcpNameUri
+ //
+ if (Private->OfferCount[HttpOfferTypeDhcpIpUri] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUri][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpIpUriDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpIpUriDns][0] + 1;
+
+ }else if (Private->OfferCount[HttpOfferTypeDhcpNameUriDns] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpNameUriDns][0] + 1;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpOnly] > 0 &&
+ Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpOnly][0] + 1;
+ Private->SelectProxyType = HttpOfferTypeProxyIpUri;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
+ Private->OfferCount[HttpOfferTypeProxyIpUri] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+ Private->SelectProxyType = HttpOfferTypeProxyIpUri;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
+ Private->OfferCount[HttpOfferTypeProxyNameUri] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+ Private->SelectProxyType = HttpOfferTypeProxyNameUri;
+
+ } else if (Private->OfferCount[HttpOfferTypeDhcpDns] > 0 &&
+ Private->OfferCount[HttpOfferTypeDhcpNameUri] > 0) {
+
+ Private->SelectIndex = Private->OfferIndex[HttpOfferTypeDhcpDns][0] + 1;
+ Private->SelectProxyType = HttpOfferTypeDhcpNameUri;
+ }
+ }
+}
+
+
+/**
+ EFI_DHCP4_CALLBACK is provided by the consumer of the EFI DHCPv4 Protocol driver
+ to intercept events that occurred in the configuration process.
+
+ @param[in] This Pointer to the EFI DHCPv4 Protocol.
+ @param[in] Context Pointer to the context set by EFI_DHCP4_PROTOCOL.Configure().
+ @param[in] CurrentState The current operational state of the EFI DHCPv4 Protocol driver.
+ @param[in] Dhcp4Event The event that occurs in the current state, which usually means a
+ state transition.
+ @param[in] Packet The DHCPv4 packet that is going to be sent or already received.
+ @param[out] NewPacket The packet that is used to replace the above Packet.
+
+ @retval EFI_SUCCESS Tells the EFI DHCPv4 Protocol driver to continue the DHCP process.
+ @retval EFI_NOT_READY Only used in the Dhcp4Selecting state. The EFI DHCPv4 Protocol
+ driver will continue to wait for more DHCPOFFER packets until the
+ retry timeout expires.
+ @retval EFI_ABORTED Tells the EFI DHCPv4 Protocol driver to abort the current process
+ and return to the Dhcp4Init or Dhcp4InitReboot state.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDhcp4CallBack (
+ IN EFI_DHCP4_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP4_STATE CurrentState,
+ IN EFI_DHCP4_EVENT Dhcp4Event,
+ IN EFI_DHCP4_PACKET *Packet OPTIONAL,
+ OUT EFI_DHCP4_PACKET **NewPacket OPTIONAL
+ )
+{
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ EFI_DHCP4_PACKET_OPTION *MaxMsgSize;
+ UINT16 Value;
+ EFI_STATUS Status;
+ BOOLEAN Received;
+
+ if ((Dhcp4Event != Dhcp4SendDiscover) &&
+ (Dhcp4Event != Dhcp4RcvdOffer) &&
+ (Dhcp4Event != Dhcp4SendRequest) &&
+ (Dhcp4Event != Dhcp4RcvdAck) &&
+ (Dhcp4Event != Dhcp4SelectOffer)) {
+ return EFI_SUCCESS;
+ }
+
+ Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
+
+ //
+ // Override the Maximum DHCP Message Size.
+ //
+ MaxMsgSize = HttpBootParseDhcp4Options (
+ Packet->Dhcp4.Option,
+ GET_OPTION_BUFFER_LEN (Packet),
+ DHCP4_TAG_MAXMSG
+ );
+ if (MaxMsgSize != NULL) {
+ Value = HTONS (HTTP_BOOT_DHCP4_PACKET_MAX_SIZE);
+ CopyMem (MaxMsgSize->Data, &Value, sizeof (Value));
+ }
+
+ //
+ // Callback to user if any packets sent or received.
+ //
+ if (Private->HttpBootCallback != NULL && Dhcp4Event != Dhcp4SelectOffer) {
+ Received = (BOOLEAN) (Dhcp4Event == Dhcp4RcvdOffer || Dhcp4Event == Dhcp4RcvdAck);
+ Status = Private->HttpBootCallback->Callback (
+ Private->HttpBootCallback,
+ HttpBootDhcp4,
+ Received,
+ Packet->Length,
+ &Packet->Dhcp4
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ }
+
+ Status = EFI_SUCCESS;
+ switch (Dhcp4Event) {
+ case Dhcp4RcvdOffer:
+ Status = EFI_NOT_READY;
+ if (Packet->Length > HTTP_BOOT_DHCP4_PACKET_MAX_SIZE) {
+ //
+ // Ignore the incoming packets which exceed the maximum length.
+ //
+ break;
+ }
+ if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {
+ //
+ // Cache the DHCPv4 offers to OfferBuffer[] for select later, and record
+ // the OfferIndex and OfferCount.
+ // If error happens, just ignore this packet and continue to wait more offer.
+ //
+ HttpBootCacheDhcp4Offer (Private, Packet);
+ }
+ break;
+
+ case Dhcp4SelectOffer:
+ //
+ // Select offer according to the priority in UEFI spec, and record the SelectIndex
+ // and SelectProxyType.
+ //
+ HttpBootSelectDhcpOffer (Private);
+
+ if (Private->SelectIndex == 0) {
+ Status = EFI_ABORTED;
+ } else {
+ *NewPacket = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp4.Packet.Offer;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return Status;
+}
+
+/**
+ This function will register the IPv4 gateway address to the network device.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The new IP configuration has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootRegisterIp4Gateway (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+
+ ASSERT (!Private->UsingIpv6);
+
+ Ip4Config2 = Private->Ip4Config2;
+
+ //
+ // Configure the gateway if valid.
+ //
+ if (!EFI_IP4_EQUAL (&Private->GatewayIp, &mZeroIp4Addr)) {
+ Status = Ip4Config2->SetData (
+ Ip4Config2,
+ Ip4Config2DataTypeGateway,
+ sizeof (EFI_IPv4_ADDRESS),
+ &Private->GatewayIp
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function will register the default DNS addresses to the network device.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+ @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes.
+ @param[in] DnsServerData Point a list of DNS server address in an array
+ of EFI_IPv4_ADDRESS instances.
+
+ @retval EFI_SUCCESS The DNS configuration has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootRegisterIp4Dns (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN DataLength,
+ IN VOID *DnsServerData
+ )
+{
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+
+ ASSERT (!Private->UsingIpv6);
+
+ Ip4Config2 = Private->Ip4Config2;
+
+ return Ip4Config2->SetData (
+ Ip4Config2,
+ Ip4Config2DataTypeDnsServer,
+ DataLength,
+ DnsServerData
+ );
+}
+
+
+/**
+ This function will switch the IP4 configuration policy to Static.
+
+ @param[in] Private Pointer to HTTP boot driver private data.
+
+ @retval EFI_SUCCESS The policy is already configured to static.
+ @retval Others Other error as indicated..
+
+**/
+EFI_STATUS
+HttpBootSetIp4Policy (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_IP4_CONFIG2_POLICY Policy;
+ EFI_STATUS Status;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ UINTN DataSize;
+
+ Ip4Config2 = Private->Ip4Config2;
+
+ DataSize = sizeof (EFI_IP4_CONFIG2_POLICY);
+ Status = Ip4Config2->GetData (
+ Ip4Config2,
+ Ip4Config2DataTypePolicy,
+ &DataSize,
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Policy != Ip4Config2PolicyStatic) {
+ Policy = Ip4Config2PolicyStatic;
+ Status= Ip4Config2->SetData (
+ Ip4Config2,
+ Ip4Config2DataTypePolicy,
+ sizeof (EFI_IP4_CONFIG2_POLICY),
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information.
+
+ @param[in] Private Pointer to HTTP boot driver private data.
+
+ @retval EFI_SUCCESS The D.O.R.A process successfully finished.
+ @retval Others Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+HttpBootDhcp4Dora (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ UINT32 OptCount;
+ EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_OPTION_MAX_NUM];
+ UINT8 Buffer[HTTP_BOOT_DHCP4_OPTION_MAX_SIZE];
+ EFI_DHCP4_CONFIG_DATA Config;
+ EFI_STATUS Status;
+ EFI_DHCP4_MODE_DATA Mode;
+
+ Dhcp4 = Private->Dhcp4;
+ ASSERT (Dhcp4 != NULL);
+
+ Status = HttpBootSetIp4Policy (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Build option list for the request packet.
+ //
+ OptCount = HttpBootBuildDhcp4Options (Private, OptList, Buffer);
+ ASSERT (OptCount > 0);
+
+ ZeroMem (&Config, sizeof(Config));
+ Config.OptionCount = OptCount;
+ Config.OptionList = OptList;
+ Config.Dhcp4Callback = HttpBootDhcp4CallBack;
+ Config.CallbackContext = Private;
+ Config.DiscoverTryCount = HTTP_BOOT_DHCP_RETRIES;
+ Config.DiscoverTimeout = mHttpDhcpTimeout;
+
+ //
+ // Configure the DHCPv4 instance for HTTP boot.
+ //
+ Status = Dhcp4->Configure (Dhcp4, &Config);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Initialize the record fields for DHCPv4 offer in private data.
+ //
+ Private->OfferNum = 0;
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+ //
+ // Start DHCPv4 D.O.R.A. process to acquire IPv4 address.
+ //
+ Status = Dhcp4->Start (Dhcp4, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Get the acquired IPv4 address and store them.
+ //
+ Status = Dhcp4->GetModeData (Dhcp4, &Mode);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ ASSERT (Mode.State == Dhcp4Bound);
+ CopyMem (&Private->StationIp, &Mode.ClientAddress, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->SubnetMask, &Mode.SubnetMask, sizeof (EFI_IPv4_ADDRESS));
+ CopyMem (&Private->GatewayIp, &Mode.RouterAddress, sizeof (EFI_IPv4_ADDRESS));
+
+ Status = HttpBootRegisterIp4Gateway (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ AsciiPrint ("\n Station IP address is ");
+ HttpBootShowIp4Addr (&Private->StationIp.v4);
+ AsciiPrint ("\n");
+
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ Dhcp4->Stop (Dhcp4);
+ Dhcp4->Configure (Dhcp4, NULL);
+ } else {
+ ZeroMem (&Config, sizeof (EFI_DHCP4_CONFIG_DATA));
+ Dhcp4->Configure (Dhcp4, &Config);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h
new file mode 100644
index 00000000..9f670ad2
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp4.h
@@ -0,0 +1,253 @@
+/** @file
+ Functions declaration related with DHCPv4 for HTTP boot driver.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+Copyright (c) 2020, Hewlett Packard Enterprise Development LP. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_UEFI_HTTP_BOOT_DHCP4_H__
+#define __EFI_UEFI_HTTP_BOOT_DHCP4_H__
+
+#define HTTP_BOOT_DHCP4_OPTION_MAX_NUM 16
+#define HTTP_BOOT_DHCP4_OPTION_MAX_SIZE 312
+#define HTTP_BOOT_DHCP4_PACKET_MAX_SIZE 1472
+
+#define HTTP_BOOT_DHCP4_OPCODE_REQUEST 1
+#define HTTP_BOOT_DHCP4_OPCODE_REPLY 2
+#define HTTP_BOOT_DHCP4_MSG_TYPE_REQUEST 3
+#define HTTP_BOOT_DHCP4_MAGIC 0x63538263 // network byte order
+
+#define HTTP_BOOT_DHCP4_OVERLOAD_FILE 1
+#define HTTP_BOOT_DHCP4_OVERLOAD_SERVER_NAME 2
+
+///
+/// HTTP Tag definition that identifies the processor
+/// and programming environment of the client system.
+/// These identifiers are defined by IETF:
+/// http://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml
+///
+#if defined (MDE_CPU_IA32)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_IA32
+#elif defined (MDE_CPU_X64)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_X64
+#elif defined (MDE_CPU_ARM)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_ARM
+#elif defined (MDE_CPU_AARCH64)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_AARCH64
+#elif defined (MDE_CPU_RISCV64)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_RISCV64
+#elif defined (MDE_CPU_EBC)
+#define EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE HTTP_CLIENT_ARCH_EBC
+#endif
+
+/// DHCP offer types among HTTP boot.
+/// Dhcp4 and Dhcp6 share this definition, and corresponding
+/// relationship is as follows:
+/// Dhcp4Discover <> Dhcp6Solicit
+/// Dhcp4Offer <> Dhcp6Advertise
+/// Dhcp4Request <> Dhcp6Request
+/// Dhcp4Ack <> DHcp6Reply
+///
+typedef enum {
+ //
+ // <IP address, IP expressed URI>
+ //
+ HttpOfferTypeDhcpIpUri,
+ //
+ // <IP address, IP expressed URI, Name-server>
+ //
+ HttpOfferTypeDhcpIpUriDns,
+ //
+ // <IP address, Domain-name expressed URI, Name-server>
+ //
+ HttpOfferTypeDhcpNameUriDns,
+ //
+ // <IP address, Name-server>
+ //
+ HttpOfferTypeDhcpDns,
+ //
+ // <IP address>
+ //
+ HttpOfferTypeDhcpOnly,
+ //
+ // <Domain-name expressed URI> or
+ // <Domain-name expressed URI, Name-server (will be ignored)>
+ //
+ HttpOfferTypeProxyNameUri,
+ //
+ // <IP expressed URI> or
+ // <IP expressed URI, Name-server (will be ignored)>
+ //
+ HttpOfferTypeProxyIpUri,
+ //
+ // <IP address, Domain-name expressed URI>
+ //
+ HttpOfferTypeDhcpNameUri,
+ HttpOfferTypeMax
+} HTTP_BOOT_OFFER_TYPE;
+
+#define HTTP_BOOT_DHCP_RETRIES 4
+#define HTTP_BOOT_OFFER_MAX_NUM 16
+
+// The array index of the DHCP4 option tag interested
+//
+#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE_LEN 0
+#define HTTP_BOOT_DHCP4_TAG_INDEX_OVERLOAD 1
+#define HTTP_BOOT_DHCP4_TAG_INDEX_MSG_TYPE 2
+#define HTTP_BOOT_DHCP4_TAG_INDEX_SERVER_ID 3
+#define HTTP_BOOT_DHCP4_TAG_INDEX_CLASS_ID 4
+#define HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE 5
+#define HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER 6
+#define HTTP_BOOT_DHCP4_TAG_INDEX_MAX 7
+
+#pragma pack(1)
+
+typedef struct {
+ UINT8 ParaList[135];
+} HTTP_BOOT_DHCP4_OPTION_PARA;
+
+typedef struct {
+ UINT16 Size;
+} HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 MajorVer;
+ UINT8 MinorVer;
+} HTTP_BOOT_DHCP4_OPTION_UNDI;
+
+typedef struct {
+ UINT8 Type;
+} HTTP_BOOT_DHCP4_OPTION_MESG;
+
+typedef struct {
+ UINT16 Type;
+} HTTP_BOOT_DHCP4_OPTION_ARCH;
+
+typedef struct {
+ UINT8 ClassIdentifier[11];
+ UINT8 ArchitecturePrefix[5];
+ UINT8 ArchitectureType[5];
+ UINT8 Lit3[1];
+ UINT8 InterfaceName[4];
+ UINT8 Lit4[1];
+ UINT8 UndiMajor[3];
+ UINT8 UndiMinor[3];
+} HTTP_BOOT_DHCP4_OPTION_CLID;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 Guid[16];
+} HTTP_BOOT_DHCP4_OPTION_UUID;
+
+typedef struct {
+ UINT16 Type;
+ UINT16 Layer;
+} HTTP_BOOT_OPTION_BOOT_ITEM;
+
+#pragma pack()
+
+typedef union {
+ HTTP_BOOT_DHCP4_OPTION_PARA *Para;
+ HTTP_BOOT_DHCP4_OPTION_UNDI *Undi;
+ HTTP_BOOT_DHCP4_OPTION_ARCH *Arch;
+ HTTP_BOOT_DHCP4_OPTION_CLID *Clid;
+ HTTP_BOOT_DHCP4_OPTION_UUID *Uuid;
+ HTTP_BOOT_DHCP4_OPTION_MESG *Mesg;
+ HTTP_BOOT_DHCP4_OPTION_MAX_MESG_SIZE *MaxMesgSize;
+} HTTP_BOOT_DHCP4_OPTION_ENTRY;
+
+#define GET_NEXT_DHCP_OPTION(Opt) \
+ (EFI_DHCP4_PACKET_OPTION *) ((UINT8 *) (Opt) + \
+ sizeof (EFI_DHCP4_PACKET_OPTION) + (Opt)->Length - 1)
+
+#define GET_OPTION_BUFFER_LEN(Pkt) \
+ ((Pkt)->Length - sizeof (EFI_DHCP4_HEADER) - 4)
+
+#define DEFAULT_CLASS_ID_DATA "HTTPClient:Arch:xxxxx:UNDI:003000"
+#define DEFAULT_UNDI_TYPE 1
+#define DEFAULT_UNDI_MAJOR 3
+#define DEFAULT_UNDI_MINOR 0
+
+typedef struct {
+ UINT32 Reserved;
+} HTTP_BOOT_VENDOR_OPTION;
+
+#define HTTP_CACHED_DHCP4_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP4_PACKET, Dhcp4) + HTTP_BOOT_DHCP4_PACKET_MAX_SIZE)
+
+typedef union {
+ EFI_DHCP4_PACKET Offer;
+ EFI_DHCP4_PACKET Ack;
+ UINT8 Buffer[HTTP_CACHED_DHCP4_PACKET_MAX_SIZE];
+} HTTP_BOOT_DHCP4_PACKET;
+
+typedef struct {
+ //
+ // URI component
+ //
+ CHAR8 *Scheme;
+ CHAR8 *Authority;
+ CHAR8 *Path;
+ CHAR8 *Query;
+ CHAR8 *Fragment; /// TODO: may not required in HTTP URL
+
+ CHAR8 *RegName; /// Point to somewhere in Authority
+ BOOLEAN AddrIsOk;
+ EFI_IP_ADDRESS Address;
+ UINT16 Port;
+} HTTP_BOOT_URI_CONTENT;
+
+typedef struct {
+ HTTP_BOOT_DHCP4_PACKET Packet;
+ HTTP_BOOT_OFFER_TYPE OfferType;
+ VOID *UriParser;
+ EFI_DHCP4_PACKET_OPTION *OptList[HTTP_BOOT_DHCP4_TAG_INDEX_MAX];
+} HTTP_BOOT_DHCP4_PACKET_CACHE;
+
+/**
+ Select an DHCPv4 or DHCP6 offer, and record SelectIndex and SelectProxyType.
+
+ @param[in] Private Pointer to HTTP boot driver private data.
+
+**/
+VOID
+HttpBootSelectDhcpOffer (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ Start the D.O.R.A DHCPv4 process to acquire the IPv4 address and other Http boot information.
+
+ @param[in] Private Pointer to HTTP_BOOT private data.
+
+ @retval EFI_SUCCESS The D.O.R.A process successfully finished.
+ @retval Others Failed to finish the D.O.R.A process.
+
+**/
+EFI_STATUS
+HttpBootDhcp4Dora (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ This function will register the default DNS addresses to the network device.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+ @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes.
+ @param[in] DnsServerData Point a list of DNS server address in an array
+ of EFI_IPv4_ADDRESS instances.
+
+ @retval EFI_SUCCESS The DNS configuration has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootRegisterIp4Dns (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN DataLength,
+ IN VOID *DnsServerData
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c
new file mode 100644
index 00000000..b5e8ce7c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.c
@@ -0,0 +1,1032 @@
+/** @file
+ Functions implementation related with DHCPv6 for HTTP boot driver.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+
+/**
+ Build the options buffer for the DHCPv6 request packet.
+
+ @param[in] Private The pointer to HTTP BOOT driver private data.
+ @param[out] OptList The pointer to the option pointer array.
+ @param[in] Buffer The pointer to the buffer to contain the option list.
+
+ @return Index The count of the built-in options.
+
+**/
+UINT32
+HttpBootBuildDhcp6Options (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ OUT EFI_DHCP6_PACKET_OPTION **OptList,
+ IN UINT8 *Buffer
+ )
+{
+ HTTP_BOOT_DHCP6_OPTION_ENTRY OptEnt;
+ UINT16 Value;
+ UINT32 Index;
+
+ Index = 0;
+ OptList[0] = (EFI_DHCP6_PACKET_OPTION *) Buffer;
+
+ //
+ // Append client option request option
+ //
+ OptList[Index]->OpCode = HTONS (DHCP6_OPT_ORO);
+ OptList[Index]->OpLen = HTONS (8);
+ OptEnt.Oro = (HTTP_BOOT_DHCP6_OPTION_ORO *) OptList[Index]->Data;
+ OptEnt.Oro->OpCode[0] = HTONS(DHCP6_OPT_BOOT_FILE_URL);
+ OptEnt.Oro->OpCode[1] = HTONS(DHCP6_OPT_BOOT_FILE_PARAM);
+ OptEnt.Oro->OpCode[2] = HTONS(DHCP6_OPT_DNS_SERVERS);
+ OptEnt.Oro->OpCode[3] = HTONS(DHCP6_OPT_VENDOR_CLASS);
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client network device interface option
+ //
+ OptList[Index]->OpCode = HTONS (DHCP6_OPT_UNDI);
+ OptList[Index]->OpLen = HTONS ((UINT16)3);
+ OptEnt.Undi = (HTTP_BOOT_DHCP6_OPTION_UNDI *) OptList[Index]->Data;
+
+ if (Private->Nii != NULL) {
+ OptEnt.Undi->Type = Private->Nii->Type;
+ OptEnt.Undi->MajorVer = Private->Nii->MajorVer;
+ OptEnt.Undi->MinorVer = Private->Nii->MinorVer;
+ } else {
+ OptEnt.Undi->Type = DEFAULT_UNDI_TYPE;
+ OptEnt.Undi->MajorVer = DEFAULT_UNDI_MAJOR;
+ OptEnt.Undi->MinorVer = DEFAULT_UNDI_MINOR;
+ }
+
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append client system architecture option
+ //
+ OptList[Index]->OpCode = HTONS (DHCP6_OPT_ARCH);
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_ARCH));
+ OptEnt.Arch = (HTTP_BOOT_DHCP6_OPTION_ARCH *) OptList[Index]->Data;
+ Value = HTONS (EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE);
+ CopyMem (&OptEnt.Arch->Type, &Value, sizeof (UINT16));
+ Index++;
+ OptList[Index] = GET_NEXT_DHCP6_OPTION (OptList[Index - 1]);
+
+ //
+ // Append vendor class identify option.
+ //
+ OptList[Index]->OpCode = HTONS (DHCP6_OPT_VENDOR_CLASS);
+ OptList[Index]->OpLen = HTONS ((UINT16) sizeof (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS));
+ OptEnt.VendorClass = (HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *) OptList[Index]->Data;
+ OptEnt.VendorClass->Vendor = HTONL (HTTP_BOOT_DHCP6_ENTERPRISE_NUM);
+ OptEnt.VendorClass->ClassLen = HTONS ((UINT16) sizeof (HTTP_BOOT_CLASS_ID));
+ CopyMem (
+ &OptEnt.VendorClass->ClassId,
+ DEFAULT_CLASS_ID_DATA,
+ sizeof (HTTP_BOOT_CLASS_ID)
+ );
+ HttpBootUintnToAscDecWithFormat (
+ EFI_HTTP_BOOT_CLIENT_SYSTEM_ARCHITECTURE,
+ OptEnt.VendorClass->ClassId.ArchitectureType,
+ sizeof (OptEnt.VendorClass->ClassId.ArchitectureType)
+ );
+
+ if (Private->Nii != NULL) {
+ CopyMem (
+ OptEnt.VendorClass->ClassId.InterfaceName,
+ Private->Nii->StringId,
+ sizeof (OptEnt.VendorClass->ClassId.InterfaceName)
+ );
+ HttpBootUintnToAscDecWithFormat (
+ Private->Nii->MajorVer,
+ OptEnt.VendorClass->ClassId.UndiMajor,
+ sizeof (OptEnt.VendorClass->ClassId.UndiMajor)
+ );
+ HttpBootUintnToAscDecWithFormat (
+ Private->Nii->MinorVer,
+ OptEnt.VendorClass->ClassId.UndiMinor,
+ sizeof (OptEnt.VendorClass->ClassId.UndiMinor)
+ );
+ }
+
+ Index++;
+
+ return Index;
+}
+
+/**
+ Parse out a DHCPv6 option by OptTag, and find the position in buffer.
+
+ @param[in] Buffer The pointer to the option buffer.
+ @param[in] Length Length of the option buffer.
+ @param[in] OptTag The required option tag.
+
+ @retval NULL Failed to parse the required option.
+ @retval Others The position of the required option in buffer.
+
+**/
+EFI_DHCP6_PACKET_OPTION *
+HttpBootParseDhcp6Options (
+ IN UINT8 *Buffer,
+ IN UINT32 Length,
+ IN UINT16 OptTag
+ )
+{
+ EFI_DHCP6_PACKET_OPTION *Option;
+ UINT32 Offset;
+
+ Option = (EFI_DHCP6_PACKET_OPTION *) Buffer;
+ Offset = 0;
+
+ //
+ // OpLen and OpCode here are both stored in network order.
+ //
+ while (Offset < Length) {
+
+ if (NTOHS (Option->OpCode) == OptTag) {
+
+ return Option;
+ }
+
+ Offset += (NTOHS(Option->OpLen) + 4);
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Buffer + Offset);
+ }
+
+ return NULL;
+
+}
+
+/**
+ Parse the cached DHCPv6 packet, including all the options.
+
+ @param[in] Cache6 The pointer to a cached DHCPv6 packet.
+
+ @retval EFI_SUCCESS Parsed the DHCPv6 packet successfully.
+ @retval EFI_DEVICE_ERROR Failed to parse and invalid the packet.
+
+**/
+EFI_STATUS
+HttpBootParseDhcp6Packet (
+ IN HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6
+ )
+{
+ EFI_DHCP6_PACKET *Offer;
+ EFI_DHCP6_PACKET_OPTION **Options;
+ EFI_DHCP6_PACKET_OPTION *Option;
+ HTTP_BOOT_OFFER_TYPE OfferType;
+ EFI_IPv6_ADDRESS IpAddr;
+ BOOLEAN IsProxyOffer;
+ BOOLEAN IsHttpOffer;
+ BOOLEAN IsDnsOffer;
+ BOOLEAN IpExpressedUri;
+ EFI_STATUS Status;
+ UINT32 Offset;
+ UINT32 Length;
+
+ IsDnsOffer = FALSE;
+ IpExpressedUri = FALSE;
+ IsProxyOffer = TRUE;
+ IsHttpOffer = FALSE;
+ Offer = &Cache6->Packet.Offer;
+ Options = Cache6->OptList;
+
+ ZeroMem (Cache6->OptList, sizeof (Cache6->OptList));
+
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option);
+ Offset = 0;
+ Length = GET_DHCP6_OPTION_SIZE (Offer);
+
+ //
+ // OpLen and OpCode here are both stored in network order, since they are from original packet.
+ //
+ while (Offset < Length) {
+
+ if (NTOHS (Option->OpCode) == DHCP6_OPT_IA_NA) {
+ Options[HTTP_BOOT_DHCP6_IDX_IA_NA] = Option;
+ } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_URL) {
+ //
+ // The server sends this option to inform the client about an URL to a boot file.
+ //
+ Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] = Option;
+ } else if (NTOHS (Option->OpCode) == DHCP6_OPT_BOOT_FILE_PARAM) {
+ Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM] = Option;
+ } else if (NTOHS (Option->OpCode) == DHCP6_OPT_VENDOR_CLASS) {
+ Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS] = Option;
+ } else if (NTOHS (Option->OpCode) == DHCP6_OPT_DNS_SERVERS) {
+ Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER] = Option;
+ }
+
+ Offset += (NTOHS (Option->OpLen) + 4);
+ Option = (EFI_DHCP6_PACKET_OPTION *) (Offer->Dhcp6.Option + Offset);
+ }
+ //
+ // The offer with assigned client address is NOT a proxy offer.
+ // An ia_na option, embedded with valid ia_addr option and a status_code of success.
+ //
+ Option = Options[HTTP_BOOT_DHCP6_IDX_IA_NA];
+ if (Option != NULL) {
+ Option = HttpBootParseDhcp6Options (
+ Option->Data + 12,
+ NTOHS (Option->OpLen),
+ DHCP6_OPT_STATUS_CODE
+ );
+ if ((Option != NULL && Option->Data[0] == 0) || (Option == NULL)) {
+ IsProxyOffer = FALSE;
+ }
+ }
+
+ //
+ // The offer with "HTTPClient" is a Http offer.
+ //
+ Option = Options[HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS];
+
+ if (Option != NULL &&
+ NTOHS(Option->OpLen) >= 16 &&
+ CompareMem ((Option->Data + 6), DEFAULT_CLASS_ID_DATA, 10) == 0) {
+ IsHttpOffer = TRUE;
+ }
+
+ //
+ // The offer with Domain Server is a DNS offer.
+ //
+ Option = Options[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];
+ if (Option != NULL) {
+ IsDnsOffer = TRUE;
+ }
+
+ //
+ // Http offer must have a boot URI.
+ //
+ if (IsHttpOffer && Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL] == NULL) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Try to retrieve the IP of HTTP server from URI.
+ //
+ if (IsHttpOffer) {
+ Status = HttpParseUrl (
+ (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,
+ (UINT32) AsciiStrLen ((CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data),
+ FALSE,
+ &Cache6->UriParser
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = HttpUrlGetIp6 (
+ (CHAR8*) Options[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data,
+ Cache6->UriParser,
+ &IpAddr
+ );
+ IpExpressedUri = !EFI_ERROR (Status);
+ }
+
+ //
+ // Determine offer type of the DHCPv6 packet.
+ //
+ if (IsHttpOffer) {
+ if (IpExpressedUri) {
+ if (IsProxyOffer) {
+ OfferType = HttpOfferTypeProxyIpUri;
+ } else {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpIpUriDns : HttpOfferTypeDhcpIpUri;
+ }
+ } else {
+ if (!IsProxyOffer) {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpNameUriDns : HttpOfferTypeDhcpNameUri;
+ } else {
+ OfferType = HttpOfferTypeProxyNameUri;
+ }
+ }
+
+ } else {
+ if (!IsProxyOffer) {
+ OfferType = IsDnsOffer ? HttpOfferTypeDhcpDns : HttpOfferTypeDhcpOnly;
+ } else {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ Cache6->OfferType = OfferType;
+ return EFI_SUCCESS;
+}
+
+/**
+ Cache the DHCPv6 packet.
+
+ @param[in] Dst The pointer to the cache buffer for DHCPv6 packet.
+ @param[in] Src The pointer to the DHCPv6 packet to be cached.
+
+ @retval EFI_SUCCESS Packet is copied.
+ @retval EFI_BUFFER_TOO_SMALL Cache buffer is not big enough to hold the packet.
+
+**/
+EFI_STATUS
+HttpBootCacheDhcp6Packet (
+ IN EFI_DHCP6_PACKET *Dst,
+ IN EFI_DHCP6_PACKET *Src
+ )
+{
+ if (Dst->Size < Src->Length) {
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ CopyMem (&Dst->Dhcp6, &Src->Dhcp6, Src->Length);
+ Dst->Length = Src->Length;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Cache all the received DHCPv6 offers, and set OfferIndex and OfferCount.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+ @param[in] RcvdOffer The pointer to the received offer packet.
+
+ @retval EFI_SUCCESS Cache and parse the packet successfully.
+ @retval Others Operation failed.
+
+**/
+EFI_STATUS
+HttpBootCacheDhcp6Offer (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN EFI_DHCP6_PACKET *RcvdOffer
+ )
+{
+ HTTP_BOOT_DHCP6_PACKET_CACHE *Cache6;
+ EFI_DHCP6_PACKET *Offer;
+ HTTP_BOOT_OFFER_TYPE OfferType;
+ EFI_STATUS Status;
+
+ Cache6 = &Private->OfferBuffer[Private->OfferNum].Dhcp6;
+ Offer = &Cache6->Packet.Offer;
+
+ //
+ // Cache the content of DHCPv6 packet firstly.
+ //
+ Status = HttpBootCacheDhcp6Packet(Offer, RcvdOffer);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Validate the DHCPv6 packet, and parse the options and offer type.
+ //
+ if (EFI_ERROR (HttpBootParseDhcp6Packet (Cache6))) {
+ return EFI_ABORTED;
+ }
+
+ //
+ // Determine whether cache the current offer by type, and record OfferIndex and OfferCount.
+ //
+ OfferType = Cache6->OfferType;
+ ASSERT (OfferType < HttpOfferTypeMax);
+ ASSERT (Private->OfferCount[OfferType] < HTTP_BOOT_OFFER_MAX_NUM);
+ Private->OfferIndex[OfferType][Private->OfferCount[OfferType]] = Private->OfferNum;
+ Private->OfferCount[OfferType]++;
+ Private->OfferNum++;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ EFI_DHCP6_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol driver
+ to intercept events that occurred in the configuration process.
+
+ @param[in] This The pointer to the EFI DHCPv6 Protocol.
+ @param[in] Context The pointer to the context set by EFI_DHCP6_PROTOCOL.Configure().
+ @param[in] CurrentState The current operational state of the EFI DHCPv Protocol driver.
+ @param[in] Dhcp6Event The event that occurs in the current state, which usually means a
+ state transition.
+ @param[in] Packet The DHCPv6 packet that is going to be sent or was already received.
+ @param[out] NewPacket The packet that is used to replace the Packet above.
+
+ @retval EFI_SUCCESS Told the EFI DHCPv6 Protocol driver to continue the DHCP process.
+ @retval EFI_NOT_READY Only used in the Dhcp6Selecting state. The EFI DHCPv6 Protocol
+ driver will continue to wait for more packets.
+ @retval EFI_ABORTED Told the EFI DHCPv6 Protocol driver to abort the current process.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDhcp6CallBack (
+ IN EFI_DHCP6_PROTOCOL *This,
+ IN VOID *Context,
+ IN EFI_DHCP6_STATE CurrentState,
+ IN EFI_DHCP6_EVENT Dhcp6Event,
+ IN EFI_DHCP6_PACKET *Packet,
+ OUT EFI_DHCP6_PACKET **NewPacket OPTIONAL
+ )
+{
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ EFI_DHCP6_PACKET *SelectAd;
+ EFI_STATUS Status;
+ BOOLEAN Received;
+
+ if ((Dhcp6Event != Dhcp6SendSolicit) &&
+ (Dhcp6Event != Dhcp6RcvdAdvertise) &&
+ (Dhcp6Event != Dhcp6SendRequest) &&
+ (Dhcp6Event != Dhcp6RcvdReply) &&
+ (Dhcp6Event != Dhcp6SelectAdvertise)) {
+ return EFI_SUCCESS;
+ }
+
+ ASSERT (Packet != NULL);
+
+ Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
+ Status = EFI_SUCCESS;
+ if (Private->HttpBootCallback != NULL && Dhcp6Event != Dhcp6SelectAdvertise) {
+ Received = (BOOLEAN) (Dhcp6Event == Dhcp6RcvdAdvertise || Dhcp6Event == Dhcp6RcvdReply);
+ Status = Private->HttpBootCallback->Callback (
+ Private->HttpBootCallback,
+ HttpBootDhcp6,
+ Received,
+ Packet->Length,
+ &Packet->Dhcp6
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_ABORTED;
+ }
+ }
+ switch (Dhcp6Event) {
+
+ case Dhcp6RcvdAdvertise:
+ Status = EFI_NOT_READY;
+ if (Packet->Length > HTTP_BOOT_DHCP6_PACKET_MAX_SIZE) {
+ //
+ // Ignore the incoming packets which exceed the maximum length.
+ //
+ break;
+ }
+ if (Private->OfferNum < HTTP_BOOT_OFFER_MAX_NUM) {
+ //
+ // Cache the dhcp offers to OfferBuffer[] for select later, and record
+ // the OfferIndex and OfferCount.
+ // If error happens, just ignore this packet and continue to wait more offer.
+ //
+ HttpBootCacheDhcp6Offer (Private, Packet);
+ }
+ break;
+
+ case Dhcp6SelectAdvertise:
+ //
+ // Select offer by the default policy or by order, and record the SelectIndex
+ // and SelectProxyType.
+ //
+ HttpBootSelectDhcpOffer (Private);
+
+ if (Private->SelectIndex == 0) {
+ Status = EFI_ABORTED;
+ } else {
+ ASSERT (NewPacket != NULL);
+ SelectAd = &Private->OfferBuffer[Private->SelectIndex - 1].Dhcp6.Packet.Offer;
+ *NewPacket = AllocateZeroPool (SelectAd->Size);
+ if (*NewPacket == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (*NewPacket, SelectAd, SelectAd->Size);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return Status;
+}
+
+/**
+ Check whether IP driver could route the message which will be sent to ServerIp address.
+
+ This function will check the IP6 route table every 1 seconds until specified timeout is expired, if a valid
+ route is found in IP6 route table, the address will be filed in GatewayAddr and return.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+ @param[in] TimeOutInSecond Timeout value in seconds.
+ @param[out] GatewayAddr Pointer to store the gateway IP address.
+
+ @retval EFI_SUCCESS Found a valid gateway address successfully.
+ @retval EFI_TIMEOUT The operation is time out.
+ @retval Other Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootCheckRouteTable (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN TimeOutInSecond,
+ OUT EFI_IPv6_ADDRESS *GatewayAddr
+ )
+{
+ EFI_STATUS Status;
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_IP6_MODE_DATA Ip6ModeData;
+ UINTN Index;
+ EFI_EVENT TimeOutEvt;
+ UINTN RetryCount;
+ BOOLEAN GatewayIsFound;
+
+ ASSERT (GatewayAddr != NULL);
+ ASSERT (Private != NULL);
+
+ Ip6 = Private->Ip6;
+ GatewayIsFound = FALSE;
+ RetryCount = 0;
+ TimeOutEvt = NULL;
+ Status = EFI_SUCCESS;
+ ZeroMem (GatewayAddr, sizeof (EFI_IPv6_ADDRESS));
+
+ while (TRUE) {
+ Status = Ip6->GetModeData (Ip6, &Ip6ModeData, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Find out the gateway address which can route the message which send to ServerIp.
+ //
+ for (Index = 0; Index < Ip6ModeData.RouteCount; Index++) {
+ if (NetIp6IsNetEqual (&Private->ServerIp.v6, &Ip6ModeData.RouteTable[Index].Destination, Ip6ModeData.RouteTable[Index].PrefixLength)) {
+ IP6_COPY_ADDRESS (GatewayAddr, &Ip6ModeData.RouteTable[Index].Gateway);
+ GatewayIsFound = TRUE;
+ break;
+ }
+ }
+
+ if (Ip6ModeData.AddressList != NULL) {
+ FreePool (Ip6ModeData.AddressList);
+ }
+ if (Ip6ModeData.GroupTable != NULL) {
+ FreePool (Ip6ModeData.GroupTable);
+ }
+ if (Ip6ModeData.RouteTable != NULL) {
+ FreePool (Ip6ModeData.RouteTable);
+ }
+ if (Ip6ModeData.NeighborCache != NULL) {
+ FreePool (Ip6ModeData.NeighborCache);
+ }
+ if (Ip6ModeData.PrefixTable != NULL) {
+ FreePool (Ip6ModeData.PrefixTable);
+ }
+ if (Ip6ModeData.IcmpTypeList != NULL) {
+ FreePool (Ip6ModeData.IcmpTypeList);
+ }
+
+ if (GatewayIsFound || RetryCount == TimeOutInSecond) {
+ break;
+ }
+
+ RetryCount++;
+
+ //
+ // Delay 1 second then recheck it again.
+ //
+ if (TimeOutEvt == NULL) {
+ Status = gBS->CreateEvent (
+ EVT_TIMER,
+ TPL_CALLBACK,
+ NULL,
+ NULL,
+ &TimeOutEvt
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ Status = gBS->SetTimer (TimeOutEvt, TimerRelative, TICKS_PER_SECOND);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ while (EFI_ERROR (gBS->CheckEvent (TimeOutEvt))) {
+ Ip6->Poll (Ip6);
+ }
+ }
+
+ON_EXIT:
+ if (TimeOutEvt != NULL) {
+ gBS->CloseEvent (TimeOutEvt);
+ }
+
+ if (GatewayIsFound) {
+ Status = EFI_SUCCESS;
+ } else if (RetryCount == TimeOutInSecond) {
+ Status = EFI_TIMEOUT;
+ }
+
+ return Status;
+}
+
+/**
+ Set the IP6 policy to Automatic.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS Switch the IP policy successfully.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Policy (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_IP6_CONFIG_POLICY Policy;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ EFI_STATUS Status;
+ UINTN DataSize;
+
+ Ip6Config = Private->Ip6Config;
+ DataSize = sizeof (EFI_IP6_CONFIG_POLICY);
+
+ //
+ // Get and store the current policy of IP6 driver.
+ //
+ Status = Ip6Config->GetData (
+ Ip6Config,
+ Ip6ConfigDataTypePolicy,
+ &DataSize,
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (Policy == Ip6ConfigPolicyManual) {
+ Policy = Ip6ConfigPolicyAutomatic;
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypePolicy,
+ sizeof(EFI_IP6_CONFIG_POLICY),
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ return EFI_SUCCESS;
+}
+
+/**
+ This function will register the default DNS addresses to the network device.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+ @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes.
+ @param[in] DnsServerData Point a list of DNS server address in an array
+ of EFI_IPv6_ADDRESS instances.
+
+ @retval EFI_SUCCESS The DNS configuration has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Dns (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN DataLength,
+ IN VOID *DnsServerData
+ )
+{
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+
+ ASSERT (Private->UsingIpv6);
+
+ Ip6Config = Private->Ip6Config;
+
+ return Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeDnsServer,
+ DataLength,
+ DnsServerData
+ );
+}
+
+/**
+ This function will register the IPv6 gateway address to the network device.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The new IP configuration has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Gateway (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ EFI_STATUS Status;
+
+ ASSERT (Private->UsingIpv6);
+ Ip6Config = Private->Ip6Config;
+
+ //
+ // Set the default gateway address.
+ //
+ if (!Private->NoGateway && !NetIp6IsUnspecifiedAddr (&Private->GatewayIp.v6)) {
+ Status = Ip6Config->SetData (
+ Ip6Config,
+ Ip6ConfigDataTypeGateway,
+ sizeof (EFI_IPv6_ADDRESS),
+ &Private->GatewayIp.v6
+ );
+ if (EFI_ERROR(Status)) {
+ return Status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function will register the station IP address.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The new IP address has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Address (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+)
+{
+ EFI_STATUS Status;
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Cfg;
+ EFI_IP6_CONFIG_POLICY Policy;
+ EFI_IP6_CONFIG_MANUAL_ADDRESS CfgAddr;
+ EFI_IPv6_ADDRESS *Ip6Addr;
+ EFI_IPv6_ADDRESS GatewayAddr;
+ EFI_IP6_CONFIG_DATA Ip6CfgData;
+ EFI_EVENT MappedEvt;
+ UINTN DataSize;
+ BOOLEAN IsAddressOk;
+ UINTN Index;
+
+ ASSERT (Private->UsingIpv6);
+
+ MappedEvt = NULL;
+ IsAddressOk = FALSE;
+ Ip6Addr = NULL;
+ Ip6Cfg = Private->Ip6Config;
+ Ip6 = Private->Ip6;
+
+ ZeroMem (&CfgAddr, sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS));
+ CopyMem (&CfgAddr, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
+ ZeroMem (&Ip6CfgData, sizeof (EFI_IP6_CONFIG_DATA));
+
+ Ip6CfgData.AcceptIcmpErrors = TRUE;
+ Ip6CfgData.DefaultProtocol = IP6_ICMP;
+ Ip6CfgData.HopLimit = HTTP_BOOT_DEFAULT_HOPLIMIT;
+ Ip6CfgData.ReceiveTimeout = HTTP_BOOT_DEFAULT_LIFETIME;
+ Ip6CfgData.TransmitTimeout = HTTP_BOOT_DEFAULT_LIFETIME;
+
+ Status = Ip6->Configure (Ip6, &Ip6CfgData);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Retrieve the gateway address from IP6 route table.
+ //
+ Status = HttpBootCheckRouteTable (Private, HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT, &GatewayAddr);
+ if (EFI_ERROR (Status)) {
+ Private->NoGateway = TRUE;
+ } else {
+ IP6_COPY_ADDRESS (&Private->GatewayIp.v6, &GatewayAddr);
+ }
+
+ //
+ // Set the new address by Ip6ConfigProtocol manually.
+ //
+ Policy = Ip6ConfigPolicyManual;
+ Status = Ip6Cfg->SetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypePolicy,
+ sizeof(EFI_IP6_CONFIG_POLICY),
+ &Policy
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Create a notify event to set address flag when DAD if IP6 driver succeeded.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpBootCommonNotify,
+ &IsAddressOk,
+ &MappedEvt
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Set static host ip6 address. This is a asynchronous process.
+ //
+ Status = Ip6Cfg->RegisterDataNotify (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ MappedEvt
+ );
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+
+ Status = Ip6Cfg->SetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ sizeof (EFI_IP6_CONFIG_MANUAL_ADDRESS),
+ &CfgAddr
+ );
+ if (EFI_ERROR (Status) && Status != EFI_NOT_READY) {
+ goto ON_EXIT;
+ } else if (Status == EFI_NOT_READY) {
+ //
+ // Poll the network until the asynchronous process is finished.
+ //
+ while (!IsAddressOk) {
+ Ip6->Poll (Ip6);
+ }
+ //
+ // Check whether the Ip6 Address setting is successed.
+ //
+ DataSize = 0;
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ &DataSize,
+ NULL
+ );
+ if (Status != EFI_BUFFER_TOO_SMALL || DataSize == 0) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ Ip6Addr = AllocatePool (DataSize);
+ if (Ip6Addr == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Status = Ip6Cfg->GetData (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ &DataSize,
+ (VOID *) Ip6Addr
+ );
+ if (EFI_ERROR (Status)) {
+ Status = EFI_DEVICE_ERROR;
+ goto ON_EXIT;
+ }
+
+ for (Index = 0; Index < DataSize / sizeof (EFI_IPv6_ADDRESS); Index ++) {
+ if (CompareMem (Ip6Addr + Index, &CfgAddr, sizeof (EFI_IPv6_ADDRESS)) == 0) {
+ break;
+ }
+ }
+ if (Index == DataSize / sizeof (EFI_IPv6_ADDRESS)) {
+ Status = EFI_ABORTED;
+ goto ON_EXIT;
+ }
+ }
+
+ON_EXIT:
+ if (MappedEvt != NULL) {
+ Ip6Cfg->UnregisterDataNotify (
+ Ip6Cfg,
+ Ip6ConfigDataTypeManualAddress,
+ MappedEvt
+ );
+ gBS->CloseEvent (MappedEvt);
+ }
+
+ if (Ip6Addr != NULL) {
+ FreePool (Ip6Addr);
+ }
+
+ return Status;
+}
+
+/**
+ Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information.
+
+ @param[in] Private Pointer to HTTP_BOOT private data.
+
+ @retval EFI_SUCCESS The S.A.R.R process successfully finished.
+ @retval Others Failed to finish the S.A.R.R process.
+
+**/
+EFI_STATUS
+HttpBootDhcp6Sarr (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ EFI_DHCP6_CONFIG_DATA Config;
+ EFI_DHCP6_MODE_DATA Mode;
+ EFI_DHCP6_RETRANSMISSION *Retransmit;
+ EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_OPTION_MAX_NUM];
+ UINT32 OptCount;
+ UINT8 Buffer[HTTP_BOOT_DHCP6_OPTION_MAX_SIZE];
+ EFI_STATUS Status;
+
+ Dhcp6 = Private->Dhcp6;
+ ASSERT (Dhcp6 != NULL);
+
+ //
+ // Build options list for the request packet.
+ //
+ OptCount = HttpBootBuildDhcp6Options (Private, OptList, Buffer);
+ ASSERT (OptCount >0);
+
+ Retransmit = AllocateZeroPool (sizeof (EFI_DHCP6_RETRANSMISSION));
+ if (Retransmit == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ ZeroMem (&Mode, sizeof (EFI_DHCP6_MODE_DATA));
+ ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
+
+ Config.OptionCount = OptCount;
+ Config.OptionList = OptList;
+ Config.Dhcp6Callback = HttpBootDhcp6CallBack;
+ Config.CallbackContext = Private;
+ Config.IaInfoEvent = NULL;
+ Config.RapidCommit = FALSE;
+ Config.ReconfigureAccept = FALSE;
+ Config.IaDescriptor.IaId = NET_RANDOM (NetRandomInitSeed ());
+ Config.IaDescriptor.Type = EFI_DHCP6_IA_TYPE_NA;
+ Config.SolicitRetransmission = Retransmit;
+ Retransmit->Irt = 4;
+ Retransmit->Mrc = 4;
+ Retransmit->Mrt = 32;
+ Retransmit->Mrd = 60;
+
+ //
+ // Configure the DHCPv6 instance for HTTP boot.
+ //
+ Status = Dhcp6->Configure (Dhcp6, &Config);
+ FreePool (Retransmit);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ //
+ // Initialize the record fields for DHCPv6 offer in private data.
+ //
+ Private->OfferNum = 0;
+ Private->SelectIndex = 0;
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+ //
+ // Start DHCPv6 S.A.R.R. process to acquire IPv6 address.
+ //
+ Status = Dhcp6->Start (Dhcp6);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ //
+ // Get the acquired IPv6 address and store them.
+ //
+ Status = Dhcp6->GetModeData (Dhcp6, &Mode, NULL);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+
+ ASSERT (Mode.Ia->State == Dhcp6Bound);
+ CopyMem (&Private->StationIp.v6, &Mode.Ia->IaAddress[0].IpAddress, sizeof (EFI_IPv6_ADDRESS));
+
+ AsciiPrint ("\n Station IPv6 address is ");
+ HttpBootShowIp6Addr (&Private->StationIp.v6);
+ AsciiPrint ("\n");
+
+ON_EXIT:
+ if (EFI_ERROR (Status)) {
+ Dhcp6->Stop (Dhcp6);
+ Dhcp6->Configure (Dhcp6, NULL);
+ } else {
+ ZeroMem (&Config, sizeof (EFI_DHCP6_CONFIG_DATA));
+ Dhcp6->Configure (Dhcp6, &Config);
+ if (Mode.ClientId != NULL) {
+ FreePool (Mode.ClientId);
+ }
+ if (Mode.Ia != NULL) {
+ FreePool (Mode.Ia);
+ }
+ }
+
+ return Status;
+
+}
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h
new file mode 100644
index 00000000..2543fced
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDhcp6.h
@@ -0,0 +1,169 @@
+/** @file
+ Functions declaration related with DHCPv6 for HTTP boot driver.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+
+#ifndef __EFI_HTTP_BOOT_DHCP6_H__
+#define __EFI_HTTP_BOOT_DHCP6_H__
+
+#define HTTP_BOOT_OFFER_MAX_NUM 16
+#define HTTP_BOOT_DHCP6_OPTION_MAX_NUM 16
+#define HTTP_BOOT_DHCP6_OPTION_MAX_SIZE 312
+#define HTTP_BOOT_DHCP6_PACKET_MAX_SIZE 1472
+#define HTTP_BOOT_IP6_ROUTE_TABLE_TIMEOUT 10
+#define HTTP_BOOT_DEFAULT_HOPLIMIT 64
+#define HTTP_BOOT_DEFAULT_LIFETIME 50000
+
+#define HTTP_BOOT_DHCP6_ENTERPRISE_NUM 343 // TODO: IANA TBD: temporarily using Intel's
+#define HTTP_BOOT_DHCP6_MAX_BOOT_FILE_SIZE 65535 // It's a limitation of bit length, 65535*512 bytes.
+
+#define HTTP_BOOT_DHCP6_IDX_IA_NA 0
+#define HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL 1
+#define HTTP_BOOT_DHCP6_IDX_BOOT_FILE_PARAM 2
+#define HTTP_BOOT_DHCP6_IDX_VENDOR_CLASS 3
+#define HTTP_BOOT_DHCP6_IDX_DNS_SERVER 4
+#define HTTP_BOOT_DHCP6_IDX_MAX 5
+
+#pragma pack(1)
+typedef struct {
+ UINT16 OpCode[256];
+} HTTP_BOOT_DHCP6_OPTION_ORO;
+
+typedef struct {
+ UINT8 Type;
+ UINT8 MajorVer;
+ UINT8 MinorVer;
+} HTTP_BOOT_DHCP6_OPTION_UNDI;
+
+typedef struct {
+ UINT16 Type;
+} HTTP_BOOT_DHCP6_OPTION_ARCH;
+
+typedef struct {
+ UINT8 ClassIdentifier[11];
+ UINT8 ArchitecturePrefix[5];
+ UINT8 ArchitectureType[5];
+ UINT8 Lit3[1];
+ UINT8 InterfaceName[4];
+ UINT8 Lit4[1];
+ UINT8 UndiMajor[3];
+ UINT8 UndiMinor[3];
+} HTTP_BOOT_CLASS_ID;
+
+typedef struct {
+ UINT32 Vendor;
+ UINT16 ClassLen;
+ HTTP_BOOT_CLASS_ID ClassId;
+} HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS;
+
+#pragma pack()
+
+typedef union {
+ HTTP_BOOT_DHCP6_OPTION_ORO *Oro;
+ HTTP_BOOT_DHCP6_OPTION_UNDI *Undi;
+ HTTP_BOOT_DHCP6_OPTION_ARCH *Arch;
+ HTTP_BOOT_DHCP6_OPTION_VENDOR_CLASS *VendorClass;
+} HTTP_BOOT_DHCP6_OPTION_ENTRY;
+
+#define HTTP_CACHED_DHCP6_PACKET_MAX_SIZE (OFFSET_OF (EFI_DHCP6_PACKET, Dhcp6) + HTTP_BOOT_DHCP6_PACKET_MAX_SIZE)
+
+typedef union {
+ EFI_DHCP6_PACKET Offer;
+ EFI_DHCP6_PACKET Ack;
+ UINT8 Buffer[HTTP_CACHED_DHCP6_PACKET_MAX_SIZE];
+} HTTP_BOOT_DHCP6_PACKET;
+
+typedef struct {
+ HTTP_BOOT_DHCP6_PACKET Packet;
+ HTTP_BOOT_OFFER_TYPE OfferType;
+ EFI_DHCP6_PACKET_OPTION *OptList[HTTP_BOOT_DHCP6_IDX_MAX];
+ VOID *UriParser;
+} HTTP_BOOT_DHCP6_PACKET_CACHE;
+
+#define GET_NEXT_DHCP6_OPTION(Opt) \
+ (EFI_DHCP6_PACKET_OPTION *) ((UINT8 *) (Opt) + \
+ sizeof (EFI_DHCP6_PACKET_OPTION) + (NTOHS ((Opt)->OpLen)) - 1)
+
+#define GET_DHCP6_OPTION_SIZE(Pkt) \
+ ((Pkt)->Length - sizeof (EFI_DHCP6_HEADER))
+
+/**
+ Start the S.A.R.R DHCPv6 process to acquire the IPv6 address and other Http boot information.
+
+ @param[in] Private Pointer to HTTP_BOOT private data.
+
+ @retval EFI_SUCCESS The S.A.R.R process successfully finished.
+ @retval Others Failed to finish the S.A.R.R process.
+
+**/
+EFI_STATUS
+HttpBootDhcp6Sarr (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ Set the IP6 policy to Automatic.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS Switch the IP policy successfully.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Policy (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ This function will register the default DNS addresses to the network device.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+ @param[in] DataLength Size of the buffer pointed to by DnsServerData in bytes.
+ @param[in] DnsServerData Point a list of DNS server address in an array
+ of EFI_IPv6_ADDRESS instances.
+
+ @retval EFI_SUCCESS The DNS configuration has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Dns (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN DataLength,
+ IN VOID *DnsServerData
+ );
+
+/**
+ This function will register the IPv6 gateway address to the network device.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The new IP configuration has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Gateway (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ This function will register the station IP address.
+
+ @param[in] Private The pointer to HTTP_BOOT_PRIVATE_DATA.
+
+ @retval EFI_SUCCESS The new IP address has been configured successfully.
+ @retval Others Failed to configure the address.
+
+**/
+EFI_STATUS
+HttpBootSetIp6Address (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.c
new file mode 100644
index 00000000..fb6b5f62
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.c
@@ -0,0 +1,1332 @@
+/** @file
+ Driver Binding functions implementation for UEFI HTTP boot.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+
+///
+/// Driver Binding Protocol instance
+///
+EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp4DxeDriverBinding = {
+ HttpBootIp4DxeDriverBindingSupported,
+ HttpBootIp4DxeDriverBindingStart,
+ HttpBootIp4DxeDriverBindingStop,
+ HTTP_BOOT_DXE_VERSION,
+ NULL,
+ NULL
+};
+
+EFI_DRIVER_BINDING_PROTOCOL gHttpBootIp6DxeDriverBinding = {
+ HttpBootIp6DxeDriverBindingSupported,
+ HttpBootIp6DxeDriverBindingStart,
+ HttpBootIp6DxeDriverBindingStop,
+ HTTP_BOOT_DXE_VERSION,
+ NULL,
+ NULL
+};
+
+
+
+/**
+ Check whether UNDI protocol supports IPv6.
+
+ @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA.
+ @param[out] Ipv6Support TRUE if UNDI supports IPv6.
+
+ @retval EFI_SUCCESS Get the result whether UNDI supports IPv6 by NII or AIP protocol successfully.
+ @retval EFI_NOT_FOUND Don't know whether UNDI supports IPv6 since NII or AIP is not available.
+
+**/
+EFI_STATUS
+HttpBootCheckIpv6Support (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ OUT BOOLEAN *Ipv6Support
+ )
+{
+ EFI_HANDLE Handle;
+ EFI_ADAPTER_INFORMATION_PROTOCOL *Aip;
+ EFI_STATUS Status;
+ EFI_GUID *InfoTypesBuffer;
+ UINTN InfoTypeBufferCount;
+ UINTN TypeIndex;
+ BOOLEAN Supported;
+ VOID *InfoBlock;
+ UINTN InfoBlockSize;
+
+ ASSERT (Private != NULL && Ipv6Support != NULL);
+
+ //
+ // Check whether the UNDI supports IPv6 by NII protocol.
+ //
+ if (Private->Nii != NULL) {
+ *Ipv6Support = Private->Nii->Ipv6Supported;
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Get the NIC handle by SNP protocol.
+ //
+ Handle = NetLibGetSnpHandle (Private->Controller, NULL);
+ if (Handle == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ Aip = NULL;
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEfiAdapterInformationProtocolGuid,
+ (VOID *) &Aip
+ );
+ if (EFI_ERROR (Status) || Aip == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ InfoTypesBuffer = NULL;
+ InfoTypeBufferCount = 0;
+ Status = Aip->GetSupportedTypes (Aip, &InfoTypesBuffer, &InfoTypeBufferCount);
+ if (EFI_ERROR (Status) || InfoTypesBuffer == NULL) {
+ FreePool (InfoTypesBuffer);
+ return EFI_NOT_FOUND;
+ }
+
+ Supported = FALSE;
+ for (TypeIndex = 0; TypeIndex < InfoTypeBufferCount; TypeIndex++) {
+ if (CompareGuid (&InfoTypesBuffer[TypeIndex], &gEfiAdapterInfoUndiIpv6SupportGuid)) {
+ Supported = TRUE;
+ break;
+ }
+ }
+
+ FreePool (InfoTypesBuffer);
+ if (!Supported) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // We now have adapter information block.
+ //
+ InfoBlock = NULL;
+ InfoBlockSize = 0;
+ Status = Aip->GetInformation (Aip, &gEfiAdapterInfoUndiIpv6SupportGuid, &InfoBlock, &InfoBlockSize);
+ if (EFI_ERROR (Status) || InfoBlock == NULL) {
+ FreePool (InfoBlock);
+ return EFI_NOT_FOUND;
+ }
+
+ *Ipv6Support = ((EFI_ADAPTER_INFO_UNDI_IPV6_SUPPORT *) InfoBlock)->Ipv6Support;
+ FreePool (InfoBlock);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Destroy the HTTP child based on IPv4 stack.
+
+ @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
+ @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA.
+
+**/
+VOID
+HttpBootDestroyIp4Children (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ ASSERT (This != NULL);
+ ASSERT (Private != NULL);
+
+ if (Private->Dhcp4Child != NULL) {
+ gBS->CloseProtocol (
+ Private->Dhcp4Child,
+ &gEfiDhcp4ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ Private->Dhcp4Child
+ );
+ }
+
+ if (Private->Ip6Nic == NULL && Private->HttpCreated) {
+ HttpIoDestroyIo (&Private->HttpIo);
+ Private->HttpCreated = FALSE;
+ }
+
+ if (Private->Ip4Nic != NULL) {
+
+ gBS->CloseProtocol (
+ Private->Controller,
+ &gEfiCallerIdGuid,
+ This->DriverBindingHandle,
+ Private->Ip4Nic->Controller
+ );
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Private->Ip4Nic->Controller,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip4Nic->LoadFile,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip4Nic->DevicePath,
+ NULL
+ );
+ FreePool (Private->Ip4Nic);
+ Private->Ip4Nic = NULL;
+ }
+
+}
+
+/**
+ Destroy the HTTP child based on IPv6 stack.
+
+ @param[in] This Pointer to the EFI_DRIVER_BINDING_PROTOCOL.
+ @param[in] Private Pointer to HTTP_BOOT_PRIVATE_DATA.
+
+**/
+VOID
+HttpBootDestroyIp6Children (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ ASSERT (This != NULL);
+ ASSERT (Private != NULL);
+
+ if (Private->Ip6Child != NULL) {
+ gBS->CloseProtocol (
+ Private->Ip6Child,
+ &gEfiIp6ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ Private->Ip6Child
+ );
+ }
+
+ if (Private->Dhcp6Child != NULL) {
+ gBS->CloseProtocol (
+ Private->Dhcp6Child,
+ &gEfiDhcp6ProtocolGuid,
+ This->DriverBindingHandle,
+ Private->Controller
+ );
+
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ This->DriverBindingHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ Private->Dhcp6Child
+ );
+ }
+
+ if (Private->Ip4Nic == NULL && Private->HttpCreated) {
+ HttpIoDestroyIo(&Private->HttpIo);
+ Private->HttpCreated = FALSE;
+ }
+
+ if (Private->Ip6Nic != NULL) {
+
+ gBS->CloseProtocol (
+ Private->Controller,
+ &gEfiCallerIdGuid,
+ This->DriverBindingHandle,
+ Private->Ip6Nic->Controller
+ );
+
+ gBS->UninstallMultipleProtocolInterfaces (
+ Private->Ip6Nic->Controller,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip6Nic->LoadFile,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip6Nic->DevicePath,
+ NULL
+ );
+ FreePool (Private->Ip6Nic);
+ Private->Ip6Nic = NULL;
+ }
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Try to open the DHCP4, HTTP4 and Device Path protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiHttpServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+}
+
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ EFI_DEV_PATH *Node;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ UINT32 *Id;
+ BOOLEAN FirstStart;
+
+ FirstStart = FALSE;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &Id,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID(Id);
+ } else {
+ FirstStart = TRUE;
+
+ //
+ // Initialize the private data structure.
+ //
+ Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA));
+ if (Private == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE;
+ Private->Controller = ControllerHandle;
+ InitializeListHead (&Private->CacheList);
+ //
+ // Get the NII interface if it exists, it's not required.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &Private->Nii,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Private->Nii = NULL;
+ }
+
+ //
+ // Open Device Path Protocol to prepare for appending IP and URI node.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &Private->ParentDevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Initialize the HII configuration form.
+ //
+ Status = HttpBootConfigFormInit (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between
+ // NIC handle and the private data.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ &gEfiCallerIdGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->Id
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ }
+
+ if (Private->Ip4Nic != NULL) {
+ //
+ // Already created before
+ //
+ return EFI_SUCCESS;
+ }
+
+ Private->Ip4Nic = AllocateZeroPool (sizeof (HTTP_BOOT_VIRTUAL_NIC));
+ if (Private->Ip4Nic == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ Private->Ip4Nic->Private = Private;
+ Private->Ip4Nic->ImageHandle = This->DriverBindingHandle;
+ Private->Ip4Nic->Signature = HTTP_BOOT_VIRTUAL_NIC_SIGNATURE;
+
+ //
+ // Create DHCP4 child instance.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiDhcp4ServiceBindingProtocolGuid,
+ &Private->Dhcp4Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Dhcp4Child,
+ &gEfiDhcp4ProtocolGuid,
+ (VOID **) &Private->Dhcp4,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Get the Ip4Config2 protocol, it's required to configure the default gateway address.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp4Config2ProtocolGuid,
+ (VOID **) &Private->Ip4Config2,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Append IPv4 device path node.
+ //
+ Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv4.Header.SubType = MSG_IPv4_DP;
+ SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
+ Node->Ipv4.StaticIpAddress = FALSE;
+ DevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ if (DevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Append URI device path node.
+ //
+ Node = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_URI_DP;
+ SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL));
+ Private->Ip4Nic->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ FreePool (DevicePath);
+ if (Private->Ip4Nic->DevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it.
+ //
+ CopyMem (&Private->Ip4Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (EFI_LOAD_FILE_PROTOCOL));
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Private->Ip4Nic->Controller,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip4Nic->LoadFile,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip4Nic->DevicePath,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Open the Caller Id child to setup a parent-child relationship between
+ // real NIC handle and the HTTP boot Ipv4 NIC handle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &Id,
+ This->DriverBindingHandle,
+ Private->Ip4Nic->Controller,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (Private != NULL) {
+ if (FirstStart) {
+ gBS->UninstallProtocolInterface (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ &Private->Id
+ );
+ }
+
+ HttpBootDestroyIp4Children (This, Private);
+ HttpBootConfigFormUnload (Private);
+
+ if (FirstStart) {
+ FreePool (Private);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ EFI_HANDLE NicHandle;
+ UINT32 *Id;
+
+ //
+ // Try to get the Load File Protocol from the controller handle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiLoadFileProtocolGuid,
+ (VOID **) &LoadFile,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // If failed, try to find the NIC handle for this controller.
+ //
+ NicHandle = HttpBootGetNicByIp4Children (ControllerHandle);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Try to retrieve the private data by the Caller Id Guid.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &Id,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID (Id);
+ } else {
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (LoadFile);
+ NicHandle = Private->Controller;
+ }
+
+ //
+ // Disable the HTTP boot function.
+ //
+ Status = HttpBootStop (Private);
+ if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) {
+ return Status;
+ }
+
+ //
+ // Destroy all child instance and uninstall protocol interface.
+ //
+ HttpBootDestroyIp4Children (This, Private);
+
+ if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) {
+ //
+ // Release the cached data.
+ //
+ HttpBootFreeCacheList (Private);
+
+ //
+ // Unload the config form.
+ //
+ HttpBootConfigFormUnload (Private);
+
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiCallerIdGuid,
+ &Private->Id
+ );
+ FreePool (Private);
+
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp6DxeDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Try to open the DHCP6, HTTP and Device Path protocol.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiHttpServiceBindingProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ NULL,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+
+ return Status;
+
+}
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp6DxeDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ EFI_DEV_PATH *Node;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ UINT32 *Id;
+ BOOLEAN Ipv6Available;
+ BOOLEAN FirstStart;
+
+ FirstStart = FALSE;
+
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &Id,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+
+ if (!EFI_ERROR (Status)) {
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID(Id);
+ } else {
+ FirstStart = TRUE;
+
+ //
+ // Initialize the private data structure.
+ //
+ Private = AllocateZeroPool (sizeof (HTTP_BOOT_PRIVATE_DATA));
+ if (Private == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ Private->Signature = HTTP_BOOT_PRIVATE_DATA_SIGNATURE;
+ Private->Controller = ControllerHandle;
+ InitializeListHead (&Private->CacheList);
+ //
+ // Get the NII interface if it exists, it's not required.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiNetworkInterfaceIdentifierProtocolGuid_31,
+ (VOID **) &Private->Nii,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ Private->Nii = NULL;
+ }
+
+ //
+ // Open Device Path Protocol to prepare for appending IP and URI node.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &Private->ParentDevicePath,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Initialize the HII configuration form.
+ //
+ Status = HttpBootConfigFormInit (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Install a protocol with Caller Id Guid to the NIC, this is just to build the relationship between
+ // NIC handle and the private data.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ &gEfiCallerIdGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->Id
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ }
+
+ //
+ // Set IPv6 available flag.
+ //
+ Status = HttpBootCheckIpv6Support (Private, &Ipv6Available);
+ if (EFI_ERROR (Status)) {
+ //
+ // Fail to get the data whether UNDI supports IPv6.
+ // Set default value to TRUE.
+ //
+ Ipv6Available = TRUE;
+ }
+
+ if (!Ipv6Available) {
+ Status = EFI_UNSUPPORTED;
+ goto ON_ERROR;
+ }
+
+ if (Private->Ip6Nic != NULL) {
+ //
+ // Already created before
+ //
+ return EFI_SUCCESS;
+ }
+
+ Private->Ip6Nic = AllocateZeroPool (sizeof (HTTP_BOOT_VIRTUAL_NIC));
+ if (Private->Ip6Nic == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ Private->Ip6Nic->Private = Private;
+ Private->Ip6Nic->ImageHandle = This->DriverBindingHandle;
+ Private->Ip6Nic->Signature = HTTP_BOOT_VIRTUAL_NIC_SIGNATURE;
+
+ //
+ // Create Dhcp6 child and open Dhcp6 protocol
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiDhcp6ServiceBindingProtocolGuid,
+ &Private->Dhcp6Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Dhcp6Child,
+ &gEfiDhcp6ProtocolGuid,
+ (VOID **) &Private->Dhcp6,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Create Ip6 child and open Ip6 protocol for background ICMP packets.
+ //
+ Status = NetLibCreateServiceChild (
+ ControllerHandle,
+ This->DriverBindingHandle,
+ &gEfiIp6ServiceBindingProtocolGuid,
+ &Private->Ip6Child
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ Status = gBS->OpenProtocol (
+ Private->Ip6Child,
+ &gEfiIp6ProtocolGuid,
+ (VOID **) &Private->Ip6,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Locate Ip6Config protocol, it's required to configure the default gateway address.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiIp6ConfigProtocolGuid,
+ (VOID **) &Private->Ip6Config,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Append IPv6 device path node.
+ //
+ Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;
+ Node->Ipv6.Header.SubType = MSG_IPv6_DP;
+ Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH;
+ SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
+ DevicePath = AppendDevicePathNode(Private->ParentDevicePath, (EFI_DEVICE_PATH*) Node);
+ FreePool(Node);
+ if (DevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Append URI device path node.
+ //
+ Node = AllocateZeroPool (sizeof (EFI_DEVICE_PATH_PROTOCOL));
+ if (Node == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+ Node->DevPath.Type = MESSAGING_DEVICE_PATH;
+ Node->DevPath.SubType = MSG_URI_DP;
+ SetDevicePathNodeLength (Node, sizeof (EFI_DEVICE_PATH_PROTOCOL));
+ Private->Ip6Nic->DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
+ FreePool (Node);
+ FreePool (DevicePath);
+ if (Private->Ip6Nic->DevicePath == NULL) {
+ Status = EFI_OUT_OF_RESOURCES;
+ goto ON_ERROR;
+ }
+
+ //
+ // Create a child handle for the HTTP boot and install DevPath and Load file protocol on it.
+ //
+ CopyMem (&Private->Ip6Nic->LoadFile, &gHttpBootDxeLoadFile, sizeof (Private->LoadFile));
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Private->Ip6Nic->Controller,
+ &gEfiLoadFileProtocolGuid,
+ &Private->Ip6Nic->LoadFile,
+ &gEfiDevicePathProtocolGuid,
+ Private->Ip6Nic->DevicePath,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ //
+ // Open the Caller Id child to setup a parent-child relationship between
+ // real NIC handle and the HTTP boot child handle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &Id,
+ This->DriverBindingHandle,
+ Private->Ip6Nic->Controller,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ );
+ if (EFI_ERROR (Status)) {
+ goto ON_ERROR;
+ }
+
+ return EFI_SUCCESS;
+
+ON_ERROR:
+ if (Private != NULL) {
+ if (FirstStart) {
+ gBS->UninstallProtocolInterface (
+ ControllerHandle,
+ &gEfiCallerIdGuid,
+ &Private->Id
+ );
+ }
+
+ HttpBootDestroyIp6Children(This, Private);
+ HttpBootConfigFormUnload (Private);
+
+ if (FirstStart) {
+ FreePool (Private);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp6DxeDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ EFI_HANDLE NicHandle;
+ UINT32 *Id;
+
+ //
+ // Try to get the Load File Protocol from the controller handle.
+ //
+ Status = gBS->OpenProtocol (
+ ControllerHandle,
+ &gEfiLoadFileProtocolGuid,
+ (VOID **) &LoadFile,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // If failed, try to find the NIC handle for this controller.
+ //
+ NicHandle = HttpBootGetNicByIp6Children (ControllerHandle);
+ if (NicHandle == NULL) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Try to retrieve the private data by the Caller Id Guid.
+ //
+ Status = gBS->OpenProtocol (
+ NicHandle,
+ &gEfiCallerIdGuid,
+ (VOID **) &Id,
+ This->DriverBindingHandle,
+ ControllerHandle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_ID (Id);
+ } else {
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE (LoadFile);
+ NicHandle = Private->Controller;
+ }
+
+ //
+ // Disable the HTTP boot function.
+ //
+ Status = HttpBootStop (Private);
+ if (Status != EFI_SUCCESS && Status != EFI_NOT_STARTED) {
+ return Status;
+ }
+
+ //
+ // Destroy all child instance and uninstall protocol interface.
+ //
+ HttpBootDestroyIp6Children (This, Private);
+
+ if (Private->Ip4Nic == NULL && Private->Ip6Nic == NULL) {
+ //
+ // Release the cached data.
+ //
+ HttpBootFreeCacheList (Private);
+
+ //
+ // Unload the config form.
+ //
+ HttpBootConfigFormUnload (Private);
+
+ gBS->UninstallProtocolInterface (
+ NicHandle,
+ &gEfiCallerIdGuid,
+ &Private->Id
+ );
+ FreePool (Private);
+
+ }
+
+ return EFI_SUCCESS;
+}
+/**
+ This is the declaration of an EFI image entry point. This entry point is
+ the same for UEFI Applications, UEFI OS Loaders, and UEFI Drivers including
+ both device drivers and bus drivers.
+
+ @param[in] ImageHandle The firmware allocated handle for the UEFI image.
+ @param[in] SystemTable A pointer to the EFI System Table.
+
+ @retval EFI_SUCCESS The operation completed successfully.
+ @retval Others An unexpected error occurred.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeDriverEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ //
+ // Install UEFI Driver Model protocol(s).
+ //
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gHttpBootIp4DxeDriverBinding,
+ ImageHandle,
+ &gHttpBootDxeComponentName,
+ &gHttpBootDxeComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EfiLibInstallDriverBindingComponentName2 (
+ ImageHandle,
+ SystemTable,
+ &gHttpBootIp6DxeDriverBinding,
+ NULL,
+ &gHttpBootDxeComponentName,
+ &gHttpBootDxeComponentName2
+ );
+ if (EFI_ERROR (Status)) {
+ EfiLibUninstallDriverBindingComponentName2(
+ &gHttpBootIp4DxeDriverBinding,
+ &gHttpBootDxeComponentName,
+ &gHttpBootDxeComponentName2
+ );
+ }
+ return Status;
+}
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.h
new file mode 100644
index 00000000..55491935
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.h
@@ -0,0 +1,525 @@
+/** @file
+ UEFI HTTP boot driver's private data structure and interfaces declaration.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_HTTP_BOOT_DXE_H__
+#define __EFI_HTTP_BOOT_DXE_H__
+
+#include <Uefi.h>
+
+#include <IndustryStandard/Http11.h>
+#include <IndustryStandard/Dhcp.h>
+
+//
+// Libraries
+//
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiHiiServicesLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/BaseLib.h>
+#include <Library/UefiLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/DebugLib.h>
+#include <Library/NetLib.h>
+#include <Library/HttpLib.h>
+#include <Library/HttpIoLib.h>
+#include <Library/HiiLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DpcLib.h>
+
+//
+// UEFI Driver Model Protocols
+//
+#include <Protocol/DriverBinding.h>
+#include <Protocol/ComponentName2.h>
+#include <Protocol/ComponentName.h>
+
+//
+// Consumed Protocols
+//
+#include <Protocol/ServiceBinding.h>
+#include <Protocol/HiiConfigAccess.h>
+#include <Protocol/NetworkInterfaceIdentifier.h>
+#include <Protocol/Dhcp4.h>
+#include <Protocol/Dhcp6.h>
+#include <Protocol/Dns6.h>
+#include <Protocol/Http.h>
+#include <Protocol/Ip4Config2.h>
+#include <Protocol/Ip6Config.h>
+#include <Protocol/RamDisk.h>
+#include <Protocol/AdapterInformation.h>
+
+//
+// Produced Protocols
+//
+#include <Protocol/LoadFile.h>
+#include <Protocol/HttpBootCallback.h>
+
+//
+// Consumed Guids
+//
+#include <Guid/HttpBootConfigHii.h>
+
+//
+// Driver Version
+//
+#define HTTP_BOOT_DXE_VERSION 0xa
+
+//
+// Standard Media Types defined in
+// http://www.iana.org/assignments/media-types
+//
+#define HTTP_CONTENT_TYPE_APP_EFI "application/efi"
+#define HTTP_CONTENT_TYPE_APP_IMG "application/vnd.efi-img"
+#define HTTP_CONTENT_TYPE_APP_ISO "application/vnd.efi-iso"
+
+//
+// Protocol instances
+//
+extern EFI_DRIVER_BINDING_PROTOCOL gHttpBootDxeDriverBinding;
+extern EFI_COMPONENT_NAME2_PROTOCOL gHttpBootDxeComponentName2;
+extern EFI_COMPONENT_NAME_PROTOCOL gHttpBootDxeComponentName;
+
+//
+// Private data structure
+//
+typedef struct _HTTP_BOOT_PRIVATE_DATA HTTP_BOOT_PRIVATE_DATA;
+typedef struct _HTTP_BOOT_VIRTUAL_NIC HTTP_BOOT_VIRTUAL_NIC;
+
+typedef enum {
+ ImageTypeEfi,
+ ImageTypeVirtualCd,
+ ImageTypeVirtualDisk,
+ ImageTypeMax
+} HTTP_BOOT_IMAGE_TYPE;
+
+//
+// Include files with internal function prototypes
+//
+#include "HttpBootComponentName.h"
+#include "HttpBootDhcp4.h"
+#include "HttpBootDhcp6.h"
+#include "HttpBootImpl.h"
+#include "HttpBootSupport.h"
+#include "HttpBootClient.h"
+#include "HttpBootConfig.h"
+
+typedef union {
+ HTTP_BOOT_DHCP4_PACKET_CACHE Dhcp4;
+ HTTP_BOOT_DHCP6_PACKET_CACHE Dhcp6;
+} HTTP_BOOT_DHCP_PACKET_CACHE;
+
+struct _HTTP_BOOT_VIRTUAL_NIC {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+ EFI_HANDLE ImageHandle;
+ EFI_LOAD_FILE_PROTOCOL LoadFile;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+};
+
+#define HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_INFO(Callback) \
+ CR ( \
+ Callback, \
+ HTTP_BOOT_PRIVATE_DATA, \
+ CallbackInfo, \
+ HTTP_BOOT_PRIVATE_DATA_SIGNATURE \
+ )
+
+#define HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_PROTOCOL(CallbackProtocol) \
+ CR ( \
+ CallbackProtocol, \
+ HTTP_BOOT_PRIVATE_DATA, \
+ LoadFileCallback, \
+ HTTP_BOOT_PRIVATE_DATA_SIGNATURE \
+ )
+
+struct _HTTP_BOOT_PRIVATE_DATA {
+ UINT32 Signature;
+ EFI_HANDLE Controller;
+
+ HTTP_BOOT_VIRTUAL_NIC *Ip4Nic;
+ HTTP_BOOT_VIRTUAL_NIC *Ip6Nic;
+
+ //
+ // Consumed children
+ //
+ EFI_HANDLE Ip6Child;
+ EFI_HANDLE Dhcp4Child;
+ EFI_HANDLE Dhcp6Child;
+ HTTP_IO HttpIo;
+ BOOLEAN HttpCreated;
+
+ //
+ // Consumed protocol
+ //
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *Nii;
+ EFI_IP6_PROTOCOL *Ip6;
+ EFI_IP4_CONFIG2_PROTOCOL *Ip4Config2;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ EFI_DHCP4_PROTOCOL *Dhcp4;
+ EFI_DHCP6_PROTOCOL *Dhcp6;
+ EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath;
+
+
+ //
+ // Produced protocol
+ //
+ EFI_LOAD_FILE_PROTOCOL LoadFile;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ UINT32 Id;
+ EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;
+ EFI_HTTP_BOOT_CALLBACK_PROTOCOL LoadFileCallback;
+
+ //
+ // Data for the default HTTP Boot callback protocol
+ //
+ UINT64 FileSize;
+ UINT64 ReceivedSize;
+ UINT32 Percentage;
+
+ //
+ // HII callback info block
+ //
+ HTTP_BOOT_FORM_CALLBACK_INFO CallbackInfo;
+
+ //
+ // Mode data
+ //
+ BOOLEAN UsingIpv6;
+ BOOLEAN Started;
+ EFI_IP_ADDRESS StationIp;
+ EFI_IP_ADDRESS SubnetMask;
+ EFI_IP_ADDRESS GatewayIp;
+ EFI_IP_ADDRESS ServerIp;
+ UINT16 Port;
+ UINT32 DnsServerCount;
+ EFI_IP_ADDRESS *DnsServerIp;
+
+ //
+ // The URI string attempt to download through HTTP, may point to
+ // the memory in cached DHCP offer, or to the memory in FilePathUri.
+ //
+ CHAR8 *BootFileUri;
+ VOID *BootFileUriParser;
+ UINTN BootFileSize;
+ BOOLEAN NoGateway;
+ HTTP_BOOT_IMAGE_TYPE ImageType;
+
+ //
+ // URI string extracted from the input FilePath parameter.
+ //
+ CHAR8 *FilePathUri;
+ VOID *FilePathUriParser;
+
+ //
+ // Cached HTTP data
+ //
+ LIST_ENTRY CacheList;
+
+ //
+ // Cached DHCP offer
+ //
+ // OfferIndex records the index of DhcpOffer[] buffer, and OfferCount records the num of each type of offer.
+ //
+ // It supposed that
+ //
+ // OfferNum: 8
+ // OfferBuffer: [ProxyNameUri, DhcpNameUri, DhcpIpUri, ProxyNameUri, ProxyIpUri, DhcpOnly, DhcpIpUri, DhcpNameUriDns]
+ // (OfferBuffer is 0-based.)
+ //
+ // And assume that (DhcpIpUri is the first priority actually.)
+ //
+ // SelectIndex: 5
+ // SelectProxyType: HttpOfferTypeProxyIpUri
+ // (SelectIndex is 1-based, and 0 means no one is selected.)
+ //
+ // So it should be
+ //
+ // DhcpIpUri DhcpNameUriDns DhcpDns DhcpOnly ProxyNameUri ProxyIpUri DhcpNameUri
+ // OfferCount: [ 2, 1, 0, 1, 2, 1, 1]
+ //
+ // OfferIndex: {[ 2, 7, 0, 5, 0, *4, 1]
+ // [ 6, 0, 0, 0, 3, 0, 0]
+ // [ 0, 0, 0, 0, 0, 0, 0]
+ // ... ]}
+ // (OfferIndex is 0-based.)
+ //
+ //
+ UINT32 SelectIndex;
+ UINT32 SelectProxyType;
+ HTTP_BOOT_DHCP_PACKET_CACHE OfferBuffer[HTTP_BOOT_OFFER_MAX_NUM];
+ UINT32 OfferNum;
+ UINT32 OfferCount[HttpOfferTypeMax];
+ UINT32 OfferIndex[HttpOfferTypeMax][HTTP_BOOT_OFFER_MAX_NUM];
+};
+
+#define HTTP_BOOT_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('H', 'B', 'P', 'D')
+#define HTTP_BOOT_VIRTUAL_NIC_SIGNATURE SIGNATURE_32 ('H', 'B', 'V', 'N')
+#define HTTP_BOOT_PRIVATE_DATA_FROM_LOADFILE(a) CR (a, HTTP_BOOT_PRIVATE_DATA, LoadFile, HTTP_BOOT_PRIVATE_DATA_SIGNATURE)
+#define HTTP_BOOT_PRIVATE_DATA_FROM_ID(a) CR (a, HTTP_BOOT_PRIVATE_DATA, Id, HTTP_BOOT_PRIVATE_DATA_SIGNATURE)
+#define HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE(a) CR (a, HTTP_BOOT_VIRTUAL_NIC, LoadFile, HTTP_BOOT_VIRTUAL_NIC_SIGNATURE)
+extern EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile;
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp4DxeDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+
+/**
+ Tests to see if this driver supports a given controller. If a child device is provided,
+ it further tests to see if this driver supports creating a handle for the specified child device.
+
+ This function checks to see if the driver specified by This supports the device specified by
+ ControllerHandle. Drivers will typically use the device path attached to
+ ControllerHandle and/or the services from the bus I/O abstraction attached to
+ ControllerHandle to determine if the driver supports ControllerHandle. This function
+ may be called many times during platform initialization. In order to reduce boot times, the tests
+ performed by this function must be very small, and take as little time as possible to execute. This
+ function must not change the state of any hardware devices, and this function must be aware that the
+ device specified by ControllerHandle may already be managed by the same driver or a
+ different driver. This function must match its calls to AllocatePages() with FreePages(),
+ AllocatePool() with FreePool(), and OpenProtocol() with CloseProtocol().
+ Because ControllerHandle may have been previously started by the same driver, if a protocol is
+ already in the opened state, then it must not be closed with CloseProtocol(). This is required
+ to guarantee the state of ControllerHandle is not modified by this function.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to test. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For bus drivers, if this parameter is not NULL, then
+ the bus driver must determine if the bus controller specified
+ by ControllerHandle and the child controller specified
+ by RemainingDevicePath are both supported by this
+ bus driver.
+
+ @retval EFI_SUCCESS The device specified by ControllerHandle and
+ RemainingDevicePath is supported by the driver specified by This.
+ @retval EFI_ALREADY_STARTED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by the driver
+ specified by This.
+ @retval EFI_ACCESS_DENIED The device specified by ControllerHandle and
+ RemainingDevicePath is already being managed by a different
+ driver or an application that requires exclusive access.
+ Currently not implemented.
+ @retval EFI_UNSUPPORTED The device specified by ControllerHandle and
+ RemainingDevicePath is not supported by the driver specified by This.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp6DxeDriverBindingSupported (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Starts a device controller or a bus controller.
+
+ The Start() function is designed to be invoked from the EFI boot service ConnectController().
+ As a result, much of the error checking on the parameters to Start() has been moved into this
+ common boot service. It is legal to call Start() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE.
+ 2. If RemainingDevicePath is not NULL, then it must be a pointer to a naturally aligned
+ EFI_DEVICE_PATH_PROTOCOL.
+ 3. Prior to calling Start(), the Supported() function for the driver specified by This must
+ have been called with the same calling parameters, and Supported() must have returned EFI_SUCCESS.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle The handle of the controller to start. This handle
+ must support a protocol interface that supplies
+ an I/O abstraction to the driver.
+ @param[in] RemainingDevicePath A pointer to the remaining portion of a device path. This
+ parameter is ignored by device drivers, and is optional for bus
+ drivers. For a bus driver, if this parameter is NULL, then handles
+ for all the children of Controller are created by this driver.
+ If this parameter is not NULL and the first Device Path Node is
+ not the End of Device Path Node, then only the handle for the
+ child device specified by the first Device Path Node of
+ RemainingDevicePath is created by this driver.
+ If the first Device Path Node of RemainingDevicePath is
+ the End of Device Path Node, no child handle is created by this
+ driver.
+
+ @retval EFI_SUCCESS The device was started.
+ @retval EFI_DEVICE_ERROR The device could not be started due to a device error.Currently not implemented.
+ @retval EFI_OUT_OF_RESOURCES The request could not be completed due to a lack of resources.
+ @retval Others The driver failed to start the device.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp6DxeDriverBindingStart (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath OPTIONAL
+ );
+
+/**
+ Stops a device controller or a bus controller.
+
+ The Stop() function is designed to be invoked from the EFI boot service DisconnectController().
+ As a result, much of the error checking on the parameters to Stop() has been moved
+ into this common boot service. It is legal to call Stop() from other locations,
+ but the following calling restrictions must be followed, or the system behavior will not be deterministic.
+ 1. ControllerHandle must be a valid EFI_HANDLE that was used on a previous call to this
+ same driver's Start() function.
+ 2. The first NumberOfChildren handles of ChildHandleBuffer must all be a valid
+ EFI_HANDLE. In addition, all of these handles must have been created in this driver's
+ Start() function, and the Start() function must have called OpenProtocol() on
+ ControllerHandle with an Attribute of EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER.
+
+ @param[in] This A pointer to the EFI_DRIVER_BINDING_PROTOCOL instance.
+ @param[in] ControllerHandle A handle to the device being stopped. The handle must
+ support a bus specific I/O protocol for the driver
+ to use to stop the device.
+ @param[in] NumberOfChildren The number of child device handles in ChildHandleBuffer.
+ @param[in] ChildHandleBuffer An array of child handles to be freed. May be NULL
+ if NumberOfChildren is 0.
+
+ @retval EFI_SUCCESS The device was stopped.
+ @retval EFI_DEVICE_ERROR The device could not be stopped due to a device error.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootIp6DxeDriverBindingStop (
+ IN EFI_DRIVER_BINDING_PROTOCOL *This,
+ IN EFI_HANDLE ControllerHandle,
+ IN UINTN NumberOfChildren,
+ IN EFI_HANDLE *ChildHandleBuffer OPTIONAL
+ );
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.inf b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.inf
new file mode 100644
index 00000000..d64755ff
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.inf
@@ -0,0 +1,101 @@
+## @file
+# This modules produce the Load File Protocol for UEFI HTTP boot.
+#
+# Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = HttpBootDxe
+ FILE_GUID = ecebcb00-d9c8-11e4-af3d-8cdcd426c973
+ MODULE_TYPE = UEFI_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = HttpBootDxeDriverEntryPoint
+ UNLOAD_IMAGE = NetLibDefaultUnload
+ MODULE_UNI_FILE = HttpBootDxe.uni
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ NetworkPkg/NetworkPkg.dec
+
+[Sources]
+ HttpBootConfigNVDataStruc.h
+ HttpBootDxe.h
+ HttpBootDxe.c
+ HttpBootConfig.h
+ HttpBootConfig.c
+ HttpBootComponentName.h
+ HttpBootComponentName.c
+ HttpBootImpl.h
+ HttpBootImpl.c
+ HttpBootDhcp4.h
+ HttpBootDhcp4.c
+ HttpBootDhcp6.h
+ HttpBootDhcp6.c
+ HttpBootSupport.h
+ HttpBootSupport.c
+ HttpBootClient.h
+ HttpBootClient.c
+ HttpBootConfigVfr.vfr
+ HttpBootConfigStrings.uni
+
+[LibraryClasses]
+ UefiDriverEntryPoint
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseLib
+ UefiLib
+ DevicePathLib
+ DebugLib
+ NetLib
+ HttpLib
+ HttpIoLib
+ HiiLib
+ PrintLib
+ DpcLib
+ UefiHiiServicesLib
+ UefiBootManagerLib
+
+[Protocols]
+ ## TO_START
+ ## BY_START
+ gEfiDevicePathProtocolGuid
+
+ gEfiLoadFileProtocolGuid ## BY_START
+ gEfiHttpServiceBindingProtocolGuid ## CONSUMES
+ gEfiHttpProtocolGuid ## CONSUMES
+ gEfiDhcp4ServiceBindingProtocolGuid ## TO_START
+ gEfiDhcp4ProtocolGuid ## TO_START
+ gEfiIp4Config2ProtocolGuid ## TO_START
+ gEfiDhcp6ServiceBindingProtocolGuid ## TO_START
+ gEfiDhcp6ProtocolGuid ## TO_START
+ gEfiDns6ServiceBindingProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDns6ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiIp6ServiceBindingProtocolGuid ## TO_START
+ gEfiIp6ProtocolGuid ## TO_START
+ gEfiIp6ConfigProtocolGuid ## TO_START
+ gEfiNetworkInterfaceIdentifierProtocolGuid_31 ## SOMETIMES_CONSUMES
+ gEfiRamDiskProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiHiiConfigAccessProtocolGuid ## BY_START
+ gEfiHttpBootCallbackProtocolGuid ## SOMETIMES_PRODUCES
+ gEfiAdapterInformationProtocolGuid ## SOMETIMES_CONSUMES
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## GUID # HiiIsConfigHdrMatch mHttpBootConfigStorageName
+ ## SOMETIMES_PRODUCES ## GUID # HiiConstructConfigHdr mHttpBootConfigStorageName
+ ## SOMETIMES_PRODUCES ## GUID # HiiGetBrowserData mHttpBootConfigStorageName
+ ## SOMETIMES_CONSUMES ## HII
+ gHttpBootConfigGuid
+ gEfiVirtualCdGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiVirtualDiskGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiAdapterInfoUndiIpv6SupportGuid ## SOMETIMES_CONSUMES ## GUID
+
+[Pcd]
+ gEfiNetworkPkgTokenSpaceGuid.PcdAllowHttpConnections ## CONSUMES
+
+[UserExtensions.TianoCore."ExtraFiles"]
+ HttpBootDxeExtra.uni
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.uni
new file mode 100644
index 00000000..68143cc4
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxe.uni
@@ -0,0 +1,18 @@
+// /** @file
+// UEFI HTTP boot service.
+//
+// This driver provides EFI Load File Protocol which is used to download
+// the boot image from HTTP server. It could work with an IPv4 or IPv6 stack.
+//
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "UEFI HTTP boot service"
+
+#string STR_MODULE_DESCRIPTION #language en-US "This driver provides EFI Load File Protocol which is used to download the boot image from HTTP server. It could work with an IPv4 or IPv6 stack."
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni
new file mode 100644
index 00000000..b1b4ed0f
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootDxeExtra.uni
@@ -0,0 +1,14 @@
+// /** @file
+// HttpBootDxe Localized Strings and Content
+//
+// Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+#string STR_PROPERTIES_MODULE_NAME
+#language en-US
+"UEFI HTTP BOOT DXE"
+
+
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.c
new file mode 100644
index 00000000..f91a9b2b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.c
@@ -0,0 +1,767 @@
+/** @file
+ The implementation of EFI_LOAD_FILE_PROTOCOL for UEFI HTTP boot.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+
+/**
+ Install HTTP Boot Callback Protocol if not installed before.
+
+ @param[in] Private Pointer to HTTP Boot private data.
+
+ @retval EFI_SUCCESS HTTP Boot Callback Protocol installed successfully.
+ @retval Others Failed to install HTTP Boot Callback Protocol.
+
+**/
+EFI_STATUS
+HttpBootInstallCallback (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE ControllerHandle;
+
+ if (!Private->UsingIpv6) {
+ ControllerHandle = Private->Ip4Nic->Controller;
+ } else {
+ ControllerHandle = Private->Ip6Nic->Controller;
+ }
+
+ //
+ // Check whether gEfiHttpBootCallbackProtocolGuid already installed.
+ //
+ Status = gBS->HandleProtocol (
+ ControllerHandle,
+ &gEfiHttpBootCallbackProtocolGuid,
+ (VOID **) &Private->HttpBootCallback
+ );
+ if (Status == EFI_UNSUPPORTED) {
+
+ CopyMem (
+ &Private->LoadFileCallback,
+ &gHttpBootDxeHttpBootCallback,
+ sizeof (EFI_HTTP_BOOT_CALLBACK_PROTOCOL)
+ );
+
+ //
+ // Install a default callback if user didn't offer one.
+ //
+ Status = gBS->InstallProtocolInterface (
+ &ControllerHandle,
+ &gEfiHttpBootCallbackProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &Private->LoadFileCallback
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Private->HttpBootCallback = &Private->LoadFileCallback;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Uninstall HTTP Boot Callback Protocol if it's installed by this driver.
+
+ @param[in] Private Pointer to HTTP Boot private data.
+
+**/
+VOID
+HttpBootUninstallCallback (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ if (Private->HttpBootCallback == &Private->LoadFileCallback) {
+ gBS->UninstallProtocolInterface (
+ Private->Controller,
+ &gEfiHttpBootCallbackProtocolGuid,
+ &Private->HttpBootCallback
+ );
+ Private->HttpBootCallback = NULL;
+ }
+}
+
+/**
+ Enable the use of UEFI HTTP boot function.
+
+ If the driver has already been started but not satisfy the requirement (IP stack and
+ specified boot file path), this function will stop the driver and start it again.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] UsingIpv6 Specifies the type of IP addresses that are to be
+ used during the session that is being started.
+ Set to TRUE for IPv6, and FALSE for IPv4.
+ @param[in] FilePath The device specific path of the file to load.
+
+ @retval EFI_SUCCESS HTTP boot was successfully enabled.
+ @retval EFI_INVALID_PARAMETER Private is NULL or FilePath is NULL.
+ @retval EFI_INVALID_PARAMETER The FilePath doesn't contain a valid URI device path node.
+ @retval EFI_ALREADY_STARTED The driver is already in started state.
+ @retval EFI_OUT_OF_RESOURCES There are not enough resources.
+
+**/
+EFI_STATUS
+HttpBootStart (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN BOOLEAN UsingIpv6,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+ CHAR8 *Uri;
+
+ Uri = NULL;
+
+ if (Private == NULL || FilePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check the URI in the input FilePath, in order to see whether it is
+ // required to boot from a new specified boot file.
+ //
+ Status = HttpBootParseFilePath (FilePath, &Uri);
+ if (EFI_ERROR (Status)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Check whether we need to stop and restart the HTTP boot driver.
+ //
+ if (Private->Started) {
+ //
+ // Restart is needed in 2 cases:
+ // 1. Http boot driver has already been started but not on the required IP stack.
+ // 2. The specified boot file URI in FilePath is different with the one we have
+ // recorded before.
+ //
+ if ((UsingIpv6 != Private->UsingIpv6) ||
+ ((Uri != NULL) && (AsciiStrCmp (Private->BootFileUri, Uri) != 0))) {
+ //
+ // Restart is required, first stop then continue this start function.
+ //
+ Status = HttpBootStop (Private);
+ if (EFI_ERROR (Status)) {
+ if (Uri != NULL) {
+ FreePool (Uri);
+ }
+ return Status;
+ }
+ } else {
+ //
+ // Restart is not required.
+ //
+ if (Uri != NULL) {
+ FreePool (Uri);
+ }
+ return EFI_ALREADY_STARTED;
+ }
+ }
+
+ //
+ // Detect whether using ipv6 or not, and set it to the private data.
+ //
+ if (UsingIpv6 && Private->Ip6Nic != NULL) {
+ Private->UsingIpv6 = TRUE;
+ } else if (!UsingIpv6 && Private->Ip4Nic != NULL) {
+ Private->UsingIpv6 = FALSE;
+ } else {
+ if (Uri != NULL) {
+ FreePool (Uri);
+ }
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // Record the specified URI and prepare the URI parser if needed.
+ //
+ Private->FilePathUri = Uri;
+ if (Private->FilePathUri != NULL) {
+ Status = HttpParseUrl (
+ Private->FilePathUri,
+ (UINT32) AsciiStrLen (Private->FilePathUri),
+ FALSE,
+ &Private->FilePathUriParser
+ );
+ if (EFI_ERROR (Status)) {
+ FreePool (Private->FilePathUri);
+ return Status;
+ }
+ }
+
+ //
+ // Init the content of cached DHCP offer list.
+ //
+ ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
+ if (!Private->UsingIpv6) {
+ for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
+ Private->OfferBuffer[Index].Dhcp4.Packet.Offer.Size = HTTP_CACHED_DHCP4_PACKET_MAX_SIZE;
+ }
+ } else {
+ for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
+ Private->OfferBuffer[Index].Dhcp6.Packet.Offer.Size = HTTP_CACHED_DHCP6_PACKET_MAX_SIZE;
+ }
+ }
+
+ if (Private->UsingIpv6) {
+ //
+ // Set Ip6 policy to Automatic to start the Ip6 router discovery.
+ //
+ Status = HttpBootSetIp6Policy (Private);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ }
+ Private->Started = TRUE;
+ Print (L"\n>>Start HTTP Boot over IPv%d", Private->UsingIpv6 ? 6 : 4);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Boot info was successfully retrieved.
+ @retval EFI_INVALID_PARAMETER Private is NULL.
+ @retval EFI_NOT_STARTED The driver is in stopped state.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpBootDhcp (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ EFI_STATUS Status;
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Private->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ Status = EFI_DEVICE_ERROR;
+
+ if (!Private->UsingIpv6) {
+ //
+ // Start D.O.R.A process to get a IPv4 address and other boot information.
+ //
+ Status = HttpBootDhcp4Dora (Private);
+ } else {
+ //
+ // Start S.A.R.R process to get a IPv6 address and other boot information.
+ //
+ Status = HttpBootDhcp6Sarr (Private);
+ }
+
+ return Status;
+}
+
+/**
+ Attempt to download the boot file through HTTP message exchange.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param[in] Buffer The memory buffer to transfer the file to. If Buffer is NULL,
+ then the size of the requested file is returned in
+ BufferSize.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS Boot file was loaded successfully.
+ @retval EFI_INVALID_PARAMETER Private is NULL, or ImageType is NULL, or BufferSize is NULL.
+ @retval EFI_INVALID_PARAMETER *BufferSize is not zero, and Buffer is NULL.
+ @retval EFI_NOT_STARTED The driver is in stopped state.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the boot file. BufferSize has
+ been updated with the size needed to complete the request.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpBootLoadFile (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer, OPTIONAL
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ EFI_STATUS Status;
+
+ if (Private == NULL || ImageType == NULL || BufferSize == NULL ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (*BufferSize != 0 && Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Private->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ Status = HttpBootInstallCallback (Private);
+ if (EFI_ERROR(Status)) {
+ goto ON_EXIT;
+ }
+
+ if (Private->BootFileUri == NULL) {
+ //
+ // Parse the cached offer to get the boot file URL first.
+ //
+ Status = HttpBootDiscoverBootInfo (Private);
+ if (EFI_ERROR (Status)) {
+ AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP server.\n");
+ goto ON_EXIT;
+ }
+ }
+
+ if (!Private->HttpCreated) {
+ //
+ // Create HTTP child.
+ //
+ Status = HttpBootCreateHttpIo (Private);
+ if (EFI_ERROR (Status)) {
+ goto ON_EXIT;
+ }
+ }
+
+ if (Private->BootFileSize == 0) {
+ //
+ // Discover the information about the bootfile if we haven't.
+ //
+
+ //
+ // Try to use HTTP HEAD method.
+ //
+ Status = HttpBootGetBootFile (
+ Private,
+ TRUE,
+ &Private->BootFileSize,
+ NULL,
+ &Private->ImageType
+ );
+ if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ //
+ // Failed to get file size by HEAD method, may be trunked encoding, try HTTP GET method.
+ //
+ ASSERT (Private->BootFileSize == 0);
+ Status = HttpBootGetBootFile (
+ Private,
+ FALSE,
+ &Private->BootFileSize,
+ NULL,
+ &Private->ImageType
+ );
+ if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
+ AsciiPrint ("\n Error: Could not retrieve NBP file size from HTTP server.\n");
+ goto ON_EXIT;
+ }
+ }
+ }
+
+ if (*BufferSize < Private->BootFileSize) {
+ *BufferSize = Private->BootFileSize;
+ *ImageType = Private->ImageType;
+ Status = EFI_BUFFER_TOO_SMALL;
+ goto ON_EXIT;
+ }
+
+ //
+ // Load the boot file into Buffer
+ //
+ Status = HttpBootGetBootFile (
+ Private,
+ FALSE,
+ BufferSize,
+ Buffer,
+ ImageType
+ );
+
+ON_EXIT:
+ HttpBootUninstallCallback (Private);
+
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_ACCESS_DENIED) {
+ AsciiPrint ("\n Error: Could not establish connection with HTTP server.\n");
+ } else if (Status == EFI_BUFFER_TOO_SMALL && Buffer != NULL) {
+ AsciiPrint ("\n Error: Buffer size is smaller than the requested file.\n");
+ } else if (Status == EFI_OUT_OF_RESOURCES) {
+ AsciiPrint ("\n Error: Could not allocate I/O buffers.\n");
+ } else if (Status == EFI_DEVICE_ERROR) {
+ AsciiPrint ("\n Error: Network device error.\n");
+ } else if (Status == EFI_TIMEOUT) {
+ AsciiPrint ("\n Error: Server response timeout.\n");
+ } else if (Status == EFI_ABORTED) {
+ AsciiPrint ("\n Error: Remote boot cancelled.\n");
+ } else if (Status != EFI_BUFFER_TOO_SMALL) {
+ AsciiPrint ("\n Error: Unexpected network error.\n");
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Disable the use of UEFI HTTP boot function.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS HTTP boot was successfully disabled.
+ @retval EFI_NOT_STARTED The driver is already in stopped state.
+ @retval EFI_INVALID_PARAMETER Private is NULL.
+ @retval Others Unexpected error when stop the function.
+
+**/
+EFI_STATUS
+HttpBootStop (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ )
+{
+ UINTN Index;
+
+ if (Private == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!Private->Started) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Private->HttpCreated) {
+ HttpIoDestroyIo (&Private->HttpIo);
+ Private->HttpCreated = FALSE;
+ }
+
+ Private->Started = FALSE;
+ ZeroMem (&Private->StationIp, sizeof (EFI_IP_ADDRESS));
+ ZeroMem (&Private->SubnetMask, sizeof (EFI_IP_ADDRESS));
+ ZeroMem (&Private->GatewayIp, sizeof (EFI_IP_ADDRESS));
+ Private->Port = 0;
+ Private->BootFileUri = NULL;
+ Private->BootFileUriParser = NULL;
+ Private->BootFileSize = 0;
+ Private->SelectIndex = 0;
+ Private->SelectProxyType = HttpOfferTypeMax;
+
+ if (!Private->UsingIpv6) {
+ //
+ // Stop and release the DHCP4 child.
+ //
+ Private->Dhcp4->Stop (Private->Dhcp4);
+ Private->Dhcp4->Configure (Private->Dhcp4, NULL);
+
+ for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
+ if (Private->OfferBuffer[Index].Dhcp4.UriParser) {
+ HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp4.UriParser);
+ }
+ }
+ } else {
+ //
+ // Stop and release the DHCP6 child.
+ //
+ Private->Dhcp6->Stop (Private->Dhcp6);
+ Private->Dhcp6->Configure (Private->Dhcp6, NULL);
+
+ for (Index = 0; Index < HTTP_BOOT_OFFER_MAX_NUM; Index++) {
+ if (Private->OfferBuffer[Index].Dhcp6.UriParser) {
+ HttpUrlFreeParser (Private->OfferBuffer[Index].Dhcp6.UriParser);
+ }
+ }
+ }
+
+ if (Private->DnsServerIp != NULL) {
+ FreePool (Private->DnsServerIp);
+ Private->DnsServerIp = NULL;
+ }
+
+ if (Private->FilePathUri!= NULL) {
+ FreePool (Private->FilePathUri);
+ HttpUrlFreeParser (Private->FilePathUriParser);
+ Private->FilePathUri = NULL;
+ Private->FilePathUriParser = NULL;
+ }
+
+ ZeroMem (Private->OfferBuffer, sizeof (Private->OfferBuffer));
+ Private->OfferNum = 0;
+ ZeroMem (Private->OfferCount, sizeof (Private->OfferCount));
+ ZeroMem (Private->OfferIndex, sizeof (Private->OfferIndex));
+
+ HttpBootFreeCacheList (Private);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Causes the driver to load a specified file.
+
+ @param This Protocol instance pointer.
+ @param FilePath The device specific path of the file to load.
+ @param BootPolicy If TRUE, indicates that the request originates from the
+ boot manager is attempting to load FilePath as a boot
+ selection. If FALSE, then FilePath must match as exact file
+ to be loaded.
+ @param BufferSize On input the size of Buffer in bytes. On output with a return
+ code of EFI_SUCCESS, the amount of data transferred to
+ Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
+ the size of Buffer required to retrieve the requested file.
+ @param Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
+ then the size of the requested file is returned in
+ BufferSize.
+
+ @retval EFI_SUCCESS The file was loaded.
+ @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy
+ @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or
+ BufferSize is NULL.
+ @retval EFI_NO_MEDIA No medium was present to load the file.
+ @retval EFI_DEVICE_ERROR The file was not loaded due to a device error.
+ @retval EFI_NO_RESPONSE The remote system did not respond.
+ @retval EFI_NOT_FOUND The file was not found.
+ @retval EFI_ABORTED The file load process was manually cancelled.
+ @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
+ BufferSize has been updated with the size needed to complete
+ the request.
+
+**/
+EFI_STATUS
+EFIAPI
+HttpBootDxeLoadFile (
+ IN EFI_LOAD_FILE_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ IN VOID *Buffer OPTIONAL
+ )
+{
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ HTTP_BOOT_VIRTUAL_NIC *VirtualNic;
+ EFI_STATUS MediaStatus;
+ BOOLEAN UsingIpv6;
+ EFI_STATUS Status;
+ HTTP_BOOT_IMAGE_TYPE ImageType;
+
+ if (This == NULL || BufferSize == NULL || FilePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Only support BootPolicy
+ //
+ if (!BootPolicy) {
+ return EFI_UNSUPPORTED;
+ }
+
+ VirtualNic = HTTP_BOOT_VIRTUAL_NIC_FROM_LOADFILE (This);
+ Private = VirtualNic->Private;
+
+ //
+ // Check media status before HTTP boot start
+ //
+ MediaStatus = EFI_SUCCESS;
+ NetLibDetectMediaWaitTimeout (Private->Controller, HTTP_BOOT_CHECK_MEDIA_WAITING_TIME, &MediaStatus);
+ if (MediaStatus != EFI_SUCCESS) {
+ AsciiPrint ("\n Error: Could not detect network connection.\n");
+ return EFI_NO_MEDIA;
+ }
+
+ //
+ // Check whether the virtual nic is using IPv6 or not.
+ //
+ UsingIpv6 = FALSE;
+ if (VirtualNic == Private->Ip6Nic) {
+ UsingIpv6 = TRUE;
+ }
+
+ //
+ // Initialize HTTP boot.
+ //
+ Status = HttpBootStart (Private, UsingIpv6, FilePath);
+ if (Status != EFI_SUCCESS && Status != EFI_ALREADY_STARTED) {
+ return Status;
+ }
+
+ //
+ // Load the boot file.
+ //
+ ImageType = ImageTypeMax;
+ Status = HttpBootLoadFile (Private, BufferSize, Buffer, &ImageType);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_BUFFER_TOO_SMALL && (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk)) {
+ Status = EFI_WARN_FILE_SYSTEM;
+ } else if (Status != EFI_BUFFER_TOO_SMALL) {
+ HttpBootStop (Private);
+ }
+ return Status;
+ }
+
+ //
+ // Register the RAM Disk to the system if needed.
+ //
+ if (ImageType == ImageTypeVirtualCd || ImageType == ImageTypeVirtualDisk) {
+ Status = HttpBootRegisterRamDisk (Private, *BufferSize, Buffer, ImageType);
+ if (!EFI_ERROR (Status)) {
+ Status = EFI_WARN_FILE_SYSTEM;
+ } else {
+ AsciiPrint ("\n Error: Could not register RAM disk to the system.\n");
+ }
+ }
+
+ //
+ // Stop the HTTP Boot service after the boot image is downloaded.
+ //
+ HttpBootStop (Private);
+ return Status;
+}
+
+///
+/// Load File Protocol instance
+///
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_LOAD_FILE_PROTOCOL gHttpBootDxeLoadFile = {
+ HttpBootDxeLoadFile
+};
+
+/**
+ Callback function that is invoked when the HTTP Boot driver is about to transmit or has received a
+ packet.
+
+ This function is invoked when the HTTP Boot driver is about to transmit or has received packet.
+ Parameters DataType and Received specify the type of event and the format of the buffer pointed
+ to by Data. Due to the polling nature of UEFI device drivers, this callback function should not
+ execute for more than 5 ms.
+ The returned status code determines the behavior of the HTTP Boot driver.
+
+ @param[in] This Pointer to the EFI_HTTP_BOOT_CALLBACK_PROTOCOL instance.
+ @param[in] DataType The event that occurs in the current state.
+ @param[in] Received TRUE if the callback is being invoked due to a receive event.
+ FALSE if the callback is being invoked due to a transmit event.
+ @param[in] DataLength The length in bytes of the buffer pointed to by Data.
+ @param[in] Data A pointer to the buffer of data, the data type is specified by
+ DataType.
+
+ @retval EFI_SUCCESS Tells the HTTP Boot driver to continue the HTTP Boot process.
+ @retval EFI_ABORTED Tells the HTTP Boot driver to abort the current HTTP Boot process.
+**/
+EFI_STATUS
+EFIAPI
+HttpBootCallback (
+ IN EFI_HTTP_BOOT_CALLBACK_PROTOCOL *This,
+ IN EFI_HTTP_BOOT_CALLBACK_DATA_TYPE DataType,
+ IN BOOLEAN Received,
+ IN UINT32 DataLength,
+ IN VOID *Data OPTIONAL
+ )
+{
+ EFI_HTTP_MESSAGE *HttpMessage;
+ EFI_HTTP_HEADER *HttpHeader;
+ HTTP_BOOT_PRIVATE_DATA *Private;
+ UINT32 Percentage;
+
+ Private = HTTP_BOOT_PRIVATE_DATA_FROM_CALLBACK_PROTOCOL(This);
+
+ switch (DataType) {
+ case HttpBootDhcp4:
+ case HttpBootDhcp6:
+ Print (L".");
+ break;
+
+ case HttpBootHttpRequest:
+ if (Data != NULL) {
+ HttpMessage = (EFI_HTTP_MESSAGE *) Data;
+ if (HttpMessage->Data.Request->Method == HttpMethodGet &&
+ HttpMessage->Data.Request->Url != NULL) {
+ Print (L"\n URI: %s\n", HttpMessage->Data.Request->Url);
+ }
+ }
+ break;
+
+ case HttpBootHttpResponse:
+ if (Data != NULL) {
+ HttpMessage = (EFI_HTTP_MESSAGE *) Data;
+
+ if (HttpMessage->Data.Response != NULL) {
+ if (HttpBootIsHttpRedirectStatusCode (HttpMessage->Data.Response->StatusCode)) {
+ //
+ // Server indicates the resource has been redirected to a different URL
+ // according to the section 6.4 of RFC7231 and the RFC 7538.
+ // Display the redirect information on the screen.
+ //
+ HttpHeader = HttpFindHeader (
+ HttpMessage->HeaderCount,
+ HttpMessage->Headers,
+ HTTP_HEADER_LOCATION
+ );
+ if (HttpHeader != NULL) {
+ Print (L"\n HTTP ERROR: Resource Redirected.\n New Location: %a\n", HttpHeader->FieldValue);
+ }
+ break;
+ }
+ }
+
+ HttpHeader = HttpFindHeader (
+ HttpMessage->HeaderCount,
+ HttpMessage->Headers,
+ HTTP_HEADER_CONTENT_LENGTH
+ );
+ if (HttpHeader != NULL) {
+ Private->FileSize = AsciiStrDecimalToUintn (HttpHeader->FieldValue);
+ Private->ReceivedSize = 0;
+ Private->Percentage = 0;
+ }
+ }
+ break;
+
+ case HttpBootHttpEntityBody:
+ if (DataLength != 0) {
+ if (Private->FileSize != 0) {
+ //
+ // We already know the file size, print in percentage format.
+ //
+ if (Private->ReceivedSize == 0) {
+ Print (L" File Size: %lu Bytes\n", Private->FileSize);
+ }
+ Private->ReceivedSize += DataLength;
+ Percentage = (UINT32) DivU64x64Remainder (MultU64x32 (Private->ReceivedSize, 100), Private->FileSize, NULL);
+ if (Private->Percentage != Percentage) {
+ Private->Percentage = Percentage;
+ Print (L"\r Downloading...%d%%", Percentage);
+ }
+ } else {
+ //
+ // In some case we couldn't get the file size from the HTTP header, so we
+ // just print the downloaded file size.
+ //
+ Private->ReceivedSize += DataLength;
+ Print (L"\r Downloading...%lu Bytes", Private->ReceivedSize);
+ }
+ }
+ break;
+
+ default:
+ break;
+ };
+
+ return EFI_SUCCESS;
+}
+
+///
+/// HTTP Boot Callback Protocol instance
+///
+GLOBAL_REMOVE_IF_UNREFERENCED
+EFI_HTTP_BOOT_CALLBACK_PROTOCOL gHttpBootDxeHttpBootCallback = {
+ HttpBootCallback
+};
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.h
new file mode 100644
index 00000000..0e0d8e26
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootImpl.h
@@ -0,0 +1,48 @@
+/** @file
+ The declaration of UEFI HTTP boot function.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+#ifndef __EFI_HTTP_BOOT_IMPL_H__
+#define __EFI_HTTP_BOOT_IMPL_H__
+
+#define HTTP_BOOT_CHECK_MEDIA_WAITING_TIME EFI_TIMER_PERIOD_SECONDS(20)
+
+/**
+ Attempt to complete a DHCPv4 D.O.R.A or DHCPv6 S.R.A.A sequence to retrieve the boot resource information.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS Boot info was successfully retrieved.
+ @retval EFI_INVALID_PARAMETER Private is NULL.
+ @retval EFI_NOT_STARTED The driver is in stopped state.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurred.
+ @retval Others Other errors as indicated.
+
+**/
+EFI_STATUS
+HttpBootDhcp (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+/**
+ Disable the use of UEFI HTTP boot function.
+
+ @param[in] Private The pointer to the driver's private data.
+
+ @retval EFI_SUCCESS HTTP boot was successfully disabled.
+ @retval EFI_NOT_STARTED The driver is already in stopped state.
+ @retval EFI_INVALID_PARAMETER Private is NULL.
+ @retval Others Unexpected error when stop the function.
+
+**/
+EFI_STATUS
+HttpBootStop (
+ IN HTTP_BOOT_PRIVATE_DATA *Private
+ );
+
+extern EFI_HTTP_BOOT_CALLBACK_PROTOCOL gHttpBootDxeHttpBootCallback;
+
+#endif
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.c b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.c
new file mode 100644
index 00000000..44966fe6
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.c
@@ -0,0 +1,779 @@
+/** @file
+ Support functions implementation for UEFI HTTP boot driver.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 - 2020 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "HttpBootDxe.h"
+
+
+/**
+ Get the Nic handle using any child handle in the IPv4 stack.
+
+ @param[in] ControllerHandle Pointer to child handle over IPv4.
+
+ @return NicHandle The pointer to the Nic handle.
+ @return NULL Can't find the Nic handle.
+
+**/
+EFI_HANDLE
+HttpBootGetNicByIp4Children (
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_HANDLE NicHandle;
+
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp4ProtocolGuid);
+ if (NicHandle == NULL) {
+ return NULL;
+ }
+ }
+
+ return NicHandle;
+}
+
+/**
+ Get the Nic handle using any child handle in the IPv6 stack.
+
+ @param[in] ControllerHandle Pointer to child handle over IPv6.
+
+ @return NicHandle The pointer to the Nic handle.
+ @return NULL Can't find the Nic handle.
+
+**/
+EFI_HANDLE
+HttpBootGetNicByIp6Children (
+ IN EFI_HANDLE ControllerHandle
+ )
+{
+ EFI_HANDLE NicHandle;
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiHttpProtocolGuid);
+ if (NicHandle == NULL) {
+ NicHandle = NetLibGetNicHandle (ControllerHandle, &gEfiDhcp6ProtocolGuid);
+ if (NicHandle == NULL) {
+ return NULL;
+ }
+ }
+
+ return NicHandle;
+}
+
+/**
+ This function is to convert UINTN to ASCII string with the required formatting.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer The pointer to the buffer for ASCII string.
+ @param[in] Length The length of the required format.
+
+**/
+VOID
+HttpBootUintnToAscDecWithFormat (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN INTN Length
+ )
+{
+ UINTN Remainder;
+
+ for (; Length > 0; Length--) {
+ Remainder = Number % 10;
+ Number /= 10;
+ Buffer[Length - 1] = (UINT8) ('0' + Remainder);
+ }
+}
+
+/**
+ This function is to display the IPv4 address.
+
+ @param[in] Ip The pointer to the IPv4 address.
+
+**/
+VOID
+HttpBootShowIp4Addr (
+ IN EFI_IPv4_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < 4; Index++) {
+ AsciiPrint ("%d", Ip->Addr[Index]);
+ if (Index < 3) {
+ AsciiPrint (".");
+ }
+ }
+}
+
+/**
+ This function is to display the IPv6 address.
+
+ @param[in] Ip The pointer to the IPv6 address.
+
+**/
+VOID
+HttpBootShowIp6Addr (
+ IN EFI_IPv6_ADDRESS *Ip
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < 16; Index++) {
+
+ if (Ip->Addr[Index] != 0) {
+ AsciiPrint ("%x", Ip->Addr[Index]);
+ }
+ Index++;
+ if (Index > 15) {
+ return;
+ }
+ if (((Ip->Addr[Index] & 0xf0) == 0) && (Ip->Addr[Index - 1] != 0)) {
+ AsciiPrint ("0");
+ }
+ AsciiPrint ("%x", Ip->Addr[Index]);
+ if (Index < 15) {
+ AsciiPrint (":");
+ }
+ }
+}
+
+/**
+ This function is to display the HTTP error status.
+
+ @param[in] StatusCode The status code value in HTTP message.
+
+**/
+VOID
+HttpBootPrintErrorMessage (
+ EFI_HTTP_STATUS_CODE StatusCode
+ )
+{
+ AsciiPrint ("\n");
+
+ switch (StatusCode) {
+ case HTTP_STATUS_300_MULTIPLE_CHOICES:
+ AsciiPrint ("\n Redirection: 300 Multiple Choices");
+ break;
+
+ case HTTP_STATUS_301_MOVED_PERMANENTLY:
+ AsciiPrint ("\n Redirection: 301 Moved Permanently");
+ break;
+
+ case HTTP_STATUS_302_FOUND:
+ AsciiPrint ("\n Redirection: 302 Found");
+ break;
+
+ case HTTP_STATUS_303_SEE_OTHER:
+ AsciiPrint ("\n Redirection: 303 See Other");
+ break;
+
+ case HTTP_STATUS_304_NOT_MODIFIED:
+ AsciiPrint ("\n Redirection: 304 Not Modified");
+ break;
+
+ case HTTP_STATUS_305_USE_PROXY:
+ AsciiPrint ("\n Redirection: 305 Use Proxy");
+ break;
+
+ case HTTP_STATUS_307_TEMPORARY_REDIRECT:
+ AsciiPrint ("\n Redirection: 307 Temporary Redirect");
+ break;
+
+ case HTTP_STATUS_308_PERMANENT_REDIRECT:
+ AsciiPrint ("\n Redirection: 308 Permanent Redirect");
+ break;
+
+ case HTTP_STATUS_400_BAD_REQUEST:
+ AsciiPrint ("\n Client Error: 400 Bad Request");
+ break;
+
+ case HTTP_STATUS_401_UNAUTHORIZED:
+ AsciiPrint ("\n Client Error: 401 Unauthorized");
+ break;
+
+ case HTTP_STATUS_402_PAYMENT_REQUIRED:
+ AsciiPrint ("\n Client Error: 402 Payment Required");
+ break;
+
+ case HTTP_STATUS_403_FORBIDDEN:
+ AsciiPrint ("\n Client Error: 403 Forbidden");
+ break;
+
+ case HTTP_STATUS_404_NOT_FOUND:
+ AsciiPrint ("\n Client Error: 404 Not Found");
+ break;
+
+ case HTTP_STATUS_405_METHOD_NOT_ALLOWED:
+ AsciiPrint ("\n Client Error: 405 Method Not Allowed");
+ break;
+
+ case HTTP_STATUS_406_NOT_ACCEPTABLE:
+ AsciiPrint ("\n Client Error: 406 Not Acceptable");
+ break;
+
+ case HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED:
+ AsciiPrint ("\n Client Error: 407 Proxy Authentication Required");
+ break;
+
+ case HTTP_STATUS_408_REQUEST_TIME_OUT:
+ AsciiPrint ("\n Client Error: 408 Request Timeout");
+ break;
+
+ case HTTP_STATUS_409_CONFLICT:
+ AsciiPrint ("\n Client Error: 409 Conflict");
+ break;
+
+ case HTTP_STATUS_410_GONE:
+ AsciiPrint ("\n Client Error: 410 Gone");
+ break;
+
+ case HTTP_STATUS_411_LENGTH_REQUIRED:
+ AsciiPrint ("\n Client Error: 411 Length Required");
+ break;
+
+ case HTTP_STATUS_412_PRECONDITION_FAILED:
+ AsciiPrint ("\n Client Error: 412 Precondition Failed");
+ break;
+
+ case HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE:
+ AsciiPrint ("\n Client Error: 413 Request Entity Too Large");
+ break;
+
+ case HTTP_STATUS_414_REQUEST_URI_TOO_LARGE:
+ AsciiPrint ("\n Client Error: 414 Request URI Too Long");
+ break;
+
+ case HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE:
+ AsciiPrint ("\n Client Error: 415 Unsupported Media Type");
+ break;
+
+ case HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED:
+ AsciiPrint ("\n Client Error: 416 Requested Range Not Satisfiable");
+ break;
+
+ case HTTP_STATUS_417_EXPECTATION_FAILED:
+ AsciiPrint ("\n Client Error: 417 Expectation Failed");
+ break;
+
+ case HTTP_STATUS_500_INTERNAL_SERVER_ERROR:
+ AsciiPrint ("\n Server Error: 500 Internal Server Error");
+ break;
+
+ case HTTP_STATUS_501_NOT_IMPLEMENTED:
+ AsciiPrint ("\n Server Error: 501 Not Implemented");
+ break;
+
+ case HTTP_STATUS_502_BAD_GATEWAY:
+ AsciiPrint ("\n Server Error: 502 Bad Gateway");
+ break;
+
+ case HTTP_STATUS_503_SERVICE_UNAVAILABLE:
+ AsciiPrint ("\n Server Error: 503 Service Unavailable");
+ break;
+
+ case HTTP_STATUS_504_GATEWAY_TIME_OUT:
+ AsciiPrint ("\n Server Error: 504 Gateway Timeout");
+ break;
+
+ case HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED:
+ AsciiPrint ("\n Server Error: 505 HTTP Version Not Supported");
+ break;
+
+ default: ;
+
+ }
+}
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Event The triggered event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpBootCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ *((BOOLEAN *) Context) = TRUE;
+}
+
+/**
+ Retrieve the host address using the EFI_DNS6_PROTOCOL.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] HostName Pointer to buffer containing hostname.
+ @param[out] IpAddress On output, pointer to buffer containing IPv6 address.
+
+ @retval EFI_SUCCESS Operation succeeded.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurred.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+HttpBootDns (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN CHAR16 *HostName,
+ OUT EFI_IPv6_ADDRESS *IpAddress
+ )
+{
+ EFI_STATUS Status;
+ EFI_DNS6_PROTOCOL *Dns6;
+ EFI_DNS6_CONFIG_DATA Dns6ConfigData;
+ EFI_DNS6_COMPLETION_TOKEN Token;
+ EFI_HANDLE Dns6Handle;
+ EFI_IP6_CONFIG_PROTOCOL *Ip6Config;
+ EFI_IPv6_ADDRESS *DnsServerList;
+ UINTN DnsServerListCount;
+ UINTN DataSize;
+ BOOLEAN IsDone;
+
+ DnsServerList = NULL;
+ DnsServerListCount = 0;
+ Dns6 = NULL;
+ Dns6Handle = NULL;
+ ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
+
+ //
+ // Get DNS server list from EFI IPv6 Configuration protocol.
+ //
+ Status = gBS->HandleProtocol (Private->Controller, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Get the required size.
+ //
+ DataSize = 0;
+ Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ DnsServerList = AllocatePool (DataSize);
+ if (DnsServerList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList);
+ if (EFI_ERROR (Status)) {
+ FreePool (DnsServerList);
+ DnsServerList = NULL;
+ } else {
+ DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS);
+ }
+ }
+ }
+ //
+ // Create a DNSv6 child instance and get the protocol.
+ //
+ Status = NetLibCreateServiceChild (
+ Private->Controller,
+ Private->Ip6Nic->ImageHandle,
+ &gEfiDns6ServiceBindingProtocolGuid,
+ &Dns6Handle
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Status = gBS->OpenProtocol (
+ Dns6Handle,
+ &gEfiDns6ProtocolGuid,
+ (VOID **) &Dns6,
+ Private->Ip6Nic->ImageHandle,
+ Private->Controller,
+ EFI_OPEN_PROTOCOL_BY_DRIVER
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Configure DNS6 instance for the DNS server address and protocol.
+ //
+ ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));
+ Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount;
+ Dns6ConfigData.DnsServerList = DnsServerList;
+ Dns6ConfigData.EnableDnsCache = TRUE;
+ Dns6ConfigData.Protocol = EFI_IP_PROTO_UDP;
+ IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp,&Private->StationIp.v6);
+ Status = Dns6->Configure (
+ Dns6,
+ &Dns6ConfigData
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ Token.Status = EFI_NOT_READY;
+ IsDone = FALSE;
+ //
+ // Create event to set the IsDone flag when name resolution is finished.
+ //
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ HttpBootCommonNotify,
+ &IsDone,
+ &Token.Event
+ );
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ //
+ // Start asynchronous name resolution.
+ //
+ Status = Dns6->HostNameToIp (Dns6, HostName, &Token);
+ if (EFI_ERROR (Status)) {
+ goto Exit;
+ }
+
+ while (!IsDone) {
+ Dns6->Poll (Dns6);
+ }
+
+ //
+ // Name resolution is done, check result.
+ //
+ Status = Token.Status;
+ if (!EFI_ERROR (Status)) {
+ if (Token.RspData.H2AData == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+ if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {
+ Status = EFI_DEVICE_ERROR;
+ goto Exit;
+ }
+ //
+ // We just return the first IPv6 address from DNS protocol.
+ //
+ IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
+ Status = EFI_SUCCESS;
+ }
+Exit:
+
+ if (Token.Event != NULL) {
+ gBS->CloseEvent (Token.Event);
+ }
+ if (Token.RspData.H2AData != NULL) {
+ if (Token.RspData.H2AData->IpList != NULL) {
+ FreePool (Token.RspData.H2AData->IpList);
+ }
+ FreePool (Token.RspData.H2AData);
+ }
+
+ if (Dns6 != NULL) {
+ Dns6->Configure (Dns6, NULL);
+
+ gBS->CloseProtocol (
+ Dns6Handle,
+ &gEfiDns6ProtocolGuid,
+ Private->Ip6Nic->ImageHandle,
+ Private->Controller
+ );
+ }
+
+ if (Dns6Handle != NULL) {
+ NetLibDestroyServiceChild (
+ Private->Controller,
+ Private->Ip6Nic->ImageHandle,
+ &gEfiDns6ServiceBindingProtocolGuid,
+ Dns6Handle
+ );
+ }
+
+ if (DnsServerList != NULL) {
+ FreePool (DnsServerList);
+ }
+
+ return Status;
+}
+
+/**
+ This function checks the HTTP(S) URI scheme.
+
+ @param[in] Uri The pointer to the URI string.
+
+ @retval EFI_SUCCESS The URI scheme is valid.
+ @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS.
+ @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP.
+
+**/
+EFI_STATUS
+HttpBootCheckUriScheme (
+ IN CHAR8 *Uri
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+
+ Status = EFI_SUCCESS;
+
+ //
+ // Convert the scheme to all lower case.
+ //
+ for (Index = 0; Index < AsciiStrLen (Uri); Index++) {
+ if (Uri[Index] == ':') {
+ break;
+ }
+ if (Uri[Index] >= 'A' && Uri[Index] <= 'Z') {
+ Uri[Index] -= (CHAR8)('A' - 'a');
+ }
+ }
+
+ //
+ // Return EFI_INVALID_PARAMETER if the URI is not HTTP or HTTPS.
+ //
+ if ((AsciiStrnCmp (Uri, "http://", 7) != 0) && (AsciiStrnCmp (Uri, "https://", 8) != 0)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: Invalid Uri.\n"));
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // HTTP is disabled, return EFI_ACCESS_DENIED if the URI is HTTP.
+ //
+ if (!PcdGetBool (PcdAllowHttpConnections) && (AsciiStrnCmp (Uri, "http://", 7) == 0)) {
+ DEBUG ((EFI_D_ERROR, "HttpBootCheckUriScheme: HTTP is disabled.\n"));
+ return EFI_ACCESS_DENIED;
+ }
+
+ return Status;
+}
+
+/**
+ Get the URI address string from the input device path.
+
+ Caller need to free the buffer in the UriAddress pointer.
+
+ @param[in] FilePath Pointer to the device path which contains a URI device path node.
+ @param[out] UriAddress The URI address string extract from the device path.
+
+ @retval EFI_SUCCESS The URI string is returned.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootParseFilePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT CHAR8 **UriAddress
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ URI_DEVICE_PATH *UriDevicePath;
+ CHAR8 *Uri;
+ UINTN UriStrLength;
+
+ if (FilePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *UriAddress = NULL;
+
+ //
+ // Extract the URI address from the FilePath
+ //
+ TempDevicePath = FilePath;
+ while (!IsDevicePathEnd (TempDevicePath)) {
+ if ((DevicePathType (TempDevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (TempDevicePath) == MSG_URI_DP)) {
+ UriDevicePath = (URI_DEVICE_PATH*) TempDevicePath;
+ //
+ // UEFI Spec doesn't require the URI to be a NULL-terminated string
+ // So we allocate a new buffer and always append a '\0' to it.
+ //
+ UriStrLength = DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL);
+ if (UriStrLength == 0) {
+ //
+ // return a NULL UriAddress if it's a empty URI device path node.
+ //
+ break;
+ }
+ Uri = AllocatePool (UriStrLength + 1);
+ if (Uri == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (Uri, UriDevicePath->Uri, DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL));
+ Uri[DevicePathNodeLength (UriDevicePath) - sizeof(EFI_DEVICE_PATH_PROTOCOL)] = '\0';
+
+ *UriAddress = Uri;
+ }
+ TempDevicePath = NextDevicePathNode (TempDevicePath);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ This function returns the image type according to server replied HTTP message
+ and also the image's URI info.
+
+ @param[in] Uri The pointer to the image's URI string.
+ @param[in] UriParser URI Parse result returned by NetHttpParseUrl().
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS The image type is returned in ImageType.
+ @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL.
+ @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL.
+ @retval EFI_NOT_FOUND Failed to identify the image type.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootCheckImageType (
+ IN CHAR8 *Uri,
+ IN VOID *UriParser,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ )
+{
+ EFI_STATUS Status;
+ EFI_HTTP_HEADER *Header;
+ CHAR8 *FilePath;
+ CHAR8 *FilePost;
+
+ if (Uri == NULL || UriParser == NULL || ImageType == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (HeaderCount != 0 && Headers == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Determine the image type by the HTTP Content-Type header field first.
+ // "application/efi" -> EFI Image
+ // "application/vnd.efi-iso" -> CD/DVD Image
+ // "application/vnd.efi-img" -> Virtual Disk Image
+ //
+ Header = HttpFindHeader (HeaderCount, Headers, HTTP_HEADER_CONTENT_TYPE);
+ if (Header != NULL) {
+ if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_EFI) == 0) {
+ *ImageType = ImageTypeEfi;
+ return EFI_SUCCESS;
+ } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_ISO) == 0) {
+ *ImageType = ImageTypeVirtualCd;
+ return EFI_SUCCESS;
+ } else if (AsciiStriCmp (Header->FieldValue, HTTP_CONTENT_TYPE_APP_IMG) == 0) {
+ *ImageType = ImageTypeVirtualDisk;
+ return EFI_SUCCESS;
+ }
+ }
+
+ //
+ // Determine the image type by file extension:
+ // *.efi -> EFI Image
+ // *.iso -> CD/DVD Image
+ // *.img -> Virtual Disk Image
+ //
+ Status = HttpUrlGetPath (
+ Uri,
+ UriParser,
+ &FilePath
+ );
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ FilePost = FilePath + AsciiStrLen (FilePath) - 4;
+ if (AsciiStrCmp (FilePost, ".efi") == 0) {
+ *ImageType = ImageTypeEfi;
+ } else if (AsciiStrCmp (FilePost, ".iso") == 0) {
+ *ImageType = ImageTypeVirtualCd;
+ } else if (AsciiStrCmp (FilePost, ".img") == 0) {
+ *ImageType = ImageTypeVirtualDisk;
+ } else {
+ *ImageType = ImageTypeMax;
+ }
+
+ FreePool (FilePath);
+
+ return (*ImageType < ImageTypeMax) ? EFI_SUCCESS : EFI_NOT_FOUND;
+}
+
+/**
+ This function register the RAM disk info to the system.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] BufferSize The size of Buffer in bytes.
+ @param[in] Buffer The base address of the RAM disk.
+ @param[in] ImageType The image type of the file in Buffer.
+
+ @retval EFI_SUCCESS The RAM disk has been registered.
+ @retval EFI_NOT_FOUND No RAM disk protocol instances were found.
+ @retval EFI_UNSUPPORTED The ImageType is not supported.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootRegisterRamDisk (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN BufferSize,
+ IN VOID *Buffer,
+ IN HTTP_BOOT_IMAGE_TYPE ImageType
+ )
+{
+ EFI_RAM_DISK_PROTOCOL *RamDisk;
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_GUID *RamDiskType;
+
+ ASSERT (Private != NULL);
+ ASSERT (Buffer != NULL);
+ ASSERT (BufferSize != 0);
+
+ Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID**) &RamDisk);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HTTP Boot: Couldn't find the RAM Disk protocol - %r\n", Status));
+ return Status;
+ }
+
+ if (ImageType == ImageTypeVirtualCd) {
+ RamDiskType = &gEfiVirtualCdGuid;
+ } else if (ImageType == ImageTypeVirtualDisk) {
+ RamDiskType = &gEfiVirtualDiskGuid;
+ } else {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = RamDisk->Register (
+ (UINTN)Buffer,
+ (UINT64)BufferSize,
+ RamDiskType,
+ Private->UsingIpv6 ? Private->Ip6Nic->DevicePath : Private->Ip4Nic->DevicePath,
+ &DevicePath
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "HTTP Boot: Failed to register RAM Disk - %r\n", Status));
+ }
+
+ return Status;
+}
+
+/**
+ Indicate if the HTTP status code indicates a redirection.
+
+ @param[in] StatusCode HTTP status code from server.
+
+ @return TRUE if it's redirection.
+
+**/
+BOOLEAN
+HttpBootIsHttpRedirectStatusCode (
+ IN EFI_HTTP_STATUS_CODE StatusCode
+ )
+{
+ if (StatusCode == HTTP_STATUS_301_MOVED_PERMANENTLY ||
+ StatusCode == HTTP_STATUS_302_FOUND ||
+ StatusCode == HTTP_STATUS_307_TEMPORARY_REDIRECT ||
+ StatusCode == HTTP_STATUS_308_PERMANENT_REDIRECT) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.h b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.h
new file mode 100644
index 00000000..ea1de45e
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/NetworkPkg/HttpBootDxe/HttpBootSupport.h
@@ -0,0 +1,215 @@
+/** @file
+ Support functions declaration for UEFI HTTP boot driver.
+
+Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2020 Hewlett-Packard Development Company, L.P.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef __EFI_HTTP_BOOT_SUPPORT_H__
+#define __EFI_HTTP_BOOT_SUPPORT_H__
+
+/**
+ Get the Nic handle using any child handle in the IPv4 stack.
+
+ @param[in] ControllerHandle Pointer to child handle over IPv4.
+
+ @return NicHandle The pointer to the Nic handle.
+ @return NULL Can't find the Nic handle.
+
+**/
+EFI_HANDLE
+HttpBootGetNicByIp4Children (
+ IN EFI_HANDLE ControllerHandle
+ );
+
+/**
+ Get the Nic handle using any child handle in the IPv6 stack.
+
+ @param[in] ControllerHandle Pointer to child handle over IPv6.
+
+ @return NicHandle The pointer to the Nic handle.
+ @return NULL Can't find the Nic handle.
+
+**/
+EFI_HANDLE
+HttpBootGetNicByIp6Children (
+ IN EFI_HANDLE ControllerHandle
+ );
+
+/**
+ This function is to convert UINTN to ASCII string with the required formatting.
+
+ @param[in] Number Numeric value to be converted.
+ @param[in] Buffer The pointer to the buffer for ASCII string.
+ @param[in] Length The length of the required format.
+
+**/
+VOID
+HttpBootUintnToAscDecWithFormat (
+ IN UINTN Number,
+ IN UINT8 *Buffer,
+ IN INTN Length
+ );
+
+
+/**
+ This function is to display the IPv4 address.
+
+ @param[in] Ip The pointer to the IPv4 address.
+
+**/
+VOID
+HttpBootShowIp4Addr (
+ IN EFI_IPv4_ADDRESS *Ip
+ );
+
+/**
+ This function is to display the IPv6 address.
+
+ @param[in] Ip The pointer to the IPv6 address.
+
+**/
+VOID
+HttpBootShowIp6Addr (
+ IN EFI_IPv6_ADDRESS *Ip
+ );
+
+/**
+ This function is to display the HTTP error status.
+
+ @param[in] StatusCode The status code value in HTTP message.
+
+**/
+VOID
+HttpBootPrintErrorMessage (
+ EFI_HTTP_STATUS_CODE StatusCode
+ );
+
+/**
+ Retrieve the host address using the EFI_DNS6_PROTOCOL.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] HostName Pointer to buffer containing hostname.
+ @param[out] IpAddress On output, pointer to buffer containing IPv6 address.
+
+ @retval EFI_SUCCESS Operation succeeded.
+ @retval EFI_DEVICE_ERROR An unexpected network error occurred.
+ @retval Others Other errors as indicated.
+**/
+EFI_STATUS
+HttpBootDns (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN CHAR16 *HostName,
+ OUT EFI_IPv6_ADDRESS *IpAddress
+ );
+
+/**
+ Notify the callback function when an event is triggered.
+
+ @param[in] Event The triggered event.
+ @param[in] Context The opaque parameter to the function.
+
+**/
+VOID
+EFIAPI
+HttpBootCommonNotify (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ This function checks the HTTP(S) URI scheme.
+
+ @param[in] Uri The pointer to the URI string.
+
+ @retval EFI_SUCCESS The URI scheme is valid.
+ @retval EFI_INVALID_PARAMETER The URI scheme is not HTTP or HTTPS.
+ @retval EFI_ACCESS_DENIED HTTP is disabled and the URI is HTTP.
+
+**/
+EFI_STATUS
+HttpBootCheckUriScheme (
+ IN CHAR8 *Uri
+ );
+
+/**
+ Get the URI address string from the input device path.
+
+ Caller need to free the buffer in the UriAddress pointer.
+
+ @param[in] FilePath Pointer to the device path which contains a URI device path node.
+ @param[out] UriAddress The URI address string extract from the device path.
+
+ @retval EFI_SUCCESS The URI string is returned.
+ @retval EFI_OUT_OF_RESOURCES Failed to allocate memory.
+
+**/
+EFI_STATUS
+HttpBootParseFilePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT CHAR8 **UriAddress
+ );
+
+/**
+ This function returns the image type according to server replied HTTP message
+ and also the image's URI info.
+
+ @param[in] Uri The pointer to the image's URI string.
+ @param[in] UriParser URI Parse result returned by NetHttpParseUrl().
+ @param[in] HeaderCount Number of HTTP header structures in Headers list.
+ @param[in] Headers Array containing list of HTTP headers.
+ @param[out] ImageType The image type of the downloaded file.
+
+ @retval EFI_SUCCESS The image type is returned in ImageType.
+ @retval EFI_INVALID_PARAMETER ImageType, Uri or UriParser is NULL.
+ @retval EFI_INVALID_PARAMETER HeaderCount is not zero, and Headers is NULL.
+ @retval EFI_NOT_FOUND Failed to identify the image type.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootCheckImageType (
+ IN CHAR8 *Uri,
+ IN VOID *UriParser,
+ IN UINTN HeaderCount,
+ IN EFI_HTTP_HEADER *Headers,
+ OUT HTTP_BOOT_IMAGE_TYPE *ImageType
+ );
+
+/**
+ This function register the RAM disk info to the system.
+
+ @param[in] Private The pointer to the driver's private data.
+ @param[in] BufferSize The size of Buffer in bytes.
+ @param[in] Buffer The base address of the RAM disk.
+ @param[in] ImageType The image type of the file in Buffer.
+
+ @retval EFI_SUCCESS The RAM disk has been registered.
+ @retval EFI_NOT_FOUND No RAM disk protocol instances were found.
+ @retval EFI_UNSUPPORTED The ImageType is not supported.
+ @retval Others Unexpected error happened.
+
+**/
+EFI_STATUS
+HttpBootRegisterRamDisk (
+ IN HTTP_BOOT_PRIVATE_DATA *Private,
+ IN UINTN BufferSize,
+ IN VOID *Buffer,
+ IN HTTP_BOOT_IMAGE_TYPE ImageType
+ );
+
+/**
+ Indicate if the HTTP status code indicates a redirection.
+
+ @param[in] StatusCode HTTP status code from server.
+
+ @return TRUE if it's redirection.
+
+**/
+BOOLEAN
+HttpBootIsHttpRedirectStatusCode (
+ IN EFI_HTTP_STATUS_CODE StatusCode
+ );
+#endif