From: Michael Chang <mchang@suse.com>
Date: Mon, 9 Jan 2023 18:30:35 -0500
Subject: Support UEFI networking protocols

References: fate#320130, bsc#1015589, bsc#1076132, rhbz#1732765
Co-authored-by: Peter Jones <pjones@redhat.com>
Co-authored-by: Sebastian Krahmer <krahmer@suse.com>
Co-authored-by: Javier Martinez Canillas <javierm@redhat.com>
Co-authored-by: Robbie Harwood <rharwood@redhat.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Michael Chang <mchang@suse.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Signed-off-by: Robbie Harwood <rharwood@redhat.com>
---
 grub-core/Makefile.core.def        |    6 +
 grub-core/io/bufio.c               |    2 +-
 grub-core/kern/efi/efi.c           |   96 ++-
 grub-core/net/drivers/efi/efinet.c |   28 +
 grub-core/net/efi/dhcp.c           |  357 +++++++++
 grub-core/net/efi/http.c           |  406 +++++++++++
 grub-core/net/efi/ip4_config.c     |  417 +++++++++++
 grub-core/net/efi/ip6_config.c     |  423 +++++++++++
 grub-core/net/efi/net.c            | 1413 ++++++++++++++++++++++++++++++++++++
 grub-core/net/efi/pxe.c            |  351 +++++++++
 grub-core/net/http.c               |    3 +
 grub-core/net/net.c                |   74 ++
 include/grub/efi/api.h             |  211 +++++-
 include/grub/efi/dhcp.h            |  347 +++++++++
 include/grub/efi/http.h            |  215 ++++++
 include/grub/net/efi.h             |  144 ++++
 util/grub-mknetdir.c               |    5 +-
 17 files changed, 4449 insertions(+), 49 deletions(-)
 create mode 100644 grub-core/net/efi/dhcp.c
 create mode 100644 grub-core/net/efi/http.c
 create mode 100644 grub-core/net/efi/ip4_config.c
 create mode 100644 grub-core/net/efi/ip6_config.c
 create mode 100644 grub-core/net/efi/net.c
 create mode 100644 grub-core/net/efi/pxe.c
 create mode 100644 include/grub/efi/dhcp.h
 create mode 100644 include/grub/efi/http.h
 create mode 100644 include/grub/net/efi.h

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 15be5db..044e53c 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2359,6 +2359,12 @@ module = {
   common = net/ethernet.c;
   common = net/arp.c;
   common = net/netbuff.c;
+  efi = net/efi/net.c;
+  efi = net/efi/http.c;
+  efi = net/efi/pxe.c;
+  efi = net/efi/ip4_config.c;
+  efi = net/efi/ip6_config.c;
+  efi = net/efi/dhcp.c;
 };
 
 module = {
diff --git a/grub-core/io/bufio.c b/grub-core/io/bufio.c
index a458c3a..1637731 100644
--- a/grub-core/io/bufio.c
+++ b/grub-core/io/bufio.c
@@ -139,7 +139,7 @@ grub_bufio_read (grub_file_t file, char *buf, grub_size_t len)
     return res;
 
   /* Need to read some more.  */
-  next_buf = (file->offset + res + len - 1) & ~((grub_off_t) bufio->block_size - 1);
+  next_buf = (grub_divmod64 (file->offset + res + len - 1, bufio->block_size, NULL)) * bufio->block_size;
   /* Now read between file->offset + res and bufio->buffer_at.  */
   if (file->offset + res < next_buf)
     {
diff --git a/grub-core/kern/efi/efi.c b/grub-core/kern/efi/efi.c
index b93ae3a..b674816 100644
--- a/grub-core/kern/efi/efi.c
+++ b/grub-core/kern/efi/efi.c
@@ -770,7 +770,7 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
 	      {
 		grub_efi_ipv4_device_path_t *ipv4
 		  = (grub_efi_ipv4_device_path_t *) dp;
-		grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)",
+		grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x",
 			     (unsigned) ipv4->local_ip_address[0],
 			     (unsigned) ipv4->local_ip_address[1],
 			     (unsigned) ipv4->local_ip_address[2],
@@ -783,33 +783,60 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
 			     (unsigned) ipv4->remote_port,
 			     (unsigned) ipv4->protocol,
 			     (unsigned) ipv4->static_ip_address);
+		if (len == sizeof (*ipv4))
+		  {
+		    grub_printf (",%u.%u.%u.%u,%u.%u.%u.%u",
+			(unsigned) ipv4->gateway_ip_address[0],
+			(unsigned) ipv4->gateway_ip_address[1],
+			(unsigned) ipv4->gateway_ip_address[2],
+			(unsigned) ipv4->gateway_ip_address[3],
+			(unsigned) ipv4->subnet_mask[0],
+			(unsigned) ipv4->subnet_mask[1],
+			(unsigned) ipv4->subnet_mask[2],
+			(unsigned) ipv4->subnet_mask[3]);
+		  }
+		grub_printf (")");
 	      }
 	      break;
 	    case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE:
 	      {
 		grub_efi_ipv6_device_path_t *ipv6
 		  = (grub_efi_ipv6_device_path_t *) dp;
-		grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)",
-			     (unsigned) ipv6->local_ip_address[0],
-			     (unsigned) ipv6->local_ip_address[1],
-			     (unsigned) ipv6->local_ip_address[2],
-			     (unsigned) ipv6->local_ip_address[3],
-			     (unsigned) ipv6->local_ip_address[4],
-			     (unsigned) ipv6->local_ip_address[5],
-			     (unsigned) ipv6->local_ip_address[6],
-			     (unsigned) ipv6->local_ip_address[7],
-			     (unsigned) ipv6->remote_ip_address[0],
-			     (unsigned) ipv6->remote_ip_address[1],
-			     (unsigned) ipv6->remote_ip_address[2],
-			     (unsigned) ipv6->remote_ip_address[3],
-			     (unsigned) ipv6->remote_ip_address[4],
-			     (unsigned) ipv6->remote_ip_address[5],
-			     (unsigned) ipv6->remote_ip_address[6],
-			     (unsigned) ipv6->remote_ip_address[7],
+		grub_printf ("/IPv6(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%u,%u,%x,%x",
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[0]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[1]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[2]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[3]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[4]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[5]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[6]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[7]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[0]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[1]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[2]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[3]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[4]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[5]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[6]),
+			     (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[7]),
 			     (unsigned) ipv6->local_port,
 			     (unsigned) ipv6->remote_port,
 			     (unsigned) ipv6->protocol,
 			     (unsigned) ipv6->static_ip_address);
+		if (len == sizeof (*ipv6))
+		  {
+		    grub_printf (",%u,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+			(unsigned) ipv6->prefix_length,
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[0]),
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[1]),
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[2]),
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[3]),
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[4]),
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[5]),
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[6]),
+			(unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[7]));
+		  }
+		grub_printf (")");
 	      }
 	      break;
 	    case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE:
@@ -856,6 +883,39 @@ grub_efi_print_device_path (grub_efi_device_path_t *dp)
 	      dump_vendor_path ("Messaging",
 				(grub_efi_vendor_device_path_t *) dp);
 	      break;
+	    case GRUB_EFI_URI_DEVICE_PATH_SUBTYPE:
+	      {
+		grub_efi_uri_device_path_t *uri
+		  = (grub_efi_uri_device_path_t *) dp;
+		grub_printf ("/URI(%s)", uri->uri);
+	      }
+	      break;
+	    case GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE:
+	      {
+		grub_efi_dns_device_path_t *dns
+		  = (grub_efi_dns_device_path_t *) dp;
+		if (dns->is_ipv6)
+		  {
+		    grub_printf ("/DNS(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x)",
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0]) >> 16),
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[0])),
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1]) >> 16),
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[1])),
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2]) >> 16),
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[2])),
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3]) >> 16),
+			    (grub_uint16_t)(grub_be_to_cpu32(dns->dns_server_ip[0].addr[3])));
+		  }
+		else
+		  {
+		    grub_printf ("/DNS(%d.%d.%d.%d)",
+			  dns->dns_server_ip[0].v4.addr[0],
+			  dns->dns_server_ip[0].v4.addr[1],
+			  dns->dns_server_ip[0].v4.addr[2],
+			  dns->dns_server_ip[0].v4.addr[3]);
+		  }
+	      }
+	      break;
 	    default:
 	      grub_printf ("/UnknownMessaging(%x)", (unsigned) subtype);
 	      break;
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
index 3a4a92f..69004d5 100644
--- a/grub-core/net/drivers/efi/efinet.c
+++ b/grub-core/net/drivers/efi/efinet.c
@@ -27,6 +27,7 @@
 #include <grub/lib/hexdump.h>
 #include <grub/types.h>
 #include <grub/net/netbuff.h>
+#include <grub/env.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -499,6 +500,18 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
   ldp->length = sizeof (*ldp);
 
   ldp = grub_efi_find_last_device_path (ddp);
+
+  /* Skip the DNS Device */
+  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+      && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE)
+    {
+      ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+      ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+      ldp->length = sizeof (*ldp);
+
+      ldp = grub_efi_find_last_device_path (ddp);
+    }
+
   if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
       || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
           && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
@@ -772,6 +785,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
 	if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
 	    || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
 		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE
+		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE
 		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
 	  continue;
 	dup_dp = grub_efi_duplicate_device_path (dp);
@@ -786,6 +800,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
 	    dup_ldp->length = sizeof (*dup_ldp);
 	  }
 
+	dup_ldp = grub_efi_find_last_device_path (dup_dp);
+	if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE)
+	  {
+	    dup_ldp = grub_efi_find_last_device_path (dup_dp);
+	    dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+	    dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+	    dup_ldp->length = sizeof (*dup_ldp);
+	  }
+
 	dup_ldp = grub_efi_find_last_device_path (dup_dp);
 	dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
 	dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
@@ -876,6 +899,9 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
 
 GRUB_MOD_INIT(efinet)
 {
+  if (grub_efi_net_config)
+    return;
+
   grub_efinet_findcards ();
   grub_efi_net_config = grub_efi_net_config_real;
 }
@@ -887,5 +913,7 @@ GRUB_MOD_FINI(efinet)
   FOR_NET_CARDS_SAFE (card, next)
     if (card->driver == &efidriver)
       grub_net_card_unregister (card);
+
+  grub_efi_net_config = NULL;
 }
 
diff --git a/grub-core/net/efi/dhcp.c b/grub-core/net/efi/dhcp.c
new file mode 100644
index 0000000..33dd228
--- /dev/null
+++ b/grub-core/net/efi/dhcp.c
@@ -0,0 +1,357 @@
+#include <grub/mm.h>
+#include <grub/command.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/misc.h>
+#include <grub/net/efi.h>
+#include <grub/charset.h>
+
+#ifdef GRUB_EFI_NET_DEBUG
+static void
+dhcp4_mode_print (grub_efi_dhcp4_mode_data_t *mode)
+{
+  switch (mode->state)
+    {
+    case GRUB_EFI_DHCP4_STOPPED:
+      grub_printf ("STATE: STOPPED\n");
+      break;
+    case GRUB_EFI_DHCP4_INIT:
+      grub_printf ("STATE: INIT\n");
+      break;
+    case GRUB_EFI_DHCP4_SELECTING:
+      grub_printf ("STATE: SELECTING\n");
+      break;
+    case GRUB_EFI_DHCP4_REQUESTING:
+      grub_printf ("STATE: REQUESTING\n");
+      break;
+    case GRUB_EFI_DHCP4_BOUND:
+      grub_printf ("STATE: BOUND\n");
+      break;
+    case GRUB_EFI_DHCP4_RENEWING:
+      grub_printf ("STATE: RENEWING\n");
+      break;
+    case GRUB_EFI_DHCP4_REBINDING:
+      grub_printf ("STATE: REBINDING\n");
+      break;
+    case GRUB_EFI_DHCP4_INIT_REBOOT:
+      grub_printf ("STATE: INIT_REBOOT\n");
+      break;
+    case GRUB_EFI_DHCP4_REBOOTING:
+      grub_printf ("STATE: REBOOTING\n");
+      break;
+    default:
+      grub_printf ("STATE: UNKNOWN\n");
+      break;
+    }
+
+  grub_printf ("CLIENT_ADDRESS: %u.%u.%u.%u\n",
+               mode->client_address[0],
+               mode->client_address[1],
+               mode->client_address[2],
+               mode->client_address[3]);
+  grub_printf ("SERVER_ADDRESS: %u.%u.%u.%u\n",
+               mode->server_address[0],
+               mode->server_address[1],
+               mode->server_address[2],
+               mode->server_address[3]);
+  grub_printf ("SUBNET_MASK: %u.%u.%u.%u\n",
+               mode->subnet_mask[0],
+               mode->subnet_mask[1],
+               mode->subnet_mask[2],
+               mode->subnet_mask[3]);
+  grub_printf ("ROUTER_ADDRESS: %u.%u.%u.%u\n",
+               mode->router_address[0],
+               mode->router_address[1],
+               mode->router_address[2],
+               mode->router_address[3]);
+}
+#endif
+
+static grub_efi_ipv4_address_t *
+grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packet_t *reply_packet)
+{
+  grub_efi_dhcp4_packet_option_t **option_list;
+  grub_efi_status_t status;
+  grub_efi_uint32_t option_count = 0;
+  grub_efi_uint32_t i;
+
+  status = dhcp4->parse (dhcp4, reply_packet, &option_count, NULL);
+
+  if (status != GRUB_EFI_BUFFER_TOO_SMALL)
+    return NULL;
+
+  option_list = grub_calloc (option_count, sizeof (*option_list));
+  if (!option_list)
+    return NULL;
+
+  status = dhcp4->parse ( dhcp4, reply_packet, &option_count, option_list);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (option_list);
+      return NULL;
+    }
+
+  for (i = 0; i < option_count; ++i)
+    {
+      if (option_list[i]->op_code == 6)
+	{
+	  grub_efi_ipv4_address_t *dns_address;
+
+	  if (((option_list[i]->length & 0x3) != 0) || (option_list[i]->length == 0))
+	    continue;
+
+	  /* We only contact primary dns */
+	  dns_address = grub_malloc (sizeof (*dns_address));
+	  if (!dns_address)
+	    {
+	      grub_free (option_list);
+	      return NULL;
+	    }
+	  grub_memcpy (dns_address, option_list[i]->data, sizeof (dns_address));
+	  grub_free (option_list);
+	  return dns_address;
+	}
+    }
+
+  grub_free (option_list);
+  return NULL;
+}
+
+static grub_err_t
+grub_cmd_efi_bootp (struct grub_command *cmd __attribute__ ((unused)),
+		    int argc,
+		    char **args)
+{
+  struct grub_efi_net_device *netdev;
+
+  for (netdev = net_devices; netdev; netdev = netdev->next)
+    {
+      grub_efi_status_t status;
+      grub_efi_dhcp4_mode_data_t mode;
+      grub_efi_dhcp4_config_data_t config;
+      grub_efi_dhcp4_packet_option_t *options;
+      grub_efi_ipv4_address_t *dns_address;
+      grub_efi_net_ip_manual_address_t net_ip;
+      grub_efi_net_ip_address_t ip_addr;
+      grub_efi_net_interface_t *inf = NULL;
+
+      if (argc > 0 && grub_strcmp (netdev->card_name, args[0]) != 0)
+	continue;
+
+      grub_memset (&config, 0, sizeof (config));
+
+      config.option_count = 1;
+      options = grub_malloc (sizeof (*options) + 2);
+      /* Parameter request list */
+      options->op_code = 55;
+      options->length = 3;
+      /* subnet mask */
+      options->data[0] = 1;
+      /* router */
+      options->data[1] = 3;
+      /* DNS */
+      options->data[2] = 6;
+      config.option_list = &options;
+
+      /* FIXME: What if the dhcp has bounded */
+      status = netdev->dhcp4->configure (netdev->dhcp4, &config);
+      grub_free (options);
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  grub_printf ("dhcp4 configure failed, %d\n", (int) status);
+	  continue;
+	}
+
+      status = netdev->dhcp4->start (netdev->dhcp4, NULL);
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  grub_printf ("dhcp4 start failed, %d\n", (int) status);
+	  continue;
+	}
+
+      status = netdev->dhcp4->get_mode_data (netdev->dhcp4, &mode);
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  grub_printf ("dhcp4 get mode failed, %d\n", (int) status);
+	  continue;
+	}
+
+#ifdef GRUB_EFI_NET_DEBUG
+      dhcp4_mode_print (&mode);
+#endif
+
+      for (inf = netdev->net_interfaces; inf; inf = inf->next)
+	if (inf->prefer_ip6 == 0)
+	  break;
+
+      grub_memcpy (net_ip.ip4.address, mode.client_address, sizeof (net_ip.ip4.address));
+      grub_memcpy (net_ip.ip4.subnet_mask, mode.subnet_mask, sizeof (net_ip.ip4.subnet_mask));
+
+      if (!inf)
+	{
+	  char *name = grub_xasprintf ("%s:dhcp", netdev->card_name);
+
+	  net_ip.is_ip6 = 0;
+	  inf = grub_efi_net_create_interface (netdev, name, &net_ip, 1);
+	  grub_free (name);
+	}
+      else
+	{
+	  efi_net_interface_set_address (inf, &net_ip, 1);
+	}
+
+      grub_memcpy (ip_addr.ip4, mode.router_address, sizeof (ip_addr.ip4));
+      efi_net_interface_set_gateway (inf, &ip_addr);
+
+      dns_address = grub_efi_dhcp4_parse_dns (netdev->dhcp4, mode.reply_packet);
+      if (dns_address)
+	efi_net_interface_set_dns (inf, (grub_efi_net_ip_address_t *) &dns_address);
+
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+
+static grub_err_t
+grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
+		    int argc, char **args)
+{
+  struct grub_efi_net_device *dev;
+  grub_efi_uint32_t ia_id;
+
+  for (dev = net_devices, ia_id = 0; dev; dev = dev->next, ia_id++)
+    {
+      grub_efi_dhcp6_config_data_t config;
+      grub_efi_dhcp6_packet_option_t *option_list[1];
+      grub_efi_dhcp6_packet_option_t *opt;
+      grub_efi_status_t status;
+      grub_efi_dhcp6_mode_data_t mode;
+      grub_efi_dhcp6_retransmission_t retrans;
+      grub_efi_net_ip_manual_address_t net_ip;
+      grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
+      grub_efi_net_interface_t *inf = NULL;
+
+      if (argc > 0 && grub_strcmp (dev->card_name, args[0]) != 0)
+	continue;
+
+      opt = grub_malloc (sizeof (*opt) + 2 * sizeof (grub_efi_uint16_t));
+
+#define GRUB_EFI_DHCP6_OPT_ORO 6
+
+      opt->op_code = grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_ORO);
+      opt->op_len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_efi_uint16_t));
+
+#define GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL 59
+#define GRUB_EFI_DHCP6_OPT_DNS_SERVERS 23
+
+      grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL));
+      grub_set_unaligned16 (opt->data + 1 * sizeof (grub_efi_uint16_t),
+	      grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_DNS_SERVERS));
+
+      option_list[0] = opt;
+      retrans.irt = 4;
+      retrans.mrc = 4;
+      retrans.mrt = 32;
+      retrans.mrd = 60;
+
+      config.dhcp6_callback = NULL;
+      config.callback_context = NULL;
+      config.option_count = 1;
+      config.option_list = option_list;
+      config.ia_descriptor.ia_id = ia_id;
+      config.ia_descriptor.type = GRUB_EFI_DHCP6_IA_TYPE_NA;
+      config.ia_info_event = NULL;
+      config.reconfigure_accept = 0;
+      config.rapid_commit = 0;
+      config.solicit_retransmission = &retrans;
+
+      status = dev->dhcp6->configure (dev->dhcp6, &config);
+      grub_free (opt);
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  grub_printf ("dhcp6 configure failed, %d\n", (int) status);
+	  continue;
+	}
+      status = dev->dhcp6->start (dev->dhcp6);
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  grub_printf ("dhcp6 start failed, %d\n", (int) status);
+	  continue;
+	}
+
+      status = dev->dhcp6->get_mode_data (dev->dhcp6, &mode, NULL);
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  grub_printf ("dhcp4 get mode failed, %d\n", (int) status);
+	  continue;
+	}
+
+      for (inf = dev->net_interfaces; inf; inf = inf->next)
+	if (inf->prefer_ip6 == 1)
+	  break;
+
+      grub_memcpy (net_ip.ip6.address, mode.ia->ia_address[0].ip_address, sizeof (net_ip.ip6.address));
+      net_ip.ip6.prefix_length = 64;
+      net_ip.ip6.is_anycast = 0;
+      net_ip.is_ip6 = 1;
+
+      if (!inf)
+	{
+	  char *name = grub_xasprintf ("%s:dhcp", dev->card_name);
+
+	  inf = grub_efi_net_create_interface (dev, name, &net_ip, 1);
+	  grub_free (name);
+	}
+      else
+	{
+	  efi_net_interface_set_address (inf, &net_ip, 1);
+	}
+
+      {
+	grub_efi_uint32_t count = 0;
+	grub_efi_dhcp6_packet_option_t **options = NULL;
+	grub_efi_uint32_t i;
+
+	status = dev->dhcp6->parse (dev->dhcp6, mode.ia->reply_packet, &count, NULL);
+
+	if (status == GRUB_EFI_BUFFER_TOO_SMALL && count)
+	  {
+	    options = grub_calloc (count, sizeof (*options));
+	    if (options)
+	      status = dev->dhcp6->parse (dev->dhcp6, mode.ia->reply_packet, &count, options);
+	    else
+	      status = GRUB_EFI_OUT_OF_RESOURCES;
+	  }
+
+	if (status != GRUB_EFI_SUCCESS)
+	  {
+	    if (options)
+	      grub_free (options);
+	    continue;
+	  }
+
+	for (i = 0; i < count; ++i)
+	  {
+	    if (options[i]->op_code == grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_DNS_SERVERS))
+	      {
+		grub_efi_net_ip_address_t dns;
+		grub_memcpy (dns.ip6, options[i]->data, sizeof (net_ip.ip6));
+		efi_net_interface_set_dns (inf, &dns);
+		break;
+	      }
+	  }
+
+	if (options)
+	  grub_free (options);
+      }
+
+      b->free_pool (mode.client_id);
+      b->free_pool (mode.ia);
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+grub_command_func_t grub_efi_net_bootp = grub_cmd_efi_bootp;
+grub_command_func_t grub_efi_net_bootp6 = grub_cmd_efi_bootp6;
diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
new file mode 100644
index 0000000..359231b
--- /dev/null
+++ b/grub-core/net/efi/http.c
@@ -0,0 +1,406 @@
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/misc.h>
+#include <grub/net/efi.h>
+#include <grub/charset.h>
+
+static void
+http_configure (struct grub_efi_net_device *dev, int prefer_ip6)
+{
+  grub_efi_http_config_data_t http_config;
+  grub_efi_httpv4_access_point_t httpv4_node;
+  grub_efi_httpv6_access_point_t httpv6_node;
+  grub_efi_status_t status;
+
+  grub_efi_http_t *http = dev->http;
+
+  grub_memset (&http_config, 0, sizeof (http_config));
+  http_config.http_version = GRUB_EFI_HTTPVERSION11;
+  http_config.timeout_millisec = 5000;
+
+  if (prefer_ip6)
+    {
+      grub_efi_uintn_t sz;
+      grub_efi_ip6_config_manual_address_t manual_address;
+
+      http_config.local_address_is_ipv6 = 1;
+      sz = sizeof (manual_address);
+      status = dev->ip6_config->get_data (dev->ip6_config,
+                           GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
+                           &sz, &manual_address);
+
+      if (status == GRUB_EFI_NOT_FOUND)
+	{
+	  grub_printf ("The MANUAL ADDRESS is not found\n");
+	}
+
+      /* The manual interface would return BUFFER TOO SMALL !!! */
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  grub_printf ("??? %d\n",(int) status);
+	  return;
+	}
+
+      grub_memcpy (httpv6_node.local_address, manual_address.address, sizeof (httpv6_node.local_address));
+      httpv6_node.local_port = 0;
+      http_config.access_point.ipv6_node = &httpv6_node;
+    }
+  else
+    {
+      http_config.local_address_is_ipv6 = 0;
+      grub_memset (&httpv4_node, 0, sizeof (httpv4_node));
+      httpv4_node.use_default_address = 1;
+
+      /* Use random port here */
+      /* See TcpBind() in edk2/NetworkPkg/TcpDxe/TcpDispatcher.c */
+      httpv4_node.local_port = 0;
+      http_config.access_point.ipv4_node = &httpv4_node;
+    }
+
+  status = http->configure (http, &http_config);
+
+  if (status == GRUB_EFI_ALREADY_STARTED)
+    {
+      return;
+    }
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_error (GRUB_ERR_IO, N_("couldn't configure http protocol, reason: %d"), (int) status);
+      grub_print_error ();
+      return ;
+    }
+}
+
+static grub_efi_boolean_t request_callback_done;
+static grub_efi_boolean_t response_callback_done;
+
+static void __grub_efi_api
+grub_efi_http_request_callback (grub_efi_event_t event __attribute__ ((unused)),
+				void *context __attribute__ ((unused)))
+{
+  request_callback_done = 1;
+}
+
+static void __grub_efi_api
+grub_efi_http_response_callback (grub_efi_event_t event __attribute__ ((unused)),
+                                 void *context __attribute__ ((unused)))
+{
+  response_callback_done = 1;
+}
+
+static grub_err_t
+efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, int headeronly, grub_off_t *file_size)
+{
+  grub_efi_http_request_data_t request_data;
+  grub_efi_http_message_t request_message;
+  grub_efi_http_token_t request_token;
+  grub_efi_http_response_data_t response_data;
+  grub_efi_http_message_t response_message;
+  grub_efi_http_token_t response_token;
+  grub_efi_http_header_t request_headers[3];
+
+  grub_efi_status_t status;
+  grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
+  char *url = NULL;
+
+  request_headers[0].field_name = (grub_efi_char8_t *) "Host";
+  request_headers[0].field_value = (grub_efi_char8_t *) server;
+  request_headers[1].field_name = (grub_efi_char8_t *) "Accept";
+  request_headers[1].field_value = (grub_efi_char8_t *) "*/*";
+  request_headers[2].field_name = (grub_efi_char8_t *) "User-Agent";
+  request_headers[2].field_value = (grub_efi_char8_t *) "UefiHttpBoot/1.0";
+
+  {
+    grub_efi_ipv6_address_t address;
+    const char *rest;
+    grub_efi_char16_t *ucs2_url;
+    grub_size_t url_len, ucs2_url_len;
+    const char *protocol = (use_https == 1) ? "https" : "http";
+
+    if (grub_efi_string_to_ip6_address (server, &address, &rest) && *rest == 0)
+      url = grub_xasprintf ("%s://[%s]%s", protocol, server, name);
+    else
+      url = grub_xasprintf ("%s://%s%s", protocol, server, name);
+
+    if (!url)
+      {
+	return grub_errno;
+      }
+
+    url_len = grub_strlen (url);
+    ucs2_url_len = url_len * GRUB_MAX_UTF16_PER_UTF8;
+    ucs2_url = grub_malloc ((ucs2_url_len + 1) * sizeof (ucs2_url[0]));
+
+    if (!ucs2_url)
+      {
+	grub_free (url);
+	return grub_errno;
+      }
+
+    /* convert string format from ascii to usc2 */
+    ucs2_url_len = grub_utf8_to_utf16 (ucs2_url, ucs2_url_len,
+                                       (grub_uint8_t *) url, url_len, NULL);
+    ucs2_url[ucs2_url_len] = 0;
+    grub_free (url);
+    request_data.url = ucs2_url;
+  }
+
+  request_data.method = (headeronly > 0) ? GRUB_EFI_HTTPMETHODHEAD : GRUB_EFI_HTTPMETHODGET;
+
+  request_message.data.request = &request_data;
+  request_message.header_count = 3;
+  request_message.headers = request_headers;
+  request_message.body_length = 0;
+  request_message.body = NULL;
+
+  /* request token */
+  request_token.event = NULL;
+  request_token.status = GRUB_EFI_NOT_READY;
+  request_token.message = &request_message;
+
+  request_callback_done = 0;
+  status = b->create_event (GRUB_EFI_EVT_NOTIFY_SIGNAL,
+                       GRUB_EFI_TPL_CALLBACK,
+                       grub_efi_http_request_callback,
+                       NULL,
+                       &request_token.event);
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (request_data.url);
+      return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%"PRIxGRUB_EFI_UINTN_T"\n", status);
+    }
+
+  status = http->request (http, &request_token);
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      b->close_event (request_token.event);
+      grub_free (request_data.url);
+      return grub_error (GRUB_ERR_IO, "Fail to send a request! status=0x%"PRIxGRUB_EFI_UINTN_T"\n", status);
+    }
+  while (!request_callback_done)
+    http->poll (http);
+
+  response_data.status_code = GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS;
+  response_message.data.response = &response_data;
+  /* herader_count will be updated by the HTTP driver on response */
+  response_message.header_count = 0;
+  /* headers will be populated by the driver on response */
+  response_message.headers = NULL;
+  /* use zero BodyLength to only receive the response headers */
+  response_message.body_length = 0;
+  response_message.body = NULL;
+  response_token.event = NULL;
+
+  status = b->create_event (GRUB_EFI_EVT_NOTIFY_SIGNAL,
+                       GRUB_EFI_TPL_CALLBACK,
+                       grub_efi_http_response_callback,
+                       NULL,
+                       &response_token.event);
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      b->close_event (request_token.event);
+      grub_free (request_data.url);
+      return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%"PRIxGRUB_EFI_UINTN_T"\n", status);
+    }
+
+  response_token.status = GRUB_EFI_SUCCESS;
+  response_token.message = &response_message;
+
+  /* wait for HTTP response */
+  response_callback_done = 0;
+  status = http->response (http, &response_token);
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      b->close_event (response_token.event);
+      b->close_event (request_token.event);
+      grub_free (request_data.url);
+      return grub_error (GRUB_ERR_IO, "Fail to receive a response! status=%d\n", (int) status);
+    }
+
+  while (!response_callback_done)
+    http->poll (http);
+
+  if (response_message.data.response->status_code != GRUB_EFI_HTTP_STATUS_200_OK)
+    {
+      grub_efi_http_status_code_t status_code = response_message.data.response->status_code;
+
+      if (response_message.headers)
+	b->free_pool (response_message.headers);
+      b->close_event (response_token.event);
+      b->close_event (request_token.event);
+      grub_free (request_data.url);
+      if (status_code == GRUB_EFI_HTTP_STATUS_404_NOT_FOUND)
+	{
+	  return grub_error (GRUB_ERR_FILE_NOT_FOUND, _("file `%s' not found"), name);
+	}
+      else
+	{
+	  return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR,
+                             _("unsupported uefi http status code 0x%x"), status_code);
+	}
+    }
+
+  if (file_size)
+    {
+      int i;
+      /* parse the length of the file from the ContentLength header */
+      for (*file_size = 0, i = 0; i < (int) response_message.header_count; ++i)
+	{
+	  if (!grub_strcmp ((const char*) response_message.headers[i].field_name, "Content-Length"))
+	    {
+	      *file_size = grub_strtoul((const char*)response_message.headers[i].field_value, 0, 10);
+	      break;
+	    }
+	}
+    }
+
+  if (response_message.headers)
+    b->free_pool (response_message.headers);
+  b->close_event (response_token.event);
+  b->close_event (request_token.event);
+  grub_free (request_data.url);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_ssize_t
+efihttp_read (struct grub_efi_net_device *dev, char *buf, grub_size_t len)
+{
+  grub_efi_http_message_t response_message;
+  grub_efi_http_token_t response_token;
+
+  grub_efi_status_t status;
+  grub_size_t sum = 0;
+  grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
+  grub_efi_http_t *http = dev->http;
+
+  if (!len)
+    {
+      grub_error (GRUB_ERR_BUG, "Invalid arguments to EFI HTTP Read");
+      return -1;
+    }
+
+  b->create_event (GRUB_EFI_EVT_NOTIFY_SIGNAL,
+              GRUB_EFI_TPL_CALLBACK,
+              grub_efi_http_response_callback,
+              NULL,
+              &response_token.event);
+
+  while (len)
+    {
+      response_message.data.response = NULL;
+      response_message.header_count = 0;
+      response_message.headers = NULL;
+      response_message.body_length = len;
+      response_message.body = buf;
+
+      response_token.message = &response_message;
+      response_token.status = GRUB_EFI_NOT_READY;
+
+      response_callback_done = 0;
+
+      status = http->response (http, &response_token);
+      if (status != GRUB_EFI_SUCCESS)
+	{
+	  b->close_event (response_token.event);
+	  grub_error (GRUB_ERR_IO, "Error! status=%d\n", (int) status);
+	  return -1;
+	}
+
+      while (!response_callback_done)
+	http->poll (http);
+
+      sum += response_message.body_length;
+      buf += response_message.body_length;
+      len -= response_message.body_length;
+    }
+
+  b->close_event (response_token.event);
+
+  return sum;
+}
+
+static grub_err_t
+grub_efihttp_open (struct grub_efi_net_device *dev,
+		  int prefer_ip6 __attribute__ ((unused)),
+		  grub_file_t file,
+		  const char *filename __attribute__ ((unused)),
+		  int type)
+{
+  grub_err_t err;
+  grub_off_t size = 0;
+  char *buf = NULL;
+
+  err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 1, 0);
+  if (err != GRUB_ERR_NONE)
+    return err;
+
+  err = efihttp_request (dev->http, file->device->net->server, file->device->net->name, type, 0, &size);
+  if (err != GRUB_ERR_NONE)
+    return err;
+
+  if (size)
+    {
+      buf = grub_malloc (size);
+      efihttp_read (dev, buf, size);
+    }
+
+  file->size = size;
+  file->data = buf;
+  file->not_easily_seekable = 0;
+  file->device->net->offset = 0;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_efihttp_close (struct grub_efi_net_device *dev __attribute__ ((unused)),
+		    int prefer_ip6 __attribute__ ((unused)),
+		    grub_file_t file)
+{
+  if (file->data)
+    grub_free (file->data);
+
+  file->data = 0;
+  file->offset = 0;
+  file->size = 0;
+  file->device->net->offset = 0;
+  return GRUB_ERR_NONE;
+}
+
+static grub_ssize_t
+grub_efihttp_read (struct grub_efi_net_device *dev __attribute__((unused)),
+		  int prefer_ip6 __attribute__((unused)),
+		  grub_file_t file,
+		  char *buf,
+		  grub_size_t len)
+{
+  grub_size_t r = len;
+
+  if (!file->data || !buf || !len)
+    return 0;
+
+  if ((file->device->net->offset + len) > file->size)
+    r = file->size - file->device->net->offset;
+
+  if (r)
+    {
+      grub_memcpy (buf, (char *) file->data + file->device->net->offset, r);
+      file->device->net->offset += r;
+    }
+
+  return r;
+}
+
+struct grub_efi_net_io io_http =
+  {
+    .configure = http_configure,
+    .open = grub_efihttp_open,
+    .read = grub_efihttp_read,
+    .close = grub_efihttp_close
+  };
diff --git a/grub-core/net/efi/ip4_config.c b/grub-core/net/efi/ip4_config.c
new file mode 100644
index 0000000..891333d
--- /dev/null
+++ b/grub-core/net/efi/ip4_config.c
@@ -0,0 +1,417 @@
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/misc.h>
+#include <grub/net/efi.h>
+#include <grub/charset.h>
+#include <grub/safemath.h>
+
+char *
+grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address)
+{
+  char *hw_addr, *p;
+  grub_size_t sz, s, i;
+
+  if (grub_mul (hw_address_size, sizeof ("XX:") - 1, &sz) ||
+      grub_add (sz, 1, &sz))
+    {
+      grub_errno = GRUB_ERR_OUT_OF_RANGE;
+      return NULL;
+    }
+
+  hw_addr = grub_malloc (sz);
+  if (!hw_addr)
+    return NULL;
+
+  p = hw_addr;
+  s = sz;
+  for (i = 0; i < hw_address_size; i++)
+    {
+      grub_snprintf (p, sz, "%02x:", hw_address[i]);
+      p += sizeof ("XX:") - 1;
+      s -= sizeof ("XX:") - 1;
+    }
+
+  hw_addr[sz - 2] = '\0';
+  return hw_addr;
+}
+
+char *
+grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address)
+{
+  char *addr;
+
+  addr = grub_malloc (sizeof ("XXX.XXX.XXX.XXX"));
+  if (!addr)
+      return NULL;
+
+  grub_snprintf (addr,
+                 sizeof ("XXX.XXX.XXX.XXX"),
+                 "%u.%u.%u.%u",
+                 (*address)[0],
+                 (*address)[1],
+                 (*address)[2],
+                 (*address)[3]);
+
+  return addr;
+}
+
+int
+grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest)
+{
+  grub_uint32_t newip = 0;
+  grub_size_t i, ncolon = 0;
+  const char *ptr = val;
+
+  /* Check that is not an IPv6 address */
+  for (i = 0; i < grub_strlen (ptr); i++)
+    {
+      if (ptr[i] == '[' && i == 0)
+        return 0;
+
+      if (ptr[i] == ':')
+          if (i == 0 || ++ncolon == 2)
+            return 0;
+    }
+
+  for (i = 0; i < 4; i++)
+    {
+      unsigned long t;
+      t = grub_strtoul (ptr, (const char **) &ptr, 0);
+      if (grub_errno)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  return 0;
+	}
+      if (*ptr != '.' && i == 0)
+	{
+	  /* XXX: t is in host byte order */
+	  newip = t;
+	  break;
+	}
+      if (t & ~0xff)
+	return 0;
+      newip <<= 8;
+      newip |= t;
+      if (i != 3 && *ptr != '.')
+	return 0;
+      ptr++;
+    }
+
+  newip = grub_cpu_to_be32 (newip);
+
+  grub_memcpy (address, &newip, sizeof (*address));
+
+  if (rest)
+    *rest = ptr - 1;
+  return 1;
+}
+
+static grub_efi_ip4_config2_interface_info_t *
+efi_ip4_config_interface_info (grub_efi_ip4_config2_protocol_t *ip4_config)
+{
+  grub_efi_uintn_t sz;
+  grub_efi_status_t status;
+  grub_efi_ip4_config2_interface_info_t *interface_info;
+
+  sz = sizeof (*interface_info) + sizeof (*interface_info->route_table);
+  interface_info = grub_malloc (sz);
+  if (!interface_info)
+    return NULL;
+
+  status = ip4_config->get_data (ip4_config,
+                       GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
+                       &sz, interface_info);
+
+  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
+    {
+      grub_free (interface_info);
+      interface_info = grub_malloc (sz);
+      status = ip4_config->get_data (ip4_config,
+                           GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
+                           &sz, interface_info);
+    }
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (interface_info);
+      return NULL;
+    }
+
+  return interface_info;
+}
+
+static grub_efi_ip4_config2_manual_address_t *
+efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config)
+{
+  grub_efi_uintn_t sz;
+  grub_efi_status_t status;
+  grub_efi_ip4_config2_manual_address_t *manual_address;
+
+  sz = sizeof (*manual_address);
+  manual_address = grub_malloc (sz);
+  if (!manual_address)
+    return NULL;
+
+  status = ip4_config->get_data (ip4_config,
+                       GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
+                       &sz, manual_address);
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (manual_address);
+      return NULL;
+    }
+
+  return manual_address;
+}
+
+char *
+grub_efi_ip4_interface_name (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip4_config2_interface_info_t *interface_info;
+  char *name;
+
+  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
+
+  if (!interface_info)
+    return NULL;
+
+  name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE
+		      * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name,
+                       GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0;
+  grub_free (interface_info);
+  return name;
+}
+
+static char *
+grub_efi_ip4_interface_hw_address (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip4_config2_interface_info_t *interface_info;
+  char *hw_addr;
+
+  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
+
+  if (!interface_info)
+    return NULL;
+
+  hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address);
+  grub_free (interface_info);
+
+  return hw_addr;
+}
+
+static char *
+grub_efi_ip4_interface_address (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip4_config2_manual_address_t *manual_address;
+  char *addr;
+
+  manual_address = efi_ip4_config_manual_address (dev->ip4_config);
+
+  if (!manual_address)
+    return NULL;
+
+  addr = grub_efi_ip4_address_to_string (&manual_address->address);
+  grub_free (manual_address);
+  return addr;
+}
+
+
+static int
+address_mask_size (grub_efi_ipv4_address_t *address)
+{
+  grub_uint8_t i;
+  grub_uint32_t u32_addr = grub_be_to_cpu32 (grub_get_unaligned32 (address));
+
+  if (u32_addr == 0)
+    return 0;
+
+  for (i = 0; i < 32; ++i)
+    {
+      if (u32_addr == ((0xffffffff >> i) << i))
+	return (32 - i);
+    }
+
+  return -1;
+}
+
+static char **
+grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip4_config2_interface_info_t *interface_info;
+  char **ret;
+  int id;
+  grub_size_t i, nmemb;
+
+  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
+  if (!interface_info)
+    return NULL;
+
+  if (grub_add (interface_info->route_table_size, 1, &nmemb))
+    {
+      grub_errno = GRUB_ERR_OUT_OF_RANGE;
+      return NULL;
+    }
+
+  ret = grub_calloc (nmemb, sizeof (*ret));
+  if (!ret)
+    {
+      grub_free (interface_info);
+      return NULL;
+    }
+
+  id = 0;
+  for (i = 0; i < interface_info->route_table_size; i++)
+    {
+      char *subnet, *gateway, *mask;
+      grub_uint32_t u32_subnet, u32_gateway;
+      int mask_size;
+      grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i;
+      grub_efi_net_interface_t *inf;
+      char *interface_name = NULL;
+
+      for (inf = dev->net_interfaces; inf; inf = inf->next)
+	if (!inf->prefer_ip6)
+	  interface_name = inf->name;
+
+      u32_gateway = grub_get_unaligned32 (&route_table->gateway_address);
+      gateway = grub_efi_ip4_address_to_string (&route_table->gateway_address);
+      u32_subnet = grub_get_unaligned32 (&route_table->subnet_address);
+      subnet = grub_efi_ip4_address_to_string (&route_table->subnet_address);
+      mask_size = address_mask_size (&route_table->subnet_mask);
+      mask = grub_efi_ip4_address_to_string (&route_table->subnet_mask);
+      if (u32_subnet && !u32_gateway && interface_name)
+	ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, subnet, mask_size, interface_name);
+      else if (u32_subnet && u32_gateway)
+	ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, subnet, mask_size, gateway);
+      else if (!u32_subnet && u32_gateway)
+	ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, subnet, mask_size, gateway);
+      grub_free (subnet);
+      grub_free (gateway);
+      grub_free (mask);
+    }
+
+  ret[id] = NULL;
+  grub_free (interface_info);
+  return ret;
+}
+
+static grub_efi_net_interface_t *
+grub_efi_ip4_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address)
+{
+  grub_efi_ip4_config2_interface_info_t *interface_info;
+  grub_efi_net_interface_t *inf;
+  int i;
+  grub_efi_ipv4_address_t *address = &ip_address->ip4;
+
+  interface_info = efi_ip4_config_interface_info (dev->ip4_config);
+  if (!interface_info)
+    return NULL;
+
+  for (i = 0; i < (int) interface_info->route_table_size; i++)
+    {
+      grub_efi_ip4_route_table_t *route_table = interface_info->route_table + i;
+      grub_uint32_t u32_address, u32_mask, u32_subnet;
+
+      u32_address = grub_get_unaligned32 (address);
+      u32_subnet = grub_get_unaligned32 (route_table->subnet_address);
+      u32_mask = grub_get_unaligned32 (route_table->subnet_mask);
+
+      /* SKIP Default GATEWAY */
+      if (!u32_subnet && !u32_mask)
+	continue;
+
+      if ((u32_address & u32_mask) == u32_subnet)
+	{
+	  for (inf = dev->net_interfaces; inf; inf = inf->next)
+	    if (!inf->prefer_ip6)
+	      {
+		grub_free (interface_info);
+		return inf;
+	      }
+	}
+    }
+
+  grub_free (interface_info);
+  return NULL;
+}
+
+static int
+grub_efi_ip4_interface_set_manual_address (struct grub_efi_net_device *dev,
+	    grub_efi_net_ip_manual_address_t *net_ip,
+	    int with_subnet)
+{
+  grub_efi_status_t status;
+  grub_efi_ip4_config2_manual_address_t *address = &net_ip->ip4;
+
+  if (!with_subnet)
+    {
+      grub_efi_ip4_config2_manual_address_t *manual_address =
+        efi_ip4_config_manual_address (dev->ip4_config);
+
+      if (manual_address)
+	{
+	  grub_memcpy (address->subnet_mask, manual_address->subnet_mask, sizeof (address->subnet_mask));
+	  grub_free (manual_address);
+	}
+      else
+	{
+	  address->subnet_mask[0] = 0xff;
+	  address->subnet_mask[1] = 0xff;
+	  address->subnet_mask[2] = 0xff;
+	  address->subnet_mask[3] = 0;
+	}
+    }
+
+  status = dev->ip4_config->set_data (dev->ip4_config,
+                       GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
+                       sizeof (*address), address);
+
+  if (status != GRUB_EFI_SUCCESS)
+    return 0;
+
+  return 1;
+}
+
+static int
+grub_efi_ip4_interface_set_gateway (struct grub_efi_net_device *dev,
+	      grub_efi_net_ip_address_t *address)
+{
+  grub_efi_status_t status;
+
+  status = dev->ip4_config->set_data (dev->ip4_config,
+                       GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY,
+                       sizeof (address->ip4), &address->ip4);
+
+  if (status != GRUB_EFI_SUCCESS)
+    return 0;
+  return 1;
+}
+
+/* FIXME: Multiple DNS */
+static int
+grub_efi_ip4_interface_set_dns (struct grub_efi_net_device *dev,
+	      grub_efi_net_ip_address_t *address)
+{
+  grub_efi_status_t status;
+
+  status = dev->ip4_config->set_data (dev->ip4_config,
+                       GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
+                       sizeof (address->ip4), &address->ip4);
+
+  if (status != GRUB_EFI_SUCCESS)
+    return 0;
+  return 1;
+}
+
+grub_efi_net_ip_config_t *efi_net_ip4_config = &(grub_efi_net_ip_config_t)
+  {
+    .get_hw_address = grub_efi_ip4_interface_hw_address,
+    .get_address = grub_efi_ip4_interface_address,
+    .get_route_table = grub_efi_ip4_interface_route_table,
+    .best_interface = grub_efi_ip4_interface_match,
+    .set_address = grub_efi_ip4_interface_set_manual_address,
+    .set_gateway = grub_efi_ip4_interface_set_gateway,
+    .set_dns = grub_efi_ip4_interface_set_dns
+  };
diff --git a/grub-core/net/efi/ip6_config.c b/grub-core/net/efi/ip6_config.c
new file mode 100644
index 0000000..0f63836
--- /dev/null
+++ b/grub-core/net/efi/ip6_config.c
@@ -0,0 +1,423 @@
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/misc.h>
+#include <grub/net/efi.h>
+#include <grub/charset.h>
+#include <grub/safemath.h>
+
+char *
+grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address)
+{
+  char *str = grub_malloc (sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX"));
+  char *p;
+  int i;
+  int squash;
+
+  if (!str)
+    return NULL;
+
+  p = str;
+  squash = 0;
+  for (i = 0; i < 8; ++i)
+    {
+      grub_uint16_t addr;
+
+      if (i == 7)
+	squash = 2;
+
+      addr = grub_get_unaligned16 (address->addr + i * 2);
+
+      if (grub_be_to_cpu16 (addr))
+	{
+	  char buf[sizeof ("XXXX")];
+	  if (i > 0)
+	    *p++ = ':';
+	  grub_snprintf (buf, sizeof (buf), "%x", grub_be_to_cpu16 (addr));
+	  grub_strcpy (p, buf);
+	  p += grub_strlen (buf);
+
+	  if (squash == 1)
+	    squash = 2;
+	}
+      else
+	{
+	  if (squash == 0)
+	    {
+	      *p++ = ':';
+	      squash = 1;
+	    }
+	  else if (squash == 2)
+	    {
+	      *p++ = ':';
+	      *p++ = '0';
+	    }
+	}
+    }
+  *p = '\0';
+  return str;
+}
+
+int
+grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest)
+{
+  grub_uint16_t newip[8];
+  const char *ptr = val;
+  int word, quaddot = -1;
+  int bracketed = 0;
+
+  if (ptr[0] == '[')
+    {
+      bracketed = 1;
+      ptr++;
+    }
+
+  if (ptr[0] == ':' && ptr[1] != ':')
+    return 0;
+  if (ptr[0] == ':')
+    ptr++;
+
+  for (word = 0; word < 8; word++)
+    {
+      unsigned long t;
+      if (*ptr == ':')
+	{
+	  quaddot = word;
+	  word--;
+	  ptr++;
+	  continue;
+	}
+      t = grub_strtoul (ptr, (const char **) &ptr, 16);
+      if (grub_errno)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  break;
+	}
+      if (t & ~0xffff)
+	return 0;
+      newip[word] = grub_cpu_to_be16 (t);
+      if (*ptr != ':')
+	break;
+      ptr++;
+    }
+  if (quaddot == -1 && word < 7)
+    return 0;
+  if (quaddot != -1)
+    {
+      grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot],
+		    (word - quaddot + 1) * sizeof (newip[0]));
+      grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0]));
+    }
+  grub_memcpy (address, newip, 16);
+  if (bracketed && *ptr == ']')
+    ptr++;
+  if (rest)
+    *rest = ptr;
+  return 1;
+}
+
+static grub_efi_ip6_config_interface_info_t *
+efi_ip6_config_interface_info (grub_efi_ip6_config_protocol_t *ip6_config)
+{
+  grub_efi_uintn_t sz;
+  grub_efi_status_t status;
+  grub_efi_ip6_config_interface_info_t *interface_info;
+
+  sz = sizeof (*interface_info) + sizeof (*interface_info->route_table);
+  interface_info = grub_malloc (sz);
+
+  status = ip6_config->get_data (ip6_config,
+                       GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
+                       &sz, interface_info);
+  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
+    {
+      grub_free (interface_info);
+      interface_info = grub_malloc (sz);
+      status = ip6_config->get_data (ip6_config,
+                           GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
+                           &sz, interface_info);
+    }
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (interface_info);
+      return NULL;
+    }
+
+  return interface_info;
+}
+
+static grub_efi_ip6_config_manual_address_t *
+efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config)
+{
+  grub_efi_uintn_t sz;
+  grub_efi_status_t status;
+  grub_efi_ip6_config_manual_address_t *manual_address;
+
+  sz = sizeof (*manual_address);
+  manual_address = grub_malloc (sz);
+  if (!manual_address)
+    return NULL;
+
+  status = ip6_config->get_data (ip6_config,
+                       GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
+                       &sz, manual_address);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (manual_address);
+      return NULL;
+    }
+
+  return manual_address;
+}
+
+char *
+grub_efi_ip6_interface_name (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip6_config_interface_info_t *interface_info;
+  char *name;
+
+  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
+  if (!interface_info)
+    return NULL;
+
+  name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE
+		      * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  *grub_utf16_to_utf8 ((grub_uint8_t *) name, interface_info->name,
+		      GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0;
+  grub_free (interface_info);
+  return name;
+}
+
+static char *
+grub_efi_ip6_interface_hw_address (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip6_config_interface_info_t *interface_info;
+  char *hw_addr;
+
+  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
+
+  if (!interface_info)
+    return NULL;
+
+  hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address);
+  grub_free (interface_info);
+
+  return hw_addr;
+}
+
+static char *
+grub_efi_ip6_interface_address (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip6_config_manual_address_t *manual_address;
+  char *addr;
+
+  manual_address = efi_ip6_config_manual_address (dev->ip6_config);
+
+  if (!manual_address)
+    return NULL;
+
+  addr = grub_efi_ip6_address_to_string ((grub_efi_pxe_ipv6_address_t *) &manual_address->address);
+  grub_free (manual_address);
+  return addr;
+}
+
+static char **
+grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev)
+{
+  grub_efi_ip6_config_interface_info_t *interface_info;
+  char **ret;
+  int id;
+  grub_size_t i, nmemb;
+
+  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
+  if (!interface_info)
+    return NULL;
+
+  if (grub_add (interface_info->route_count, 1, &nmemb))
+    {
+      grub_errno = GRUB_ERR_OUT_OF_RANGE;
+      return NULL;
+    }
+
+  ret = grub_calloc (nmemb, sizeof (*ret));
+  if (!ret)
+    {
+      grub_free (interface_info);
+      return NULL;
+    }
+
+  id = 0;
+  for (i = 0; i < interface_info->route_count ; i++)
+    {
+      char *gateway, *destination;
+      grub_uint64_t u64_gateway[2];
+      grub_uint64_t u64_destination[2];
+      grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i;
+      grub_efi_net_interface_t *inf;
+      char *interface_name = NULL;
+
+      gateway = grub_efi_ip6_address_to_string (&route_table->gateway);
+      destination = grub_efi_ip6_address_to_string (&route_table->destination);
+
+      u64_gateway[0] = grub_get_unaligned64 (route_table->gateway.addr);
+      u64_gateway[1] = grub_get_unaligned64 (route_table->gateway.addr + 8);
+      u64_destination[0] = grub_get_unaligned64 (route_table->destination.addr);
+      u64_destination[1] = grub_get_unaligned64 (route_table->destination.addr + 8);
+
+      for (inf = dev->net_interfaces; inf; inf = inf->next)
+	if (inf->prefer_ip6)
+	  interface_name = inf->name;
+
+      if ((!u64_gateway[0] && !u64_gateway[1])
+	  && (u64_destination[0] || u64_destination[1]))
+	{
+	  if (interface_name)
+	    {
+	      if ((grub_be_to_cpu64 (u64_destination[0]) == 0xfe80000000000000ULL)
+	      && (!u64_destination[1])
+	      && (route_table->prefix_length == 64))
+		ret[id++] = grub_xasprintf ("%s:link %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name);
+	      else
+		ret[id++] = grub_xasprintf ("%s:local %s/%d %s", dev->card_name, destination, route_table->prefix_length, interface_name);
+	    }
+	}
+      else if ((u64_gateway[0] || u64_gateway[1])
+	  && (u64_destination[0] || u64_destination[1]))
+	ret[id++] = grub_xasprintf ("%s:gw %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway);
+      else if ((u64_gateway[0] || u64_gateway[1])
+	  && (!u64_destination[0] && !u64_destination[1]))
+	ret[id++] = grub_xasprintf ("%s:default %s/%d gw %s", dev->card_name, destination, route_table->prefix_length, gateway);
+
+      grub_free (gateway);
+      grub_free (destination);
+    }
+
+  ret[id] = NULL;
+  grub_free (interface_info);
+  return ret;
+}
+
+static grub_efi_net_interface_t *
+grub_efi_ip6_interface_match (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *ip_address)
+{
+  grub_efi_ip6_config_interface_info_t *interface_info;
+  grub_efi_net_interface_t *inf;
+  int i;
+  grub_efi_ipv6_address_t *address = &ip_address->ip6;
+
+  interface_info = efi_ip6_config_interface_info (dev->ip6_config);
+  if (!interface_info)
+    return NULL;
+
+  for (i = 0; i < (int)interface_info->route_count ; i++)
+    {
+      grub_uint64_t u64_addr[2];
+      grub_uint64_t u64_subnet[2];
+      grub_uint64_t u64_mask[2];
+
+      grub_efi_ip6_route_table_t *route_table = interface_info->route_table + i;
+
+      /* SKIP Default GATEWAY */
+      if (route_table->prefix_length == 0)
+	continue;
+
+      u64_addr[0] = grub_get_unaligned64 (address);
+      u64_addr[1] = grub_get_unaligned64 (address + 4);
+      u64_subnet[0] = grub_get_unaligned64 (route_table->destination.addr);
+      u64_subnet[1] = grub_get_unaligned64 (route_table->destination.addr + 8);
+      u64_mask[0] = (route_table->prefix_length <= 64) ?
+	    0xffffffffffffffffULL << (64 - route_table->prefix_length) :
+	    0xffffffffffffffffULL;
+      u64_mask[1] = (route_table->prefix_length <= 64) ?
+	    0 :
+	    0xffffffffffffffffULL << (128 - route_table->prefix_length);
+
+      if (((u64_addr[0] & u64_mask[0]) == u64_subnet[0])
+	  && ((u64_addr[1] & u64_mask[1]) == u64_subnet[1]))
+	{
+	  for (inf = dev->net_interfaces; inf; inf = inf->next)
+	    if (inf->prefer_ip6)
+	      {
+		grub_free (interface_info);
+		return inf;
+	      }
+	}
+    }
+
+  grub_free (interface_info);
+  return NULL;
+}
+
+static int
+grub_efi_ip6_interface_set_manual_address (struct grub_efi_net_device *dev,
+	    grub_efi_net_ip_manual_address_t *net_ip,
+	    int with_subnet)
+{
+  grub_efi_status_t status;
+  grub_efi_ip6_config_manual_address_t *address = &net_ip->ip6;
+
+  if (!with_subnet)
+    {
+      grub_efi_ip6_config_manual_address_t *manual_address =
+        efi_ip6_config_manual_address (dev->ip6_config);
+
+      if (manual_address)
+	{
+	  address->prefix_length = manual_address->prefix_length;
+	  grub_free (manual_address);
+	}
+      else
+	{
+	  address->prefix_length = 64;
+	}
+    }
+
+  status = dev->ip6_config->set_data (dev->ip6_config,
+                       GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
+                       sizeof (*address), address);
+
+  if (status != GRUB_EFI_SUCCESS)
+    return 0;
+
+  return 1;
+}
+
+static int
+grub_efi_ip6_interface_set_gateway (struct grub_efi_net_device *dev,
+	      grub_efi_net_ip_address_t *address)
+{
+  grub_efi_status_t status;
+
+  status = dev->ip6_config->set_data (dev->ip6_config,
+                       GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY,
+                       sizeof (address->ip6), &address->ip6);
+  if (status != GRUB_EFI_SUCCESS)
+    return 0;
+  return 1;
+}
+
+static int
+grub_efi_ip6_interface_set_dns (struct grub_efi_net_device *dev,
+	      grub_efi_net_ip_address_t *address)
+{
+
+  grub_efi_status_t status;
+
+  status = dev->ip6_config->set_data (dev->ip6_config,
+                       GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
+                       sizeof (address->ip6), &address->ip6);
+  if (status != GRUB_EFI_SUCCESS)
+    return 0;
+  return 1;
+}
+
+grub_efi_net_ip_config_t *efi_net_ip6_config = &(grub_efi_net_ip_config_t)
+  {
+    .get_hw_address = grub_efi_ip6_interface_hw_address,
+    .get_address = grub_efi_ip6_interface_address,
+    .get_route_table = grub_efi_ip6_interface_route_table,
+    .best_interface = grub_efi_ip6_interface_match,
+    .set_address = grub_efi_ip6_interface_set_manual_address,
+    .set_gateway = grub_efi_ip6_interface_set_gateway,
+    .set_dns = grub_efi_ip6_interface_set_dns
+  };
diff --git a/grub-core/net/efi/net.c b/grub-core/net/efi/net.c
new file mode 100644
index 0000000..eeafdcd
--- /dev/null
+++ b/grub-core/net/efi/net.c
@@ -0,0 +1,1413 @@
+#include <grub/net.h>
+#include <grub/env.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/dl.h>
+#include <grub/command.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/i18n.h>
+#include <grub/bufio.h>
+#include <grub/efi/http.h>
+#include <grub/efi/dhcp.h>
+#include <grub/net/efi.h>
+#include <grub/charset.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define GRUB_EFI_IP6_PREFIX_LENGTH 64
+
+static grub_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID;
+static grub_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID;
+static grub_guid_t http_service_binding_guid = GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
+static grub_guid_t http_guid = GRUB_EFI_HTTP_PROTOCOL_GUID;
+static grub_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
+static grub_guid_t dhcp4_service_binding_guid = GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID;
+static grub_guid_t dhcp4_guid = GRUB_EFI_DHCP4_PROTOCOL_GUID;
+static grub_guid_t dhcp6_service_binding_guid = GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID;
+static grub_guid_t dhcp6_guid = GRUB_EFI_DHCP6_PROTOCOL_GUID;
+
+struct grub_efi_net_device *net_devices;
+
+static char *default_server;
+static grub_efi_net_interface_t *net_interface;
+static grub_efi_net_interface_t *net_default_interface;
+
+#define efi_net_interface_configure(inf) inf->io->configure (inf->dev, inf->prefer_ip6)
+#define efi_net_interface_open(inf, file, name) inf->io->open (inf->dev, inf->prefer_ip6, file, name, inf->io_type)
+#define efi_net_interface_read(inf, file, buf, sz) inf->io->read (inf->dev, inf->prefer_ip6, file, buf, sz)
+#define efi_net_interface_close(inf, file) inf->io->close (inf->dev, inf->prefer_ip6, file)
+#define efi_net_interface(m,...) efi_net_interface_ ## m (net_interface, ## __VA_ARGS__)
+
+static grub_efi_handle_t
+grub_efi_locate_device_path (grub_guid_t *protocol, grub_efi_device_path_t *device_path,
+                            grub_efi_device_path_t **r_device_path)
+{
+  grub_efi_handle_t handle;
+  grub_efi_status_t status;
+
+  status = grub_efi_system_table->boot_services->locate_device_path (
+    protocol, &device_path, &handle);
+  if (status != GRUB_EFI_SUCCESS)
+    return 0;
+
+  if (r_device_path)
+    *r_device_path = device_path;
+
+  return handle;
+}
+
+static int
+url_parse_fields (const char *url, char **proto, char **host, char **path)
+{
+  const char *p, *ps;
+  grub_size_t l;
+
+  *proto = *host = *path = NULL;
+  ps = p = url;
+
+  while ((p = grub_strchr (p, ':')))
+    {
+      if (grub_strlen (p) < sizeof ("://") - 1)
+	break;
+      if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0)
+	{
+	  l = p - ps;
+	  *proto = grub_malloc (l + 1);
+	  if (!*proto)
+	    {
+	      grub_print_error ();
+	      return 0;
+	    }
+
+	  grub_memcpy (*proto, ps, l);
+	  (*proto)[l] = '\0';
+	  p += sizeof ("://") - 1;
+	  break;
+	}
+      ++p;
+    }
+
+  if (!*proto)
+    {
+      grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url);
+      return 0;
+    }
+
+  ps = p;
+  p = grub_strchr (p, '/');
+
+  if (!p)
+    {
+      grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url);
+      grub_free (*proto);
+      *proto = NULL;
+      return 0;
+    }
+
+  l = p - ps;
+  if (l > 2 && ps[0] == '[' && ps[l - 1] == ']')
+    {
+      *host = grub_malloc (l - 1);
+      if (!*host)
+	{
+	  grub_print_error ();
+	  grub_free (*proto);
+	  *proto = NULL;
+	  return 0;
+	}
+      grub_memcpy (*host, ps + 1, l - 2);
+      (*host)[l - 2] = 0;
+    }
+  else
+    {
+      *host = grub_malloc (l + 1);
+      if (!*host)
+	{
+	  grub_print_error ();
+	  grub_free (*proto);
+	  *proto = NULL;
+	  return 0;
+	}
+      grub_memcpy (*host, ps, l);
+      (*host)[l] = 0;
+    }
+
+  *path = grub_strdup (p);
+  if (!*path)
+    {
+      grub_print_error ();
+      grub_free (*host);
+      grub_free (*proto);
+      *host = NULL;
+      *proto = NULL;
+      return 0;
+    }
+  return 1;
+}
+
+static void
+url_get_boot_location (const char *url, char **device, char **path, int is_default)
+{
+  char *protocol, *server, *file;
+  char *slash;
+
+  if (!url_parse_fields (url, &protocol, &server, &file))
+    return;
+
+  if ((slash = grub_strrchr (file, '/')))
+    *slash = 0;
+  else
+    *file = 0;
+
+  *device = grub_xasprintf ("%s,%s", protocol, server);
+  *path = grub_strdup(file);
+
+  if (is_default)
+    default_server = server;
+  else
+    grub_free (server);
+
+  grub_free (protocol);
+  grub_free (file);
+}
+
+static void
+pxe_get_boot_location (const struct grub_net_bootp_packet *bp,
+                       char **device,
+                       char **path,
+                       int is_default)
+{
+  char *server = grub_xasprintf ("%d.%d.%d.%d",
+                                 ((grub_uint8_t *) &bp->server_ip)[0],
+                                 ((grub_uint8_t *) &bp->server_ip)[1],
+                                 ((grub_uint8_t *) &bp->server_ip)[2],
+                                 ((grub_uint8_t *) &bp->server_ip)[3]);
+
+  *device = grub_xasprintf ("tftp,%s", server);
+
+  *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
+
+  if (*path)
+    {
+      char *slash;
+      slash = grub_strrchr (*path, '/');
+      if (slash)
+	*slash = 0;
+      else
+	**path = 0;
+    }
+
+  if (is_default)
+    default_server = server;
+  else
+    grub_free (server);
+}
+
+static void
+pxe_get_boot_location_v6 (const struct grub_net_dhcp6_packet *dp,
+                          grub_size_t dhcp_size,
+                          char **device,
+                          char **path)
+{
+  struct grub_net_dhcp6_option *dhcp_opt;
+  grub_size_t dhcp_remain_size;
+  *device = *path = 0;
+
+  if (dhcp_size < sizeof (*dp))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small"));
+      return;
+    }
+
+  dhcp_remain_size = dhcp_size - sizeof (*dp);
+  dhcp_opt = (struct grub_net_dhcp6_option *) dp->dhcp_options;
+
+  while (dhcp_remain_size)
+    {
+      grub_uint16_t code = grub_be_to_cpu16 (dhcp_opt->code);
+      grub_uint16_t len = grub_be_to_cpu16 (dhcp_opt->len);
+      grub_uint16_t option_size = sizeof (*dhcp_opt) + len;
+
+      if (dhcp_remain_size < option_size || code == 0)
+	break;
+
+      if (code == GRUB_NET_DHCP6_OPTION_BOOTFILE_URL)
+	{
+	  char *url = grub_malloc (len + 1);
+
+	  grub_memcpy (url, dhcp_opt->data, len);
+	  url[len] = 0;
+
+	  url_get_boot_location ((const char *) url, device, path, 1);
+	  grub_free (url);
+	  break;
+	}
+
+      dhcp_remain_size -= option_size;
+      dhcp_opt = (struct grub_net_dhcp6_option *) ((grub_uint8_t *) dhcp_opt + option_size);
+    }
+}
+
+static grub_efi_net_interface_t *
+grub_efi_net_config_from_device_path (grub_efi_device_path_t *dp,
+                                      struct grub_efi_net_device *netdev,
+                                      char **device,
+                                      char **path)
+{
+  grub_efi_net_interface_t *inf = NULL;
+
+  while (1)
+    {
+      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
+      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
+      grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+
+      if (type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE)
+	{
+	  if (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
+	    {
+	      grub_efi_uri_device_path_t *uri_dp;
+	      uri_dp = (grub_efi_uri_device_path_t *) dp;
+	      /* Beware that uri_dp->uri may not be null terminated */
+	      url_get_boot_location ((const char *) uri_dp->uri, device, path, 1);
+	    }
+	  else if (subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
+	    {
+	      grub_efi_net_ip_manual_address_t net_ip;
+	      grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) dp;
+
+	      if (inf)
+		continue;
+	      grub_memcpy (net_ip.ip4.address, ipv4->local_ip_address, sizeof (net_ip.ip4.address));
+	      grub_memcpy (net_ip.ip4.subnet_mask, ipv4->subnet_mask, sizeof (net_ip.ip4.subnet_mask));
+	      net_ip.is_ip6 = 0;
+	      inf = grub_efi_net_create_interface (netdev,
+                                                   netdev->card_name,
+                                                   &net_ip,
+                                                   1);
+	    }
+	  else if (subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)
+	    {
+	      grub_efi_net_ip_manual_address_t net_ip;
+	      grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) dp;
+
+	      if (inf)
+		continue;
+	      grub_memcpy (net_ip.ip6.address, ipv6->local_ip_address, sizeof (net_ip.ip6.address));
+	      net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH;
+	      net_ip.ip6.is_anycast = 0;
+	      net_ip.is_ip6 = 1;
+	      inf = grub_efi_net_create_interface (netdev,
+                                                   netdev->card_name,
+                                                   &net_ip,
+                                                   1);
+	    }
+	}
+
+      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
+        break;
+      dp = (grub_efi_device_path_t *) ((char *) dp + len);
+    }
+
+  return inf;
+}
+
+static grub_efi_net_interface_t *
+grub_efi_net_config_from_handle (grub_efi_handle_t *hnd,
+                                 struct grub_efi_net_device *netdev,
+                                 char **device,
+                                 char **path)
+{
+  grub_efi_pxe_t *pxe = NULL;
+
+  if (hnd == netdev->ip4_pxe_handle)
+    pxe = netdev->ip4_pxe;
+  else if (hnd == netdev->ip6_pxe_handle)
+    pxe = netdev->ip6_pxe;
+
+  if (!pxe)
+    return grub_efi_net_config_from_device_path (
+		grub_efi_get_device_path (hnd),
+		netdev,
+		device,
+		path);
+
+  if (pxe->mode->using_ipv6)
+    {
+      grub_efi_net_ip_manual_address_t net_ip;
+
+      pxe_get_boot_location_v6 (
+	    (const struct grub_net_dhcp6_packet *) &pxe->mode->dhcp_ack,
+	    sizeof (pxe->mode->dhcp_ack),
+	    device,
+	    path);
+
+      grub_memcpy (net_ip.ip6.address, pxe->mode->station_ip.v6, sizeof(net_ip.ip6.address));
+      net_ip.ip6.prefix_length = GRUB_EFI_IP6_PREFIX_LENGTH;
+      net_ip.ip6.is_anycast = 0;
+      net_ip.is_ip6 = 1;
+      return (grub_efi_net_create_interface (netdev,
+		    netdev->card_name,
+		    &net_ip,
+		    1));
+    }
+  else
+    {
+      grub_efi_net_ip_manual_address_t net_ip;
+
+      pxe_get_boot_location (
+		(const struct grub_net_bootp_packet *) &pxe->mode->dhcp_ack,
+		device,
+		path,
+		1);
+
+      grub_memcpy (net_ip.ip4.address, pxe->mode->station_ip.v4, sizeof (net_ip.ip4.address));
+      grub_memcpy (net_ip.ip4.subnet_mask, pxe->mode->subnet_mask.v4, sizeof (net_ip.ip4.subnet_mask));
+      net_ip.is_ip6 = 0;
+      return (grub_efi_net_create_interface (netdev,
+		    netdev->card_name,
+		    &net_ip,
+		    1));
+    }
+}
+
+static const char *
+grub_efi_net_var_get_address (struct grub_env_var *var,
+                              const char *val __attribute__ ((unused)))
+{
+  struct grub_efi_net_device *dev;
+
+  for (dev = net_devices; dev; dev = dev->next)
+    {
+      grub_efi_net_interface_t *inf;
+
+      for (inf = dev->net_interfaces; inf; inf = inf->next)
+	{
+	  char *var_name;
+
+	  var_name = grub_xasprintf ("net_%s_ip", inf->name);
+	  if (grub_strcmp (var_name, var->name) == 0)
+	    return efi_net_interface_get_address (inf);
+	  grub_free (var_name);
+	  var_name = grub_xasprintf ("net_%s_mac", inf->name);
+	  if (grub_strcmp (var_name, var->name) == 0)
+	    return efi_net_interface_get_hw_address (inf);
+	  grub_free (var_name);
+	}
+    }
+
+  return NULL;
+}
+
+static char *
+grub_efi_net_var_set_interface (struct grub_env_var *var __attribute__ ((unused)),
+                                const char *val)
+{
+  struct grub_efi_net_device *dev;
+  grub_efi_net_interface_t *inf;
+
+  for (dev = net_devices; dev; dev = dev->next)
+    for (inf = dev->net_interfaces; inf; inf = inf->next)
+      if (grub_strcmp (inf->name, val) == 0)
+	{
+	  net_default_interface = inf;
+	  return grub_strdup (val);
+	}
+
+  return NULL;
+}
+
+static char *
+grub_efi_net_var_set_server (struct grub_env_var *var __attribute__ ((unused)),
+                             const char *val)
+{
+  grub_free (default_server);
+  default_server = grub_strdup (val);
+  return grub_strdup (val);
+}
+
+static const char *
+grub_efi_net_var_get_server (struct grub_env_var *var __attribute__ ((unused)),
+                             const char *val __attribute__ ((unused)))
+{
+  return default_server ? : "";
+}
+
+static const char *
+grub_efi_net_var_get_ip (struct grub_env_var *var __attribute__ ((unused)),
+                         const char *val __attribute__ ((unused)))
+{
+  const char *intf = grub_env_get ("net_default_interface");
+  const char *ret = NULL;
+  if (intf)
+    {
+      char *buf = grub_xasprintf ("net_%s_ip", intf);
+      if (buf)
+	ret = grub_env_get (buf);
+      grub_free (buf);
+    }
+  return ret;
+}
+
+static const char *
+grub_efi_net_var_get_mac (struct grub_env_var *var __attribute__ ((unused)),
+	       const char *val __attribute__ ((unused)))
+{
+  const char *intf = grub_env_get ("net_default_interface");
+  const char *ret = NULL;
+  if (intf)
+    {
+      char *buf = grub_xasprintf ("net_%s_mac", intf);
+      if (buf)
+	ret = grub_env_get (buf);
+      grub_free (buf);
+    }
+  return ret;
+}
+
+static void
+grub_efi_net_export_interface_vars (void)
+{
+  struct grub_efi_net_device *dev;
+
+  for (dev = net_devices; dev; dev = dev->next)
+    {
+      grub_efi_net_interface_t *inf;
+
+      for (inf = dev->net_interfaces; inf; inf = inf->next)
+	{
+	  char *var;
+
+	  var = grub_xasprintf ("net_%s_ip", inf->name);
+	  grub_register_variable_hook (var, grub_efi_net_var_get_address, 0);
+	  grub_env_export (var);
+	  grub_free (var);
+	  var = grub_xasprintf ("net_%s_mac", inf->name);
+	  grub_register_variable_hook (var, grub_efi_net_var_get_address, 0);
+	  grub_env_export (var);
+	  grub_free (var);
+	}
+    }
+}
+
+static void
+grub_efi_net_unset_interface_vars (void)
+{
+  struct grub_efi_net_device *dev;
+
+  for (dev = net_devices; dev; dev = dev->next)
+    {
+      grub_efi_net_interface_t *inf;
+
+      for (inf = dev->net_interfaces; inf; inf = inf->next)
+	{
+	  char *var;
+
+	  var = grub_xasprintf ("net_%s_ip", inf->name);
+	  grub_register_variable_hook (var, 0, 0);
+	  grub_env_unset (var);
+	  grub_free (var);
+	  var = grub_xasprintf ("net_%s_mac", inf->name);
+	  grub_register_variable_hook (var, 0, 0);
+	  grub_env_unset (var);
+	  grub_free (var);
+	}
+    }
+}
+
+grub_efi_net_interface_t *
+grub_efi_net_create_interface (struct grub_efi_net_device *dev,
+                               const char *interface_name,
+                               grub_efi_net_ip_manual_address_t *net_ip,
+                               int has_subnet)
+{
+  grub_efi_net_interface_t *inf;
+
+  for (inf = dev->net_interfaces; inf; inf = inf->next)
+    {
+      if (inf->prefer_ip6 == net_ip->is_ip6)
+	break;
+    }
+
+  if (!inf)
+    {
+      inf = grub_malloc (sizeof (*inf));
+      inf->name = grub_strdup (interface_name);
+      inf->prefer_ip6 = net_ip->is_ip6;
+      inf->dev = dev;
+      inf->next = dev->net_interfaces;
+      inf->ip_config = (net_ip->is_ip6) ? efi_net_ip6_config : efi_net_ip4_config ;
+      dev->net_interfaces = inf;
+    }
+  else
+    {
+      grub_free (inf->name);
+      inf->name = grub_strdup (interface_name);
+    }
+
+  if (!efi_net_interface_set_address (inf, net_ip, has_subnet))
+    {
+      grub_error (GRUB_ERR_BUG, N_("Set Address Failed"));
+      return NULL;
+    }
+
+  return inf;
+}
+
+static void
+grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
+			  char **path)
+{
+  grub_efi_handle_t config_hnd;
+
+  struct grub_efi_net_device *netdev;
+  grub_efi_net_interface_t *inf;
+
+  config_hnd = grub_efi_locate_device_path (&ip4_config_guid, grub_efi_get_device_path (hnd), NULL);
+
+  if (!config_hnd)
+    return;
+
+  for (netdev = net_devices; netdev; netdev = netdev->next)
+    if (netdev->handle == config_hnd)
+      break;
+
+  if (!netdev)
+    return;
+
+  if (!(inf = grub_efi_net_config_from_handle (hnd, netdev, device, path)))
+    return;
+
+  grub_env_set ("net_default_interface", inf->name);
+  grub_efi_net_export_interface_vars ();
+}
+
+static grub_err_t
+grub_efi_netfs_dir (grub_device_t device, const char *path __attribute__ ((unused)),
+		 grub_fs_dir_hook_t hook __attribute__ ((unused)),
+		 void *hook_data __attribute__ ((unused)))
+{
+  if (!device->net)
+    return grub_error (GRUB_ERR_BUG, "invalid net device");
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_efi_netfs_open (struct grub_file *file_out __attribute__ ((unused)),
+		  const char *name __attribute__ ((unused)))
+{
+  struct grub_file *file, *bufio;
+
+  file = grub_malloc (sizeof (*file));
+  if (!file)
+    return grub_errno;
+
+  grub_memcpy (file, file_out, sizeof (struct grub_file));
+  file->device->net->name = grub_strdup (name);
+
+  if (!file->device->net->name)
+    {
+      grub_free (file);
+      return grub_errno;
+    }
+
+  efi_net_interface (open, file, name);
+  grub_print_error ();
+
+  bufio = grub_bufio_open (file, 32768);
+  if (!bufio)
+    {
+      grub_free (file->device->net->name);
+      grub_free (file);
+      return grub_errno;
+    }
+  grub_memcpy (file_out, bufio, sizeof (struct grub_file));
+  grub_free (bufio);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_ssize_t
+grub_efihttp_chunk_read (grub_file_t file, char *buf,
+                         grub_size_t len, grub_size_t chunk_size)
+{
+  char *chunk = grub_malloc (chunk_size);
+  grub_size_t sum = 0;
+
+  while (len)
+    {
+      grub_ssize_t rd;
+      grub_size_t sz = (len > chunk_size) ? chunk_size : len;
+
+      rd = efi_net_interface (read, file, chunk, sz);
+
+      if (rd <= 0)
+        {
+          grub_free (chunk);
+          return rd;
+        }
+
+      if (buf)
+	{
+	  grub_memcpy (buf, chunk, rd);
+	  buf += rd;
+	}
+      sum += rd;
+      len -= rd;
+    }
+
+  grub_free (chunk);
+  return sum;
+}
+
+static grub_ssize_t
+grub_efi_netfs_read (grub_file_t file __attribute__ ((unused)),
+		  char *buf __attribute__ ((unused)), grub_size_t len __attribute__ ((unused)))
+{
+  if (file->offset > file->device->net->offset)
+    {
+      grub_efihttp_chunk_read (file, NULL, file->offset - file->device->net->offset, 10240);
+    }
+  else if (file->offset < file->device->net->offset)
+    {
+      efi_net_interface (close, file);
+      efi_net_interface (open, file, file->device->net->name);
+      if (file->offset)
+	grub_efihttp_chunk_read (file, NULL, file->offset, 10240);
+    }
+
+  return efi_net_interface (read, file, buf, len);
+}
+
+static grub_err_t
+grub_efi_netfs_close (grub_file_t file)
+{
+  efi_net_interface (close, file);
+  return GRUB_ERR_NONE;
+}
+
+static grub_efi_handle_t
+grub_efi_service_binding (grub_efi_handle_t dev, grub_guid_t *service_binding_guid)
+{
+  grub_efi_service_binding_t *service;
+  grub_efi_status_t status;
+  grub_efi_handle_t child_dev = NULL;
+
+  service = grub_efi_open_protocol (dev, service_binding_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+  if (!service)
+    {
+      grub_error (GRUB_ERR_IO, N_("couldn't open efi service binding protocol"));
+      return NULL;
+    }
+
+  status = service->create_child (service, &child_dev);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_error (GRUB_ERR_IO, N_("Failed to create child device of http service 0x%"PRIxGRUB_EFI_UINTN_T), status);
+      return NULL;
+    }
+
+  return child_dev;
+}
+
+static grub_err_t
+grub_efi_net_parse_address (const char *address,
+    grub_efi_ip4_config2_manual_address_t *ip4,
+    grub_efi_ip6_config_manual_address_t *ip6,
+    int *is_ip6,
+    int *has_cidr)
+{
+  const char *rest;
+
+  if (grub_efi_string_to_ip4_address (address, &ip4->address, &rest))
+    {
+      *is_ip6 = 0;
+      if (*rest == '/')
+	{
+	  grub_uint32_t subnet_mask_size;
+
+	  subnet_mask_size = grub_strtoul (rest + 1, (const char **) &rest, 0);
+
+	  if (!grub_errno && subnet_mask_size <= 32 && *rest == 0)
+	    {
+	      grub_uint32_t subnet_mask;
+
+	      subnet_mask = grub_cpu_to_be32 ((0xffffffffU << (32 - subnet_mask_size)));
+	      grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask));
+	      if (has_cidr)
+		*has_cidr = 1;
+	      return GRUB_ERR_NONE;
+	    }
+	}
+      else if (*rest == 0 || *rest == ':')
+	{
+	  grub_uint32_t subnet_mask = 0xffffffffU;
+	  grub_memcpy (ip4->subnet_mask, &subnet_mask, sizeof (ip4->subnet_mask));
+	  if (has_cidr)
+	    *has_cidr = 0;
+	  return GRUB_ERR_NONE;
+	}
+    }
+  else if (grub_efi_string_to_ip6_address (address, &ip6->address, &rest))
+    {
+      *is_ip6 = 1;
+      if (*rest == '/')
+	{
+	  grub_efi_uint8_t prefix_length;
+
+	  prefix_length = grub_strtoul (rest + 1, (const char **) &rest, 0);
+	  if (!grub_errno && prefix_length <= 128 && *rest == 0)
+	    {
+	      ip6->prefix_length = prefix_length;
+	      ip6->is_anycast = 0;
+	      if (has_cidr)
+		*has_cidr = 1;
+	      return GRUB_ERR_NONE;
+	    }
+	}
+      else if (*rest == 0 || *rest == ':')
+	{
+	  ip6->prefix_length = 128;
+	  ip6->is_anycast = 0;
+	  if (has_cidr)
+	    *has_cidr = 0;
+	  return GRUB_ERR_NONE;
+	}
+    }
+
+  grub_dprintf ("efinet", "unrecognised network address '%s'\n", address);
+
+  return GRUB_ERR_NET_BAD_ADDRESS;
+}
+
+static grub_efi_net_interface_t *
+match_route (const char *server)
+{
+  grub_err_t err;
+  grub_efi_ip4_config2_manual_address_t ip4;
+  grub_efi_ip6_config_manual_address_t ip6;
+  grub_efi_net_interface_t *inf;
+  int is_ip6 = 0;
+
+  err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0);
+  if (err)
+      return NULL;
+
+  if (is_ip6)
+    {
+      struct grub_efi_net_device *dev;
+      grub_efi_net_ip_address_t addr;
+
+      grub_memcpy (addr.ip6, ip6.address, sizeof(ip6.address));
+
+      for (dev = net_devices; dev; dev = dev->next)
+	  if ((inf = efi_net_ip6_config->best_interface (dev, &addr)))
+	    return inf;
+    }
+  else
+    {
+      struct grub_efi_net_device *dev;
+      grub_efi_net_ip_address_t addr;
+
+      grub_memcpy (addr.ip4, ip4.address, sizeof(ip4.address));
+
+      for (dev = net_devices; dev; dev = dev->next)
+	  if ((inf = efi_net_ip4_config->best_interface (dev, &addr)))
+	    return inf;
+    }
+
+  return 0;
+}
+
+static void
+grub_efi_net_add_pxebc_to_cards (void)
+{
+  grub_efi_uintn_t num_handles;
+  grub_efi_handle_t *handles;
+  grub_efi_handle_t *handle;
+
+  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &pxe_io_guid,
+				    0, &num_handles);
+  if (!handles)
+    return;
+
+  for (handle = handles; num_handles--; handle++)
+    {
+      grub_efi_device_path_t *dp, *ddp, *ldp;
+      grub_efi_pxe_t *pxe;
+      struct grub_efi_net_device *d;
+      int is_ip6 = 0;
+
+      dp = grub_efi_get_device_path (*handle);
+      if (!dp)
+	continue;
+
+      ddp = grub_efi_duplicate_device_path (dp);
+      ldp = grub_efi_find_last_device_path (ddp);
+
+      if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+	  && ldp->subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
+	{
+	  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+	  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+	  ldp->length = sizeof (*ldp);
+	}
+      else if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+	  && ldp->subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)
+	{
+	  is_ip6 = 1;
+	  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+	  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+	  ldp->length = sizeof (*ldp);
+	}
+
+      for (d = net_devices; d; d = d->next)
+	if (grub_efi_compare_device_paths (ddp, grub_efi_get_device_path (d->handle)) == 0)
+	  break;
+
+      if (!d)
+	{
+	  grub_free (ddp);
+	  continue;
+	}
+
+      pxe = grub_efi_open_protocol (*handle, &pxe_io_guid,
+                                    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+      if (!pxe)
+	{
+	  grub_free (ddp);
+	  continue;
+	}
+
+      if (is_ip6)
+	{
+	  d->ip6_pxe_handle = *handle;
+	  d->ip6_pxe = pxe;
+	}
+      else
+	{
+	  d->ip4_pxe_handle = *handle;
+	  d->ip4_pxe = pxe;
+	}
+
+      grub_free (ddp);
+    }
+
+  grub_free (handles);
+}
+
+static void
+set_ip_policy_to_static (void)
+{
+  struct grub_efi_net_device *dev;
+
+  for (dev = net_devices; dev; dev = dev->next)
+    {
+      grub_efi_ip4_config2_policy_t ip4_policy = GRUB_EFI_IP4_CONFIG2_POLICY_STATIC;
+
+      if (dev->ip4_config->set_data (dev->ip4_config,
+                      GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
+                      sizeof (ip4_policy), &ip4_policy) != GRUB_EFI_SUCCESS)
+	grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP4_CONFIG2_POLICY_STATIC on dev `%s'", dev->card_name);
+
+      if (dev->ip6_config)
+	{
+	  grub_efi_ip6_config_policy_t ip6_policy = GRUB_EFI_IP6_CONFIG_POLICY_MANUAL;
+
+	  if (dev->ip6_config->set_data (dev->ip6_config,
+                          GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY,
+                          sizeof (ip6_policy), &ip6_policy) != GRUB_EFI_SUCCESS)
+	    grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP6_CONFIG_POLICY_MANUAL on dev `%s'", dev->card_name);
+	}
+    }
+}
+
+static void
+grub_efi_net_find_cards (void)
+{
+  grub_efi_uintn_t num_handles;
+  grub_efi_handle_t *handles;
+  grub_efi_handle_t *handle;
+  int id;
+
+  handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &ip4_config_guid,
+				    0, &num_handles);
+  if (!handles)
+    return;
+
+  for (id = 0, handle = handles; num_handles--; handle++, id++)
+    {
+      grub_efi_device_path_t *dp;
+      grub_efi_ip4_config2_protocol_t *ip4_config;
+      grub_efi_ip6_config_protocol_t *ip6_config;
+      grub_efi_handle_t http_handle;
+      grub_efi_http_t *http;
+      grub_efi_handle_t dhcp4_handle;
+      grub_efi_dhcp4_protocol_t *dhcp4;
+      grub_efi_handle_t dhcp6_handle;
+      grub_efi_dhcp6_protocol_t *dhcp6;
+
+      struct grub_efi_net_device *d;
+
+      dp = grub_efi_get_device_path (*handle);
+      if (!dp)
+	continue;
+
+      ip4_config = grub_efi_open_protocol (*handle, &ip4_config_guid,
+                                           GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+      if (!ip4_config)
+	continue;
+
+      ip6_config = grub_efi_open_protocol (*handle, &ip6_config_guid,
+                                           GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+      http_handle = grub_efi_service_binding (*handle, &http_service_binding_guid);
+      grub_errno = GRUB_ERR_NONE;
+      http = (http_handle)
+	? grub_efi_open_protocol (http_handle, &http_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL)
+	: NULL;
+
+      dhcp4_handle = grub_efi_service_binding (*handle, &dhcp4_service_binding_guid);
+      grub_errno = GRUB_ERR_NONE;
+      dhcp4 = (dhcp4_handle)
+	? grub_efi_open_protocol (dhcp4_handle, &dhcp4_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL)
+	: NULL;
+
+      dhcp6_handle = grub_efi_service_binding (*handle, &dhcp6_service_binding_guid);
+      grub_errno = GRUB_ERR_NONE;
+      dhcp6 = (dhcp6_handle)
+	? grub_efi_open_protocol (dhcp6_handle, &dhcp6_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL)
+	: NULL;
+
+      d = grub_malloc (sizeof (*d));
+      if (!d)
+	{
+	  grub_free (handles);
+	  while (net_devices)
+	    {
+	      d = net_devices->next;
+	      grub_free (net_devices);
+	      net_devices = d;
+	    }
+	  return;
+	}
+      d->handle = *handle;
+      d->ip4_config = ip4_config;
+      d->ip6_config = ip6_config;
+      d->http_handle = http_handle;
+      d->http = http;
+      d->dhcp4_handle = dhcp4_handle;
+      d->dhcp4 = dhcp4;
+      d->dhcp6_handle = dhcp6_handle;
+      d->dhcp6 = dhcp6;
+      d->next = net_devices;
+      d->card_name = grub_xasprintf ("efinet%d", id);
+      d->net_interfaces = NULL;
+      net_devices = d;
+    }
+
+  grub_efi_net_add_pxebc_to_cards ();
+  grub_free (handles);
+  set_ip_policy_to_static ();
+}
+
+static void
+listroutes_ip4 (struct grub_efi_net_device *netdev)
+{
+  char **routes;
+
+  routes = NULL;
+
+  if ((routes = efi_net_ip4_config->get_route_table (netdev)))
+    {
+      char **r;
+
+      for (r = routes; *r; ++r)
+	grub_printf ("%s\n", *r);
+    }
+
+  if (routes)
+    {
+      char **r;
+
+      for (r = routes; *r; ++r)
+	grub_free (*r);
+      grub_free (routes);
+    }
+}
+
+static void
+listroutes_ip6 (struct grub_efi_net_device *netdev)
+{
+  char **routes;
+
+  routes = NULL;
+
+  if ((routes = efi_net_ip6_config->get_route_table (netdev)))
+    {
+      char **r;
+
+      for (r = routes; *r; ++r)
+	grub_printf ("%s\n", *r);
+    }
+
+  if (routes)
+    {
+      char **r;
+
+      for (r = routes; *r; ++r)
+	grub_free (*r);
+      grub_free (routes);
+    }
+}
+
+static grub_err_t
+grub_cmd_efi_listroutes (struct grub_command *cmd __attribute__ ((unused)),
+                         int argc __attribute__ ((unused)),
+                         char **args __attribute__ ((unused)))
+{
+  struct grub_efi_net_device *netdev;
+
+  for (netdev = net_devices; netdev; netdev = netdev->next)
+    {
+      listroutes_ip4 (netdev);
+      listroutes_ip6 (netdev);
+    }
+
+  return GRUB_ERR_NONE;
+}
+static grub_err_t
+grub_cmd_efi_listcards (struct grub_command *cmd __attribute__ ((unused)),
+                        int argc __attribute__ ((unused)),
+                        char **args __attribute__ ((unused)))
+{
+  struct grub_efi_net_device *dev;
+
+  for (dev = net_devices; dev; dev = dev->next)
+    {
+      char *hw_addr;
+
+      hw_addr = efi_net_ip4_config->get_hw_address (dev);
+
+      if (hw_addr)
+	{
+	  grub_printf ("%s %s\n", dev->card_name, hw_addr);
+	  grub_free (hw_addr);
+	}
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_efi_listaddrs (struct grub_command *cmd __attribute__ ((unused)),
+		    int argc __attribute__ ((unused)),
+		    char **args __attribute__ ((unused)))
+{
+  struct grub_efi_net_device *dev;
+  grub_efi_net_interface_t *inf;
+
+  for (dev = net_devices; dev; dev = dev->next)
+    for (inf = dev->net_interfaces; inf; inf = inf->next)
+      {
+	char *hw_addr = NULL;
+	char *addr = NULL;
+
+	if ((hw_addr = efi_net_interface_get_hw_address (inf))
+	    && (addr = efi_net_interface_get_address (inf)))
+	  grub_printf ("%s %s %s\n", inf->name, hw_addr, addr);
+
+	if (hw_addr)
+	  grub_free (hw_addr);
+	if (addr)
+	  grub_free (addr);
+      }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_cmd_efi_addaddr (struct grub_command *cmd __attribute__ ((unused)),
+                  int argc, char **args)
+{
+  struct grub_efi_net_device *dev;
+  grub_err_t err;
+  grub_efi_ip4_config2_manual_address_t ip4;
+  grub_efi_ip6_config_manual_address_t ip6;
+  grub_efi_net_ip_manual_address_t net_ip;
+  int is_ip6 = 0;
+  int cidr = 0;
+
+  if (argc != 3)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("three arguments expected"));
+
+  for (dev = net_devices; dev; dev = dev->next)
+    {
+      if (grub_strcmp (dev->card_name, args[1]) == 0)
+	break;
+    }
+
+  if (!dev)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("card not found"));
+
+  err = grub_efi_net_parse_address (args[2], &ip4, &ip6, &is_ip6, &cidr);
+  if (err)
+    return err;
+
+  net_ip.is_ip6 = is_ip6;
+  if (is_ip6)
+    grub_memcpy (&net_ip.ip6, &ip6, sizeof(net_ip.ip6));
+  else
+    grub_memcpy (&net_ip.ip4, &ip4, sizeof(net_ip.ip4));
+
+  if (!grub_efi_net_create_interface (dev, args[0], &net_ip, cidr))
+    return grub_errno;
+
+  return GRUB_ERR_NONE;
+}
+
+static struct grub_fs grub_efi_netfs;
+
+static grub_net_t
+grub_net_open_real (const char *name __attribute__ ((unused)))
+{
+  grub_size_t protnamelen;
+  const char *protname, *server;
+  grub_net_t ret;
+
+  net_interface = NULL;
+
+  if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0)
+    {
+      protname = "tftp";
+      protnamelen = sizeof ("tftp") - 1;
+      server = name + sizeof ("pxe:") - 1;
+    }
+  else if (grub_strcmp (name, "pxe") == 0)
+    {
+      protname = "tftp";
+      protnamelen = sizeof ("tftp") - 1;
+      server = default_server;
+    }
+  else
+    {
+      const char *comma;
+
+      comma = grub_strchr (name, ',');
+      if (comma)
+	{
+	  protnamelen = comma - name;
+	  server = comma + 1;
+	  protname = name;
+	}
+      else
+	{
+	  protnamelen = grub_strlen (name);
+	  server = default_server;
+	  protname = name;
+	}
+    }
+
+  if (!server)
+    {
+      grub_error (GRUB_ERR_NET_BAD_ADDRESS,
+		  N_("no server is specified"));
+      return NULL;
+    }
+
+  net_interface = match_route (server);
+  if (!net_interface && net_default_interface)
+    {
+      net_interface = net_default_interface;
+      grub_dprintf ("efinet", "interface lookup failed, using default '%s'\n",
+                    net_interface->name);
+    }
+
+  if (!net_interface)
+    {
+      grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' no route found"),
+		  name);
+      return NULL;
+    }
+
+  if ((protnamelen == (sizeof ("https") - 1)
+	&& grub_memcmp ("https", protname, protnamelen) == 0))
+    {
+      net_interface->io = &io_http;
+      net_interface->io_type = 1;
+    }
+  else if ((protnamelen == (sizeof ("http") - 1)
+	&& grub_memcmp ("http", protname, protnamelen) == 0))
+    {
+      net_interface->io = &io_http;
+      net_interface->io_type = 0;
+    }
+  else if (protnamelen == (sizeof ("tftp") - 1)
+	&& grub_memcmp ("tftp", protname, protnamelen) == 0)
+    {
+      net_interface->io = &io_pxe;
+      net_interface->io_type = 0;
+    }
+  else
+    {
+      grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
+		  name);
+      return NULL;
+    }
+
+  efi_net_interface (configure);
+
+  ret = grub_zalloc (sizeof (*ret));
+  if (!ret)
+    return NULL;
+
+  ret->server = grub_strdup (server);
+  if (!ret->server)
+    {
+      grub_free (ret);
+      return NULL;
+    }
+
+  ret->fs = &grub_efi_netfs;
+  return ret;
+}
+
+static struct grub_fs grub_efi_netfs =
+  {
+    .name = "efi netfs",
+    .fs_dir = grub_efi_netfs_dir,
+    .fs_open = grub_efi_netfs_open,
+    .fs_read = grub_efi_netfs_read,
+    .fs_close = grub_efi_netfs_close,
+    .fs_label = NULL,
+    .fs_uuid = NULL,
+    .fs_mtime = NULL,
+  };
+
+int
+grub_efi_net_boot_from_https (void)
+{
+  grub_efi_loaded_image_t *image = NULL;
+  grub_efi_device_path_t *dp;
+
+  image = grub_efi_get_loaded_image (grub_efi_image_handle);
+  if (!image)
+    return 0;
+
+  dp = grub_efi_get_device_path (image->device_handle);
+
+  while (1)
+    {
+      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
+      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
+      grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+
+      if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE)
+	  && (subtype == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
+	{
+	  grub_efi_uri_device_path_t *uri_dp = (grub_efi_uri_device_path_t *) dp;
+	  return (grub_strncmp ((const char*) uri_dp->uri, "https://", sizeof ("https://") - 1) == 0) ? 1 : 0;
+	}
+
+      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
+        break;
+      dp = (grub_efi_device_path_t *) ((char *) dp + len);
+    }
+
+  return 0;
+}
+
+int
+grub_efi_net_boot_from_opa (void)
+{
+  grub_efi_loaded_image_t *image = NULL;
+  grub_efi_device_path_t *dp;
+
+  image = grub_efi_get_loaded_image (grub_efi_image_handle);
+  if (!image)
+    return 0;
+
+  dp = grub_efi_get_device_path (image->device_handle);
+
+  while (1)
+    {
+      grub_efi_uint8_t type = GRUB_EFI_DEVICE_PATH_TYPE (dp);
+      grub_efi_uint8_t subtype = GRUB_EFI_DEVICE_PATH_SUBTYPE (dp);
+      grub_efi_uint16_t len = GRUB_EFI_DEVICE_PATH_LENGTH (dp);
+
+      if ((type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE)
+	  && (subtype == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE))
+	{
+	  grub_efi_mac_address_device_path_t *mac_dp  = (grub_efi_mac_address_device_path_t *) dp;
+	  return (mac_dp->if_type == 0xC7) ? 1 : 0;
+	}
+
+      if (GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp))
+        break;
+      dp = (grub_efi_device_path_t *) ((char *) dp + len);
+    }
+
+  return 0;
+}
+
+static char *
+grub_env_write_readonly (struct grub_env_var *var __attribute__ ((unused)),
+			 const char *val __attribute__ ((unused)))
+{
+  return NULL;
+}
+
+grub_command_func_t grub_efi_net_list_routes = grub_cmd_efi_listroutes;
+grub_command_func_t grub_efi_net_list_cards = grub_cmd_efi_listcards;
+grub_command_func_t grub_efi_net_list_addrs = grub_cmd_efi_listaddrs;
+grub_command_func_t grub_efi_net_add_addr = grub_cmd_efi_addaddr;
+
+int
+grub_efi_net_fs_init ()
+{
+  grub_efi_net_find_cards ();
+  grub_efi_net_config = grub_efi_net_config_real;
+  grub_net_open = grub_net_open_real;
+  grub_register_variable_hook ("net_default_server", grub_efi_net_var_get_server,
+			       grub_efi_net_var_set_server);
+  grub_env_export ("net_default_server");
+  grub_register_variable_hook ("pxe_default_server", grub_efi_net_var_get_server,
+			       grub_efi_net_var_set_server);
+  grub_env_export ("pxe_default_server");
+  grub_register_variable_hook ("net_default_interface", 0,
+			       grub_efi_net_var_set_interface);
+  grub_env_export ("net_default_interface");
+  grub_register_variable_hook ("net_default_ip", grub_efi_net_var_get_ip,
+			       0);
+  grub_env_export ("net_default_ip");
+  grub_register_variable_hook ("net_default_mac", grub_efi_net_var_get_mac,
+			       0);
+  grub_env_export ("net_default_mac");
+
+  grub_env_set ("grub_netfs_type", "efi");
+  grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly);
+  grub_env_export ("grub_netfs_type");
+
+  return 1;
+}
+
+void
+grub_efi_net_fs_fini (void)
+{
+  grub_env_unset ("grub_netfs_type");
+  grub_efi_net_unset_interface_vars ();
+  grub_register_variable_hook ("net_default_server", 0, 0);
+  grub_env_unset ("net_default_server");
+  grub_register_variable_hook ("net_default_interface", 0, 0);
+  grub_env_unset ("net_default_interface");
+  grub_register_variable_hook ("pxe_default_server", 0, 0);
+  grub_env_unset ("pxe_default_server");
+  grub_register_variable_hook ("net_default_ip", 0, 0);
+  grub_env_unset ("net_default_ip");
+  grub_register_variable_hook ("net_default_mac", 0, 0);
+  grub_env_unset ("net_default_mac");
+  grub_efi_net_config = NULL;
+  grub_net_open = NULL;
+  grub_fs_unregister (&grub_efi_netfs);
+}
diff --git a/grub-core/net/efi/pxe.c b/grub-core/net/efi/pxe.c
new file mode 100644
index 0000000..e0b303b
--- /dev/null
+++ b/grub-core/net/efi/pxe.c
@@ -0,0 +1,351 @@
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/misc.h>
+#include <grub/net/efi.h>
+#include <grub/charset.h>
+
+static grub_efi_ip6_config_manual_address_t *
+efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config)
+{
+  grub_efi_uintn_t sz;
+  grub_efi_status_t status;
+  grub_efi_ip6_config_manual_address_t *manual_address;
+
+  sz = sizeof (*manual_address);
+  manual_address = grub_malloc (sz);
+  if (!manual_address)
+    return NULL;
+
+  status = ip6_config->get_data (ip6_config,
+                       GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
+                       &sz, manual_address);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (manual_address);
+      return NULL;
+    }
+
+  return manual_address;
+}
+
+static grub_efi_ip4_config2_manual_address_t *
+efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config)
+{
+  grub_efi_uintn_t sz;
+  grub_efi_status_t status;
+  grub_efi_ip4_config2_manual_address_t *manual_address;
+
+  sz = sizeof (*manual_address);
+  manual_address = grub_malloc (sz);
+  if (!manual_address)
+    return NULL;
+
+  status = ip4_config->get_data (ip4_config,
+                       GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
+                       &sz, manual_address);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_free (manual_address);
+      return NULL;
+    }
+
+  return manual_address;
+}
+
+static void
+pxe_configure (struct grub_efi_net_device *dev, int prefer_ip6)
+{
+  grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe;
+  grub_efi_pxe_mode_t *mode = pxe->mode;
+
+  if (!mode->started)
+    {
+      grub_efi_status_t status;
+
+      status = pxe->start (pxe, prefer_ip6);
+      if (status != GRUB_EFI_SUCCESS)
+	  grub_printf ("Couldn't start PXE\n");
+    }
+
+  if (mode->using_ipv6)
+    {
+      grub_efi_ip6_config_manual_address_t *manual_address;
+
+      manual_address = efi_ip6_config_manual_address (dev->ip6_config);
+      if (manual_address &&
+	  grub_memcmp (manual_address->address, mode->station_ip.v6, sizeof (manual_address->address)) != 0)
+	{
+	  grub_efi_status_t status;
+	  grub_efi_pxe_ip_address_t station_ip;
+
+	  grub_memcpy (station_ip.v6.addr, manual_address->address, sizeof (station_ip.v6.addr));
+
+	  status = pxe->set_station_ip (pxe, &station_ip, NULL);
+	  if (status != GRUB_EFI_SUCCESS)
+	      grub_printf ("Couldn't set station ip\n");
+
+	  grub_free (manual_address);
+	}
+    }
+  else
+    {
+      grub_efi_ip4_config2_manual_address_t *manual_address;
+
+      manual_address = efi_ip4_config_manual_address (dev->ip4_config);
+      if (manual_address &&
+	  grub_memcmp (manual_address->address, mode->station_ip.v4, sizeof (manual_address->address)) != 0)
+	{
+	  grub_efi_status_t status;
+	  grub_efi_pxe_ip_address_t station_ip;
+	  grub_efi_pxe_ip_address_t subnet_mask;
+
+	  grub_memcpy (station_ip.v4.addr, manual_address->address, sizeof (station_ip.v4.addr));
+	  grub_memcpy (subnet_mask.v4.addr, manual_address->subnet_mask, sizeof (subnet_mask.v4.addr));
+
+	  status = pxe->set_station_ip (pxe, &station_ip, &subnet_mask);
+	  if (status != GRUB_EFI_SUCCESS)
+	      grub_printf ("Couldn't set station ip\n");
+
+	  grub_free (manual_address);
+	}
+    }
+}
+
+static int
+parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest)
+{
+  grub_uint16_t newip[8];
+  const char *ptr = val;
+  int word, quaddot = -1;
+  int bracketed = 0;
+
+  if (ptr[0] == '[') {
+    bracketed = 1;
+    ptr++;
+  }
+
+  if (ptr[0] == ':' && ptr[1] != ':')
+    return 0;
+  if (ptr[0] == ':')
+    ptr++;
+
+  for (word = 0; word < 8; word++)
+    {
+      unsigned long t;
+      if (*ptr == ':')
+	{
+	  quaddot = word;
+	  word--;
+	  ptr++;
+	  continue;
+	}
+      t = grub_strtoul (ptr, (const char **) &ptr, 16);
+      if (grub_errno)
+	{
+	  grub_errno = GRUB_ERR_NONE;
+	  break;
+	}
+      if (t & ~0xffff)
+	return 0;
+      newip[word] = grub_cpu_to_be16 (t);
+      if (*ptr != ':')
+	break;
+      ptr++;
+    }
+  if (quaddot == -1 && word < 7)
+    return 0;
+  if (quaddot != -1)
+    {
+      grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot],
+		    (word - quaddot + 1) * sizeof (newip[0]));
+      grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0]));
+    }
+  grub_memcpy (ip, newip, 16);
+  if (bracketed && *ptr == ']')
+    {
+      ptr++;
+    }
+  if (rest)
+    *rest = ptr;
+  return 1;
+}
+
+static grub_err_t
+pxe_open (struct grub_efi_net_device *dev,
+	  int prefer_ip6,
+	  grub_file_t file,
+	  const char *filename,
+	  int type __attribute__((unused)))
+{
+  int i;
+  char *p;
+  grub_efi_status_t status;
+  grub_efi_pxe_ip_address_t server_ip;
+  grub_efi_uint64_t file_size = 0;
+  grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe;
+
+  if (pxe->mode->using_ipv6)
+    {
+      const char *rest;
+      grub_uint64_t ip6[2];
+      if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0)
+	grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr));
+    }
+  else
+    {
+      for (i = 0, p = file->device->net->server; i < 4; ++i, ++p)
+	server_ip.v4.addr[i] = grub_strtoul (p, (const char **) &p, 10);
+    }
+
+  status = pxe->mtftp (pxe,
+                        GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+                        NULL,
+                        0,
+                        &file_size,
+                        NULL,
+                        &server_ip,
+                        (grub_efi_char8_t *) filename,
+                        NULL,
+                        0);
+
+  if (status != GRUB_EFI_SUCCESS)
+    return grub_error (GRUB_ERR_IO, "Couldn't get file size");
+
+  file->size = (grub_off_t)file_size;
+  file->not_easily_seekable = 0;
+  file->data = 0;
+  file->device->net->offset = 0;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+pxe_close (struct grub_efi_net_device *dev __attribute__((unused)),
+	  int prefer_ip6 __attribute__((unused)),
+	  grub_file_t file __attribute__((unused)))
+{
+  file->offset = 0;
+  file->size = 0;
+  file->device->net->offset = 0;
+
+  if (file->data)
+    {
+      grub_free (file->data);
+      file->data = NULL;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_ssize_t
+pxe_read (struct grub_efi_net_device *dev,
+	  int prefer_ip6,
+	  grub_file_t file,
+	  char *buf,
+	  grub_size_t len)
+{
+  int i;
+  char *p;
+  grub_efi_status_t status;
+  grub_efi_pxe_t *pxe = (prefer_ip6) ? dev->ip6_pxe : dev->ip4_pxe;
+  grub_efi_uint64_t bufsz = len;
+  grub_efi_pxe_ip_address_t server_ip;
+  char *buf2 = NULL;
+
+  if (file->data)
+    {
+      grub_memcpy (buf, (char*)file->data + file->device->net->offset, len);
+      file->device->net->offset += len;
+      return len;
+    }
+
+  if (file->device->net->offset)
+    {
+      grub_error (GRUB_ERR_BUG, "No Offet Read Possible");
+      grub_print_error ();
+      return 0;
+    }
+
+  if (pxe->mode->using_ipv6)
+    {
+      const char *rest;
+      grub_uint64_t ip6[2];
+      if (parse_ip6 (file->device->net->server, ip6, &rest) && *rest == 0)
+	grub_memcpy (server_ip.v6.addr, ip6, sizeof (server_ip.v6.addr));
+    }
+  else
+    {
+      for (i = 0, p = file->device->net->server; i < 4; ++i, ++p)
+	server_ip.v4.addr[i] = grub_strtoul (p, (const char **) &p, 10);
+    }
+
+  status = pxe->mtftp (pxe,
+                        GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+                        buf,
+                        0,
+                        &bufsz,
+                        NULL,
+                        &server_ip,
+                        (grub_efi_char8_t *) file->device->net->name,
+                        NULL,
+                        0);
+
+  if (bufsz != file->size)
+    {
+      grub_error (GRUB_ERR_BUG, "Short read should not happen here");
+      grub_print_error ();
+      return 0;
+    }
+
+  if (status == GRUB_EFI_BUFFER_TOO_SMALL)
+    {
+
+      buf2 = grub_malloc (bufsz);
+      if (!buf2)
+	{
+	  grub_error (GRUB_ERR_OUT_OF_MEMORY, "ERROR OUT OF MEMORY");
+	  grub_print_error ();
+	  return 0;
+	}
+
+      status = pxe->mtftp(pxe,
+                            GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+                            buf2,
+                            0,
+                            &bufsz,
+                            NULL,
+                            &server_ip,
+                            (grub_efi_char8_t *) file->device->net->name,
+                            NULL,
+                            0);
+    }
+
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      if (buf2)
+	grub_free (buf2);
+
+      grub_error (GRUB_ERR_IO, "Failed to Read File");
+      grub_print_error ();
+      return 0;
+    }
+
+  if (buf2)
+    grub_memcpy (buf, buf2, len);
+
+  file->device->net->offset = len;
+
+  if (buf2)
+    file->data = buf2;
+
+  return len;
+}
+
+struct grub_efi_net_io io_pxe =
+  {
+    .configure = pxe_configure,
+    .open = pxe_open,
+    .read = pxe_read,
+    .close = pxe_close
+  };
+
diff --git a/grub-core/net/http.c b/grub-core/net/http.c
index 69b6296..f63f8b9 100644
--- a/grub-core/net/http.c
+++ b/grub-core/net/http.c
@@ -30,6 +30,7 @@
 GRUB_MOD_LICENSE ("GPLv3+");
 
 #define HTTP_PORT	((grub_uint16_t) 80)
+#define HTTP_MAX_CHUNK_SIZE 0x80000000
 
 typedef struct http_data
 {
@@ -82,6 +83,8 @@ parse_line (grub_file_t file, http_data_t data, char *ptr, grub_size_t len)
   if (data->in_chunk_len == 2)
     {
       data->chunk_rem = grub_strtoul (ptr, 0, 16);
+      if (data->chunk_rem > HTTP_MAX_CHUNK_SIZE)
+	  return GRUB_ERR_NET_PACKET_TOO_BIG;
       grub_errno = GRUB_ERR_NONE;
       if (data->chunk_rem == 0)
 	{
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index a31f0c9..7571afa 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -32,6 +32,9 @@
 #include <grub/loader.h>
 #include <grub/bufio.h>
 #include <grub/kernel.h>
+#ifdef GRUB_MACHINE_EFI
+#include <grub/net/efi.h>
+#endif
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -2086,8 +2089,48 @@ static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute;
 static grub_command_t cmd_setvlan, cmd_lsroutes, cmd_lscards;
 static grub_command_t cmd_lsaddr, cmd_slaac;
 
+#ifdef GRUB_MACHINE_EFI
+
+static enum
+  {
+    INIT_MODE_NONE,
+    INIT_MODE_GRUB,
+    INIT_MODE_EFI
+  } init_mode;
+
+static grub_command_t cmd_bootp, cmd_bootp6;
+
+#endif
+
 GRUB_MOD_INIT(net)
 {
+#ifdef GRUB_MACHINE_EFI
+  if (grub_net_open)
+    return;
+
+  if ((grub_efi_net_boot_from_https () || grub_efi_net_boot_from_opa ())
+      && grub_efi_net_fs_init ())
+    {
+      cmd_lsroutes = grub_register_command ("net_ls_routes", grub_efi_net_list_routes,
+					    "", N_("list network routes"));
+      cmd_lscards = grub_register_command ("net_ls_cards", grub_efi_net_list_cards,
+					   "", N_("list network cards"));
+      cmd_lsaddr = grub_register_command ("net_ls_addr", grub_efi_net_list_addrs,
+					  "", N_("list network addresses"));
+      cmd_addaddr = grub_register_command ("net_add_addr", grub_efi_net_add_addr,
+                                           N_("SHORTNAME CARD ADDRESS [HWADDRESS]"),
+                                           N_("Add a network address."));
+      cmd_bootp = grub_register_command ("net_bootp", grub_efi_net_bootp,
+					 N_("[CARD]"),
+					 N_("perform a bootp autoconfiguration"));
+      cmd_bootp6 = grub_register_command ("net_bootp6", grub_efi_net_bootp6,
+                                          N_("[CARD]"),
+                                          N_("perform a bootp autoconfiguration"));
+      init_mode = INIT_MODE_EFI;
+      return;
+    }
+#endif
+
   grub_register_variable_hook ("net_default_server", defserver_get_env,
 			       defserver_set_env);
   grub_env_export ("net_default_server");
@@ -2138,10 +2181,37 @@ GRUB_MOD_INIT(net)
 						grub_net_restore_hw,
 						GRUB_LOADER_PREBOOT_HOOK_PRIO_DISK);
   grub_net_poll_cards_idle = grub_net_poll_cards_idle_real;
+
+#ifdef GRUB_MACHINE_EFI
+  grub_env_set ("grub_netfs_type", "grub");
+  grub_register_variable_hook ("grub_netfs_type", 0, grub_env_write_readonly);
+  grub_env_export ("grub_netfs_type");
+  init_mode = INIT_MODE_GRUB;
+#endif
+
 }
 
 GRUB_MOD_FINI(net)
 {
+
+#ifdef GRUB_MACHINE_EFI
+  if (init_mode == INIT_MODE_NONE)
+    return;
+
+  if (init_mode == INIT_MODE_EFI)
+    {
+      grub_unregister_command (cmd_lsroutes);
+      grub_unregister_command (cmd_lscards);
+      grub_unregister_command (cmd_lsaddr);
+      grub_unregister_command (cmd_addaddr);
+      grub_unregister_command (cmd_bootp);
+      grub_unregister_command (cmd_bootp6);
+      grub_efi_net_fs_fini ();
+      init_mode = INIT_MODE_NONE;
+      return;
+    }
+#endif
+
   grub_register_variable_hook ("net_default_server", 0, 0);
   grub_register_variable_hook ("pxe_default_server", 0, 0);
 
@@ -2160,4 +2230,8 @@ GRUB_MOD_FINI(net)
   grub_net_fini_hw (0);
   grub_loader_unregister_preboot_hook (fini_hnd);
   grub_net_poll_cards_idle = NULL;
+
+#ifdef GRUB_MACHINE_EFI
+  init_mode = INIT_MODE_NONE;
+#endif
 }
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index b64fd49..ae02e6d 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -658,6 +658,27 @@ typedef union
 
 typedef grub_efi_uint64_t grub_efi_physical_address_t;
 typedef grub_efi_uint64_t grub_efi_virtual_address_t;
+typedef struct
+{
+  grub_uint8_t addr[4];
+} grub_efi_pxe_ipv4_address_t;
+
+typedef struct
+{
+  grub_uint8_t addr[16];
+} grub_efi_pxe_ipv6_address_t;
+
+typedef struct
+{
+  grub_uint8_t addr[32];
+} grub_efi_pxe_mac_address_t;
+
+typedef union
+{
+  grub_uint32_t addr[4];
+  grub_efi_pxe_ipv4_address_t v4;
+  grub_efi_pxe_ipv6_address_t v6;
+} grub_efi_pxe_ip_address_t;
 
 /* XXX although the spec does not specify the padding, this actually
    must have the padding!  */
@@ -907,6 +928,8 @@ struct grub_efi_ipv6_device_path
   grub_efi_uint16_t remote_port;
   grub_efi_uint16_t protocol;
   grub_efi_uint8_t static_ip_address;
+  grub_efi_uint8_t prefix_length;
+  grub_efi_ipv6_address_t gateway_ip_address;
 } GRUB_PACKED;
 typedef struct grub_efi_ipv6_device_path grub_efi_ipv6_device_path_t;
 
@@ -965,6 +988,15 @@ struct grub_efi_uri_device_path
 } GRUB_PACKED;
 typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t;
 
+#define GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE                31
+struct grub_efi_dns_device_path
+{
+  grub_efi_device_path_t header;
+  grub_efi_uint8_t is_ipv6;
+  grub_efi_pxe_ip_address_t dns_server_ip[0];
+} GRUB_PACKED;
+typedef struct grub_efi_dns_device_path grub_efi_dns_device_path_t;
+
 #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE	10
 
 /* Media Device Path.  */
@@ -1047,6 +1079,23 @@ struct grub_efi_bios_device_path
 } GRUB_PACKED;
 typedef struct grub_efi_bios_device_path grub_efi_bios_device_path_t;
 
+/* Service Binding definitions */
+struct grub_efi_service_binding;
+
+typedef grub_efi_status_t
+(__grub_efi_api *grub_efi_service_binding_create_child) (struct grub_efi_service_binding *this,
+                                          grub_efi_handle_t *child_handle);
+
+typedef grub_efi_status_t
+(__grub_efi_api  *grub_efi_service_binding_destroy_child) (struct grub_efi_service_binding *this,
+                                           grub_efi_handle_t *child_handle);
+
+typedef struct grub_efi_service_binding
+{
+  grub_efi_service_binding_create_child create_child;
+  grub_efi_service_binding_destroy_child destroy_child;
+} grub_efi_service_binding_t;
+
 struct grub_efi_open_protocol_information_entry
 {
   grub_efi_handle_t agent_handle;
@@ -1607,23 +1656,29 @@ typedef struct grub_efi_pxe_tftp_error
   grub_efi_char8_t error_string[127];
 } grub_efi_pxe_tftp_error_t;
 
-typedef struct {
-  grub_uint8_t addr[4];
-} grub_efi_pxe_ipv4_address_t;
-
-typedef struct {
-  grub_uint8_t addr[16];
-} grub_efi_pxe_ipv6_address_t;
-
-typedef struct {
-  grub_uint8_t addr[32];
-} grub_efi_pxe_mac_address_t;
+typedef grub_efi_uint16_t grub_efi_pxe_base_code_udp_port_t;
 
-typedef union {
-  grub_uint32_t addr[4];
-  grub_efi_pxe_ipv4_address_t v4;
-  grub_efi_pxe_ipv6_address_t v6;
-} grub_efi_pxe_ip_address_t;
+typedef enum
+  {
+    GRUB_EFI_PXE_BASE_CODE_TFTP_FIRST,
+    GRUB_EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+    GRUB_EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+    GRUB_EFI_PXE_BASE_CODE_TFTP_WRITE_FILE,
+    GRUB_EFI_PXE_BASE_CODE_TFTP_READ_DIRECTORY,
+    GRUB_EFI_PXE_BASE_CODE_MTFTP_GET_FILE_SIZE,
+    GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_FILE,
+    GRUB_EFI_PXE_BASE_CODE_MTFTP_READ_DIRECTORY,
+    GRUB_EFI_PXE_BASE_CODE_MTFTP_LAST,
+  } grub_efi_pxe_base_code_tftp_opcode_t;
+
+typedef struct
+{
+  grub_efi_ip_address_t mcast_ip;
+  grub_efi_pxe_base_code_udp_port_t c_port;
+  grub_efi_pxe_base_code_udp_port_t s_port;
+  grub_efi_uint16_t listen_timeout;
+  grub_efi_uint16_t transmit_timeout;
+} grub_efi_pxe_base_code_mtftp_info_t;
 
 #define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8
 typedef struct grub_efi_pxe_ip_filter
@@ -1690,18 +1745,30 @@ typedef struct grub_efi_pxe_mode
 typedef struct grub_efi_pxe
 {
   grub_uint64_t rev;
-  void *start;
-  void *stop;
-  void *dhcp;
-  void *discover;
-  void *mftp;
-  void *udpwrite;
-  void *udpread;
-  void *setipfilter;
-  void *arp;
-  void *setparams;
-  void *setstationip;
-  void *setpackets;
+  grub_efi_status_t (__grub_efi_api *start) (struct grub_efi_pxe *this, grub_efi_boolean_t use_ipv6);
+  void (__grub_efi_api *stop) (void);
+  grub_efi_status_t (__grub_efi_api *dhcp) (struct grub_efi_pxe *this,
+                             grub_efi_boolean_t sort_offers);
+  void (__grub_efi_api *discover) (void);
+  grub_efi_status_t (__grub_efi_api *mtftp) (struct grub_efi_pxe *this,
+                              grub_efi_pxe_base_code_tftp_opcode_t operation,
+                              void *buffer_ptr,
+                              grub_efi_boolean_t overwrite,
+                              grub_efi_uint64_t *buffer_size,
+                              grub_efi_uintn_t *block_size,
+                              grub_efi_pxe_ip_address_t *server_ip,
+                              grub_efi_char8_t *filename,
+                              grub_efi_pxe_base_code_mtftp_info_t *info,
+                              grub_efi_boolean_t dont_use_buffer);
+  void (__grub_efi_api *udpwrite) (void);
+  void (__grub_efi_api *udpread) (void);
+  void (__grub_efi_api *setipfilter) (void);
+  void (__grub_efi_api *arp) (void);
+  void (__grub_efi_api *setparams) (void);
+  grub_efi_status_t (__grub_efi_api *set_station_ip) (struct grub_efi_pxe *this,
+			    grub_efi_pxe_ip_address_t *new_station_ip,
+			    grub_efi_pxe_ip_address_t *new_subnet_mask);
+  void (__grub_efi_api *setpackets) (void);
   struct grub_efi_pxe_mode *mode;
 } grub_efi_pxe_t;
 
@@ -1986,6 +2053,45 @@ struct grub_efi_ip4_config2_protocol
 };
 typedef struct grub_efi_ip4_config2_protocol grub_efi_ip4_config2_protocol_t;
 
+struct grub_efi_ip4_route_table
+{
+  grub_efi_ipv4_address_t subnet_address;
+  grub_efi_ipv4_address_t subnet_mask;
+  grub_efi_ipv4_address_t gateway_address;
+};
+typedef struct grub_efi_ip4_route_table grub_efi_ip4_route_table_t;
+
+#define GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE 32
+
+struct grub_efi_ip4_config2_interface_info
+{
+  grub_efi_char16_t name[GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE];
+  grub_efi_uint8_t if_type;
+  grub_efi_uint32_t hw_address_size;
+  grub_efi_mac_address_t hw_address;
+  grub_efi_ipv4_address_t station_address;
+  grub_efi_ipv4_address_t subnet_mask;
+  grub_efi_uint32_t route_table_size;
+  grub_efi_ip4_route_table_t *route_table;
+};
+
+typedef struct grub_efi_ip4_config2_interface_info grub_efi_ip4_config2_interface_info_t;
+
+enum grub_efi_ip4_config2_policy
+  {
+    GRUB_EFI_IP4_CONFIG2_POLICY_STATIC,
+    GRUB_EFI_IP4_CONFIG2_POLICY_DHCP,
+    GRUB_EFI_IP4_CONFIG2_POLICY_MAX
+  };
+typedef enum grub_efi_ip4_config2_policy grub_efi_ip4_config2_policy_t;
+
+struct grub_efi_ip4_config2_manual_address
+{
+  grub_efi_ipv4_address_t address;
+  grub_efi_ipv4_address_t subnet_mask;
+};
+typedef struct grub_efi_ip4_config2_manual_address grub_efi_ip4_config2_manual_address_t;
+
 enum grub_efi_ip6_config_data_type
   {
     GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
@@ -2021,6 +2127,55 @@ struct grub_efi_ip6_config_protocol
 };
 typedef struct grub_efi_ip6_config_protocol grub_efi_ip6_config_protocol_t;
 
+enum grub_efi_ip6_config_policy
+  {
+    GRUB_EFI_IP6_CONFIG_POLICY_MANUAL,
+    GRUB_EFI_IP6_CONFIG_POLICY_AUTOMATIC
+  };
+typedef enum grub_efi_ip6_config_policy grub_efi_ip6_config_policy_t;
+
+struct grub_efi_ip6_address_info
+{
+  grub_efi_ipv6_address_t address;
+  grub_efi_uint8_t prefix_length;
+};
+typedef struct grub_efi_ip6_address_info grub_efi_ip6_address_info_t;
+
+struct grub_efi_ip6_route_table
+{
+  grub_efi_pxe_ipv6_address_t gateway;
+  grub_efi_pxe_ipv6_address_t destination;
+  grub_efi_uint8_t prefix_length;
+};
+typedef struct grub_efi_ip6_route_table grub_efi_ip6_route_table_t;
+
+struct grub_efi_ip6_config_interface_info
+{
+  grub_efi_char16_t name[32];
+  grub_efi_uint8_t if_type;
+  grub_efi_uint32_t hw_address_size;
+  grub_efi_mac_address_t hw_address;
+  grub_efi_uint32_t address_info_count;
+  grub_efi_ip6_address_info_t *address_info;
+  grub_efi_uint32_t route_count;
+  grub_efi_ip6_route_table_t *route_table;
+};
+typedef struct grub_efi_ip6_config_interface_info grub_efi_ip6_config_interface_info_t;
+
+struct grub_efi_ip6_config_dup_addr_detect_transmits
+{
+  grub_efi_uint32_t dup_addr_detect_transmits;
+};
+typedef struct grub_efi_ip6_config_dup_addr_detect_transmits grub_efi_ip6_config_dup_addr_detect_transmits_t;
+
+struct grub_efi_ip6_config_manual_address
+{
+  grub_efi_ipv6_address_t address;
+  grub_efi_boolean_t is_anycast;
+  grub_efi_uint8_t prefix_length;
+};
+typedef struct grub_efi_ip6_config_manual_address grub_efi_ip6_config_manual_address_t;
+
 struct grub_efi_load_file2
 {
   grub_efi_status_t (__grub_efi_api *load_file)(struct grub_efi_load_file2 *this,
diff --git a/include/grub/efi/dhcp.h b/include/grub/efi/dhcp.h
new file mode 100644
index 0000000..b74dc24
--- /dev/null
+++ b/include/grub/efi/dhcp.h
@@ -0,0 +1,347 @@
+#ifndef GRUB_EFI_DHCP_HEADER
+#define GRUB_EFI_DHCP_HEADER	1
+
+#define GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID \
+  { 0x9d9a39d8, 0xbd42, 0x4a73, \
+    { 0xa4, 0xd5, 0x8e, 0xe9, 0x4b, 0xe1, 0x13, 0x80 } \
+  }
+
+#define GRUB_EFI_DHCP4_PROTOCOL_GUID \
+  { 0x8a219718, 0x4ef5, 0x4761, \
+    { 0x91, 0xc8, 0xc0, 0xf0, 0x4b, 0xda, 0x9e, 0x56 } \
+  }
+
+#define GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID \
+  { 0x9fb9a8a1, 0x2f4a, 0x43a6, \
+    { 0x88, 0x9c, 0xd0, 0xf7, 0xb6, 0xc4 ,0x7a, 0xd5 } \
+  }
+
+#define GRUB_EFI_DHCP6_PROTOCOL_GUID \
+  { 0x87c8bad7, 0x595, 0x4053, \
+    { 0x82, 0x97, 0xde, 0xde, 0x39, 0x5f, 0x5d, 0x5b } \
+  }
+
+typedef struct grub_efi_dhcp4_protocol grub_efi_dhcp4_protocol_t;
+
+enum grub_efi_dhcp4_state
+  {
+    GRUB_EFI_DHCP4_STOPPED,
+    GRUB_EFI_DHCP4_INIT,
+    GRUB_EFI_DHCP4_SELECTING,
+    GRUB_EFI_DHCP4_REQUESTING,
+    GRUB_EFI_DHCP4_BOUND,
+    GRUB_EFI_DHCP4_RENEWING,
+    GRUB_EFI_DHCP4_REBINDING,
+    GRUB_EFI_DHCP4_INIT_REBOOT,
+    GRUB_EFI_DHCP4_REBOOTING
+  };
+typedef enum grub_efi_dhcp4_state grub_efi_dhcp4_state_t;
+
+struct grub_efi_dhcp4_header
+{
+  grub_efi_uint8_t op_code;
+  grub_efi_uint8_t hw_type;
+  grub_efi_uint8_t hw_addr_len;
+  grub_efi_uint8_t hops;
+  grub_efi_uint32_t xid;
+  grub_efi_uint16_t seconds;
+  grub_efi_uint16_t reserved;
+  grub_efi_ipv4_address_t client_addr;
+  grub_efi_ipv4_address_t your_addr;
+  grub_efi_ipv4_address_t server_addr;
+  grub_efi_ipv4_address_t gateway_addr;
+  grub_efi_uint8_t client_hw_addr[16];
+  grub_efi_char8_t server_name[64];
+  grub_efi_char8_t boot_file_name[128];
+} GRUB_PACKED;
+
+typedef struct grub_efi_dhcp4_header grub_efi_dhcp4_header_t;
+
+struct grub_efi_dhcp4_packet
+{
+  grub_efi_uint32_t size;
+  grub_efi_uint32_t length;
+  struct
+  {
+    grub_efi_dhcp4_header_t header;
+    grub_efi_uint32_t magik;
+    grub_efi_uint8_t option[1];
+  } dhcp4;
+} GRUB_PACKED;
+
+typedef struct grub_efi_dhcp4_packet grub_efi_dhcp4_packet_t;
+
+struct grub_efi_dhcp4_listen_point
+{
+  grub_efi_ipv4_address_t listen_address;
+  grub_efi_ipv4_address_t subnet_mask;
+  grub_efi_uint16_t listen_port;
+};
+typedef struct grub_efi_dhcp4_listen_point grub_efi_dhcp4_listen_point_t;
+
+struct grub_efi_dhcp4_transmit_receive_token
+{
+  grub_efi_status_t status;
+  grub_efi_event_t completion_event;
+  grub_efi_ipv4_address_t remote_address;
+  grub_efi_uint16_t remote_port;
+  grub_efi_ipv4_address_t gateway_address;
+  grub_efi_uint32_t listen_point_count;
+  grub_efi_dhcp4_listen_point_t *listen_points;
+  grub_efi_uint32_t timeout_value;
+  grub_efi_dhcp4_packet_t *packet;
+  grub_efi_uint32_t response_count;
+  grub_efi_dhcp4_packet_t *response_list;
+};
+typedef struct grub_efi_dhcp4_transmit_receive_token grub_efi_dhcp4_transmit_receive_token_t;
+
+enum grub_efi_dhcp4_event
+  {
+    GRUB_EFI_DHCP4_SEND_DISCOVER = 0x01,
+    GRUB_EFI_DHCP4_RCVD_OFFER,
+    GRUB_EFI_DHCP4_SELECT_OFFER,
+    GRUB_EFI_DHCP4_SEND_REQUEST,
+    GRUB_EFI_DHCP4_RCVD_ACK,
+    GRUB_EFI_DHCP4_RCVD_NAK,
+    GRUB_EFI_DHCP4_SEND_DECLINE,
+    GRUB_EFI_DHCP4_BOUND_COMPLETED,
+    GRUB_EFI_DHCP4_ENTER_RENEWING,
+    GRUB_EFI_DHCP4_ENTER_REBINDING,
+    GRUB_EFI_DHCP4_ADDRESS_LOST,
+    GRUB_EFI_DHCP4_FAIL
+  };
+typedef enum grub_efi_dhcp4_event grub_efi_dhcp4_event_t;
+
+struct grub_efi_dhcp4_packet_option
+{
+  grub_efi_uint8_t op_code;
+  grub_efi_uint8_t length;
+  grub_efi_uint8_t data[1];
+} GRUB_PACKED;
+typedef struct grub_efi_dhcp4_packet_option grub_efi_dhcp4_packet_option_t;
+
+struct grub_efi_dhcp4_config_data
+{
+  grub_efi_uint32_t discover_try_count;
+  grub_efi_uint32_t *discover_timeout;
+  grub_efi_uint32_t request_try_count;
+  grub_efi_uint32_t *request_timeout;
+  grub_efi_ipv4_address_t client_address;
+  grub_efi_status_t (__grub_efi_api *dhcp4_callback)
+  (
+    grub_efi_dhcp4_protocol_t *this,
+    void *context,
+    grub_efi_dhcp4_state_t current_state,
+    grub_efi_dhcp4_event_t dhcp4_event,
+    grub_efi_dhcp4_packet_t *packet,
+    grub_efi_dhcp4_packet_t **new_packet
+  );
+  void *callback_context;
+  grub_efi_uint32_t option_count;
+  grub_efi_dhcp4_packet_option_t **option_list;
+};
+typedef struct grub_efi_dhcp4_config_data grub_efi_dhcp4_config_data_t;
+
+struct grub_efi_dhcp4_mode_data
+{
+  grub_efi_dhcp4_state_t state;
+  grub_efi_dhcp4_config_data_t config_data;
+  grub_efi_ipv4_address_t client_address;
+  grub_efi_mac_address_t client_mac_address;
+  grub_efi_ipv4_address_t server_address;
+  grub_efi_ipv4_address_t router_address;
+  grub_efi_ipv4_address_t subnet_mask;
+  grub_efi_uint32_t lease_time;
+  grub_efi_dhcp4_packet_t *reply_packet;
+};
+typedef struct grub_efi_dhcp4_mode_data grub_efi_dhcp4_mode_data_t;
+
+struct grub_efi_dhcp4_protocol {
+  grub_efi_status_t (__grub_efi_api *get_mode_data) (grub_efi_dhcp4_protocol_t *this,
+	      grub_efi_dhcp4_mode_data_t *dhcp4_mode_data);
+  grub_efi_status_t (__grub_efi_api *configure) (grub_efi_dhcp4_protocol_t *this,
+	      grub_efi_dhcp4_config_data_t *dhcp4_cfg_data);
+  grub_efi_status_t (__grub_efi_api *start) (grub_efi_dhcp4_protocol_t *this,
+	      grub_efi_event_t completion_event);
+  grub_efi_status_t (__grub_efi_api *renew_rebind) (grub_efi_dhcp4_protocol_t *this,
+	      grub_efi_boolean_t rebind_request,
+	      grub_efi_event_t completion_event);
+  grub_efi_status_t (__grub_efi_api *release) (grub_efi_dhcp4_protocol_t *this);
+  grub_efi_status_t (__grub_efi_api *stop) (grub_efi_dhcp4_protocol_t *this);
+  grub_efi_status_t (__grub_efi_api *build) (grub_efi_dhcp4_protocol_t *this,
+	      grub_efi_dhcp4_packet_t *seed_packet,
+	      grub_efi_uint32_t delete_count,
+	      grub_efi_uint8_t *delete_list,
+	      grub_efi_uint32_t append_count,
+	      grub_efi_dhcp4_packet_option_t *append_list[],
+	      grub_efi_dhcp4_packet_t **new_packet);
+  grub_efi_status_t (__grub_efi_api *transmit_receive) (grub_efi_dhcp4_protocol_t *this,
+	      grub_efi_dhcp4_transmit_receive_token_t *token);
+  grub_efi_status_t (__grub_efi_api *parse) (grub_efi_dhcp4_protocol_t *this,
+	      grub_efi_dhcp4_packet_t *packet,
+	      grub_efi_uint32_t *option_count,
+	      grub_efi_dhcp4_packet_option_t *packet_option_list[]);
+};
+typedef struct grub_efi_dhcp6_protocol grub_efi_dhcp6_protocol_t;
+
+struct grub_efi_dhcp6_retransmission
+{
+  grub_efi_uint32_t irt;
+  grub_efi_uint32_t mrc;
+  grub_efi_uint32_t mrt;
+  grub_efi_uint32_t mrd;
+};
+typedef struct grub_efi_dhcp6_retransmission grub_efi_dhcp6_retransmission_t;
+
+enum grub_efi_dhcp6_event
+  {
+    GRUB_EFI_DHCP6_SEND_SOLICIT,
+    GRUB_EFI_DHCP6_RCVD_ADVERTISE,
+    GRUB_EFI_DHCP6_SELECT_ADVERTISE,
+    GRUB_EFI_DHCP6_SEND_REQUEST,
+    GRUB_EFI_DHCP6_RCVD_REPLY,
+    GRUB_EFI_DHCP6_RCVD_RECONFIGURE,
+    GRUB_EFI_DHCP6_SEND_DECLINE,
+    GRUB_EFI_DHCP6_SEND_CONFIRM,
+    GRUB_EFI_DHCP6_SEND_RELEASE,
+    GRUB_EFI_DHCP6_SEND_RENEW,
+    GRUB_EFI_DHCP6_SEND_REBIND
+  };
+typedef enum grub_efi_dhcp6_event grub_efi_dhcp6_event_t;
+
+struct grub_efi_dhcp6_packet_option
+{
+  grub_efi_uint16_t op_code;
+  grub_efi_uint16_t op_len;
+  grub_efi_uint8_t data[1];
+} GRUB_PACKED;
+typedef struct grub_efi_dhcp6_packet_option grub_efi_dhcp6_packet_option_t;
+
+struct grub_efi_dhcp6_header
+{
+  grub_efi_uint32_t transaction_id:24;
+  grub_efi_uint32_t message_type:8;
+} GRUB_PACKED;
+typedef struct grub_efi_dhcp6_header grub_efi_dhcp6_header_t;
+
+struct grub_efi_dhcp6_packet
+{
+  grub_efi_uint32_t size;
+  grub_efi_uint32_t length;
+  struct
+  {
+    grub_efi_dhcp6_header_t header;
+    grub_efi_uint8_t option[1];
+  } dhcp6;
+} GRUB_PACKED;
+typedef struct grub_efi_dhcp6_packet grub_efi_dhcp6_packet_t;
+
+struct grub_efi_dhcp6_ia_address
+{
+  grub_efi_ipv6_address_t ip_address;
+  grub_efi_uint32_t preferred_lifetime;
+  grub_efi_uint32_t valid_lifetime;
+};
+typedef struct grub_efi_dhcp6_ia_address grub_efi_dhcp6_ia_address_t;
+
+enum grub_efi_dhcp6_state
+  {
+    GRUB_EFI_DHCP6_INIT,
+    GRUB_EFI_DHCP6_SELECTING,
+    GRUB_EFI_DHCP6_REQUESTING,
+    GRUB_EFI_DHCP6_DECLINING,
+    GRUB_EFI_DHCP6_CONFIRMING,
+    GRUB_EFI_DHCP6_RELEASING,
+    GRUB_EFI_DHCP6_BOUND,
+    GRUB_EFI_DHCP6_RENEWING,
+    GRUB_EFI_DHCP6_REBINDING
+  };
+typedef enum grub_efi_dhcp6_state grub_efi_dhcp6_state_t;
+
+#define GRUB_EFI_DHCP6_IA_TYPE_NA 3
+#define GRUB_EFI_DHCP6_IA_TYPE_TA 4
+
+struct grub_efi_dhcp6_ia_descriptor
+{
+  grub_efi_uint16_t type;
+  grub_efi_uint32_t ia_id;
+};
+typedef struct grub_efi_dhcp6_ia_descriptor grub_efi_dhcp6_ia_descriptor_t;
+
+struct grub_efi_dhcp6_ia
+{
+  grub_efi_dhcp6_ia_descriptor_t descriptor;
+  grub_efi_dhcp6_state_t state;
+  grub_efi_dhcp6_packet_t *reply_packet;
+  grub_efi_uint32_t ia_address_count;
+  grub_efi_dhcp6_ia_address_t ia_address[1];
+};
+typedef struct grub_efi_dhcp6_ia grub_efi_dhcp6_ia_t;
+
+struct grub_efi_dhcp6_duid
+{
+  grub_efi_uint16_t length;
+  grub_efi_uint8_t duid[1];
+};
+typedef struct grub_efi_dhcp6_duid grub_efi_dhcp6_duid_t;
+
+struct grub_efi_dhcp6_mode_data
+{
+  grub_efi_dhcp6_duid_t *client_id;
+  grub_efi_dhcp6_ia_t *ia;
+};
+typedef struct grub_efi_dhcp6_mode_data grub_efi_dhcp6_mode_data_t;
+
+struct grub_efi_dhcp6_config_data
+{
+  grub_efi_status_t (__grub_efi_api *dhcp6_callback) (grub_efi_dhcp6_protocol_t this,
+		void *context,
+		grub_efi_dhcp6_state_t current_state,
+		grub_efi_dhcp6_event_t dhcp6_event,
+		grub_efi_dhcp6_packet_t *packet,
+		grub_efi_dhcp6_packet_t **new_packet);
+  void *callback_context;
+  grub_efi_uint32_t option_count;
+  grub_efi_dhcp6_packet_option_t **option_list;
+  grub_efi_dhcp6_ia_descriptor_t ia_descriptor;
+  grub_efi_event_t ia_info_event;
+  grub_efi_boolean_t reconfigure_accept;
+  grub_efi_boolean_t rapid_commit;
+  grub_efi_dhcp6_retransmission_t *solicit_retransmission;
+};
+typedef struct grub_efi_dhcp6_config_data grub_efi_dhcp6_config_data_t;
+
+struct grub_efi_dhcp6_protocol {
+  grub_efi_status_t (__grub_efi_api *get_mode_data) (grub_efi_dhcp6_protocol_t *this,
+	    grub_efi_dhcp6_mode_data_t *dhcp6_mode_data,
+	    grub_efi_dhcp6_config_data_t *dhcp6_config_data);
+  grub_efi_status_t (__grub_efi_api *configure) (grub_efi_dhcp6_protocol_t *this,
+	    grub_efi_dhcp6_config_data_t *dhcp6_cfg_data);
+  grub_efi_status_t (__grub_efi_api *start) (grub_efi_dhcp6_protocol_t *this);
+  grub_efi_status_t (__grub_efi_api *info_request) (grub_efi_dhcp6_protocol_t *this,
+	    grub_efi_boolean_t send_client_id,
+	    grub_efi_dhcp6_packet_option_t *option_request,
+	    grub_efi_uint32_t option_count,
+	    grub_efi_dhcp6_packet_option_t *option_list[],
+	    grub_efi_dhcp6_retransmission_t *retransmission,
+	    grub_efi_event_t timeout_event,
+	    grub_efi_status_t (*reply_callback) (grub_efi_dhcp6_protocol_t *this,
+		    void *context,
+		    grub_efi_dhcp6_packet_t *packet),
+	    void *callback_context);
+  grub_efi_status_t (__grub_efi_api *renew_rebind) (grub_efi_dhcp6_protocol_t *this,
+	    grub_efi_boolean_t rebind_request);
+  grub_efi_status_t (__grub_efi_api *decline) (grub_efi_dhcp6_protocol_t *this,
+	    grub_efi_uint32_t address_count,
+	    grub_efi_ipv6_address_t *addresses);
+  grub_efi_status_t (__grub_efi_api *release) (grub_efi_dhcp6_protocol_t *this,
+	    grub_efi_uint32_t address_count,
+	    grub_efi_ipv6_address_t *addresses);
+  grub_efi_status_t (__grub_efi_api *stop) (grub_efi_dhcp6_protocol_t *this);
+  grub_efi_status_t (__grub_efi_api *parse) (grub_efi_dhcp6_protocol_t *this,
+	    grub_efi_dhcp6_packet_t *packet,
+	    grub_efi_uint32_t *option_count,
+	    grub_efi_dhcp6_packet_option_t *packet_option_list[]);
+};
+
+#endif /* ! GRUB_EFI_DHCP_HEADER */
diff --git a/include/grub/efi/http.h b/include/grub/efi/http.h
new file mode 100644
index 0000000..79592cd
--- /dev/null
+++ b/include/grub/efi/http.h
@@ -0,0 +1,215 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GRUB_EFI_HTTP_HEADER
+#define GRUB_EFI_HTTP_HEADER	1
+
+#include <grub/symbol.h>
+#include <grub/net.h>
+#include <grub/efi/api.h>
+
+#define GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID \
+  { 0xbdc8e6af, 0xd9bc, 0x4379, \
+      { 0xa7, 0x2a, 0xe0, 0xc4, 0xe7, 0x5d, 0xae, 0x1c } \
+  }
+
+#define GRUB_EFI_HTTP_PROTOCOL_GUID \
+  { 0x7A59B29B, 0x910B, 0x4171, \
+      { 0x82, 0x42, 0xA8, 0x5A, 0x0D, 0xF2, 0x5B, 0x5B } \
+  }
+
+#define EFIHTTP_WAIT_TIME 10000 // 10000ms = 10s
+#define EFIHTTP_RX_BUF_LEN 10240
+
+//******************************************
+// Protocol Interface Structure
+//******************************************
+struct grub_efi_http;
+
+//******************************************
+// EFI_HTTP_VERSION
+//******************************************
+typedef enum {
+  GRUB_EFI_HTTPVERSION10,
+  GRUB_EFI_HTTPVERSION11,
+  GRUB_EFI_HTTPVERSIONUNSUPPORTED
+} grub_efi_http_version_t;
+
+//******************************************
+// EFI_HTTPv4_ACCESS_POINT
+//******************************************
+typedef struct {
+  grub_efi_boolean_t use_default_address;
+  grub_efi_ipv4_address_t local_address;
+  grub_efi_ipv4_address_t local_subnet;
+  grub_efi_uint16_t local_port;
+} grub_efi_httpv4_access_point_t;
+
+//******************************************
+// EFI_HTTPv6_ACCESS_POINT
+//******************************************
+typedef struct {
+  grub_efi_ipv6_address_t local_address;
+  grub_efi_uint16_t local_port;
+} grub_efi_httpv6_access_point_t;
+
+//******************************************
+// EFI_HTTP_CONFIG_DATA
+//******************************************
+typedef struct {
+  grub_efi_http_version_t http_version;
+  grub_efi_uint32_t timeout_millisec;
+  grub_efi_boolean_t local_address_is_ipv6;
+  union {
+    grub_efi_httpv4_access_point_t *ipv4_node;
+    grub_efi_httpv6_access_point_t *ipv6_node;
+  } access_point;
+} grub_efi_http_config_data_t;
+
+//******************************************
+// EFI_HTTP_METHOD
+//******************************************
+typedef enum {
+  GRUB_EFI_HTTPMETHODGET,
+  GRUB_EFI_HTTPMETHODPOST,
+  GRUB_EFI_HTTPMETHODPATCH,
+  GRUB_EFI_HTTPMETHODOPTIONS,
+  GRUB_EFI_HTTPMETHODCONNECT,
+  GRUB_EFI_HTTPMETHODHEAD,
+  GRUB_EFI_HTTPMETHODPUT,
+  GRUB_EFI_HTTPMETHODDELETE,
+  GRUB_EFI_HTTPMETHODTRACE,
+} grub_efi_http_method_t;
+
+//******************************************
+// EFI_HTTP_REQUEST_DATA
+//******************************************
+typedef struct {
+  grub_efi_http_method_t method;
+  grub_efi_char16_t *url;
+} grub_efi_http_request_data_t;
+
+typedef enum {
+  GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS = 0,
+  GRUB_EFI_HTTP_STATUS_100_CONTINUE,
+  GRUB_EFI_HTTP_STATUS_101_SWITCHING_PROTOCOLS,
+  GRUB_EFI_HTTP_STATUS_200_OK,
+  GRUB_EFI_HTTP_STATUS_201_CREATED,
+  GRUB_EFI_HTTP_STATUS_202_ACCEPTED,
+  GRUB_EFI_HTTP_STATUS_203_NON_AUTHORITATIVE_INFORMATION,
+  GRUB_EFI_HTTP_STATUS_204_NO_CONTENT,
+  GRUB_EFI_HTTP_STATUS_205_RESET_CONTENT,
+  GRUB_EFI_HTTP_STATUS_206_PARTIAL_CONTENT,
+  GRUB_EFI_HTTP_STATUS_300_MULTIPLE_CHIOCES,
+  GRUB_EFI_HTTP_STATUS_301_MOVED_PERMANENTLY,
+  GRUB_EFI_HTTP_STATUS_302_FOUND,
+  GRUB_EFI_HTTP_STATUS_303_SEE_OTHER,
+  GRUB_EFI_HTTP_STATUS_304_NOT_MODIFIED,
+  GRUB_EFI_HTTP_STATUS_305_USE_PROXY,
+  GRUB_EFI_HTTP_STATUS_307_TEMPORARY_REDIRECT,
+  GRUB_EFI_HTTP_STATUS_400_BAD_REQUEST,
+  GRUB_EFI_HTTP_STATUS_401_UNAUTHORIZED,
+  GRUB_EFI_HTTP_STATUS_402_PAYMENT_REQUIRED,
+  GRUB_EFI_HTTP_STATUS_403_FORBIDDEN,
+  GRUB_EFI_HTTP_STATUS_404_NOT_FOUND,
+  GRUB_EFI_HTTP_STATUS_405_METHOD_NOT_ALLOWED,
+  GRUB_EFI_HTTP_STATUS_406_NOT_ACCEPTABLE,
+  GRUB_EFI_HTTP_STATUS_407_PROXY_AUTHENTICATION_REQUIRED,
+  GRUB_EFI_HTTP_STATUS_408_REQUEST_TIME_OUT,
+  GRUB_EFI_HTTP_STATUS_409_CONFLICT,
+  GRUB_EFI_HTTP_STATUS_410_GONE,
+  GRUB_EFI_HTTP_STATUS_411_LENGTH_REQUIRED,
+  GRUB_EFI_HTTP_STATUS_412_PRECONDITION_FAILED,
+  GRUB_EFI_HTTP_STATUS_413_REQUEST_ENTITY_TOO_LARGE,
+  GRUB_EFI_HTTP_STATUS_414_REQUEST_URI_TOO_LARGE,
+  GRUB_EFI_HTTP_STATUS_415_UNSUPPORTED_MEDIA_TYPE,
+  GRUB_EFI_HTTP_STATUS_416_REQUESTED_RANGE_NOT_SATISFIED,
+  GRUB_EFI_HTTP_STATUS_417_EXPECTATION_FAILED,
+  GRUB_EFI_HTTP_STATUS_500_INTERNAL_SERVER_ERROR,
+  GRUB_EFI_HTTP_STATUS_501_NOT_IMPLEMENTED,
+  GRUB_EFI_HTTP_STATUS_502_BAD_GATEWAY,
+  GRUB_EFI_HTTP_STATUS_503_SERVICE_UNAVAILABLE,
+  GRUB_EFI_HTTP_STATUS_504_GATEWAY_TIME_OUT,
+  GRUB_EFI_HTTP_STATUS_505_HTTP_VERSION_NOT_SUPPORTED
+} grub_efi_http_status_code_t;
+
+//******************************************
+// EFI_HTTP_RESPONSE_DATA
+//******************************************
+typedef struct {
+  grub_efi_http_status_code_t status_code;
+} grub_efi_http_response_data_t;
+
+//******************************************
+// EFI_HTTP_HEADER
+//******************************************
+typedef struct {
+  grub_efi_char8_t *field_name;
+  grub_efi_char8_t *field_value;
+} grub_efi_http_header_t;
+
+//******************************************
+// EFI_HTTP_MESSAGE
+//******************************************
+typedef struct {
+  union {
+    grub_efi_http_request_data_t *request;
+    grub_efi_http_response_data_t *response;
+  } data;
+  grub_efi_uint32_t header_count;
+  grub_efi_http_header_t *headers;
+  grub_efi_uint32_t body_length;
+  void *body;
+} grub_efi_http_message_t;
+
+//******************************************
+// EFI_HTTP_TOKEN
+//******************************************
+typedef struct {
+  grub_efi_event_t event;
+  grub_efi_status_t status;
+  grub_efi_http_message_t *message;
+} grub_efi_http_token_t;
+
+struct grub_efi_http {
+  grub_efi_status_t
+  (__grub_efi_api *get_mode_data) (struct grub_efi_http *this,
+                    grub_efi_http_config_data_t *http_config_data);
+
+  grub_efi_status_t
+  (__grub_efi_api *configure) (struct grub_efi_http *this,
+                grub_efi_http_config_data_t *http_config_data);
+
+  grub_efi_status_t
+  (__grub_efi_api *request) (struct grub_efi_http *this,
+              grub_efi_http_token_t *token);
+
+  grub_efi_status_t
+  (__grub_efi_api *cancel) (struct grub_efi_http *this,
+             grub_efi_http_token_t *token);
+
+  grub_efi_status_t
+  (__grub_efi_api *response) (struct grub_efi_http *this,
+               grub_efi_http_token_t *token);
+
+  grub_efi_status_t
+  (__grub_efi_api *poll) (struct grub_efi_http *this);
+};
+typedef struct grub_efi_http grub_efi_http_t;
+
+#endif /* !GRUB_EFI_HTTP_HEADER */
diff --git a/include/grub/net/efi.h b/include/grub/net/efi.h
new file mode 100644
index 0000000..50694c2
--- /dev/null
+++ b/include/grub/net/efi.h
@@ -0,0 +1,144 @@
+#ifndef GRUB_NET_EFI_HEADER
+#define GRUB_NET_EFI_HEADER	1
+
+#include <grub/efi/api.h>
+#include <grub/efi/http.h>
+#include <grub/efi/dhcp.h>
+#include <grub/command.h>
+
+typedef struct grub_efi_net_interface grub_efi_net_interface_t;
+typedef struct grub_efi_net_ip_config grub_efi_net_ip_config_t;
+typedef union grub_efi_net_ip_address grub_efi_net_ip_address_t;
+typedef struct grub_efi_net_ip_manual_address grub_efi_net_ip_manual_address_t;
+
+struct grub_efi_net_interface
+{
+  char *name;
+  int prefer_ip6;
+  struct grub_efi_net_device *dev;
+  struct grub_efi_net_io *io;
+  grub_efi_net_ip_config_t *ip_config;
+  int io_type;
+  struct grub_efi_net_interface *next;
+};
+
+#define efi_net_interface_get_hw_address(inf) inf->ip_config->get_hw_address (inf->dev)
+#define efi_net_interface_get_address(inf) inf->ip_config->get_address (inf->dev)
+#define efi_net_interface_get_route_table(inf) inf->ip_config->get_route_table (inf->dev)
+#define efi_net_interface_set_address(inf, addr, with_subnet) inf->ip_config->set_address (inf->dev, addr, with_subnet)
+#define efi_net_interface_set_gateway(inf, addr) inf->ip_config->set_gateway (inf->dev, addr)
+#define efi_net_interface_set_dns(inf, addr) inf->ip_config->set_dns (inf->dev, addr)
+
+struct grub_efi_net_ip_config
+{
+  char * (*get_hw_address) (struct grub_efi_net_device *dev);
+  char * (*get_address) (struct grub_efi_net_device *dev);
+  char ** (*get_route_table) (struct grub_efi_net_device *dev);
+  grub_efi_net_interface_t * (*best_interface) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address);
+  int (*set_address) (struct grub_efi_net_device *dev, grub_efi_net_ip_manual_address_t *net_ip, int with_subnet);
+  int (*set_gateway) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *address);
+  int (*set_dns) (struct grub_efi_net_device *dev, grub_efi_net_ip_address_t *dns);
+};
+
+union grub_efi_net_ip_address
+{
+  grub_efi_ipv4_address_t ip4;
+  grub_efi_ipv6_address_t ip6;
+};
+
+struct grub_efi_net_ip_manual_address
+{
+  int is_ip6;
+  union
+  {
+    grub_efi_ip4_config2_manual_address_t ip4;
+    grub_efi_ip6_config_manual_address_t ip6;
+  };
+};
+
+struct grub_efi_net_device
+{
+  grub_efi_handle_t handle;
+  grub_efi_ip4_config2_protocol_t *ip4_config;
+  grub_efi_ip6_config_protocol_t *ip6_config;
+  grub_efi_handle_t http_handle;
+  grub_efi_http_t *http;
+  grub_efi_handle_t ip4_pxe_handle;
+  grub_efi_pxe_t *ip4_pxe;
+  grub_efi_handle_t ip6_pxe_handle;
+  grub_efi_pxe_t *ip6_pxe;
+  grub_efi_handle_t dhcp4_handle;
+  grub_efi_dhcp4_protocol_t *dhcp4;
+  grub_efi_handle_t dhcp6_handle;
+  grub_efi_dhcp6_protocol_t *dhcp6;
+  char *card_name;
+  grub_efi_net_interface_t *net_interfaces;
+  struct grub_efi_net_device *next;
+};
+
+struct grub_efi_net_io
+{
+  void (*configure) (struct grub_efi_net_device *dev, int prefer_ip6);
+  grub_err_t (*open) (struct grub_efi_net_device *dev,
+                      int prefer_ip6,
+                      grub_file_t file,
+                      const char *filename,
+                      int type);
+  grub_ssize_t (*read) (struct grub_efi_net_device *dev,
+			int prefer_ip6,
+			grub_file_t file,
+			char *buf,
+			grub_size_t len);
+  grub_err_t (*close) (struct grub_efi_net_device *dev,
+		      int prefer_ip6,
+		      grub_file_t file);
+};
+
+extern struct grub_efi_net_device *net_devices;
+
+extern struct grub_efi_net_io io_http;
+extern struct grub_efi_net_io io_pxe;
+
+extern grub_efi_net_ip_config_t *efi_net_ip4_config;
+extern grub_efi_net_ip_config_t *efi_net_ip6_config;
+
+char *
+grub_efi_ip4_address_to_string (grub_efi_ipv4_address_t *address);
+
+char *
+grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address);
+
+char *
+grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address);
+
+int
+grub_efi_string_to_ip4_address (const char *val, grub_efi_ipv4_address_t *address, const char **rest);
+
+int
+grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest);
+
+char *
+grub_efi_ip6_interface_name (struct grub_efi_net_device *dev);
+
+char *
+grub_efi_ip4_interface_name (struct grub_efi_net_device *dev);
+
+grub_efi_net_interface_t *
+grub_efi_net_create_interface (struct grub_efi_net_device *dev,
+		const char *interface_name,
+		grub_efi_net_ip_manual_address_t *net_ip,
+		int has_subnet);
+
+int grub_efi_net_fs_init (void);
+void grub_efi_net_fs_fini (void);
+int grub_efi_net_boot_from_https (void);
+int grub_efi_net_boot_from_opa (void);
+
+extern grub_command_func_t grub_efi_net_list_routes;
+extern grub_command_func_t grub_efi_net_list_cards;
+extern grub_command_func_t grub_efi_net_list_addrs;
+extern grub_command_func_t grub_efi_net_add_addr;
+extern grub_command_func_t grub_efi_net_bootp;
+extern grub_command_func_t grub_efi_net_bootp6;
+
+#endif /* ! GRUB_NET_EFI_HEADER */
diff --git a/util/grub-mknetdir.c b/util/grub-mknetdir.c
index 46f304c..0924c36 100644
--- a/util/grub-mknetdir.c
+++ b/util/grub-mknetdir.c
@@ -82,7 +82,6 @@ argp_parser (int key, char *arg, struct argp_state *state)
     }
 }
 
-
 struct argp argp = {
   options, argp_parser, NULL,
   "\v"N_("Prepares GRUB network boot images at net_directory/subdir "
@@ -92,7 +91,7 @@ struct argp argp = {
 
 static char *base;
 
-static const struct
+static struct
 {
   const char *mkimage_target;
   const char *netmodule;
@@ -157,6 +156,7 @@ process_input_dir (const char *input_dir, enum grub_install_plat platform)
   grub_install_push_module (targets[platform].netmodule);
 
   output = grub_util_path_concat_ext (2, grubdir, "core", targets[platform].ext);
+
   grub_install_make_image_wrap (input_dir, prefix, output,
 				0, load_cfg,
 				targets[platform].mkimage_target, 0);
@@ -197,6 +197,7 @@ main (int argc, char *argv[])
   grub_install_mkdir_p (base);
 
   grub_install_push_module ("tftp");
+  grub_install_push_module ("http");
 
   if (!grub_install_source_directory)
     {
