You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
405 lines
12 KiB
405 lines
12 KiB
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. */
|
|
|