406 lines
12 KiB
Diff
406 lines
12 KiB
Diff
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||
|
From: Michael Chang <mchang@suse.com>
|
||
|
Date: Sun, 10 Jul 2016 23:46:31 +0800
|
||
|
Subject: [PATCH] efinet: Setting network from UEFI device path
|
||
|
|
||
|
The PXE Base Code protocol used to obtain cached PXE DHCPACK packet is no
|
||
|
longer provided for HTTP Boot. Instead, we have to get the HTTP boot
|
||
|
information from the device path nodes defined in following UEFI Specification
|
||
|
sections.
|
||
|
|
||
|
9.3.5.12 IPv4 Device Path
|
||
|
9.3.5.13 IPv6 Device Path
|
||
|
9.3.5.23 Uniform Resource Identifiers (URI) Device Path
|
||
|
|
||
|
This patch basically does:
|
||
|
|
||
|
include/grub/efi/api.h:
|
||
|
Add new structure of Uniform Resource Identifiers (URI) Device Path
|
||
|
|
||
|
grub-core/net/drivers/efi/efinet.c:
|
||
|
Check if PXE Base Code is available, if not it will try to obtain the netboot
|
||
|
information from the device path where the image booted from. The DHCPACK
|
||
|
packet is recoverd from the information in device patch and feed into the same
|
||
|
DHCP packet processing functions to ensure the network interface is setting up
|
||
|
the same way it used to be.
|
||
|
|
||
|
Signed-off-by: Michael Chang <mchang@suse.com>
|
||
|
Signed-off-by: Ken Lin <ken.lin@hpe.com>
|
||
|
---
|
||
|
grub-core/net/drivers/efi/efinet.c | 284 +++++++++++++++++++++++++++++++++++--
|
||
|
include/grub/efi/api.h | 11 ++
|
||
|
2 files changed, 280 insertions(+), 15 deletions(-)
|
||
|
|
||
|
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
|
||
|
index 014e5bf9802..8171ecaa5e4 100644
|
||
|
--- a/grub-core/net/drivers/efi/efinet.c
|
||
|
+++ b/grub-core/net/drivers/efi/efinet.c
|
||
|
@@ -26,6 +26,7 @@
|
||
|
#include <grub/i18n.h>
|
||
|
#include <grub/lib/hexdump.h>
|
||
|
#include <grub/types.h>
|
||
|
+#include <grub/net/netbuff.h>
|
||
|
|
||
|
GRUB_MOD_LICENSE ("GPLv3+");
|
||
|
|
||
|
@@ -331,6 +332,227 @@ grub_efinet_findcards (void)
|
||
|
grub_free (handles);
|
||
|
}
|
||
|
|
||
|
+static struct grub_net_buff *
|
||
|
+grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6)
|
||
|
+{
|
||
|
+ grub_efi_uint16_t uri_len;
|
||
|
+ grub_efi_device_path_t *ldp, *ddp;
|
||
|
+ grub_efi_uri_device_path_t *uri_dp;
|
||
|
+ struct grub_net_buff *nb;
|
||
|
+ grub_err_t err;
|
||
|
+
|
||
|
+ ddp = grub_efi_duplicate_device_path (dp);
|
||
|
+ if (!ddp)
|
||
|
+ return NULL;
|
||
|
+
|
||
|
+ 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_URI_DEVICE_PATH_SUBTYPE)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4 : 0;
|
||
|
+
|
||
|
+ if (!uri_len)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ uri_dp = (grub_efi_uri_device_path_t *) ldp;
|
||
|
+
|
||
|
+ 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))
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ nb = grub_netbuff_alloc (512);
|
||
|
+ if (!nb)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
|
||
|
+ {
|
||
|
+ grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp;
|
||
|
+ struct grub_net_bootp_packet *bp;
|
||
|
+ grub_uint8_t *ptr;
|
||
|
+
|
||
|
+ bp = (struct grub_net_bootp_packet *) nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof (*bp) + 4);
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ if (sizeof(bp->boot_file) < uri_len)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ grub_memcpy (bp->boot_file, uri_dp->uri, uri_len);
|
||
|
+ grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip));
|
||
|
+ grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip));
|
||
|
+
|
||
|
+ bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0;
|
||
|
+ bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1;
|
||
|
+ bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2;
|
||
|
+ bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3;
|
||
|
+
|
||
|
+ ptr = nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2);
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ *ptr++ = GRUB_NET_BOOTP_NETMASK;
|
||
|
+ *ptr++ = sizeof (ipv4->subnet_mask);
|
||
|
+ grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask));
|
||
|
+
|
||
|
+ ptr = nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2);
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ *ptr++ = GRUB_NET_BOOTP_ROUTER;
|
||
|
+ *ptr++ = sizeof (ipv4->gateway_ip_address);
|
||
|
+ grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address));
|
||
|
+
|
||
|
+ ptr = nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1);
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER;
|
||
|
+ *ptr++ = sizeof ("HTTPClient") - 1;
|
||
|
+ grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1);
|
||
|
+
|
||
|
+ ptr = nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, 1);
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ *ptr = GRUB_NET_BOOTP_END;
|
||
|
+ *use_ipv6 = 0;
|
||
|
+
|
||
|
+ 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_SUBTYPE (ldp) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)
|
||
|
+ {
|
||
|
+ grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp;
|
||
|
+ bp->hw_type = mac->if_type;
|
||
|
+ bp->hw_len = sizeof (bp->mac_addr);
|
||
|
+ grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len);
|
||
|
+ }
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp;
|
||
|
+
|
||
|
+ struct grub_net_dhcp6_packet *d6p;
|
||
|
+ struct grub_net_dhcp6_option *opt;
|
||
|
+ struct grub_net_dhcp6_option_iana *iana;
|
||
|
+ struct grub_net_dhcp6_option_iaaddr *iaaddr;
|
||
|
+
|
||
|
+ d6p = (struct grub_net_dhcp6_packet *)nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof(*d6p));
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ d6p->message_type = GRUB_NET_DHCP6_REPLY;
|
||
|
+
|
||
|
+ opt = (struct grub_net_dhcp6_option *)nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof(*opt));
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
|
||
|
+ opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr));
|
||
|
+
|
||
|
+ err = grub_netbuff_put (nb, sizeof(*iana));
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+
|
||
|
+ opt = (struct grub_net_dhcp6_option *)nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof(*opt));
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR);
|
||
|
+ opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr));
|
||
|
+
|
||
|
+ iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof(*iaaddr));
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address));
|
||
|
+
|
||
|
+ opt = (struct grub_net_dhcp6_option *)nb->tail;
|
||
|
+ err = grub_netbuff_put (nb, sizeof(*opt) + uri_len);
|
||
|
+ if (err)
|
||
|
+ {
|
||
|
+ grub_free (ddp);
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+ return NULL;
|
||
|
+ }
|
||
|
+ opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL);
|
||
|
+ opt->len = grub_cpu_to_be16 (uri_len);
|
||
|
+ grub_memcpy (opt->data, uri_dp->uri, uri_len);
|
||
|
+
|
||
|
+ *use_ipv6 = 1;
|
||
|
+ }
|
||
|
+
|
||
|
+ grub_free (ddp);
|
||
|
+ return nb;
|
||
|
+}
|
||
|
+
|
||
|
static void
|
||
|
grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
|
||
|
char **path)
|
||
|
@@ -346,7 +568,11 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
|
||
|
{
|
||
|
grub_efi_device_path_t *cdp;
|
||
|
struct grub_efi_pxe *pxe;
|
||
|
- struct grub_efi_pxe_mode *pxe_mode;
|
||
|
+ struct grub_efi_pxe_mode *pxe_mode = NULL;
|
||
|
+ grub_uint8_t *packet_buf;
|
||
|
+ grub_size_t packet_bufsz ;
|
||
|
+ int ipv6;
|
||
|
+ struct grub_net_buff *nb = NULL;
|
||
|
|
||
|
if (card->driver != &efidriver)
|
||
|
continue;
|
||
|
@@ -370,11 +596,21 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
|
||
|
*/
|
||
|
if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
|
||
|
|| (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
|
||
|
- && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
|
||
|
+ && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE
|
||
|
+ && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
|
||
|
continue;
|
||
|
dup_dp = grub_efi_duplicate_device_path (dp);
|
||
|
if (!dup_dp)
|
||
|
continue;
|
||
|
+
|
||
|
+ if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_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;
|
||
|
@@ -387,23 +623,37 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
|
||
|
|
||
|
pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
|
||
|
GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
|
||
|
- if (! pxe)
|
||
|
- continue;
|
||
|
+ if (!pxe)
|
||
|
+ {
|
||
|
+ nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6);
|
||
|
+ if (!nb)
|
||
|
+ {
|
||
|
+ grub_print_error ();
|
||
|
+ continue;
|
||
|
+ }
|
||
|
+ packet_buf = nb->head;
|
||
|
+ packet_bufsz = nb->tail - nb->head;
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ pxe_mode = pxe->mode;
|
||
|
+ packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack;
|
||
|
+ packet_bufsz = sizeof (pxe_mode->dhcp_ack);
|
||
|
+ ipv6 = pxe_mode->using_ipv6;
|
||
|
+ }
|
||
|
|
||
|
- pxe_mode = pxe->mode;
|
||
|
- if (pxe_mode->using_ipv6)
|
||
|
+ if (ipv6)
|
||
|
{
|
||
|
grub_dprintf ("efinet", "using ipv6 and dhcpv6\n");
|
||
|
- grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
|
||
|
- pxe_mode->dhcp_ack_received ? "yes" : "no",
|
||
|
- pxe_mode->dhcp_ack_received ? "" : " cannot continue");
|
||
|
- if (!pxe_mode->dhcp_ack_received)
|
||
|
- continue;
|
||
|
+ if (pxe_mode)
|
||
|
+ grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
|
||
|
+ pxe_mode->dhcp_ack_received ? "yes" : "no",
|
||
|
+ pxe_mode->dhcp_ack_received ? "" : " cannot continue");
|
||
|
|
||
|
grub_net_configure_by_dhcpv6_reply (card->name, card, 0,
|
||
|
(struct grub_net_dhcp6_packet *)
|
||
|
- &pxe_mode->dhcp_ack,
|
||
|
- sizeof (pxe_mode->dhcp_ack),
|
||
|
+ packet_buf,
|
||
|
+ packet_bufsz,
|
||
|
1, device, path);
|
||
|
if (grub_errno)
|
||
|
grub_print_error ();
|
||
|
@@ -417,11 +667,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
|
||
|
grub_dprintf ("efinet", "using ipv4 and dhcp\n");
|
||
|
grub_net_configure_by_dhcp_ack (card->name, card, 0,
|
||
|
(struct grub_net_bootp_packet *)
|
||
|
- &pxe_mode->dhcp_ack,
|
||
|
- sizeof (pxe_mode->dhcp_ack),
|
||
|
+ packet_buf,
|
||
|
+ packet_bufsz,
|
||
|
1, device, path);
|
||
|
grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
|
||
|
}
|
||
|
+
|
||
|
+ if (nb)
|
||
|
+ grub_netbuff_free (nb);
|
||
|
+
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
|
||
|
index 91ab528e4d0..4a51667adb1 100644
|
||
|
--- a/include/grub/efi/api.h
|
||
|
+++ b/include/grub/efi/api.h
|
||
|
@@ -864,6 +864,8 @@ struct grub_efi_ipv4_device_path
|
||
|
grub_efi_uint16_t remote_port;
|
||
|
grub_efi_uint16_t protocol;
|
||
|
grub_efi_uint8_t static_ip_address;
|
||
|
+ grub_efi_ipv4_address_t gateway_ip_address;
|
||
|
+ grub_efi_ipv4_address_t subnet_mask;
|
||
|
} GRUB_PACKED;
|
||
|
typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t;
|
||
|
|
||
|
@@ -918,6 +920,15 @@ struct grub_efi_sata_device_path
|
||
|
} GRUB_PACKED;
|
||
|
typedef struct grub_efi_sata_device_path grub_efi_sata_device_path_t;
|
||
|
|
||
|
+#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE 24
|
||
|
+
|
||
|
+struct grub_efi_uri_device_path
|
||
|
+{
|
||
|
+ grub_efi_device_path_t header;
|
||
|
+ grub_efi_uint8_t uri[0];
|
||
|
+} GRUB_PACKED;
|
||
|
+typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t;
|
||
|
+
|
||
|
#define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE 10
|
||
|
|
||
|
/* Media Device Path. */
|