You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
3.9 KiB
94 lines
3.9 KiB
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
|
From: Andrei Borzenkov <arvidjaar@gmail.com> |
|
Date: Thu, 7 May 2015 20:37:17 +0300 |
|
Subject: [PATCH] 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 78df215be12..caa7b50228b 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); |
|
@@ -252,7 +275,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)
|
|
|