Blob Blame History Raw
From c52ae40570c3bfbcca22d2195f5e6b31009d8a3f Mon Sep 17 00:00:00 2001
From: Andrei Borzenkov <arvidjaar@gmail.com>
Date: Thu, 7 May 2015 20:37:17 +0300
Subject: [PATCH 1/2] efinet: skip virtual IPv4 and IPv6 devices when
 enumerating cards

EDK2 PXE driver creates two child devices - IPv4 and IPv6 - with
bound SNP instance. This means we get three cards for every physical
adapter when enumerating. Not only is this confusing, this may result
in grub ignoring packets that come in via the "wrong" card.

Example of device hierarchy is

 Ctrl[91] PciRoot(0x0)/Pci(0x3,0x0)
   Ctrl[95] PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)
     Ctrl[B4] PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0)
     Ctrl[BC] PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv6(0000:0000:0000:0000:0000:0000:0000:0000)

Skip PXE created virtual devices when enumerating cards. Make sure to
find real card when applying initial autoconfiguration during PXE boot,
this information is associated with one of child devices.
---
 grub-core/net/drivers/efi/efinet.c | 51 +++++++++++++++++++++++++++++++++++++-
 1 file changed, 50 insertions(+), 1 deletion(-)

diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
index f171f20..2b53e9e 100644
--- a/grub-core/net/drivers/efi/efinet.c
+++ b/grub-core/net/drivers/efi/efinet.c
@@ -174,6 +174,29 @@ grub_efinet_findcards (void)
     {
       grub_efi_simple_network_t *net;
       struct grub_net_card *card;
+      grub_efi_device_path_t *dp, *parent = NULL, *child = NULL;
+
+      /* EDK2 UEFI PXE driver creates IPv4 and IPv6 messaging devices as
+	 children of main MAC messaging device. We only need one device with
+	 bound SNP per physical card, otherwise they compete with each other
+	 when polling for incoming packets.
+       */
+      dp = grub_efi_get_device_path (*handle);
+      if (!dp)
+	continue;
+      for (; ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp); dp = GRUB_EFI_NEXT_DEVICE_PATH (dp))
+	{
+	  parent = child;
+	  child = dp;
+	}
+      if (child
+	  && GRUB_EFI_DEVICE_PATH_TYPE (child) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+	  && (GRUB_EFI_DEVICE_PATH_SUBTYPE (child) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
+	      || GRUB_EFI_DEVICE_PATH_SUBTYPE (child) == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)
+	  && parent
+	  && GRUB_EFI_DEVICE_PATH_TYPE (parent) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+	  && GRUB_EFI_DEVICE_PATH_SUBTYPE (parent) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)
+	continue;
 
       net = grub_efi_open_protocol (*handle, &net_io_guid,
 				    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
@@ -251,7 +274,33 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
     if (! cdp)
       continue;
     if (grub_efi_compare_device_paths (dp, cdp) != 0)
-      continue;
+      {
+	grub_efi_device_path_t *ldp, *dup_dp, *dup_ldp;
+	int match;
+
+	/* EDK2 UEFI PXE driver creates pseudo devices with type IPv4/IPv6
+	   as children of Ethernet card and binds PXE and Load File protocols
+	   to it. Loaded Image Device Path protocol will point to these pseudo
+	   devices. We skip them when enumerating cards, so here we need to
+	   find matching MAC device.
+         */
+	ldp = grub_efi_find_last_device_path (dp);
+	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))
+	  continue;
+	dup_dp = grub_efi_duplicate_device_path (dp);
+	if (!dup_dp)
+	  continue;
+	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);
+	match = grub_efi_compare_device_paths (dup_dp, cdp) == 0;
+	grub_free (dup_dp);
+	if (!match)
+	  continue;
+      }
     pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
 				  GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
     if (! pxe)
-- 
1.9.3