diff options
author | Michael Brown <mcb30@ipxe.org> | 2022-12-13 14:45:44 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2022-12-14 11:51:52 +0000 |
commit | d879c8e4d9f3b00c09a7d199a2681705fc5b55d0 (patch) | |
tree | 03fa45c25742b4f605d9391c776146331b040f11 /src/interface/efi/efi_snp.c | |
parent | 5e62b4bc6c7bd7c6929b3fe540fb1ba8744fd16c (diff) | |
download | ipxe-d879c8e4d9f3b00c09a7d199a2681705fc5b55d0.tar.gz |
[efi] Provide VLAN configuration protocol
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>
Diffstat (limited to 'src/interface/efi/efi_snp.c')
-rw-r--r-- | src/interface/efi/efi_snp.c | 179 |
1 files changed, 177 insertions, 2 deletions
diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index 6649eb1b0..088a3fdbd 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -1490,6 +1490,164 @@ static EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL efi_snp_device_nii = { /****************************************************************************** * + * 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 ) |