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