4924 lines
138 KiB
Diff
4924 lines
138 KiB
Diff
From 5d6111790e1cd07d1156f47bca0733f6d715337f 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
|
|
|
|
V4:
|
|
* Add fs_ prefix with upstream commit
|
|
ad4bfeec5 Change fs functions to add fs_ prefix
|
|
|
|
V5:
|
|
* Use overflow checking primitives where the arithmetic expression for
|
|
buffer allocations may include unvalidated data
|
|
* Use grub_calloc for overflow check and return NULL when it would
|
|
occur.
|
|
|
|
V6:
|
|
* Don't force grub_print_error if no best route found as boot process
|
|
could be interrupted by logged error. The default interface will be
|
|
used as fallback in this case
|
|
|
|
---
|
|
grub-core/Makefile.core.def | 18 +
|
|
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 ++
|
|
include/grub/efi/api.h | 181 ++++-
|
|
include/grub/efi/dhcp.h | 343 +++++++++
|
|
include/grub/efi/http.h | 215 ++++++
|
|
include/grub/net/efi.h | 144 ++++
|
|
util/grub-mknetdir.c | 23 +-
|
|
17 files changed, 4627 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
|
|
|
|
--- a/grub-core/Makefile.core.def
|
|
+++ b/grub-core/Makefile.core.def
|
|
@@ -2362,6 +2362,12 @@
|
|
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 = {
|
|
--- a/grub-core/io/bufio.c
|
|
+++ b/grub-core/io/bufio.c
|
|
@@ -139,7 +139,7 @@
|
|
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)
|
|
{
|
|
--- a/grub-core/kern/efi/efi.c
|
|
+++ b/grub-core/kern/efi/efi.c
|
|
@@ -770,7 +770,7 @@
|
|
{
|
|
grub_efi_ipv4_device_path_t *ipv4
|
|
= (grub_efi_ipv4_device_path_t *) dp;
|
|
- grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x)",
|
|
+ grub_printf ("/IPv4(%u.%u.%u.%u,%u.%u.%u.%u,%u,%u,%x,%x",
|
|
(unsigned) ipv4->local_ip_address[0],
|
|
(unsigned) ipv4->local_ip_address[1],
|
|
(unsigned) ipv4->local_ip_address[2],
|
|
@@ -783,33 +783,60 @@
|
|
(unsigned) ipv4->remote_port,
|
|
(unsigned) ipv4->protocol,
|
|
(unsigned) ipv4->static_ip_address);
|
|
+ if (len == sizeof (*ipv4))
|
|
+ {
|
|
+ grub_printf (",%u.%u.%u.%u,%u.%u.%u.%u",
|
|
+ (unsigned) ipv4->gateway_ip_address[0],
|
|
+ (unsigned) ipv4->gateway_ip_address[1],
|
|
+ (unsigned) ipv4->gateway_ip_address[2],
|
|
+ (unsigned) ipv4->gateway_ip_address[3],
|
|
+ (unsigned) ipv4->subnet_mask[0],
|
|
+ (unsigned) ipv4->subnet_mask[1],
|
|
+ (unsigned) ipv4->subnet_mask[2],
|
|
+ (unsigned) ipv4->subnet_mask[3]);
|
|
+ }
|
|
+ grub_printf (")");
|
|
}
|
|
break;
|
|
case GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE:
|
|
{
|
|
grub_efi_ipv6_device_path_t *ipv6
|
|
= (grub_efi_ipv6_device_path_t *) dp;
|
|
- grub_printf ("/IPv6(%x:%x:%x:%x:%x:%x:%x:%x,%x:%x:%x:%x:%x:%x:%x:%x,%u,%u,%x,%x)",
|
|
- (unsigned) ipv6->local_ip_address[0],
|
|
- (unsigned) ipv6->local_ip_address[1],
|
|
- (unsigned) ipv6->local_ip_address[2],
|
|
- (unsigned) ipv6->local_ip_address[3],
|
|
- (unsigned) ipv6->local_ip_address[4],
|
|
- (unsigned) ipv6->local_ip_address[5],
|
|
- (unsigned) ipv6->local_ip_address[6],
|
|
- (unsigned) ipv6->local_ip_address[7],
|
|
- (unsigned) ipv6->remote_ip_address[0],
|
|
- (unsigned) ipv6->remote_ip_address[1],
|
|
- (unsigned) ipv6->remote_ip_address[2],
|
|
- (unsigned) ipv6->remote_ip_address[3],
|
|
- (unsigned) ipv6->remote_ip_address[4],
|
|
- (unsigned) ipv6->remote_ip_address[5],
|
|
- (unsigned) ipv6->remote_ip_address[6],
|
|
- (unsigned) ipv6->remote_ip_address[7],
|
|
+ grub_printf ("/IPv6(%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,%u,%u,%x,%x",
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[0]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[1]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[2]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[3]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[4]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[5]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[6]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->local_ip_address[7]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[0]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[1]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[2]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[3]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[4]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[5]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[6]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->remote_ip_address[7]),
|
|
(unsigned) ipv6->local_port,
|
|
(unsigned) ipv6->remote_port,
|
|
(unsigned) ipv6->protocol,
|
|
(unsigned) ipv6->static_ip_address);
|
|
+ if (len == sizeof (*ipv6))
|
|
+ {
|
|
+ grub_printf (",%u,%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
|
+ (unsigned) ipv6->prefix_length,
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[0]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[1]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[2]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[3]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[4]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[5]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[6]),
|
|
+ (unsigned) grub_be_to_cpu16 (ipv6->gateway_ip_address[7]));
|
|
+ }
|
|
+ grub_printf (")");
|
|
}
|
|
break;
|
|
case GRUB_EFI_INFINIBAND_DEVICE_PATH_SUBTYPE:
|
|
@@ -856,6 +883,39 @@
|
|
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;
|
|
--- a/grub-core/net/drivers/efi/efinet.c
|
|
+++ b/grub-core/net/drivers/efi/efinet.c
|
|
@@ -24,6 +24,7 @@
|
|
#include <grub/efi/efi.h>
|
|
#include <grub/i18n.h>
|
|
#include <grub/net/netbuff.h>
|
|
+#include <grub/env.h>
|
|
|
|
GRUB_MOD_LICENSE ("GPLv3+");
|
|
|
|
@@ -345,7 +346,7 @@
|
|
}
|
|
|
|
static grub_efi_handle_t
|
|
-grub_efi_locate_device_path (grub_efi_guid_t *protocol, grub_efi_device_path_t *device_path,
|
|
+grub_efi_locate_device_path (grub_guid_t *protocol, grub_efi_device_path_t *device_path,
|
|
grub_efi_device_path_t **r_device_path)
|
|
{
|
|
grub_efi_handle_t handle;
|
|
@@ -498,6 +499,17 @@
|
|
|
|
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))
|
|
@@ -765,6 +777,7 @@
|
|
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);
|
|
@@ -780,6 +793,15 @@
|
|
}
|
|
|
|
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;
|
|
dup_ldp->length = sizeof (*dup_ldp);
|
|
@@ -860,6 +882,9 @@
|
|
|
|
GRUB_MOD_INIT(efinet)
|
|
{
|
|
+ if (grub_efi_net_config)
|
|
+ return;
|
|
+
|
|
grub_efinet_findcards ();
|
|
grub_efi_net_config = grub_efi_net_config_real;
|
|
}
|
|
@@ -871,5 +896,7 @@
|
|
FOR_NET_CARDS_SAFE (card, next)
|
|
if (card->driver == &efidriver)
|
|
grub_net_card_unregister (card);
|
|
+
|
|
+ grub_efi_net_config = NULL;
|
|
}
|
|
|
|
--- /dev/null
|
|
+++ b/grub-core/net/efi/dhcp.c
|
|
@@ -0,0 +1,399 @@
|
|
+#include <grub/mm.h>
|
|
+#include <grub/command.h>
|
|
+#include <grub/efi/api.h>
|
|
+#include <grub/efi/efi.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/net/efi.h>
|
|
+#include <grub/charset.h>
|
|
+
|
|
+#ifdef GRUB_EFI_NET_DEBUG
|
|
+static void
|
|
+dhcp4_mode_print (grub_efi_dhcp4_mode_data_t *mode)
|
|
+{
|
|
+ switch (mode->state)
|
|
+ {
|
|
+ case GRUB_EFI_DHCP4_STOPPED:
|
|
+ grub_printf ("STATE: STOPPED\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_INIT:
|
|
+ grub_printf ("STATE: INIT\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_SELECTING:
|
|
+ grub_printf ("STATE: SELECTING\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_REQUESTING:
|
|
+ grub_printf ("STATE: REQUESTING\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_BOUND:
|
|
+ grub_printf ("STATE: BOUND\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_RENEWING:
|
|
+ grub_printf ("STATE: RENEWING\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_REBINDING:
|
|
+ grub_printf ("STATE: REBINDING\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_INIT_REBOOT:
|
|
+ grub_printf ("STATE: INIT_REBOOT\n");
|
|
+ break;
|
|
+ case GRUB_EFI_DHCP4_REBOOTING:
|
|
+ grub_printf ("STATE: REBOOTING\n");
|
|
+ break;
|
|
+ default:
|
|
+ grub_printf ("STATE: UNKNOWN\n");
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ grub_printf ("CLIENT_ADDRESS: %u.%u.%u.%u\n",
|
|
+ mode->client_address[0],
|
|
+ mode->client_address[1],
|
|
+ mode->client_address[2],
|
|
+ mode->client_address[3]);
|
|
+ grub_printf ("SERVER_ADDRESS: %u.%u.%u.%u\n",
|
|
+ mode->server_address[0],
|
|
+ mode->server_address[1],
|
|
+ mode->server_address[2],
|
|
+ mode->server_address[3]);
|
|
+ grub_printf ("SUBNET_MASK: %u.%u.%u.%u\n",
|
|
+ mode->subnet_mask[0],
|
|
+ mode->subnet_mask[1],
|
|
+ mode->subnet_mask[2],
|
|
+ mode->subnet_mask[3]);
|
|
+ grub_printf ("ROUTER_ADDRESS: %u.%u.%u.%u\n",
|
|
+ mode->router_address[0],
|
|
+ mode->router_address[1],
|
|
+ mode->router_address[2],
|
|
+ mode->router_address[3]);
|
|
+}
|
|
+#endif
|
|
+
|
|
+static grub_efi_ipv4_address_t *
|
|
+grub_efi_dhcp4_parse_dns (grub_efi_dhcp4_protocol_t *dhcp4, grub_efi_dhcp4_packet_t *reply_packet)
|
|
+{
|
|
+ grub_efi_dhcp4_packet_option_t **option_list;
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_uint32_t option_count = 0;
|
|
+ grub_efi_uint32_t i;
|
|
+
|
|
+ status = dhcp4->parse (dhcp4, reply_packet, &option_count, NULL);
|
|
+
|
|
+ if (status != GRUB_EFI_BUFFER_TOO_SMALL)
|
|
+ return NULL;
|
|
+
|
|
+ option_list = grub_calloc (option_count, sizeof(*option_list));
|
|
+ if (!option_list)
|
|
+ return NULL;
|
|
+
|
|
+ status = dhcp4->parse (dhcp4, reply_packet, &option_count, option_list);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_free (option_list);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < option_count; ++i)
|
|
+ {
|
|
+ if (option_list[i]->op_code == 6)
|
|
+ {
|
|
+ grub_efi_ipv4_address_t *dns_address;
|
|
+
|
|
+ if (((option_list[i]->length & 0x3) != 0) || (option_list[i]->length == 0))
|
|
+ continue;
|
|
+
|
|
+ /* We only contact primary dns */
|
|
+ dns_address = grub_malloc (sizeof (*dns_address));
|
|
+ if (!dns_address)
|
|
+ {
|
|
+ grub_free (option_list);
|
|
+ return NULL;
|
|
+ }
|
|
+ grub_memcpy (dns_address, option_list[i]->data, sizeof (dns_address));
|
|
+ grub_free (option_list);
|
|
+ return dns_address;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ grub_free (option_list);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+#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 = pxe->start (pxe, 0);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ grub_printf ("Couldn't start PXE\n");
|
|
+ }
|
|
+
|
|
+ status = 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 = netdev->dhcp4->configure (netdev->dhcp4, &config);
|
|
+ grub_free (options);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_printf ("dhcp4 configure failed, %d\n", (int)status);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ status = netdev->dhcp4->start (netdev->dhcp4, NULL);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_printf ("dhcp4 start failed, %d\n", (int)status);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ status = netdev->dhcp4->get_mode_data (netdev->dhcp4, &mode);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_printf ("dhcp4 get mode failed, %d\n", (int)status);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+#ifdef GRUB_EFI_NET_DEBUG
|
|
+ dhcp4_mode_print (&mode);
|
|
+#endif
|
|
+
|
|
+ for (inf = netdev->net_interfaces; inf; inf = inf->next)
|
|
+ if (inf->prefer_ip6 == 0)
|
|
+ break;
|
|
+
|
|
+ grub_memcpy (net_ip.ip4.address, mode.client_address, sizeof (net_ip.ip4.address));
|
|
+ grub_memcpy (net_ip.ip4.subnet_mask, mode.subnet_mask, sizeof (net_ip.ip4.subnet_mask));
|
|
+
|
|
+ if (!inf)
|
|
+ {
|
|
+ char *name = grub_xasprintf ("%s:dhcp", netdev->card_name);
|
|
+
|
|
+ net_ip.is_ip6 = 0;
|
|
+ inf = grub_efi_net_create_interface (netdev,
|
|
+ name,
|
|
+ &net_ip,
|
|
+ 1);
|
|
+ grub_free (name);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ efi_net_interface_set_address (inf, &net_ip, 1);
|
|
+ }
|
|
+
|
|
+ grub_memcpy (ip_addr.ip4, mode.router_address, sizeof (ip_addr.ip4));
|
|
+ efi_net_interface_set_gateway (inf, &ip_addr);
|
|
+
|
|
+ dns_address = grub_efi_dhcp4_parse_dns (netdev->dhcp4, mode.reply_packet);
|
|
+ if (dns_address)
|
|
+ efi_net_interface_set_dns (inf, (grub_efi_net_ip_address_t *)&dns_address);
|
|
+
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+
|
|
+static grub_err_t
|
|
+grub_cmd_efi_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
|
|
+ int argc,
|
|
+ char **args)
|
|
+{
|
|
+ struct grub_efi_net_device *dev;
|
|
+ grub_efi_uint32_t ia_id;
|
|
+
|
|
+ for (dev = net_devices, ia_id = 0; dev; dev = dev->next, ia_id++)
|
|
+ {
|
|
+ grub_efi_dhcp6_config_data_t config;
|
|
+ grub_efi_dhcp6_packet_option_t *option_list[1];
|
|
+ grub_efi_dhcp6_packet_option_t *opt;
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_dhcp6_mode_data_t mode;
|
|
+ grub_efi_dhcp6_retransmission_t retrans;
|
|
+ grub_efi_net_ip_manual_address_t net_ip;
|
|
+ grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
|
|
+ grub_efi_net_interface_t *inf = NULL;
|
|
+
|
|
+ if (argc > 0 && grub_strcmp (dev->card_name, args[0]) != 0)
|
|
+ continue;
|
|
+
|
|
+ opt = grub_malloc (sizeof(*opt) + 2 * sizeof (grub_efi_uint16_t));
|
|
+
|
|
+#define GRUB_EFI_DHCP6_OPT_ORO 6
|
|
+
|
|
+ opt->op_code = grub_cpu_to_be16_compile_time (GRUB_EFI_DHCP6_OPT_ORO);
|
|
+ opt->op_len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_efi_uint16_t));
|
|
+
|
|
+#define GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL 59
|
|
+#define GRUB_EFI_DHCP6_OPT_DNS_SERVERS 23
|
|
+
|
|
+ grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_BOOT_FILE_URL));
|
|
+ grub_set_unaligned16 (opt->data + 1 * sizeof (grub_efi_uint16_t),
|
|
+ grub_cpu_to_be16_compile_time(GRUB_EFI_DHCP6_OPT_DNS_SERVERS));
|
|
+
|
|
+ option_list[0] = opt;
|
|
+ retrans.irt = 4;
|
|
+ retrans.mrc = 4;
|
|
+ retrans.mrt = 32;
|
|
+ retrans.mrd = 60;
|
|
+
|
|
+ config.dhcp6_callback = NULL;
|
|
+ config.callback_context = NULL;
|
|
+ config.option_count = 1;
|
|
+ config.option_list = option_list;
|
|
+ config.ia_descriptor.ia_id = ia_id;
|
|
+ config.ia_descriptor.type = GRUB_EFI_DHCP6_IA_TYPE_NA;
|
|
+ config.ia_info_event = NULL;
|
|
+ config.reconfigure_accept = 0;
|
|
+ config.rapid_commit = 0;
|
|
+ config.solicit_retransmission = &retrans;
|
|
+
|
|
+ status = dev->dhcp6->configure (dev->dhcp6, &config);
|
|
+ grub_free (opt);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_printf ("dhcp6 configure failed, %d\n", (int)status);
|
|
+ continue;
|
|
+ }
|
|
+ status = dev->dhcp6->start (dev->dhcp6);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_printf ("dhcp6 start failed, %d\n", (int)status);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ status = dev->dhcp6->get_mode_data (dev->dhcp6, &mode, NULL);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_printf ("dhcp4 get mode failed, %d\n", (int)status);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ for (inf = dev->net_interfaces; inf; inf = inf->next)
|
|
+ if (inf->prefer_ip6 == 1)
|
|
+ break;
|
|
+
|
|
+ grub_memcpy (net_ip.ip6.address, mode.ia->ia_address[0].ip_address, sizeof (net_ip.ip6.address));
|
|
+ net_ip.ip6.prefix_length = 64;
|
|
+ net_ip.ip6.is_anycast = 0;
|
|
+ net_ip.is_ip6 = 1;
|
|
+
|
|
+ if (!inf)
|
|
+ {
|
|
+ char *name = grub_xasprintf ("%s:dhcp", dev->card_name);
|
|
+
|
|
+ inf = grub_efi_net_create_interface (dev,
|
|
+ name,
|
|
+ &net_ip,
|
|
+ 1);
|
|
+ grub_free (name);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ efi_net_interface_set_address (inf, &net_ip, 1);
|
|
+ }
|
|
+
|
|
+ {
|
|
+ grub_efi_uint32_t count = 0;
|
|
+ grub_efi_dhcp6_packet_option_t **options = NULL;
|
|
+ grub_efi_uint32_t i;
|
|
+
|
|
+ status = dev->dhcp6->parse (dev->dhcp6, mode.ia->reply_packet, &count, NULL);
|
|
+
|
|
+ if (status == GRUB_EFI_BUFFER_TOO_SMALL && count)
|
|
+ {
|
|
+ options = grub_calloc (count, sizeof(*options));
|
|
+ if (!options)
|
|
+ return grub_error (GRUB_ERR_OUT_OF_RANGE, N_("overflow is detected"));
|
|
+ status = 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);
|
|
+ }
|
|
+
|
|
+ b->free_pool (mode.client_id);
|
|
+ b->free_pool (mode.ia);
|
|
+ }
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+grub_command_func_t grub_efi_net_bootp = grub_cmd_efi_bootp;
|
|
+grub_command_func_t grub_efi_net_bootp6 = grub_cmd_efi_bootp6;
|
|
--- /dev/null
|
|
+++ b/grub-core/net/efi/http.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>
|
|
+#include <grub/safemath.h>
|
|
+
|
|
+static void
|
|
+http_configure (struct grub_efi_net_device *dev, int prefer_ip6)
|
|
+{
|
|
+ grub_efi_http_config_data_t http_config;
|
|
+ grub_efi_httpv4_access_point_t httpv4_node;
|
|
+ grub_efi_httpv6_access_point_t httpv6_node;
|
|
+ grub_efi_status_t status;
|
|
+
|
|
+ grub_efi_http_t *http = dev->http;
|
|
+
|
|
+ grub_memset (&http_config, 0, sizeof(http_config));
|
|
+ http_config.http_version = GRUB_EFI_HTTPVERSION11;
|
|
+ http_config.timeout_millisec = 5000;
|
|
+
|
|
+ if (prefer_ip6)
|
|
+ {
|
|
+ grub_efi_uintn_t sz;
|
|
+ grub_efi_ip6_config_manual_address_t manual_address;
|
|
+
|
|
+ http_config.local_address_is_ipv6 = 1;
|
|
+ sz = sizeof (manual_address);
|
|
+ status = dev->ip6_config->get_data (dev->ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
|
|
+ &sz, &manual_address);
|
|
+
|
|
+ if (status == GRUB_EFI_NOT_FOUND)
|
|
+ {
|
|
+ grub_printf ("The MANUAL ADDRESS is not found\n");
|
|
+ }
|
|
+
|
|
+ /* 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 = http->configure (http, &http_config);
|
|
+
|
|
+ if (status == GRUB_EFI_ALREADY_STARTED)
|
|
+ {
|
|
+ /* XXX: This hangs HTTPS boot */
|
|
+#if 0
|
|
+ if (http->configure (http, NULL) != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_error (GRUB_ERR_IO, N_("couldn't reset http instance"));
|
|
+ grub_print_error ();
|
|
+ return;
|
|
+ }
|
|
+ status = 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_api
|
|
+grub_efi_http_request_callback (grub_efi_event_t event __attribute__ ((unused)),
|
|
+ void *context __attribute__ ((unused)))
|
|
+{
|
|
+ request_callback_done = 1;
|
|
+}
|
|
+
|
|
+static void __grub_efi_api
|
|
+grub_efi_http_response_callback (grub_efi_event_t event __attribute__ ((unused)),
|
|
+ void *context __attribute__ ((unused)))
|
|
+{
|
|
+ response_callback_done = 1;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, int headeronly, grub_off_t *file_size)
|
|
+{
|
|
+ grub_efi_http_request_data_t request_data;
|
|
+ grub_efi_http_message_t request_message;
|
|
+ grub_efi_http_token_t request_token;
|
|
+ grub_efi_http_response_data_t response_data;
|
|
+ grub_efi_http_message_t response_message;
|
|
+ grub_efi_http_token_t response_token;
|
|
+ grub_efi_http_header_t request_headers[3];
|
|
+
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
|
|
+ char *url = NULL;
|
|
+
|
|
+ request_headers[0].field_name = (grub_efi_char8_t *)"Host";
|
|
+ request_headers[0].field_value = (grub_efi_char8_t *)server;
|
|
+ request_headers[1].field_name = (grub_efi_char8_t *)"Accept";
|
|
+ request_headers[1].field_value = (grub_efi_char8_t *)"*/*";
|
|
+ request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent";
|
|
+ request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0";
|
|
+
|
|
+ {
|
|
+ grub_efi_ipv6_address_t address;
|
|
+ const char *rest;
|
|
+ grub_efi_char16_t *ucs2_url;
|
|
+ grub_size_t url_len, ucs2_url_len;
|
|
+ const char *protocol = (use_https == 1) ? "https" : "http";
|
|
+ grub_size_t sz;
|
|
+
|
|
+ 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);
|
|
+ if (grub_mul (url_len, GRUB_MAX_UTF16_PER_UTF8, &ucs2_url_len) ||
|
|
+ grub_add (ucs2_url_len, 1, &sz))
|
|
+ return GRUB_ERR_OUT_OF_RANGE;
|
|
+
|
|
+ ucs2_url = grub_calloc (sz, 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 = b->create_event (
|
|
+ GRUB_EFI_EVT_NOTIFY_SIGNAL,
|
|
+ GRUB_EFI_TPL_CALLBACK,
|
|
+ grub_efi_http_request_callback,
|
|
+ NULL,
|
|
+ &request_token.event);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_free (request_data.url);
|
|
+ return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%" PRIxGRUB_SIZE, status);
|
|
+ }
|
|
+
|
|
+ status = http->request (http, &request_token);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ b->close_event (request_token.event);
|
|
+ grub_free (request_data.url);
|
|
+ return grub_error (GRUB_ERR_IO, "Fail to send a request! status=0x%" PRIxGRUB_SIZE, status);
|
|
+ }
|
|
+ /* TODO: Add Timeout */
|
|
+ while (!request_callback_done)
|
|
+ http->poll (http);
|
|
+
|
|
+ response_data.status_code = GRUB_EFI_HTTP_STATUS_UNSUPPORTED_STATUS;
|
|
+ response_message.data.response = &response_data;
|
|
+ /* herader_count will be updated by the HTTP driver on response */
|
|
+ response_message.header_count = 0;
|
|
+ /* headers will be populated by the driver on response */
|
|
+ response_message.headers = NULL;
|
|
+ /* use zero BodyLength to only receive the response headers */
|
|
+ response_message.body_length = 0;
|
|
+ response_message.body = NULL;
|
|
+ response_token.event = NULL;
|
|
+
|
|
+ status = b->create_event (
|
|
+ GRUB_EFI_EVT_NOTIFY_SIGNAL,
|
|
+ GRUB_EFI_TPL_CALLBACK,
|
|
+ grub_efi_http_response_callback,
|
|
+ NULL,
|
|
+ &response_token.event);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ b->close_event (request_token.event);
|
|
+ grub_free (request_data.url);
|
|
+ return grub_error (GRUB_ERR_IO, "Fail to create an event! status=0x%" PRIxGRUB_SIZE, status);
|
|
+ }
|
|
+
|
|
+ response_token.status = GRUB_EFI_SUCCESS;
|
|
+ response_token.message = &response_message;
|
|
+
|
|
+ /* wait for HTTP response */
|
|
+ response_callback_done = 0;
|
|
+ status = http->response (http, &response_token);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ b->close_event (response_token.event);
|
|
+ b->close_event (request_token.event);
|
|
+ grub_free (request_data.url);
|
|
+ return grub_error (GRUB_ERR_IO, "Fail to receive a response! status=%d\n", (int)status);
|
|
+ }
|
|
+
|
|
+ /* TODO: Add Timeout */
|
|
+ while (!response_callback_done)
|
|
+ http->poll (http);
|
|
+
|
|
+ if (response_message.data.response->status_code != GRUB_EFI_HTTP_STATUS_200_OK)
|
|
+ {
|
|
+ grub_efi_http_status_code_t status_code = response_message.data.response->status_code;
|
|
+
|
|
+ if (response_message.headers)
|
|
+ b->free_pool (response_message.headers);
|
|
+ b->close_event (response_token.event);
|
|
+ b->close_event (request_token.event);
|
|
+ grub_free (request_data.url);
|
|
+ if (status_code == GRUB_EFI_HTTP_STATUS_404_NOT_FOUND)
|
|
+ {
|
|
+ return grub_error (GRUB_ERR_FILE_NOT_FOUND, _("file `%s' not found"), name);
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ return grub_error (GRUB_ERR_NET_UNKNOWN_ERROR,
|
|
+ _("unsupported uefi http status code 0x%x"), status_code);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (file_size)
|
|
+ {
|
|
+ int i;
|
|
+ /* parse the length of the file from the ContentLength header */
|
|
+ for (*file_size = 0, i = 0; i < (int)response_message.header_count; ++i)
|
|
+ {
|
|
+ if (!grub_strcmp((const char*)response_message.headers[i].field_name, "Content-Length"))
|
|
+ {
|
|
+ *file_size = grub_strtoul((const char*)response_message.headers[i].field_value, 0, 10);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (response_message.headers)
|
|
+ b->free_pool (response_message.headers);
|
|
+ b->close_event (response_token.event);
|
|
+ b->close_event (request_token.event);
|
|
+ grub_free (request_data.url);
|
|
+
|
|
+ return GRUB_ERR_NONE;
|
|
+}
|
|
+
|
|
+static grub_ssize_t
|
|
+efihttp_read (struct grub_efi_net_device *dev,
|
|
+ char *buf,
|
|
+ grub_size_t len)
|
|
+{
|
|
+ grub_efi_http_message_t response_message;
|
|
+ grub_efi_http_token_t response_token;
|
|
+
|
|
+ grub_efi_status_t status;
|
|
+ grub_size_t sum = 0;
|
|
+ grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
|
|
+ grub_efi_http_t *http = dev->http;
|
|
+
|
|
+ if (!len)
|
|
+ {
|
|
+ grub_error (GRUB_ERR_BUG, "Invalid arguments to EFI HTTP Read");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ b->create_event (
|
|
+ GRUB_EFI_EVT_NOTIFY_SIGNAL,
|
|
+ GRUB_EFI_TPL_CALLBACK,
|
|
+ grub_efi_http_response_callback,
|
|
+ NULL,
|
|
+ &response_token.event);
|
|
+
|
|
+ while (len)
|
|
+ {
|
|
+ response_message.data.response = NULL;
|
|
+ response_message.header_count = 0;
|
|
+ response_message.headers = NULL;
|
|
+ response_message.body_length = len;
|
|
+ response_message.body = buf;
|
|
+
|
|
+ response_token.message = &response_message;
|
|
+ response_token.status = GRUB_EFI_NOT_READY;
|
|
+
|
|
+ response_callback_done = 0;
|
|
+
|
|
+ status = http->response (http, &response_token);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ b->close_event (response_token.event);
|
|
+ grub_error (GRUB_ERR_IO, "Error! status=%d\n", (int)status);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ while (!response_callback_done)
|
|
+ http->poll (http);
|
|
+
|
|
+ sum += response_message.body_length;
|
|
+ buf += response_message.body_length;
|
|
+ len -= response_message.body_length;
|
|
+ }
|
|
+
|
|
+ b->close_event (response_token.event);
|
|
+
|
|
+ return sum;
|
|
+}
|
|
+
|
|
+static grub_err_t
|
|
+grub_efihttp_open (struct grub_efi_net_device *dev,
|
|
+ int prefer_ip6 __attribute__ ((unused)),
|
|
+ grub_file_t file,
|
|
+ const char *filename __attribute__ ((unused)),
|
|
+ int type)
|
|
+{
|
|
+ grub_err_t err;
|
|
+ grub_off_t size;
|
|
+ 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
|
|
+ };
|
|
--- /dev/null
|
|
+++ b/grub-core/net/efi/ip4_config.c
|
|
@@ -0,0 +1,409 @@
|
|
+
|
|
+#include <grub/efi/api.h>
|
|
+#include <grub/efi/efi.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/net/efi.h>
|
|
+#include <grub/charset.h>
|
|
+#include <grub/safemath.h>
|
|
+
|
|
+char *
|
|
+grub_efi_hw_address_to_string (grub_efi_uint32_t hw_address_size, grub_efi_mac_address_t hw_address)
|
|
+{
|
|
+ char *hw_addr, *p;
|
|
+ int s;
|
|
+ int i;
|
|
+ grub_size_t sz;
|
|
+
|
|
+ if (grub_mul (hw_address_size, sizeof ("XX:") - 1, &sz) ||
|
|
+ grub_add (sz, 1, &sz))
|
|
+ return NULL;
|
|
+
|
|
+ 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, &ptr, 0);
|
|
+ if (grub_errno)
|
|
+ {
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ return 0;
|
|
+ }
|
|
+ if (*ptr != '.' && i == 0)
|
|
+ {
|
|
+ /* XXX: t is in host byte order */
|
|
+ newip = t;
|
|
+ break;
|
|
+ }
|
|
+ if (t & ~0xff)
|
|
+ return 0;
|
|
+ newip <<= 8;
|
|
+ newip |= t;
|
|
+ if (i != 3 && *ptr != '.')
|
|
+ return 0;
|
|
+ ptr++;
|
|
+ }
|
|
+
|
|
+ newip = grub_cpu_to_be32 (newip);
|
|
+
|
|
+ grub_memcpy (address, &newip, sizeof(*address));
|
|
+
|
|
+ if (rest)
|
|
+ *rest = (ptr - 1);
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static grub_efi_ip4_config2_interface_info_t *
|
|
+efi_ip4_config_interface_info (grub_efi_ip4_config2_protocol_t *ip4_config)
|
|
+{
|
|
+ grub_efi_uintn_t sz;
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_ip4_config2_interface_info_t *interface_info;
|
|
+
|
|
+ sz = sizeof (*interface_info) + sizeof (*interface_info->route_table);
|
|
+ interface_info = grub_malloc (sz);
|
|
+ if (!interface_info)
|
|
+ return NULL;
|
|
+
|
|
+ status = ip4_config->get_data (ip4_config,
|
|
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
|
|
+ &sz, interface_info);
|
|
+
|
|
+ if (status == GRUB_EFI_BUFFER_TOO_SMALL)
|
|
+ {
|
|
+ grub_free (interface_info);
|
|
+ interface_info = grub_malloc (sz);
|
|
+ status = ip4_config->get_data (ip4_config,
|
|
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_INTERFACEINFO,
|
|
+ &sz, interface_info);
|
|
+ }
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_free (interface_info);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return interface_info;
|
|
+}
|
|
+
|
|
+static grub_efi_ip4_config2_manual_address_t *
|
|
+efi_ip4_config_manual_address (grub_efi_ip4_config2_protocol_t *ip4_config)
|
|
+{
|
|
+ grub_efi_uintn_t sz;
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_ip4_config2_manual_address_t *manual_address;
|
|
+
|
|
+ sz = sizeof (*manual_address);
|
|
+ manual_address = grub_malloc (sz);
|
|
+ if (!manual_address)
|
|
+ return NULL;
|
|
+
|
|
+ status = ip4_config->get_data (ip4_config,
|
|
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
|
|
+ &sz, manual_address);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_free (manual_address);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return manual_address;
|
|
+}
|
|
+
|
|
+char *
|
|
+grub_efi_ip4_interface_name (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip4_config2_interface_info_t *interface_info;
|
|
+ char *name;
|
|
+
|
|
+ interface_info = efi_ip4_config_interface_info (dev->ip4_config);
|
|
+
|
|
+ if (!interface_info)
|
|
+ return NULL;
|
|
+
|
|
+ name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE
|
|
+ * GRUB_MAX_UTF8_PER_UTF16 + 1);
|
|
+ *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name,
|
|
+ GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0;
|
|
+ grub_free (interface_info);
|
|
+ return name;
|
|
+}
|
|
+
|
|
+static char *
|
|
+grub_efi_ip4_interface_hw_address (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip4_config2_interface_info_t *interface_info;
|
|
+ char *hw_addr;
|
|
+
|
|
+ interface_info = efi_ip4_config_interface_info (dev->ip4_config);
|
|
+
|
|
+ if (!interface_info)
|
|
+ return NULL;
|
|
+
|
|
+ hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address);
|
|
+ grub_free (interface_info);
|
|
+
|
|
+ return hw_addr;
|
|
+}
|
|
+
|
|
+static char *
|
|
+grub_efi_ip4_interface_address (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip4_config2_manual_address_t *manual_address;
|
|
+ char *addr;
|
|
+
|
|
+ manual_address = efi_ip4_config_manual_address (dev->ip4_config);
|
|
+
|
|
+ if (!manual_address)
|
|
+ return NULL;
|
|
+
|
|
+ addr = grub_efi_ip4_address_to_string (&manual_address->address);
|
|
+ grub_free (manual_address);
|
|
+ return addr;
|
|
+}
|
|
+
|
|
+
|
|
+static int
|
|
+address_mask_size (grub_efi_ipv4_address_t *address)
|
|
+{
|
|
+ grub_uint8_t i;
|
|
+ grub_uint32_t u32_addr = grub_be_to_cpu32 (grub_get_unaligned32 (address));
|
|
+
|
|
+ if (u32_addr == 0)
|
|
+ return 0;
|
|
+
|
|
+ for (i = 0; i < 32 ; ++i)
|
|
+ {
|
|
+ if (u32_addr == ((0xffffffff >> i) << i))
|
|
+ return (32 - i);
|
|
+ }
|
|
+
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static char **
|
|
+grub_efi_ip4_interface_route_table (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip4_config2_interface_info_t *interface_info;
|
|
+ char **ret;
|
|
+ int i, id;
|
|
+ grub_size_t sz;
|
|
+
|
|
+ interface_info = efi_ip4_config_interface_info (dev->ip4_config);
|
|
+ if (!interface_info)
|
|
+ return NULL;
|
|
+
|
|
+ if (grub_add (interface_info->route_table_size, 1, &sz))
|
|
+ {
|
|
+ grub_free (interface_info);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ ret = grub_calloc (sz, sizeof (*ret));
|
|
+
|
|
+ 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 = dev->ip4_config->set_data (dev->ip4_config,
|
|
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_MANUAL_ADDRESS,
|
|
+ sizeof(*address), address);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+grub_efi_ip4_interface_set_gateway (struct grub_efi_net_device *dev,
|
|
+ grub_efi_net_ip_address_t *address)
|
|
+{
|
|
+ grub_efi_status_t status;
|
|
+
|
|
+ status = dev->ip4_config->set_data (dev->ip4_config,
|
|
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_GATEWAY,
|
|
+ sizeof (address->ip4), &address->ip4);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+/* FIXME: Multiple DNS */
|
|
+static int
|
|
+grub_efi_ip4_interface_set_dns (struct grub_efi_net_device *dev,
|
|
+ grub_efi_net_ip_address_t *address)
|
|
+{
|
|
+ grub_efi_status_t status;
|
|
+
|
|
+ status = dev->ip4_config->set_data (dev->ip4_config,
|
|
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_DNSSERVER,
|
|
+ sizeof (address->ip4), &address->ip4);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+grub_efi_net_ip_config_t *efi_net_ip4_config = &(grub_efi_net_ip_config_t)
|
|
+ {
|
|
+ .get_hw_address = grub_efi_ip4_interface_hw_address,
|
|
+ .get_address = grub_efi_ip4_interface_address,
|
|
+ .get_route_table = grub_efi_ip4_interface_route_table,
|
|
+ .best_interface = grub_efi_ip4_interface_match,
|
|
+ .set_address = grub_efi_ip4_interface_set_manual_address,
|
|
+ .set_gateway = grub_efi_ip4_interface_set_gateway,
|
|
+ .set_dns = grub_efi_ip4_interface_set_dns
|
|
+ };
|
|
--- /dev/null
|
|
+++ b/grub-core/net/efi/ip6_config.c
|
|
@@ -0,0 +1,430 @@
|
|
+#include <grub/efi/api.h>
|
|
+#include <grub/efi/efi.h>
|
|
+#include <grub/misc.h>
|
|
+#include <grub/net/efi.h>
|
|
+#include <grub/charset.h>
|
|
+#include <grub/safemath.h>
|
|
+
|
|
+char *
|
|
+grub_efi_ip6_address_to_string (grub_efi_pxe_ipv6_address_t *address)
|
|
+{
|
|
+ char *str = grub_malloc (sizeof ("XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX"));
|
|
+ char *p;
|
|
+ int i;
|
|
+ int squash;
|
|
+
|
|
+ if (!str)
|
|
+ return NULL;
|
|
+
|
|
+ p = str;
|
|
+ squash = 0;
|
|
+ for (i = 0; i < 8; ++i)
|
|
+ {
|
|
+ grub_uint16_t addr;
|
|
+
|
|
+ if (i == 7)
|
|
+ squash = 2;
|
|
+
|
|
+ addr = grub_get_unaligned16 (address->addr + i * 2);
|
|
+
|
|
+ if (grub_be_to_cpu16 (addr))
|
|
+ {
|
|
+ char buf[sizeof ("XXXX")];
|
|
+ if (i > 0)
|
|
+ *p++ = ':';
|
|
+ grub_snprintf (buf, sizeof (buf), "%x", grub_be_to_cpu16 (addr));
|
|
+ grub_strcpy (p, buf);
|
|
+ p += grub_strlen (buf);
|
|
+
|
|
+ if (squash == 1)
|
|
+ squash = 2;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (squash == 0)
|
|
+ {
|
|
+ *p++ = ':';
|
|
+ squash = 1;
|
|
+ }
|
|
+ else if (squash == 2)
|
|
+ {
|
|
+ *p++ = ':';
|
|
+ *p++ = '0';
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ *p = '\0';
|
|
+ return str;
|
|
+}
|
|
+
|
|
+int
|
|
+grub_efi_string_to_ip6_address (const char *val, grub_efi_ipv6_address_t *address, const char **rest)
|
|
+{
|
|
+ grub_uint16_t newip[8];
|
|
+ const char *ptr = val;
|
|
+ int word, quaddot = -1;
|
|
+ int bracketed = 0;
|
|
+
|
|
+ if (ptr[0] == '[') {
|
|
+ bracketed = 1;
|
|
+ ptr++;
|
|
+ }
|
|
+
|
|
+ if (ptr[0] == ':' && ptr[1] != ':')
|
|
+ return 0;
|
|
+ if (ptr[0] == ':')
|
|
+ ptr++;
|
|
+
|
|
+ for (word = 0; word < 8; word++)
|
|
+ {
|
|
+ unsigned long t;
|
|
+ if (*ptr == ':')
|
|
+ {
|
|
+ quaddot = word;
|
|
+ word--;
|
|
+ ptr++;
|
|
+ continue;
|
|
+ }
|
|
+ t = grub_strtoul (ptr, &ptr, 16);
|
|
+ if (grub_errno)
|
|
+ {
|
|
+ grub_errno = GRUB_ERR_NONE;
|
|
+ break;
|
|
+ }
|
|
+ if (t & ~0xffff)
|
|
+ return 0;
|
|
+ newip[word] = grub_cpu_to_be16 (t);
|
|
+ if (*ptr != ':')
|
|
+ break;
|
|
+ ptr++;
|
|
+ }
|
|
+ if (quaddot == -1 && word < 7)
|
|
+ return 0;
|
|
+ if (quaddot != -1)
|
|
+ {
|
|
+ grub_memmove (&newip[quaddot + 7 - word], &newip[quaddot],
|
|
+ (word - quaddot + 1) * sizeof (newip[0]));
|
|
+ grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0]));
|
|
+ }
|
|
+ grub_memcpy (address, newip, 16);
|
|
+ if (bracketed && *ptr == ']') {
|
|
+ ptr++;
|
|
+ }
|
|
+ if (rest)
|
|
+ *rest = ptr;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static grub_efi_ip6_config_interface_info_t *
|
|
+efi_ip6_config_interface_info (grub_efi_ip6_config_protocol_t *ip6_config)
|
|
+{
|
|
+ grub_efi_uintn_t sz;
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_ip6_config_interface_info_t *interface_info;
|
|
+
|
|
+ sz = sizeof (*interface_info) + sizeof (*interface_info->route_table);
|
|
+ interface_info = grub_malloc (sz);
|
|
+
|
|
+ status = ip6_config->get_data (ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
|
|
+ &sz, interface_info);
|
|
+
|
|
+ if (status == GRUB_EFI_BUFFER_TOO_SMALL)
|
|
+ {
|
|
+ grub_free (interface_info);
|
|
+ interface_info = grub_malloc (sz);
|
|
+ status = ip6_config->get_data (ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_INTERFACEINFO,
|
|
+ &sz, interface_info);
|
|
+ }
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_free (interface_info);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return interface_info;
|
|
+}
|
|
+
|
|
+static grub_efi_ip6_config_manual_address_t *
|
|
+efi_ip6_config_manual_address (grub_efi_ip6_config_protocol_t *ip6_config)
|
|
+{
|
|
+ grub_efi_uintn_t sz;
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_ip6_config_manual_address_t *manual_address;
|
|
+
|
|
+ sz = sizeof (*manual_address);
|
|
+ manual_address = grub_malloc (sz);
|
|
+ if (!manual_address)
|
|
+ return NULL;
|
|
+
|
|
+ status = ip6_config->get_data (ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
|
|
+ &sz, manual_address);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_free (manual_address);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ return manual_address;
|
|
+}
|
|
+
|
|
+char *
|
|
+grub_efi_ip6_interface_name (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip6_config_interface_info_t *interface_info;
|
|
+ char *name;
|
|
+
|
|
+ interface_info = efi_ip6_config_interface_info (dev->ip6_config);
|
|
+
|
|
+ if (!interface_info)
|
|
+ return NULL;
|
|
+
|
|
+ name = grub_malloc (GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE
|
|
+ * GRUB_MAX_UTF8_PER_UTF16 + 1);
|
|
+ *grub_utf16_to_utf8 ((grub_uint8_t *)name, interface_info->name,
|
|
+ GRUB_EFI_IP4_CONFIG2_INTERFACE_INFO_NAME_SIZE) = 0;
|
|
+ grub_free (interface_info);
|
|
+ return name;
|
|
+}
|
|
+
|
|
+static char *
|
|
+grub_efi_ip6_interface_hw_address (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip6_config_interface_info_t *interface_info;
|
|
+ char *hw_addr;
|
|
+
|
|
+ interface_info = efi_ip6_config_interface_info (dev->ip6_config);
|
|
+
|
|
+ if (!interface_info)
|
|
+ return NULL;
|
|
+
|
|
+ hw_addr = grub_efi_hw_address_to_string (interface_info->hw_address_size, interface_info->hw_address);
|
|
+ grub_free (interface_info);
|
|
+
|
|
+ return hw_addr;
|
|
+}
|
|
+
|
|
+static char *
|
|
+grub_efi_ip6_interface_address (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip6_config_manual_address_t *manual_address;
|
|
+ char *addr;
|
|
+
|
|
+ manual_address = efi_ip6_config_manual_address (dev->ip6_config);
|
|
+
|
|
+ if (!manual_address)
|
|
+ return NULL;
|
|
+
|
|
+ addr = grub_efi_ip6_address_to_string ((grub_efi_pxe_ipv6_address_t *)&manual_address->address);
|
|
+ grub_free (manual_address);
|
|
+ return addr;
|
|
+}
|
|
+
|
|
+static char **
|
|
+grub_efi_ip6_interface_route_table (struct grub_efi_net_device *dev)
|
|
+{
|
|
+ grub_efi_ip6_config_interface_info_t *interface_info;
|
|
+ char **ret;
|
|
+ int i, id;
|
|
+ grub_size_t sz;
|
|
+
|
|
+ interface_info = efi_ip6_config_interface_info (dev->ip6_config);
|
|
+ if (!interface_info)
|
|
+ return NULL;
|
|
+
|
|
+ if (grub_add (interface_info->route_count, 1, &sz))
|
|
+ {
|
|
+ grub_free (interface_info);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ ret = grub_calloc (sz, sizeof (*ret));
|
|
+
|
|
+ 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 = dev->ip6_config->set_data (dev->ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_MANUAL_ADDRESS,
|
|
+ sizeof(*address), address);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+grub_efi_ip6_interface_set_gateway (struct grub_efi_net_device *dev,
|
|
+ grub_efi_net_ip_address_t *address)
|
|
+{
|
|
+ grub_efi_status_t status;
|
|
+
|
|
+ status = dev->ip6_config->set_data (dev->ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_GATEWAY,
|
|
+ sizeof (address->ip6), &address->ip6);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int
|
|
+grub_efi_ip6_interface_set_dns (struct grub_efi_net_device *dev,
|
|
+ grub_efi_net_ip_address_t *address)
|
|
+{
|
|
+
|
|
+ grub_efi_status_t status;
|
|
+
|
|
+ status = dev->ip6_config->set_data (dev->ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_DNSSERVER,
|
|
+ sizeof (address->ip6), &address->ip6);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+grub_efi_net_ip_config_t *efi_net_ip6_config = &(grub_efi_net_ip_config_t)
|
|
+ {
|
|
+ .get_hw_address = grub_efi_ip6_interface_hw_address,
|
|
+ .get_address = grub_efi_ip6_interface_address,
|
|
+ .get_route_table = grub_efi_ip6_interface_route_table,
|
|
+ .best_interface = grub_efi_ip6_interface_match,
|
|
+ .set_address = grub_efi_ip6_interface_set_manual_address,
|
|
+ .set_gateway = grub_efi_ip6_interface_set_gateway,
|
|
+ .set_dns = grub_efi_ip6_interface_set_dns
|
|
+ };
|
|
--- /dev/null
|
|
+++ b/grub-core/net/efi/net.c
|
|
@@ -0,0 +1,1440 @@
|
|
+#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>
|
|
+#include <grub/safemath.h>
|
|
+
|
|
+GRUB_MOD_LICENSE ("GPLv3+");
|
|
+
|
|
+#define GRUB_EFI_IP6_PREFIX_LENGTH 64
|
|
+
|
|
+static grub_guid_t ip4_config_guid = GRUB_EFI_IP4_CONFIG2_PROTOCOL_GUID;
|
|
+static grub_guid_t ip6_config_guid = GRUB_EFI_IP6_CONFIG_PROTOCOL_GUID;
|
|
+static grub_guid_t http_service_binding_guid = GRUB_EFI_HTTP_SERVICE_BINDING_PROTOCOL_GUID;
|
|
+static grub_guid_t http_guid = GRUB_EFI_HTTP_PROTOCOL_GUID;
|
|
+static grub_guid_t pxe_io_guid = GRUB_EFI_PXE_GUID;
|
|
+static grub_guid_t dhcp4_service_binding_guid = GRUB_EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID;
|
|
+static grub_guid_t dhcp4_guid = GRUB_EFI_DHCP4_PROTOCOL_GUID;
|
|
+static grub_guid_t dhcp6_service_binding_guid = GRUB_EFI_DHCP6_SERVICE_BINDING_PROTOCOL_GUID;
|
|
+static grub_guid_t dhcp6_guid = GRUB_EFI_DHCP6_PROTOCOL_GUID;
|
|
+
|
|
+struct grub_efi_net_device *net_devices;
|
|
+
|
|
+static char *default_server;
|
|
+static grub_efi_net_interface_t *net_interface;
|
|
+static grub_efi_net_interface_t *net_default_interface;
|
|
+
|
|
+#define efi_net_interface_configure(inf) inf->io->configure (inf->dev, inf->prefer_ip6)
|
|
+#define efi_net_interface_open(inf, file, name) inf->io->open (inf->dev, inf->prefer_ip6, file, name, inf->io_type)
|
|
+#define efi_net_interface_read(inf, file, buf, sz) inf->io->read (inf->dev, inf->prefer_ip6, file, buf, sz)
|
|
+#define efi_net_interface_close(inf, file) inf->io->close (inf->dev, inf->prefer_ip6, file)
|
|
+#define efi_net_interface(m,...) efi_net_interface_ ## m (net_interface, ## __VA_ARGS__)
|
|
+
|
|
+static grub_efi_handle_t
|
|
+grub_efi_locate_device_path (grub_guid_t *protocol, grub_efi_device_path_t *device_path,
|
|
+ grub_efi_device_path_t **r_device_path)
|
|
+{
|
|
+ grub_efi_handle_t handle;
|
|
+ grub_efi_status_t status;
|
|
+
|
|
+ status = grub_efi_system_table->boot_services->locate_device_path (
|
|
+ protocol, &device_path, &handle);
|
|
+
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ return 0;
|
|
+
|
|
+ if (r_device_path)
|
|
+ *r_device_path = device_path;
|
|
+
|
|
+ return handle;
|
|
+}
|
|
+
|
|
+static int
|
|
+url_parse_fields (const char *url, char **proto, char **host, char **path)
|
|
+{
|
|
+ const char *p, *ps;
|
|
+ grub_size_t l;
|
|
+
|
|
+ *proto = *host = *path = NULL;
|
|
+ ps = p = url;
|
|
+
|
|
+ while ((p = grub_strchr (p, ':')))
|
|
+ {
|
|
+ if (grub_strlen (p) < sizeof ("://") - 1)
|
|
+ break;
|
|
+ if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0)
|
|
+ {
|
|
+ l = p - ps;
|
|
+ *proto = grub_malloc (l + 1);
|
|
+ if (!*proto)
|
|
+ {
|
|
+ grub_print_error ();
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ grub_memcpy (*proto, ps, l);
|
|
+ (*proto)[l] = '\0';
|
|
+ p += sizeof ("://") - 1;
|
|
+ break;
|
|
+ }
|
|
+ ++p;
|
|
+ }
|
|
+
|
|
+ if (!*proto)
|
|
+ {
|
|
+ grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ ps = p;
|
|
+ p = grub_strchr (p, '/');
|
|
+
|
|
+ if (!p)
|
|
+ {
|
|
+ grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url);
|
|
+ grub_free (*proto);
|
|
+ *proto = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ l = p - ps;
|
|
+
|
|
+ if (l > 2 && ps[0] == '[' && ps[l - 1] == ']')
|
|
+ {
|
|
+ *host = grub_malloc (l - 1);
|
|
+ if (!*host)
|
|
+ {
|
|
+ grub_print_error ();
|
|
+ grub_free (*proto);
|
|
+ *proto = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+ grub_memcpy (*host, ps + 1, l - 2);
|
|
+ (*host)[l - 2] = 0;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ *host = grub_malloc (l + 1);
|
|
+ if (!*host)
|
|
+ {
|
|
+ grub_print_error ();
|
|
+ grub_free (*proto);
|
|
+ *proto = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+ grub_memcpy (*host, ps, l);
|
|
+ (*host)[l] = 0;
|
|
+ }
|
|
+
|
|
+ *path = grub_strdup (p);
|
|
+ if (!*path)
|
|
+ {
|
|
+ grub_print_error ();
|
|
+ grub_free (*host);
|
|
+ grub_free (*proto);
|
|
+ *host = NULL;
|
|
+ *proto = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static void
|
|
+url_get_boot_location (const char *url, char **device, char **path, int is_default)
|
|
+{
|
|
+ char *protocol, *server, *file;
|
|
+ char *slash;
|
|
+
|
|
+ if (!url_parse_fields (url, &protocol, &server, &file))
|
|
+ return;
|
|
+
|
|
+ if ((slash = grub_strrchr (file, '/')))
|
|
+ *slash = 0;
|
|
+ else
|
|
+ *file = 0;
|
|
+
|
|
+ *device = grub_xasprintf ("%s,%s", protocol, server);
|
|
+ *path = grub_strdup(file);
|
|
+
|
|
+ if (is_default)
|
|
+ default_server = server;
|
|
+ else
|
|
+ grub_free (server);
|
|
+
|
|
+ grub_free (protocol);
|
|
+ grub_free (file);
|
|
+}
|
|
+
|
|
+static void
|
|
+pxe_get_boot_location (const struct grub_net_bootp_packet *bp,
|
|
+ char **device,
|
|
+ char **path,
|
|
+ int is_default)
|
|
+{
|
|
+ char *server = grub_xasprintf ("%d.%d.%d.%d",
|
|
+ ((grub_uint8_t *) &bp->server_ip)[0],
|
|
+ ((grub_uint8_t *) &bp->server_ip)[1],
|
|
+ ((grub_uint8_t *) &bp->server_ip)[2],
|
|
+ ((grub_uint8_t *) &bp->server_ip)[3]);
|
|
+
|
|
+ *device = grub_xasprintf ("tftp,%s", server);
|
|
+
|
|
+ *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
|
|
+
|
|
+ if (*path)
|
|
+ {
|
|
+ char *slash;
|
|
+ slash = grub_strrchr (*path, '/');
|
|
+ if (slash)
|
|
+ *slash = 0;
|
|
+ else
|
|
+ **path = 0;
|
|
+ }
|
|
+
|
|
+ if (is_default)
|
|
+ default_server = server;
|
|
+ else
|
|
+ grub_free (server);
|
|
+}
|
|
+
|
|
+static void
|
|
+pxe_get_boot_location_v6 (const struct grub_net_dhcp6_packet *dp,
|
|
+ grub_size_t dhcp_size,
|
|
+ char **device,
|
|
+ char **path)
|
|
+{
|
|
+
|
|
+ struct grub_net_dhcp6_option *dhcp_opt;
|
|
+ grub_size_t dhcp_remain_size;
|
|
+ *device = *path = 0;
|
|
+
|
|
+ if (dhcp_size < sizeof (*dp))
|
|
+ {
|
|
+ grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small"));
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dhcp_remain_size = dhcp_size - sizeof (*dp);
|
|
+ dhcp_opt = (struct grub_net_dhcp6_option *)dp->dhcp_options;
|
|
+
|
|
+ while (dhcp_remain_size)
|
|
+ {
|
|
+ grub_uint16_t code = grub_be_to_cpu16 (dhcp_opt->code);
|
|
+ grub_uint16_t len = grub_be_to_cpu16 (dhcp_opt->len);
|
|
+ grub_uint16_t option_size = sizeof (*dhcp_opt) + len;
|
|
+
|
|
+ if (dhcp_remain_size < option_size || code == 0)
|
|
+ break;
|
|
+
|
|
+ if (code == GRUB_NET_DHCP6_OPTION_BOOTFILE_URL)
|
|
+ {
|
|
+ char *url;
|
|
+ grub_size_t sz;
|
|
+
|
|
+ if (grub_add (len, 1, &sz))
|
|
+ return;
|
|
+
|
|
+ url = grub_malloc (sz);
|
|
+ if (!url)
|
|
+ return;
|
|
+
|
|
+ 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.addr, 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.addr, sizeof (net_ip.ip4.address));
|
|
+ grub_memcpy (net_ip.ip4.subnet_mask, pxe->mode->subnet_mask.v4.addr, 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_guid_t *service_binding_guid)
|
|
+{
|
|
+ grub_efi_service_binding_t *service;
|
|
+ grub_efi_status_t status;
|
|
+ grub_efi_handle_t child_dev = NULL;
|
|
+
|
|
+ service = grub_efi_open_protocol (dev, service_binding_guid, GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
|
+ if (!service)
|
|
+ {
|
|
+ grub_error (GRUB_ERR_IO, N_("couldn't open efi service binding protocol"));
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ status = service->create_child (service, &child_dev);
|
|
+ if (status != GRUB_EFI_SUCCESS)
|
|
+ {
|
|
+ grub_error (GRUB_ERR_IO, N_("Failed to create child device of http service %" PRIxGRUB_SIZE), 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, &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, &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;
|
|
+
|
|
+ grub_error_push ();
|
|
+ err = grub_efi_net_parse_address (server, &ip4, &ip6, &is_ip6, 0);
|
|
+
|
|
+ if (err)
|
|
+ {
|
|
+ grub_dprintf ("efinetfs", "error in matching route : %s\n", grub_errmsg);
|
|
+ grub_error_pop ();
|
|
+ return NULL;
|
|
+ }
|
|
+ grub_error_pop ();
|
|
+
|
|
+ if (is_ip6)
|
|
+ {
|
|
+ struct grub_efi_net_device *dev;
|
|
+ grub_efi_net_ip_address_t addr;
|
|
+
|
|
+ grub_memcpy (addr.ip6, ip6.address, sizeof(ip6.address));
|
|
+
|
|
+ for (dev = net_devices; dev; dev = dev->next)
|
|
+ if ((inf = efi_net_ip6_config->best_interface (dev, &addr)))
|
|
+ return inf;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ struct grub_efi_net_device *dev;
|
|
+ grub_efi_net_ip_address_t addr;
|
|
+
|
|
+ grub_memcpy (addr.ip4, ip4.address, sizeof(ip4.address));
|
|
+
|
|
+ for (dev = net_devices; dev; dev = dev->next)
|
|
+ if ((inf = efi_net_ip4_config->best_interface (dev, &addr)))
|
|
+ return inf;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void
|
|
+grub_efi_net_add_pxebc_to_cards (void)
|
|
+{
|
|
+ grub_efi_uintn_t num_handles;
|
|
+ grub_efi_handle_t *handles;
|
|
+ grub_efi_handle_t *handle;
|
|
+
|
|
+ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &pxe_io_guid,
|
|
+ 0, &num_handles);
|
|
+ if (!handles)
|
|
+ return;
|
|
+
|
|
+ for (handle = handles; num_handles--; handle++)
|
|
+ {
|
|
+ grub_efi_device_path_t *dp, *ddp, *ldp;
|
|
+ grub_efi_pxe_t *pxe;
|
|
+ struct grub_efi_net_device *d;
|
|
+ int is_ip6 = 0;
|
|
+
|
|
+ dp = grub_efi_get_device_path (*handle);
|
|
+ if (!dp)
|
|
+ continue;
|
|
+
|
|
+ ddp = grub_efi_duplicate_device_path (dp);
|
|
+ ldp = grub_efi_find_last_device_path (ddp);
|
|
+
|
|
+ if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
|
|
+ && ldp->subtype == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
|
|
+ {
|
|
+ ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
|
|
+ ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
|
|
+ ldp->length = sizeof (*ldp);
|
|
+ }
|
|
+ else if (ldp->type == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
|
|
+ && ldp->subtype == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)
|
|
+ {
|
|
+ is_ip6 = 1;
|
|
+ ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
|
|
+ ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
|
|
+ ldp->length = sizeof (*ldp);
|
|
+ }
|
|
+
|
|
+ for (d = net_devices; d; d = d->next)
|
|
+ if (grub_efi_compare_device_paths (ddp, grub_efi_get_device_path (d->handle)) == 0)
|
|
+ break;
|
|
+
|
|
+ if (!d)
|
|
+ {
|
|
+ grub_free (ddp);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ pxe = grub_efi_open_protocol (*handle, &pxe_io_guid,
|
|
+ GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
|
+
|
|
+ if (!pxe)
|
|
+ {
|
|
+ grub_free (ddp);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (is_ip6)
|
|
+ {
|
|
+ d->ip6_pxe_handle = *handle;
|
|
+ d->ip6_pxe = pxe;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ d->ip4_pxe_handle = *handle;
|
|
+ d->ip4_pxe = pxe;
|
|
+ }
|
|
+
|
|
+ grub_free (ddp);
|
|
+ }
|
|
+
|
|
+ grub_free (handles);
|
|
+}
|
|
+
|
|
+static void
|
|
+set_ip_policy_to_static (void)
|
|
+{
|
|
+ struct grub_efi_net_device *dev;
|
|
+
|
|
+ for (dev = net_devices; dev; dev = dev->next)
|
|
+ {
|
|
+ grub_efi_ip4_config2_policy_t ip4_policy = GRUB_EFI_IP4_CONFIG2_POLICY_STATIC;
|
|
+
|
|
+ if (dev->ip4_config->set_data (dev->ip4_config,
|
|
+ GRUB_EFI_IP4_CONFIG2_DATA_TYPE_POLICY,
|
|
+ sizeof (ip4_policy), &ip4_policy) != GRUB_EFI_SUCCESS)
|
|
+ grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP4_CONFIG2_POLICY_STATIC on dev `%s'", dev->card_name);
|
|
+
|
|
+ if (dev->ip6_config)
|
|
+ {
|
|
+ grub_efi_ip6_config_policy_t ip6_policy = GRUB_EFI_IP6_CONFIG_POLICY_MANUAL;
|
|
+
|
|
+ if (dev->ip6_config->set_data (dev->ip6_config,
|
|
+ GRUB_EFI_IP6_CONFIG_DATA_TYPE_POLICY,
|
|
+ sizeof (ip6_policy), &ip6_policy) != GRUB_EFI_SUCCESS)
|
|
+ grub_dprintf ("efinetfs", "could not set GRUB_EFI_IP6_CONFIG_POLICY_MANUAL on dev `%s'", dev->card_name);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+/* 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");
|
|