Discussion:
[RFC] efinet: default to MNP interface and SNP as fallback
(too old to reply)
Michael Chang
2018-09-27 09:43:48 UTC
Permalink
By far grub is using exclusive Simple Network Protocol (SNP) access and can
effectively kill all running Managed Network Protocol (MNP) instance on the
same NIC. The approach is not working reliably and new problem is reported
every now and then. Here I'm trying to make a summary for known problems.

1. Grub needs to reopen and reconfigure every aspect of SNP from scratch in
case the SNP is left with unknown or unusable state. It is very hard to
sustain empirically a global working state among all firmware and drivers,
since they could constantly ask the settings to be one or the other.
2. The trick of SNP reopen, plus forcibly kicking off the MNP instance, tends
to unveil bugs in badly written firmware driver more easily.
3. It prevents grub from using any protocol laid on top MNP, for eg the UEFI
HTTP or HTTPS, as it's underlying MNP instance has been destroyed.

So that in a bid to maintain better collaboration with UEFI network stack, grub
should check for availabilty of high level protocol like MNP, and use it to
open the interface to have firmware coordinating the SNP access. The SNP is
used as fallback to be still compatible on firmware without MNP implemented.

Signed-off-by: Michael Chang <***@suse.com>
---
grub-core/net/drivers/efi/efinet.c | 304 ++++++++++++++++++++++++++++++++++++-
include/grub/efi/api.h | 109 ++++++++++++-
include/grub/net.h | 2 +
3 files changed, 410 insertions(+), 5 deletions(-)

diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
index 5388f95..4c7b125 100644
--- a/grub-core/net/drivers/efi/efinet.c
+++ b/grub-core/net/drivers/efi/efinet.c
@@ -221,10 +221,203 @@ static struct grub_net_card_driver efidriver =
.recv = get_card_packet
};

+static grub_efi_guid_t mnp_service_guid = GRUB_EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID;
+static grub_efi_guid_t mnp_io_guid = GRUB_EFI_MANAGED_NETWORK_PROTOCOL_GUID;
+
+static void __attribute__((ms_abi))
+tx_completed_notify (grub_efi_event_t event __attribute__ ((unused)),
+ void *context)
+{
+ int *done = (int *)context;
+ *done = 1;
+}
+
+static grub_err_t
+mnp_send_card_buffer (struct grub_net_card *dev,
+ struct grub_net_buff *pack)
+{
+ grub_efi_status_t status;
+
+ grub_efi_managed_network_completion_token_t *mnp_token;
+ grub_efi_managed_network_transmit_data_t *tx_data;
+ grub_efi_managed_network_t *mnp = dev->efi_mnp;
+ int tx_done = 0;
+
+ mnp_token = grub_zalloc (sizeof(*mnp_token));
+ mnp_token->status = GRUB_EFI_NOT_READY;
+ status = efi_call_5 (grub_efi_system_table->boot_services->create_event,
+ GRUB_EFI_EVT_NOTIFY_SIGNAL,
+ GRUB_EFI_TPL_CALLBACK, tx_completed_notify, &tx_done, &mnp_token->event);
+
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_free (mnp_token);
+ return grub_error (GRUB_ERR_IO, N_("couldn't send network packet"));
+ }
+
+ tx_data = grub_zalloc (sizeof(*tx_data));
+ tx_data->header_length = 14;
+ tx_data->data_length = pack->tail - pack->data - 14;
+ tx_data->fragmentcount = 1;
+ tx_data->fragment_table[0].fragment_length = tx_data->data_length + tx_data->header_length;
+
+ grub_memcpy (dev->txbuf, pack->data, pack->tail - pack->data);
+ tx_data->fragment_table[0].fragment_buffer = dev->txbuf;
+ mnp_token->packet.tx_data = tx_data;
+
+ status = efi_call_2 (mnp->transmit, mnp, mnp_token);
+
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ grub_free (tx_data);
+ efi_call_1 (grub_efi_system_table->boot_services->close_event, mnp_token->event);
+ grub_free (mnp_token);
+ return grub_error (GRUB_ERR_IO, N_("couldn't send network packet"));
+ }
+
+ do {
+ //status = efi_call_1 (mnp->poll, mnp);
+ } while (!tx_done);
+
+ if (mnp_token->status != GRUB_EFI_SUCCESS)
+ {
+ grub_free (tx_data);
+ efi_call_1 (grub_efi_system_table->boot_services->close_event, mnp_token->event);
+ grub_free (mnp_token);
+ return grub_error (GRUB_ERR_IO, N_("couldn't send network packet"));
+ }
+
+ grub_free (tx_data);
+ efi_call_1 (grub_efi_system_table->boot_services->close_event, mnp_token->event);
+ grub_free (mnp_token);
+
+ return GRUB_ERR_NONE;
+}
+
+static void __attribute__((ms_abi))
+rx_completed_notify (grub_efi_event_t event __attribute__ ((unused)),
+ void *context)
+{
+ int *done = (int *)context;
+ *done = 1;
+
+}
+
+struct mnp_rx_token
+{
+ grub_efi_managed_network_completion_token_t mnp_token;
+ int done;
+};
+
+static struct mnp_rx_token *rx_token;
+
+static struct grub_net_buff *
+mnp_get_card_packet (struct grub_net_card *dev __attribute__ ((unused)))
+{
+
+ grub_efi_status_t status;
+ grub_err_t err;
+ struct grub_net_buff *nb = NULL;
+ grub_efi_managed_network_t *mnp = dev->efi_mnp;
+
+ if (rx_token)
+ {
+ if (rx_token->done)
+ {
+ if (rx_token->mnp_token.status == GRUB_EFI_SUCCESS)
+ {
+
+ grub_efi_managed_network_receive_data_t *rx_data = rx_token->mnp_token.packet.rx_data;
+
+ nb = grub_netbuff_alloc (rx_data->packet_length + 2);
+ if (!nb)
+ {
+ goto recycle_event;
+ }
+
+ /* Reserve 2 bytes so that 2 + 14/18 bytes of ethernet header is divisible
+ by 4. So that IP header is aligned on 4 bytes. */
+ if (grub_netbuff_reserve (nb, 2))
+ {
+ grub_netbuff_free (nb);
+ nb = NULL;
+ goto recycle_event;
+ }
+ grub_memcpy (nb->data, rx_data->media_header, rx_data->packet_length);
+ err = grub_netbuff_put (nb, rx_data->packet_length);
+ if (err)
+ {
+ grub_netbuff_free (nb);
+ nb = NULL;
+ }
+recycle_event:
+ efi_call_1 (grub_efi_system_table->boot_services->signal_event, rx_data->recycle_event);
+ }
+
+
+ efi_call_1 (grub_efi_system_table->boot_services->close_event, rx_token->mnp_token.event);
+ grub_free (rx_token);
+ }
+ else
+ {
+ status = efi_call_1 (mnp->poll, mnp);
+ return NULL;
+ }
+ }
+
+ rx_token = grub_zalloc (sizeof(*rx_token));
+
+ status = efi_call_5 (grub_efi_system_table->boot_services->create_event,
+ GRUB_EFI_EVT_NOTIFY_SIGNAL,
+ GRUB_EFI_TPL_CALLBACK, rx_completed_notify, &rx_token->done, &rx_token->mnp_token.event);
+
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ efi_call_1 (grub_efi_system_table->boot_services->close_event, rx_token->mnp_token.event);
+ grub_free (rx_token);
+ rx_token = NULL;
+ goto poll_mnp;
+ }
+
+ status = efi_call_2 (mnp->receive, mnp, &rx_token->mnp_token);
+
+ if (status != GRUB_EFI_SUCCESS)
+ {
+ efi_call_1 (grub_efi_system_table->boot_services->close_event, rx_token->mnp_token.event);
+ grub_free (rx_token);
+ rx_token = NULL;
+ }
+
+poll_mnp:
+ status = efi_call_1 (mnp->poll, mnp);
+ return nb;
+}
+
+static grub_err_t
+mnp_open_card (struct grub_net_card *dev __attribute__ ((unused)))
+{
+ return GRUB_ERR_NONE;
+}
+
+static void
+mnp_close_card (struct grub_net_card *dev __attribute__ ((unused)))
+{
+ return;
+}
+
+static struct grub_net_card_driver efidriver_mnp =
+ {
+ .name = "efinet_mnp",
+ .open = mnp_open_card,
+ .close = mnp_close_card,
+ .send = mnp_send_card_buffer,
+ .recv = mnp_get_card_packet
+ };
+
grub_efi_handle_t
grub_efinet_get_device_handle (struct grub_net_card *card)
{
- if (!card || card->driver != &efidriver)
+ if (!card || (card->driver != &efidriver && card->driver != &efidriver_mnp))
return 0;
return card->efi_handle;
}
@@ -324,6 +517,108 @@ grub_efinet_findcards (void)
grub_free (handles);
}

+static int
+grub_efinet_mnp_findcards (void)
+{
+ grub_efi_uintn_t num_handles;
+ grub_efi_handle_t *handles;
+ grub_efi_handle_t *handle;
+ int i = 0;
+
+ handles = grub_efi_locate_handle (GRUB_EFI_BY_PROTOCOL, &mnp_service_guid,
+ 0, &num_handles);
+ if (!handles)
+ return 0;
+
+ for (handle = handles; num_handles--; handle++)
+ {
+ struct grub_net_card *card;
+ grub_efi_service_binding_t *service;
+ grub_efi_managed_network_t *mnp;
+ grub_efi_handle_t child_handle;
+ grub_efi_device_path_t *dp;
+ grub_efi_status_t status;
+ grub_efi_simple_network_mode_t snp_mode;
+ grub_efi_managed_network_config_data_t mnp_config = {
+ .received_queue_timeout_value = 0,
+ .transmit_queue_timeout_value = 0,
+ .protocol_type_filter = 0,
+ .enable_unicast_receive = 1,
+ .enable_multicast_receive = 1,
+ .enable_broadcast_receive = 1,
+ .enable_promiscuous_receive = 0,
+ .flush_queues_on_reset = 1,
+ .enable_receive_timestamps = 0,
+ .disable_background_polling = 1
+ };
+
+ dp = grub_efi_get_device_path (*handle);
+ if (!dp)
+ continue;
+ service = grub_efi_open_protocol (*handle, &mnp_service_guid,
+ GRUB_EFI_OPEN_PROTOCOL_BY_EXCLUSIVE);
+ //GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (!service)
+ continue;
+
+ child_handle = NULL;
+ status = efi_call_2 (service->create_child, service, &child_handle);
+ if (status != GRUB_EFI_SUCCESS)
+ continue;
+
+ mnp = grub_efi_open_protocol (child_handle, &mnp_io_guid,
+ GRUB_EFI_OPEN_PROTOCOL_BY_EXCLUSIVE);
+ //GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ if (!mnp)
+ continue;
+
+ status = efi_call_2 (mnp->configure, mnp, &mnp_config);
+ if (status != GRUB_EFI_SUCCESS)
+ continue;
+
+ status = efi_call_3 (mnp->get_mode_data, mnp, &mnp_config, &snp_mode);
+ if (status != GRUB_EFI_SUCCESS)
+ continue;
+
+ card = grub_zalloc (sizeof (struct grub_net_card));
+ if (!card)
+ {
+ grub_print_error ();
+ grub_free (handles);
+ return 0;
+ }
+
+ card->mtu = snp_mode.max_packet_size;
+ card->txbufsize = ALIGN_UP (card->mtu, 64) + 256;
+ card->txbuf = grub_zalloc (card->txbufsize);
+ if (!card->txbuf)
+ {
+ grub_print_error ();
+ grub_free (handles);
+ grub_free (card);
+ return 0;
+ }
+ card->txbusy = 0;
+
+ card->rcvbufsize = ALIGN_UP (card->mtu, 64) + 256;
+
+ card->name = grub_xasprintf ("efinet_mnp%d", i++);
+ card->driver = &efidriver_mnp;
+ card->flags = 0;
+ card->default_address.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
+ grub_memcpy (card->default_address.mac,
+ snp_mode.current_address,
+ sizeof (card->default_address.mac));
+ card->efi_handle = *handle;
+ card->efi_mnp = mnp;
+ card->efi_mnp_handle = child_handle;
+ grub_net_card_register (card);
+ }
+
+ grub_free (handles);
+ return 1;
+}
+
static void
grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
char **path)
@@ -340,7 +635,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
grub_efi_device_path_t *cdp;
struct grub_efi_pxe *pxe;
struct grub_efi_pxe_mode *pxe_mode;
- if (card->driver != &efidriver)
+ if (card->driver != &efidriver && card->driver != &efidriver_mnp)
continue;
cdp = grub_efi_get_device_path (card->efi_handle);
if (! cdp)
@@ -389,7 +684,8 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,

GRUB_MOD_INIT(efinet)
{
- grub_efinet_findcards ();
+ if (!grub_efinet_mnp_findcards())
+ grub_efinet_findcards ();
grub_efi_net_config = grub_efi_net_config_real;
}

@@ -398,7 +694,7 @@ GRUB_MOD_FINI(efinet)
struct grub_net_card *card, *next;

FOR_NET_CARDS_SAFE (card, next)
- if (card->driver == &efidriver)
+ if (card->driver == &efidriver || card->driver == &efidriver_mnp)
grub_net_card_unregister (card);
}

diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index c7c9f0e..15ec0db 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -334,6 +334,16 @@
{ 0x8B, 0x8C, 0xE2, 0x1B, 0x01, 0xAE, 0xF2, 0xB7 } \
}

+#define GRUB_EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID \
+ { 0xf36ff770, 0xa7e1, 0x42cf, \
+ { 0x9e, 0xd2, 0x56, 0xf0, 0xf2, 0x71, 0xf4, 0x4c } \
+ }
+
+#define GRUB_EFI_MANAGED_NETWORK_PROTOCOL_GUID \
+ { 0x7ab33a91, 0xace5, 0x4326, \
+ { 0xb5, 0x72, 0xe7, 0xee, 0x33, 0xd3, 0x9f, 0x16 } \
+ }
+
struct grub_efi_sal_system_table
{
grub_uint32_t signature;
@@ -1087,7 +1097,7 @@ struct grub_efi_boot_services
grub_efi_status_t
(*create_event) (grub_efi_uint32_t type,
grub_efi_tpl_t notify_tpl,
- void (*notify_function) (grub_efi_event_t event,
+ void __attribute__((ms_abi)) (*notify_function) (grub_efi_event_t event,
void *context),
void *notify_context,
grub_efi_event_t *event);
@@ -1605,6 +1615,7 @@ struct grub_efi_simple_network_mode
grub_uint8_t media_present_supported;
grub_uint8_t media_present;
};
+typedef struct grub_efi_simple_network_mode grub_efi_simple_network_mode_t;

enum
{
@@ -1685,6 +1696,102 @@ struct grub_efi_block_io
};
typedef struct grub_efi_block_io grub_efi_block_io_t;

+struct grub_efi_service_binding
+{
+ grub_efi_status_t (*create_child) (struct grub_efi_service_binding *this,
+ grub_efi_handle_t *child);
+ grub_efi_status_t (*destroy_child) (struct grub_efi_service_binding *this,
+ grub_efi_handle_t child);
+};
+typedef struct grub_efi_service_binding grub_efi_service_binding_t;
+
+struct grub_efi_managed_network_config_data
+{
+ grub_efi_uint32_t received_queue_timeout_value;
+ grub_efi_uint32_t transmit_queue_timeout_value;
+ grub_efi_uint16_t protocol_type_filter;
+ grub_efi_boolean_t enable_unicast_receive;
+ grub_efi_boolean_t enable_multicast_receive;
+ grub_efi_boolean_t enable_broadcast_receive;
+ grub_efi_boolean_t enable_promiscuous_receive;
+ grub_efi_boolean_t flush_queues_on_reset;
+ grub_efi_boolean_t enable_receive_timestamps;
+ grub_efi_boolean_t disable_background_polling;
+};
+typedef struct grub_efi_managed_network_config_data grub_efi_managed_network_config_data_t;
+
+struct grub_efi_managed_network_receive_data
+{
+ grub_efi_time_t timestamp;
+ grub_efi_event_t recycle_event;
+ grub_efi_uint32_t packet_length;
+ grub_efi_uint32_t header_length;
+ grub_efi_uint32_t address_length;
+ grub_efi_uint32_t data_length;
+ grub_efi_boolean_t broadcast_flag;
+ grub_efi_boolean_t multicast_flag;
+ grub_efi_boolean_t promiscuous_flag;
+ grub_efi_uint16_t protocol_type;
+ void *destination_address;
+ void *source_address;
+ void *media_header;
+ void *packet_data;
+};
+typedef struct grub_efi_managed_network_receive_data grub_efi_managed_network_receive_data_t;
+
+struct grub_efi_managed_network_fragment_data
+{
+ grub_efi_uint32_t fragment_length;
+ void *fragment_buffer;
+};
+typedef struct grub_efi_managed_network_fragment_data grub_efi_managed_network_fragment_data_t;
+
+struct grub_efi_managed_network_transmit_data
+{
+ grub_efi_mac_address_t *destination_address;
+ grub_efi_mac_address_t *source_address;
+ grub_efi_uint16_t protocol_type;
+ grub_efi_uint32_t data_length;
+ grub_efi_uint16_t header_length;
+ grub_efi_uint16_t fragmentcount;
+ grub_efi_managed_network_fragment_data_t fragment_table[1];
+};
+typedef struct grub_efi_managed_network_transmit_data grub_efi_managed_network_transmit_data_t;
+
+struct grub_efi_managed_network_completion_token {
+ grub_efi_event_t event;
+ grub_efi_status_t status;
+ union {
+ grub_efi_managed_network_receive_data_t *rx_data;
+ grub_efi_managed_network_transmit_data_t *tx_data;
+ } packet;
+};
+typedef struct grub_efi_managed_network_completion_token grub_efi_managed_network_completion_token_t;
+
+struct grub_efi_managed_network
+{
+ grub_efi_status_t (*get_mode_data) (struct grub_efi_managed_network *this,
+ grub_efi_managed_network_config_data_t *mnp_config_data,
+ grub_efi_simple_network_mode_t *snp_mode_data);
+ grub_efi_status_t (*configure) (struct grub_efi_managed_network *this,
+ grub_efi_managed_network_config_data_t *mnp_config_data);
+ grub_efi_status_t (*mcast_ip_to_mac) (struct grub_efi_managed_network *this,
+ grub_efi_boolean_t ipv6_flag,
+ grub_efi_ip_address_t *ip_address,
+ grub_efi_mac_address_t *mac_address);
+ grub_efi_status_t (*groups) (struct grub_efi_managed_network *this,
+ grub_efi_boolean_t join_flag,
+ grub_efi_mac_address_t *mac_address);
+ grub_efi_status_t (*transmit) (struct grub_efi_managed_network *this,
+ grub_efi_managed_network_completion_token_t *token);
+ grub_efi_status_t (*receive) (struct grub_efi_managed_network *this,
+ grub_efi_managed_network_completion_token_t *token);
+ grub_efi_status_t (*cancel) (struct grub_efi_managed_network *this,
+ grub_efi_managed_network_completion_token_t *token);
+ grub_efi_status_t (*poll) (struct grub_efi_managed_network *this);
+};
+typedef struct grub_efi_managed_network grub_efi_managed_network_t;
+
#if (GRUB_TARGET_SIZEOF_VOID_P == 4) || defined (__ia64__) \
|| defined (__aarch64__) || defined (__MINGW64__) || defined (__CYGWIN__)

diff --git a/include/grub/net.h b/include/grub/net.h
index 1096b24..0cd4deb 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -140,6 +140,8 @@ struct grub_net_card
struct grub_efi_simple_network *efi_net;
grub_efi_handle_t efi_handle;
grub_size_t last_pkt_size;
+ grub_efi_managed_network_t *efi_mnp;
+ grub_efi_handle_t efi_mnp_handle;
};
#endif
void *data;
--
2.6.2
Loading...