]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Provide VLAN configuration protocol 838/head
authorMichael Brown <mcb30@ipxe.org>
Tue, 13 Dec 2022 14:45:44 +0000 (14:45 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 14 Dec 2022 11:51:52 +0000 (11:51 +0000)
UEFI implements VLAN support within the Managed Network Protocol (MNP)
driver, which may create child VLAN devices automatically based on
stored UEFI variables.  These child devices do not themselves provide
a raw-packet interface via EFI_SIMPLE_NETWORK_PROTOCOL, and may be
consumed only via the EFI_MANAGED_NETWORK_PROTOCOL interface.

The device paths constructed for these child devices may conflict with
those for the EFI_SIMPLE_NETWORK_PROTOCOL instances that iPXE attempts
to install for its own VLAN devices.  The upshot is that creating an
iPXE VLAN device (e.g. via the "vcreate" command) will fail if the
UEFI Managed Network Protocol has already created a device for the
same VLAN tag.

Fix by providing our own EFI_VLAN_CONFIG_PROTOCOL instance on the same
device handle as EFI_SIMPLE_NETWORK_PROTOCOL.  This causes the MNP
driver to treat iPXE's device as supporting hardware VLAN offload, and
it will therefore not attempt to install its own instance of the
protocol.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/efi/efi_null.h
src/include/ipxe/efi/efi_snp.h
src/include/ipxe/vlan.h
src/interface/efi/efi_null.c
src/interface/efi/efi_snp.c
src/net/vlan.c

index 297457081817e81f1cac48266b89a94b362c08ab..d23d363499febcb8a94a94357356b1ecf627e92f 100644 (file)
@@ -19,9 +19,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/efi/Protocol/PxeBaseCode.h>
 #include <ipxe/efi/Protocol/SimpleNetwork.h>
 #include <ipxe/efi/Protocol/UsbIo.h>
+#include <ipxe/efi/Protocol/VlanConfig.h>
 
 extern void efi_nullify_snp ( EFI_SIMPLE_NETWORK_PROTOCOL *snp );
 extern void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii );
+extern void efi_nullify_vlan ( EFI_VLAN_CONFIG_PROTOCOL *vcfg );
 extern void efi_nullify_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *name2 );
 extern void efi_nullify_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file );
 extern void efi_nullify_hii ( EFI_HII_CONFIG_ACCESS_PROTOCOL *hii );
index c278b1d4cb05bc26eca8132a162d9ddf8ab15016..96373b57d5368e956841697856a67a5b9516cbe7 100644 (file)
@@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/efi/Protocol/HiiConfigAccess.h>
 #include <ipxe/efi/Protocol/HiiDatabase.h>
 #include <ipxe/efi/Protocol/LoadFile.h>
+#include <ipxe/efi/Protocol/VlanConfig.h>
 
 /** SNP transmit completion ring size */
 #define EFI_SNP_NUM_TX 32
@@ -51,6 +52,8 @@ struct efi_snp_device {
        struct list_head rx;
        /** The network interface identifier */
        EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii;
+       /** VLAN configuration protocol */
+       EFI_VLAN_CONFIG_PROTOCOL vcfg;
        /** Component name protocol */
        EFI_COMPONENT_NAME2_PROTOCOL name2;
        /** Load file protocol handle */
index e4baf4cf07569035d7ec5a437256876769e678a5..8bf79234b73a875115f803d3dbd877aae0b528f5 100644 (file)
@@ -74,6 +74,8 @@ vlan_tag ( struct net_device *netdev ) {
        return VLAN_TAG ( vlan_tci ( netdev ) );
 }
 
+extern struct net_device * vlan_find ( struct net_device *trunk,
+                                      unsigned int tag );
 extern int vlan_can_be_trunk ( struct net_device *trunk );
 extern int vlan_create ( struct net_device *trunk, unsigned int tag,
                         unsigned int priority );
index 29ca5b9b6c85803e62c29f58d565cf00c99f3b9f..d0f0428cc1187b7e547c8c807cc78d83876d14b2 100644 (file)
@@ -193,6 +193,48 @@ void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii ) {
        nii->Id = ( ( intptr_t ) &efi_null_undi );
 }
 
+/******************************************************************************
+ *
+ * VLAN configuration protocol
+ *
+ ******************************************************************************
+ */
+
+static EFI_STATUS EFIAPI
+efi_null_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused,
+                   UINT16 tag __unused, UINT8 priority __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused,
+                    UINT16 *filter __unused, UINT16 *count __unused,
+                    EFI_VLAN_FIND_DATA **entries __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_STATUS EFIAPI
+efi_null_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused,
+                      UINT16 tag __unused ) {
+       return EFI_UNSUPPORTED;
+}
+
+static EFI_VLAN_CONFIG_PROTOCOL efi_null_vlan = {
+       .Set            = efi_null_vlan_set,
+       .Find           = efi_null_vlan_find,
+       .Remove         = efi_null_vlan_remove,
+};
+
+/**
+ * Nullify VLAN configuration interface
+ *
+ * @v vcfg             VLAN configuration protocol
+ */
+void efi_nullify_vlan ( EFI_VLAN_CONFIG_PROTOCOL *vcfg ) {
+
+       memcpy ( vcfg, &efi_null_vlan, sizeof ( *vcfg ) );
+}
+
 /******************************************************************************
  *
  * Component name protocol
index 6649eb1b03039028c8c03b1e9b3e3f2c949e7625..088a3fdbda485ed705a1cd84100f08f56928dd6b 100644 (file)
@@ -1488,6 +1488,164 @@ static EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL efi_snp_device_nii = {
        .Ipv6Supported  = TRUE, /* This is a raw packet interface, FFS! */
 };
 
+/******************************************************************************
+ *
+ * VLAN configuration protocol
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Create or modify VLAN device
+ *
+ * @v vcfg             VLAN configuration protocol
+ * @v tag              VLAN tag
+ * @v priority         Default VLAN priority
+ * @ret efirc          EFI status code
+ */
+static EFI_STATUS EFIAPI efi_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg,
+                                       UINT16 tag, UINT8 priority ) {
+       struct efi_snp_device *snpdev =
+               container_of ( vcfg, struct efi_snp_device, vcfg );
+       struct net_device *trunk = snpdev->netdev;
+       struct efi_saved_tpl tpl;
+       int rc;
+
+       /* Raise TPL */
+       efi_raise_tpl ( &tpl );
+
+       /* Create or modify VLAN device */
+       if ( ( rc = vlan_create ( trunk, tag, priority ) ) != 0 ) {
+               DBGC ( snpdev, "SNPDEV %p could not create VLAN tag %d: %s\n",
+                      snpdev, tag, strerror ( rc ) );
+               goto err_create;
+       }
+       DBGC ( snpdev, "SNPDEV %p created VLAN tag %d priority %d\n",
+              snpdev, tag, priority );
+
+ err_create:
+       efi_restore_tpl ( &tpl );
+       return EFIRC ( rc );
+}
+
+/**
+ * Find VLAN device(s)
+ *
+ * @v vcfg             VLAN configuration protocol
+ * @v filter           VLAN tag, or NULL to find all VLANs
+ * @v count            Number of VLANs
+ * @v entries          List of VLANs
+ * @ret efirc          EFI status code
+ */
+static EFI_STATUS EFIAPI efi_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg,
+                                        UINT16 *filter, UINT16 *count,
+                                        EFI_VLAN_FIND_DATA **entries ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       struct efi_snp_device *snpdev =
+               container_of ( vcfg, struct efi_snp_device, vcfg );
+       struct net_device *trunk = snpdev->netdev;
+       struct net_device *vlan;
+       struct efi_saved_tpl tpl;
+       EFI_VLAN_FIND_DATA *entry;
+       VOID *buffer;
+       unsigned int tag;
+       unsigned int tci;
+       size_t len;
+       EFI_STATUS efirc;
+       int rc;
+
+       /* Raise TPL */
+       efi_raise_tpl ( &tpl );
+
+       /* Count number of matching VLANs */
+       *count = 0;
+       for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) {
+               if ( filter && ( tag != *filter ) )
+                       continue;
+               if ( ! ( vlan = vlan_find ( trunk, tag ) ) )
+                       continue;
+               (*count)++;
+       }
+
+       /* Allocate buffer to hold results */
+       len = ( (*count) * sizeof ( *entry ) );
+       if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, len,
+                                         &buffer ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               goto err_alloc;
+       }
+
+       /* Fill in buffer */
+       *entries = buffer;
+       entry = *entries;
+       for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) {
+               if ( filter && ( tag != *filter ) )
+                       continue;
+               if ( ! ( vlan = vlan_find ( trunk, tag ) ) )
+                       continue;
+               tci = vlan_tci ( vlan );
+               entry->VlanId = VLAN_TAG ( tci );
+               entry->Priority = VLAN_PRIORITY ( tci );
+               assert ( entry->VlanId == tag );
+               entry++;
+       }
+       assert ( entry == &(*entries)[*count] );
+
+       /* Success */
+       rc = 0;
+
+ err_alloc:
+       efi_restore_tpl ( &tpl );
+       return EFIRC ( rc );
+}
+
+/**
+ * Remove VLAN device
+ *
+ * @v vcfg             VLAN configuration protocol
+ * @v tag              VLAN tag
+ * @ret efirc          EFI status code
+ */
+static EFI_STATUS EFIAPI efi_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg,
+                                          UINT16 tag ) {
+       struct efi_snp_device *snpdev =
+               container_of ( vcfg, struct efi_snp_device, vcfg );
+       struct net_device *trunk = snpdev->netdev;
+       struct net_device *vlan;
+       struct efi_saved_tpl tpl;
+       int rc;
+
+       /* Raise TPL */
+       efi_raise_tpl ( &tpl );
+
+       /* Identify VLAN device */
+       vlan = vlan_find ( trunk, tag );
+       if ( ! vlan ) {
+               DBGC ( snpdev, "SNPDEV %p could not find VLAN tag %d\n",
+                      snpdev, tag );
+               rc = -ENOENT;
+               goto err_find;
+       }
+
+       /* Remove VLAN device */
+       vlan_destroy ( vlan );
+       DBGC ( snpdev, "SNPDEV %p removed VLAN tag %d\n", snpdev, tag );
+
+       /* Success */
+       rc = 0;
+
+ err_find:
+       efi_restore_tpl ( &tpl );
+       return EFIRC ( rc );
+}
+
+/** VLAN configuration protocol */
+static EFI_VLAN_CONFIG_PROTOCOL efi_vlan = {
+       .Set            = efi_vlan_set,
+       .Find           = efi_vlan_find,
+       .Remove         = efi_vlan_remove,
+};
+
 /******************************************************************************
  *
  * Component name protocol
@@ -1627,6 +1785,8 @@ static int efi_snp_probe ( struct net_device *netdev ) {
        struct efi_snp_device *snpdev;
        unsigned int ifcnt;
        void *interface;
+       unsigned int tci;
+       char vlan_name[ 12 /* ", VLAN xxxx" + NUL */ ];
        int leak = 0;
        EFI_STATUS efirc;
        int rc;
@@ -1687,17 +1847,27 @@ static int efi_snp_probe ( struct net_device *netdev ) {
        efi_snp_undi.Fudge -= efi_undi_checksum ( &efi_snp_undi,
                                                  sizeof ( efi_snp_undi ) );
 
+       /* Populate the VLAN configuration protocol */
+       memcpy ( &snpdev->vcfg, &efi_vlan, sizeof ( snpdev->vcfg ) );
+
        /* Populate the component name structure */
        efi_snprintf ( snpdev->driver_name,
                       ( sizeof ( snpdev->driver_name ) /
                         sizeof ( snpdev->driver_name[0] ) ),
                       "%s %s", product_short_name, netdev->dev->driver_name );
+       tci = vlan_tci ( netdev );
+       if ( tci ) {
+               snprintf ( vlan_name, sizeof ( vlan_name ), ", VLAN %d",
+                          VLAN_TAG ( tci ) );
+       } else {
+               vlan_name[0] = '\0';
+       }
        efi_snprintf ( snpdev->controller_name,
                       ( sizeof ( snpdev->controller_name ) /
                         sizeof ( snpdev->controller_name[0] ) ),
-                      "%s %s (%s, %s)", product_short_name,
+                      "%s %s (%s, %s%s)", product_short_name,
                       netdev->dev->driver_name, netdev->dev->name,
-                      netdev_addr ( netdev ) );
+                      netdev_addr ( netdev ), vlan_name );
        snpdev->name2.GetDriverName = efi_snp_get_driver_name;
        snpdev->name2.GetControllerName = efi_snp_get_controller_name;
        snpdev->name2.SupportedLanguages = "en";
@@ -1725,6 +1895,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
                        &efi_device_path_protocol_guid, snpdev->path,
                        &efi_nii_protocol_guid, &snpdev->nii,
                        &efi_nii31_protocol_guid, &snpdev->nii,
+                       &efi_vlan_config_protocol_guid, &snpdev->vcfg,
                        &efi_component_name2_protocol_guid, &snpdev->name2,
                        &efi_load_file_protocol_guid, &snpdev->load_file,
                        NULL ) ) != 0 ) {
@@ -1811,6 +1982,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
                        &efi_device_path_protocol_guid, snpdev->path,
                        &efi_nii_protocol_guid, &snpdev->nii,
                        &efi_nii31_protocol_guid, &snpdev->nii,
+                       &efi_vlan_config_protocol_guid, &snpdev->vcfg,
                        &efi_component_name2_protocol_guid, &snpdev->name2,
                        &efi_load_file_protocol_guid, &snpdev->load_file,
                        NULL ) ) != 0 ) {
@@ -1820,6 +1992,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
        }
        efi_nullify_snp ( &snpdev->snp );
        efi_nullify_nii ( &snpdev->nii );
+       efi_nullify_vlan ( &snpdev->vcfg );
        efi_nullify_name2 ( &snpdev->name2 );
        efi_nullify_load_file ( &snpdev->load_file );
  err_install_protocol_interface:
@@ -1899,6 +2072,7 @@ static void efi_snp_remove ( struct net_device *netdev ) {
                        &efi_device_path_protocol_guid, snpdev->path,
                        &efi_nii_protocol_guid, &snpdev->nii,
                        &efi_nii31_protocol_guid, &snpdev->nii,
+                       &efi_vlan_config_protocol_guid, &snpdev->vcfg,
                        &efi_component_name2_protocol_guid, &snpdev->name2,
                        &efi_load_file_protocol_guid, &snpdev->load_file,
                        NULL ) ) != 0 ) ) {
@@ -1908,6 +2082,7 @@ static void efi_snp_remove ( struct net_device *netdev ) {
        }
        efi_nullify_snp ( &snpdev->snp );
        efi_nullify_nii ( &snpdev->nii );
+       efi_nullify_vlan ( &snpdev->vcfg );
        efi_nullify_name2 ( &snpdev->name2 );
        efi_nullify_load_file ( &snpdev->load_file );
        if ( ! leak )
index fe4488614063cc9121bee82ed9862c9d04cc4d8f..81ec623f29adaebafe8aabc76a4bcc1338fdb560 100644 (file)
@@ -199,8 +199,7 @@ static void vlan_sync ( struct net_device *netdev ) {
  * @v tag              VLAN tag
  * @ret netdev         VLAN device, if any
  */
-static struct net_device * vlan_find ( struct net_device *trunk,
-                                      unsigned int tag ) {
+struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) {
        struct net_device *netdev;
        struct vlan_device *vlan;