nalika / rpms / grub2

Forked from rpms/grub2 2 years ago
Clone

Blame SOURCES/0162-efinet-skip-virtual-IPv4-and-IPv6-devices-when-enume.patch

28f7f8
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
ecb9bb
From: Andrei Borzenkov <arvidjaar@gmail.com>
ecb9bb
Date: Thu, 7 May 2015 20:37:17 +0300
28f7f8
Subject: [PATCH] efinet: skip virtual IPv4 and IPv6 devices when enumerating
28f7f8
 cards
ecb9bb
ecb9bb
EDK2 PXE driver creates two child devices - IPv4 and IPv6 - with
ecb9bb
bound SNP instance. This means we get three cards for every physical
ecb9bb
adapter when enumerating. Not only is this confusing, this may result
ecb9bb
in grub ignoring packets that come in via the "wrong" card.
ecb9bb
ecb9bb
Example of device hierarchy is
ecb9bb
ecb9bb
 Ctrl[91] PciRoot(0x0)/Pci(0x3,0x0)
ecb9bb
   Ctrl[95] PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)
ecb9bb
     Ctrl[B4] PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv4(0.0.0.0)
ecb9bb
     Ctrl[BC] PciRoot(0x0)/Pci(0x3,0x0)/MAC(525400123456,0x1)/IPv6(0000:0000:0000:0000:0000:0000:0000:0000)
ecb9bb
ecb9bb
Skip PXE created virtual devices when enumerating cards. Make sure to
ecb9bb
find real card when applying initial autoconfiguration during PXE boot,
ecb9bb
this information is associated with one of child devices.
ecb9bb
---
ecb9bb
 grub-core/net/drivers/efi/efinet.c | 51 +++++++++++++++++++++++++++++++++++++-
ecb9bb
 1 file changed, 50 insertions(+), 1 deletion(-)
ecb9bb
ecb9bb
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
28f7f8
index 78df215be12..caa7b50228b 100644
ecb9bb
--- a/grub-core/net/drivers/efi/efinet.c
ecb9bb
+++ b/grub-core/net/drivers/efi/efinet.c
ecb9bb
@@ -174,6 +174,29 @@ grub_efinet_findcards (void)
ecb9bb
     {
ecb9bb
       grub_efi_simple_network_t *net;
ecb9bb
       struct grub_net_card *card;
ecb9bb
+      grub_efi_device_path_t *dp, *parent = NULL, *child = NULL;
ecb9bb
+
ecb9bb
+      /* EDK2 UEFI PXE driver creates IPv4 and IPv6 messaging devices as
ecb9bb
+	 children of main MAC messaging device. We only need one device with
ecb9bb
+	 bound SNP per physical card, otherwise they compete with each other
ecb9bb
+	 when polling for incoming packets.
ecb9bb
+       */
ecb9bb
+      dp = grub_efi_get_device_path (*handle);
ecb9bb
+      if (!dp)
ecb9bb
+	continue;
ecb9bb
+      for (; ! GRUB_EFI_END_ENTIRE_DEVICE_PATH (dp); dp = GRUB_EFI_NEXT_DEVICE_PATH (dp))
ecb9bb
+	{
ecb9bb
+	  parent = child;
ecb9bb
+	  child = dp;
ecb9bb
+	}
ecb9bb
+      if (child
ecb9bb
+	  && GRUB_EFI_DEVICE_PATH_TYPE (child) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
ecb9bb
+	  && (GRUB_EFI_DEVICE_PATH_SUBTYPE (child) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
ecb9bb
+	      || GRUB_EFI_DEVICE_PATH_SUBTYPE (child) == GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE)
ecb9bb
+	  && parent
ecb9bb
+	  && GRUB_EFI_DEVICE_PATH_TYPE (parent) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
ecb9bb
+	  && GRUB_EFI_DEVICE_PATH_SUBTYPE (parent) == GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)
ecb9bb
+	continue;
ecb9bb
 
ecb9bb
       net = grub_efi_open_protocol (*handle, &net_io_guid,
ecb9bb
 				    GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
ecb9bb
@@ -252,7 +275,33 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
ecb9bb
     if (! cdp)
ecb9bb
       continue;
ecb9bb
     if (grub_efi_compare_device_paths (dp, cdp) != 0)
ecb9bb
-      continue;
ecb9bb
+      {
ecb9bb
+	grub_efi_device_path_t *ldp, *dup_dp, *dup_ldp;
ecb9bb
+	int match;
ecb9bb
+
ecb9bb
+	/* EDK2 UEFI PXE driver creates pseudo devices with type IPv4/IPv6
ecb9bb
+	   as children of Ethernet card and binds PXE and Load File protocols
ecb9bb
+	   to it. Loaded Image Device Path protocol will point to these pseudo
ecb9bb
+	   devices. We skip them when enumerating cards, so here we need to
ecb9bb
+	   find matching MAC device.
ecb9bb
+         */
ecb9bb
+	ldp = grub_efi_find_last_device_path (dp);
ecb9bb
+	if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
ecb9bb
+	    || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
ecb9bb
+		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
ecb9bb
+	  continue;
ecb9bb
+	dup_dp = grub_efi_duplicate_device_path (dp);
ecb9bb
+	if (!dup_dp)
ecb9bb
+	  continue;
ecb9bb
+	dup_ldp = grub_efi_find_last_device_path (dup_dp);
ecb9bb
+	dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
ecb9bb
+	dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
ecb9bb
+	dup_ldp->length = sizeof (*dup_ldp);
ecb9bb
+	match = grub_efi_compare_device_paths (dup_dp, cdp) == 0;
ecb9bb
+	grub_free (dup_dp);
ecb9bb
+	if (!match)
ecb9bb
+	  continue;
ecb9bb
+      }
ecb9bb
     pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
ecb9bb
 				  GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
ecb9bb
     if (! pxe)