grub2/0092-Support-UEFI-networking-protocols.patch
Javier Martinez Canillas 1d49572ef1
Update to latest content from upstream sources
The content of this branch was not automatically imported from upstream
sources. Pull the latest from upstream to have the missing changes here.

Source: https://src.fedoraproject.org/rpms/grub2.git#f2763e56df79eccae17d2e8fa13d2f51a0fe7073

Resolves: rhbz#1947696

Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
2021-04-12 01:36:21 +02:00

5054 lines
144 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Michael Chang <mchang@suse.com>
Date: Wed, 22 Feb 2017 14:27:50 +0800
Subject: [PATCH] Support UEFI networking protocols
References: fate#320130, bsc#1015589, bsc#1076132
Patch-Mainline: no
V1:
* Add preliminary support of UEFI networking protocols
* Support UEFI HTTPS Boot
V2:
* Workaround http data access in firmware
* Fix DNS device path parsing for efinet device
* Relaxed UEFI Protocol requirement
* Support Intel OPA (Omni-Path Architecture) PXE Boot
V3:
* Fix bufio in calculating address of next_buf
* Check HTTP respond code
* Use HEAD request method to test before GET
* Finish HTTP transaction in one go
* Fix bsc#1076132
Signed-off-by: Michael Chang <mchang@suse.com>
[pjones: make efi_netfs not duplicate symbols from efinet]
Signed-off-by: Peter Jones <pjones@redhat.com>
---
grub-core/Makefile.core.def | 12 +
grub-core/io/bufio.c | 2 +-
grub-core/kern/efi/efi.c | 96 ++-
grub-core/net/drivers/efi/efinet.c | 27 +
grub-core/net/efi/dhcp.c | 397 ++++++++++
grub-core/net/efi/efi_netfs.c | 57 ++
grub-core/net/efi/http.c | 419 +++++++++++
grub-core/net/efi/ip4_config.c | 398 ++++++++++
grub-core/net/efi/ip6_config.c | 422 +++++++++++
grub-core/net/efi/net.c | 1428 ++++++++++++++++++++++++++++++++++++
grub-core/net/efi/pxe.c | 424 +++++++++++
grub-core/net/net.c | 74 ++
util/grub-mknetdir.c | 23 +-
include/grub/efi/api.h | 180 ++++-
include/grub/efi/dhcp.h | 343 +++++++++
include/grub/efi/http.h | 215 ++++++
include/grub/net/efi.h | 144 ++++
17 files changed, 4620 insertions(+), 41 deletions(-)
create mode 100644 grub-core/net/efi/dhcp.c
create mode 100644 grub-core/net/efi/efi_netfs.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 4b7c45a7b06..c40170f2dd2 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -2299,6 +2299,12 @@ module = {
common = hook/datehook.c;
};
+module = {
+ name = efi_netfs;
+ common = net/efi/efi_netfs.c;
+ enable = efi;
+};
+
module = {
name = net;
common = net/net.c;
@@ -2312,6 +2318,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 a458c3aca78..1637731535e 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 d6a2fb57789..2a446f5031b 100644
--- a/grub-core/kern/efi/efi.c
+++ b/grub-core/kern/efi/efi.c
@@ -755,7 +755,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],
@@ -768,33 +768,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:
@@ -834,6 +861,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 715a6168d77..e11d759f19a 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+");
@@ -491,6 +492,17 @@ grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *u
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))
@@ -760,6 +772,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);
@@ -774,6 +787,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;
@@ -845,6 +867,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;
}
@@ -856,5 +881,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 00000000000..dbef63d8c08
--- /dev/null
+++ b/grub-core/net/efi/dhcp.c
@@ -0,0 +1,397 @@
+#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 = efi_call_4 (dhcp4->parse, dhcp4, reply_packet, &option_count, NULL);
+
+ if (status != GRUB_EFI_BUFFER_TOO_SMALL)
+ return NULL;
+
+ option_list = grub_malloc (option_count * sizeof(*option_list));
+ if (!option_list)
+ return NULL;
+
+ status = efi_call_4 (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;
+}
+
+#if 0
+/* Somehow this doesn't work ... */
+static grub_err_t
+grub_cmd_efi_bootp (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)
+ {
+ grub_efi_pxe_t *pxe = dev->ip4_pxe;
+ grub_efi_pxe_mode_t *mode = pxe->mode;
+ grub_efi_status_t status;
+
+ if (!mode->started)
+ {
+ status = efi_call_2 (pxe->start, pxe, 0);
+
+ if (status != GRUB_EFI_SUCCESS)
+ grub_printf ("Couldn't start PXE\n");
+ }
+
+ status = efi_call_2 (pxe->dhcp, pxe, 0);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_printf ("dhcp4 configure failed, %d\n", (int)status);
+ continue;
+ }
+
+ dev->prefer_ip6 = 0;
+ }
+
+ return GRUB_ERR_NONE;
+}
+#endif
+
+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 = efi_call_2 (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 = efi_call_2 (netdev->dhcp4->start, netdev->dhcp4, NULL);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_printf ("dhcp4 start failed, %d\n", (int)status);
+ continue;
+ }
+
+ status = efi_call_2 (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 = efi_call_2 (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 = efi_call_1 (dev->dhcp6->start, dev->dhcp6);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_printf ("dhcp6 start failed, %d\n", (int)status);
+ continue;
+ }
+
+ status = efi_call_3 (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 = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, NULL);
+
+ if (status == GRUB_EFI_BUFFER_TOO_SMALL && count)
+ {
+ options = grub_malloc (count * sizeof(*options));
+ status = efi_call_4 (dev->dhcp6->parse, dev->dhcp6, mode.ia->reply_packet, &count, options);
+ }
+
+ 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);
+ }
+
+ efi_call_1 (b->free_pool, mode.client_id);
+ efi_call_1 (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/efi_netfs.c b/grub-core/net/efi/efi_netfs.c
new file mode 100644
index 00000000000..ef371d885ea
--- /dev/null
+++ b/grub-core/net/efi/efi_netfs.c
@@ -0,0 +1,57 @@
+#include <grub/dl.h>
+#include <grub/env.h>
+#define EFI_NET_CMD_PREFIX "net_efi"
+#include <grub/net/efi.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_command_t cmd_efi_lsroutes;
+static grub_command_t cmd_efi_lscards;
+static grub_command_t cmd_efi_lsaddrs;
+static grub_command_t cmd_efi_addaddr;
+static grub_command_t cmd_efi_bootp;
+static grub_command_t cmd_efi_bootp6;
+
+static int initialized;
+
+GRUB_MOD_INIT(efi_netfs)
+{
+ if (grub_net_open)
+ return;
+
+ if (grub_efi_net_fs_init ())
+ {
+ cmd_efi_lsroutes = grub_register_command ("net_efi_ls_routes", grub_efi_net_list_routes,
+ "", N_("list network routes"));
+ cmd_efi_lscards = grub_register_command ("net_efi_ls_cards", grub_efi_net_list_cards,
+ "", N_("list network cards"));
+ cmd_efi_lsaddrs = grub_register_command ("net_efi_ls_addr", grub_efi_net_list_addrs,
+ "", N_("list network addresses"));
+ cmd_efi_addaddr = grub_register_command ("net_efi_add_addr", grub_efi_net_add_addr,
+ N_("SHORTNAME CARD ADDRESS [HWADDRESS]"),
+ N_("Add a network address."));
+ cmd_efi_bootp = grub_register_command ("net_efi_bootp", grub_efi_net_bootp,
+ N_("[CARD]"),
+ N_("perform a bootp autoconfiguration"));
+ cmd_efi_bootp6 = grub_register_command ("net_efi_bootp6", grub_efi_net_bootp6,
+ N_("[CARD]"),
+ N_("perform a bootp autoconfiguration"));
+ initialized = 1;
+ }
+}
+
+GRUB_MOD_FINI(efi_netfs)
+{
+ if (initialized)
+ {
+ grub_unregister_command (cmd_efi_lsroutes);
+ grub_unregister_command (cmd_efi_lscards);
+ grub_unregister_command (cmd_efi_lsaddrs);
+ grub_unregister_command (cmd_efi_addaddr);
+ grub_unregister_command (cmd_efi_bootp);
+ grub_unregister_command (cmd_efi_bootp6);
+ grub_efi_net_fs_fini ();
+ initialized = 0;
+ return;
+ }
+}
diff --git a/grub-core/net/efi/http.c b/grub-core/net/efi/http.c
new file mode 100644
index 00000000000..3f61fd2fa5b
--- /dev/null
+++ b/grub-core/net/efi/http.c
@@ -0,0 +1,419 @@
+
+#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 = efi_call_4 (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");
+ }
+
+ /* FIXME: 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 = efi_call_2 (http->configure, http, &http_config);
+
+ if (status == GRUB_EFI_ALREADY_STARTED)
+ {
+ /* XXX: This hangs HTTPS boot */
+#if 0
+ if (efi_call_2 (http->configure, http, NULL) != GRUB_EFI_SUCCESS)
+ {
+ grub_error (GRUB_ERR_IO, N_("couldn't reset http instance"));
+ grub_print_error ();
+ return;
+ }
+ status = efi_call_2 (http->configure, http, &http_config);
+#endif
+ 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_http_request_callback (grub_efi_event_t event __attribute__ ((unused)),
+ void *context __attribute__ ((unused)))
+{
+ request_callback_done = 1;
+}
+
+static void
+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;
+ }
+
+ ucs2_url_len = grub_utf8_to_utf16 (ucs2_url, ucs2_url_len, (grub_uint8_t *)url, url_len, NULL); /* convert string format from ascii to usc2 */
+ 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 = efi_call_5 (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%x\n", status);
+ }
+
+ status = efi_call_2 (http->request, http, &request_token);
+
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ efi_call_1 (b->close_event, request_token.event);
+ grub_free (request_data.url);
+ return grub_error (GRUB_ERR_IO, "Fail to send a request! status=0x%x\n", status);
+ }
+ /* TODO: Add Timeout */
+ while (!request_callback_done)
+ efi_call_1(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 = efi_call_5 (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)
+ {
+ efi_call_1 (b->close_event, request_token.event);
+ grub_free (request_data.url);
+ return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%x\n", status);
+ }
+
+ response_token.status = GRUB_EFI_SUCCESS;
+ response_token.message = &response_message;
+
+ /* wait for HTTP response */
+ response_callback_done = 0;
+ status = efi_call_2 (http->response, http, &response_token);
+
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ efi_call_1 (b->close_event, response_token.event);
+ efi_call_1 (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);
+ }
+
+ /* TODO: Add Timeout */
+ while (!response_callback_done)
+ efi_call_1 (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)
+ efi_call_1 (b->free_pool, response_message.headers);
+ efi_call_1 (b->close_event, response_token.event);
+ efi_call_1 (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)
+ efi_call_1 (b->free_pool, response_message.headers);
+ efi_call_1 (b->close_event, response_token.event);
+ efi_call_1 (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;
+ }
+
+ efi_call_5 (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 = efi_call_2 (http->response, http, &response_token);
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ efi_call_1 (b->close_event, response_token.event);
+ grub_error (GRUB_ERR_IO, "Error! status=%d\n", (int)status);
+ return -1;
+ }
+
+ while (!response_callback_done)
+ efi_call_1(http->poll, http);
+
+ sum += response_message.body_length;
+ buf += response_message.body_length;
+ len -= response_message.body_length;
+ }
+
+ efi_call_1 (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;
+ char *buf;
+
+ 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;
+
+ 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 00000000000..b711a5d9457
--- /dev/null
+++ b/grub-core/net/efi/ip4_config.c
@@ -0,0 +1,398 @@
+
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/misc.h>
+#include <grub/net/efi.h>
+#include <grub/charset.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;
+ int sz, s;
+ int i;
+
+ sz = (int)hw_address_size * (sizeof ("XX:") - 1) + 1;
+
+ hw_addr = grub_malloc (sz);
+ if (!hw_addr)
+ return NULL;
+
+ p = hw_addr;
+ s = sz;
+ for (i = 0; i < (int)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;
+
+ /* FIXME: Use grub_xasprintf ? */
+ 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;
+ int i;
+ const char *ptr = val;
+
+ for (i = 0; i < 4; i++)
+ {
+ unsigned long t;
+ t = grub_strtoul (ptr, (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 = efi_call_4 (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 = efi_call_4 (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 = efi_call_4 (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 i, id;
+
+ interface_info = efi_ip4_config_interface_info (dev->ip4_config);
+ if (!interface_info)
+ return NULL;
+
+ ret = grub_malloc (sizeof (*ret) * (interface_info->route_table_size + 1));
+
+ if (!ret)
+ {
+ grub_free (interface_info);
+ return NULL;
+ }
+
+ id = 0;
+ for (i = 0; i < (int)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
+ {
+ /* XXX: */
+ address->subnet_mask[0] = 0xff;
+ address->subnet_mask[1] = 0xff;
+ address->subnet_mask[2] = 0xff;
+ address->subnet_mask[3] = 0;
+ }
+ }
+
+ status = efi_call_4 (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 = efi_call_4 (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 = efi_call_4 (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 00000000000..017c4d05bc7
--- /dev/null
+++ b/grub-core/net/efi/ip6_config.c
@@ -0,0 +1,422 @@
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/misc.h>
+#include <grub/net/efi.h>
+#include <grub/charset.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, (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 = efi_call_4 (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 = efi_call_4 (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 = efi_call_4 (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 i, id;
+
+ interface_info = efi_ip6_config_interface_info (dev->ip6_config);
+ if (!interface_info)
+ return NULL;
+
+ ret = grub_malloc (sizeof (*ret) * (interface_info->route_count + 1));
+
+ if (!ret)
+ {
+ grub_free (interface_info);
+ return NULL;
+ }
+
+ id = 0;
+ for (i = 0; i < (int)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
+ {
+ /* XXX: */
+ address->prefix_length = 64;
+ }
+ }
+
+ status = efi_call_4 (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 = efi_call_4 (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 = efi_call_4 (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 00000000000..86bce6535d3
--- /dev/null
+++ b/grub-core/net/efi/net.c
@@ -0,0 +1,1428 @@
+#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_efi_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID;
+static grub_efi_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID;
+static grub_efi_guid_t http_service_binding_guid = GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
+static grub_efi_guid_t http_guid = GRUB_EFI_HTTP_PROTOCOL_GUID;
+static grub_efi_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
+static grub_efi_guid_t dhcp4_service_binding_guid = GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID;
+static grub_efi_guid_t dhcp4_guid = GRUB_EFI_DHCP4_PROTOCOL_GUID;
+static grub_efi_guid_t dhcp6_service_binding_guid = GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID;
+static grub_efi_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_efi_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 = efi_call_3 (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)
+ 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_efi_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 = efi_call_2 (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 %x"), 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, (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)
+ {
+ 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, (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)
+ {
+ ip6->prefix_length = 128;
+ ip6->is_anycast = 0;
+ if (has_cidr)
+ *has_cidr = 0;
+ return GRUB_ERR_NONE;
+ }
+ }
+
+ return grub_error (GRUB_ERR_NET_BAD_ADDRESS,
+ N_("unrecognised network address `%s'"),
+ 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)
+ {
+ grub_print_error ();
+ 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 (efi_call_4 (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 (efi_call_4 (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);
+ }
+ }
+}
+
+/* FIXME: Do not fail if the card did not support any of the protocol (Eg http) */
+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;
+}
+
+/* FIXME: support MAC specifying. */
+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;
+ }
+
+ /*FIXME: Use DNS translate name to address */
+ net_interface = match_route (server);
+
+ /*XXX: should we check device with default gateway ? */
+ if (!net_interface && !(net_interface = net_default_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;
+ }
+
+ /*XXX: Should we try to avoid doing excess "reconfigure" here ??? */
+ 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;
+}
+#if 0
+static grub_command_t cmd_efi_lsaddr;
+static grub_command_t cmd_efi_lscards;
+static grub_command_t cmd_efi_lsroutes;
+static grub_command_t cmd_efi_addaddr;
+#endif
+
+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 00000000000..531949cba5c
--- /dev/null
+++ b/grub-core/net/efi/pxe.c
@@ -0,0 +1,424 @@
+
+#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 = efi_call_4 (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 = efi_call_4 (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 = efi_call_2 (pxe->start, pxe, prefer_ip6);
+
+ if (status != GRUB_EFI_SUCCESS)
+ grub_printf ("Couldn't start PXE\n");
+ }
+
+#if 0
+ grub_printf ("PXE STARTED: %u\n", mode->started);
+ grub_printf ("PXE USING IPV6: %u\n", mode->using_ipv6);
+#endif
+
+ 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 = efi_call_3 (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 = efi_call_3 (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);
+ }
+ }
+
+#if 0
+ if (mode->using_ipv6)
+ {
+ grub_printf ("PXE STATION IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
+ mode->station_ip.v6.addr[0],
+ mode->station_ip.v6.addr[1],
+ mode->station_ip.v6.addr[2],
+ mode->station_ip.v6.addr[3],
+ mode->station_ip.v6.addr[4],
+ mode->station_ip.v6.addr[5],
+ mode->station_ip.v6.addr[6],
+ mode->station_ip.v6.addr[7],
+ mode->station_ip.v6.addr[8],
+ mode->station_ip.v6.addr[9],
+ mode->station_ip.v6.addr[10],
+ mode->station_ip.v6.addr[11],
+ mode->station_ip.v6.addr[12],
+ mode->station_ip.v6.addr[13],
+ mode->station_ip.v6.addr[14],
+ mode->station_ip.v6.addr[15]);
+ }
+ else
+ {
+ grub_printf ("PXE STATION IP: %d.%d.%d.%d\n",
+ mode->station_ip.v4.addr[0],
+ mode->station_ip.v4.addr[1],
+ mode->station_ip.v4.addr[2],
+ mode->station_ip.v4.addr[3]);
+ grub_printf ("PXE SUBNET MASK: %d.%d.%d.%d\n",
+ mode->subnet_mask.v4.addr[0],
+ mode->subnet_mask.v4.addr[1],
+ mode->subnet_mask.v4.addr[2],
+ mode->subnet_mask.v4.addr[3]);
+ }
+#endif
+
+ /* TODO: Set The Station IP to the IP2 Config */
+}
+
+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, (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));
+ /* TODO: ERROR Handling Here */
+#if 0
+ grub_printf ("PXE SERVER IP: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
+ server_ip.v6.addr[0],
+ server_ip.v6.addr[1],
+ server_ip.v6.addr[2],
+ server_ip.v6.addr[3],
+ server_ip.v6.addr[4],
+ server_ip.v6.addr[5],
+ server_ip.v6.addr[6],
+ server_ip.v6.addr[7],
+ server_ip.v6.addr[8],
+ server_ip.v6.addr[9],
+ server_ip.v6.addr[10],
+ server_ip.v6.addr[11],
+ server_ip.v6.addr[12],
+ server_ip.v6.addr[13],
+ server_ip.v6.addr[14],
+ server_ip.v6.addr[15]);
+#endif
+ }
+ else
+ {
+ for (i = 0, p = file->device->net->server; i < 4; ++i, ++p)
+ server_ip.v4.addr[i] = grub_strtoul (p, &p, 10);
+ }
+
+ status = efi_call_10 (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)
+ {
+ /* TODO: RANGE Check for offset and file size */
+ 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));
+ /* TODO: ERROR Handling Here */
+ }
+ else
+ {
+ for (i = 0, p = file->device->net->server; i < 4; ++i, ++p)
+ server_ip.v4.addr[i] = grub_strtoul (p, &p, 10);
+ }
+
+ status = efi_call_10 (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 = efi_call_10 (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/net.c b/grub-core/net/net.c
index 0ce5e675ed7..55aed92722c 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+");
@@ -2033,8 +2036,49 @@ static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute;
static grub_command_t 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,
+ /* TRANSLATORS: HWADDRESS stands for
+ "hardware address". */
+ 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");
@@ -2082,10 +2126,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);
@@ -2104,4 +2175,7 @@ GRUB_MOD_FINI(net)
grub_net_fini_hw (0);
grub_loader_unregister_preboot_hook (fini_hnd);
grub_net_poll_cards_idle = grub_net_poll_cards_idle_real;
+#ifdef GRUB_MACHINE_EFI
+ init_mode = INIT_MODE_NONE;
+#endif
}
diff --git a/util/grub-mknetdir.c b/util/grub-mknetdir.c
index 602574d52e8..1a61e05c6ec 100644
--- a/util/grub-mknetdir.c
+++ b/util/grub-mknetdir.c
@@ -32,13 +32,15 @@
static char *rootdir = NULL, *subdir = NULL;
static char *debug_image = NULL;
+static char efi_netfs = 0;
enum
{
OPTION_NET_DIRECTORY = 0x301,
OPTION_SUBDIR,
OPTION_DEBUG,
- OPTION_DEBUG_IMAGE
+ OPTION_DEBUG_IMAGE,
+ OPTION_DEBUG_EFI_NETFS
};
static struct argp_option options[] = {
@@ -49,6 +51,7 @@ static struct argp_option options[] = {
0, N_("relative subdirectory on network server"), 2},
{"debug", OPTION_DEBUG, 0, OPTION_HIDDEN, 0, 2},
{"debug-image", OPTION_DEBUG_IMAGE, N_("STRING"), OPTION_HIDDEN, 0, 2},
+ {"debug-efi-netfs", OPTION_DEBUG_EFI_NETFS, 0, OPTION_HIDDEN, 0, 2},
{0, 0, 0, 0, 0, 0}
};
@@ -67,6 +70,9 @@ argp_parser (int key, char *arg, struct argp_state *state)
free (subdir);
subdir = xstrdup (arg);
return 0;
+ case OPTION_DEBUG_EFI_NETFS:
+ efi_netfs = 1;
+ return 0;
/* This is an undocumented feature... */
case OPTION_DEBUG:
verbosity++;
@@ -82,7 +88,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 +97,7 @@ struct argp argp = {
static char *base;
-static const struct
+static struct
{
const char *mkimage_target;
const char *netmodule;
@@ -156,6 +161,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);
@@ -192,7 +198,16 @@ main (int argc, char *argv[])
grub_install_mkdir_p (base);
- grub_install_push_module ("tftp");
+ if (!efi_netfs)
+ {
+ grub_install_push_module ("tftp");
+ grub_install_push_module ("http");
+ }
+ else
+ {
+ targets[GRUB_INSTALL_PLATFORM_I386_EFI].netmodule = "efi_netfs";
+ targets[GRUB_INSTALL_PLATFORM_X86_64_EFI].netmodule = "efi_netfs";
+ }
if (!grub_install_source_directory)
{
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index 0b490195ad9..f431f49973e 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -622,6 +622,23 @@ 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;
struct grub_efi_guid
{
@@ -889,6 +906,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;
@@ -938,6 +957,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. */
@@ -1020,6 +1048,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_service_binding_create_child) (struct grub_efi_service_binding *this,
+ grub_efi_handle_t *child_handle);
+
+typedef grub_efi_status_t
+(*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;
@@ -1569,23 +1614,27 @@ 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 grub_efi_uint16_t grub_efi_pxe_base_code_udp_port_t;
-typedef struct {
- grub_uint8_t addr[16];
-} grub_efi_pxe_ipv6_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_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;
+ 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
@@ -1652,17 +1701,31 @@ typedef struct grub_efi_pxe_mode
typedef struct grub_efi_pxe
{
grub_uint64_t rev;
- void (*start) (void);
+ grub_efi_status_t (*start) (struct grub_efi_pxe *this, grub_efi_boolean_t use_ipv6);
void (*stop) (void);
- void (*dhcp) (void);
+ grub_efi_status_t (*dhcp) (struct grub_efi_pxe *this,
+ grub_efi_boolean_t sort_offers);
void (*discover) (void);
- void (*mftp) (void);
+ grub_efi_status_t (*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_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 (*udpwrite) (void);
void (*udpread) (void);
void (*setipfilter) (void);
void (*arp) (void);
void (*setparams) (void);
- void (*setstationip) (void);
+ grub_efi_status_t (*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 (*setstationip) (void);
void (*setpackets) (void);
struct grub_efi_pxe_mode *mode;
} grub_efi_pxe_t;
@@ -1924,6 +1987,44 @@ 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,
GRUB_EFI_IP6_CONFIG_DATA_TYPE_ALT_INTERFACEID,
@@ -1958,6 +2059,49 @@ 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;
+
#if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
|| defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__) \
|| defined(__riscv)
diff --git a/include/grub/efi/dhcp.h b/include/grub/efi/dhcp.h
new file mode 100644
index 00000000000..fdb88eb810e
--- /dev/null
+++ b/include/grub/efi/dhcp.h
@@ -0,0 +1,343 @@
+#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 (*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 (*get_mode_data) (grub_efi_dhcp4_protocol_t *this,
+ grub_efi_dhcp4_mode_data_t *dhcp4_mode_data);
+ grub_efi_status_t (*configure) (grub_efi_dhcp4_protocol_t *this,
+ grub_efi_dhcp4_config_data_t *dhcp4_cfg_data);
+ grub_efi_status_t (*start) (grub_efi_dhcp4_protocol_t *this,
+ grub_efi_event_t completion_event);
+ grub_efi_status_t (*renew_rebind) (grub_efi_dhcp4_protocol_t *this,
+ grub_efi_boolean_t rebind_request,
+ grub_efi_event_t completion_event);
+ grub_efi_status_t (*release) (grub_efi_dhcp4_protocol_t *this);
+ grub_efi_status_t (*stop) (grub_efi_dhcp4_protocol_t *this);
+ grub_efi_status_t (*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 (*transmit_receive) (grub_efi_dhcp4_protocol_t *this,
+ grub_efi_dhcp4_transmit_receive_token_t *token);
+ grub_efi_status_t (*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 (*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 (*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 (*configure) (grub_efi_dhcp6_protocol_t *this,
+ grub_efi_dhcp6_config_data_t *dhcp6_cfg_data);
+ grub_efi_status_t (*start) (grub_efi_dhcp6_protocol_t *this);
+ grub_efi_status_t (*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 (*renew_rebind) (grub_efi_dhcp6_protocol_t *this,
+ grub_efi_boolean_t rebind_request);
+ grub_efi_status_t (*decline) (grub_efi_dhcp6_protocol_t *this,
+ grub_efi_uint32_t address_count,
+ grub_efi_ipv6_address_t *addresses);
+ grub_efi_status_t (*release) (grub_efi_dhcp6_protocol_t *this,
+ grub_efi_uint32_t address_count,
+ grub_efi_ipv6_address_t *addresses);
+ grub_efi_status_t (*stop) (grub_efi_dhcp6_protocol_t *this);
+ grub_efi_status_t (*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 00000000000..c5e9a89f505
--- /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
+ (*get_mode_data) (struct grub_efi_http *this,
+ grub_efi_http_config_data_t *http_config_data);
+
+ grub_efi_status_t
+ (*configure) (struct grub_efi_http *this,
+ grub_efi_http_config_data_t *http_config_data);
+
+ grub_efi_status_t
+ (*request) (struct grub_efi_http *this,
+ grub_efi_http_token_t *token);
+
+ grub_efi_status_t
+ (*cancel) (struct grub_efi_http *this,
+ grub_efi_http_token_t *token);
+
+ grub_efi_status_t
+ (*response) (struct grub_efi_http *this,
+ grub_efi_http_token_t *token);
+
+ grub_efi_status_t
+ (*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 00000000000..de90d223e8e
--- /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 */