summaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rwxr-xr-xsrc/drivers/infiniband/golan.c2
-rw-r--r--src/drivers/net/efi/mnp.c56
-rw-r--r--src/drivers/net/efi/mnpnet.c563
-rw-r--r--src/drivers/net/efi/nii.c7
-rw-r--r--src/drivers/net/efi/snp.c54
-rw-r--r--src/drivers/net/efi/snpnet.c70
-rw-r--r--src/drivers/net/efi/snpnet.h1
-rw-r--r--src/drivers/net/efi/snponly.c136
8 files changed, 794 insertions, 95 deletions
diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c
index ce02a867..68a7c4f5 100755
--- a/src/drivers/infiniband/golan.c
+++ b/src/drivers/infiniband/golan.c
@@ -2502,7 +2502,7 @@ static mlx_status shomron_fill_eth_send_wqe ( struct ib_device *ibdev,
}
#define SHOMRON_GENERATE_CQE 0x3
-#define SHOMRON_INLINE_HEADERS_SIZE 18
+#define SHOMRON_INLINE_HEADERS_SIZE ETH_HLEN
#define SHOMRON_INLINE_HEADERS_OFFSET 32
MLX_FILL_2 ( &eth_wqe->ctrl, 0, opcode, FLEXBOOT_NODNIC_OPCODE_SEND,
wqe_index, wqe_index & 0xFFFF);
diff --git a/src/drivers/net/efi/mnp.c b/src/drivers/net/efi/mnp.c
new file mode 100644
index 00000000..33218fb1
--- /dev/null
+++ b/src/drivers/net/efi/mnp.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * MNP driver
+ *
+ */
+
+#include <errno.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/mnpnet.h>
+#include "snpnet.h"
+
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+static int mnp_supported ( EFI_HANDLE device ) {
+ EFI_GUID *binding = &efi_managed_network_service_binding_protocol_guid;
+
+ return snpnet_supported ( device, binding );
+}
+
+/** EFI MNP driver */
+struct efi_driver mnp_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+ .name = "MNP",
+ .supported = mnp_supported,
+ .start = mnpnet_start,
+ .stop = mnpnet_stop,
+};
diff --git a/src/drivers/net/efi/mnpnet.c b/src/drivers/net/efi/mnpnet.c
new file mode 100644
index 00000000..eb4b129c
--- /dev/null
+++ b/src/drivers/net/efi/mnpnet.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * MNP NIC driver
+ *
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/cachedhcp.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_service.h>
+#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/mnpnet.h>
+#include <ipxe/efi/Protocol/ManagedNetwork.h>
+
+/** An MNP transmit or receive token */
+struct mnp_token {
+ /** MNP completion token */
+ EFI_MANAGED_NETWORK_COMPLETION_TOKEN token;
+ /** Token is owned by MNP */
+ int busy;
+};
+
+/** An MNP NIC */
+struct mnp_nic {
+ /** EFI device */
+ struct efi_device *efidev;
+ /** Managed network protocol */
+ EFI_MANAGED_NETWORK_PROTOCOL *mnp;
+ /** Generic device */
+ struct device dev;
+
+ /** Transmit token */
+ struct mnp_token tx;
+ /** Transmit descriptor */
+ EFI_MANAGED_NETWORK_TRANSMIT_DATA txdata;
+ /** Transmit I/O buffer */
+ struct io_buffer *txbuf;
+
+ /** Receive token */
+ struct mnp_token rx;
+};
+
+/**
+ * Transmit or receive token event
+ *
+ * @v event Event
+ * @v context Event context
+ */
+static VOID EFIAPI mnpnet_event ( EFI_EVENT event __unused, VOID *context ) {
+ struct mnp_token *token = context;
+
+ /* Sanity check */
+ assert ( token->busy );
+
+ /* Mark token as no longer owned by MNP */
+ token->busy = 0;
+}
+
+/**
+ * Transmit packet
+ *
+ * @v netdev Network device
+ * @v iobuf I/O buffer
+ * @ret rc Return status code
+ */
+static int mnpnet_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf ) {
+ struct mnp_nic *mnp = netdev->priv;
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Do nothing if shutdown is in progress */
+ if ( efi_shutdown_in_progress )
+ return -ECANCELED;
+
+ /* Defer the packet if there is already a transmission in progress */
+ if ( mnp->txbuf ) {
+ netdev_tx_defer ( netdev, iobuf );
+ return 0;
+ }
+
+ /* Construct transmit token */
+ mnp->txdata.DataLength =
+ ( iob_len ( iobuf ) - ll_protocol->ll_header_len );
+ mnp->txdata.HeaderLength = ll_protocol->ll_header_len;
+ mnp->txdata.FragmentCount = 1;
+ mnp->txdata.FragmentTable[0].FragmentLength = iob_len ( iobuf );
+ mnp->txdata.FragmentTable[0].FragmentBuffer = iobuf->data;
+ mnp->tx.token.Packet.TxData = &mnp->txdata;
+
+ /* Record as in use */
+ mnp->tx.busy = 1;
+
+ /* Transmit packet */
+ if ( ( efirc = mnp->mnp->Transmit ( mnp->mnp, &mnp->tx.token ) ) != 0 ){
+ rc = -EEFI ( efirc );
+ DBGC ( mnp, "MNP %s could not transmit: %s\n",
+ netdev->name, strerror ( rc ) );
+ mnp->tx.busy = 0;
+ return rc;
+ }
+
+ /* Record I/O buffer */
+ mnp->txbuf = iobuf;
+
+ return 0;
+}
+
+/**
+ * Refill receive token
+ *
+ * @v netdev Network device
+ */
+static void mnpnet_refill_rx ( struct net_device *netdev ) {
+ struct mnp_nic *mnp = netdev->priv;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Do nothing if receive token is still in use */
+ if ( mnp->rx.busy )
+ return;
+
+ /* Mark as in use */
+ mnp->rx.busy = 1;
+
+ /* Queue receive token */
+ if ( ( efirc = mnp->mnp->Receive ( mnp->mnp, &mnp->rx.token ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( mnp, "MNP %s could not receive: %s\n",
+ netdev->name, strerror ( rc ) );
+ /* Wait for next refill */
+ mnp->rx.busy = 0;
+ return;
+ }
+}
+
+/**
+ * Poll for completed packets
+ *
+ * @v netdev Network device
+ */
+static void mnpnet_poll_tx ( struct net_device *netdev ) {
+ struct mnp_nic *mnp = netdev->priv;
+ struct io_buffer *iobuf;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Do nothing if transmit token is still in use */
+ if ( mnp->tx.busy )
+ return;
+
+ /* Do nothing unless we have a completion */
+ if ( ! mnp->txbuf )
+ return;
+
+ /* Get completion status */
+ efirc = mnp->tx.token.Status;
+ rc = ( efirc ? -EEFI ( efirc ) : 0 );
+
+ /* Complete transmission */
+ iobuf = mnp->txbuf;
+ mnp->txbuf = NULL;
+ netdev_tx_complete_err ( netdev, iobuf, rc );
+}
+
+/**
+ * Poll for received packets
+ *
+ * @v netdev Network device
+ */
+static void mnpnet_poll_rx ( struct net_device *netdev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct mnp_nic *mnp = netdev->priv;
+ EFI_MANAGED_NETWORK_RECEIVE_DATA *rxdata;
+ struct io_buffer *iobuf;
+ size_t len;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Do nothing unless we have a completion */
+ if ( mnp->rx.busy )
+ return;
+ rxdata = mnp->rx.token.Packet.RxData;
+
+ /* Get completion status */
+ if ( ( efirc = mnp->rx.token.Status ) != 0 ) {
+ rc = -EEFI ( efirc );
+ netdev_rx_err ( netdev, NULL, rc );
+ goto recycle;
+ }
+
+ /* Allocate and fill I/O buffer */
+ len = rxdata->PacketLength;
+ iobuf = alloc_iob ( len );
+ if ( ! iobuf ) {
+ netdev_rx_err ( netdev, NULL, -ENOMEM );
+ goto recycle;
+ }
+ memcpy ( iob_put ( iobuf, len ), rxdata->MediaHeader, len );
+
+ /* Hand off to network stack */
+ netdev_rx ( netdev, iobuf );
+
+ recycle:
+ /* Recycle token */
+ bs->SignalEvent ( rxdata->RecycleEvent );
+}
+
+/**
+ * Poll for completed packets
+ *
+ * @v netdev Network device
+ */
+static void mnpnet_poll ( struct net_device *netdev ) {
+ struct mnp_nic *mnp = netdev->priv;
+
+ /* Do nothing if shutdown is in progress */
+ if ( efi_shutdown_in_progress )
+ return;
+
+ /* Poll interface */
+ mnp->mnp->Poll ( mnp->mnp );
+
+ /* Process any transmit completions */
+ mnpnet_poll_tx ( netdev );
+
+ /* Process any receive completions */
+ mnpnet_poll_rx ( netdev );
+
+ /* Refill receive token */
+ mnpnet_refill_rx ( netdev );
+}
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int mnpnet_open ( struct net_device *netdev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ static EFI_MANAGED_NETWORK_CONFIG_DATA config = {
+ .EnableUnicastReceive = TRUE,
+ .EnableMulticastReceive = TRUE,
+ .EnableBroadcastReceive = TRUE,
+ .EnablePromiscuousReceive = TRUE,
+ .FlushQueuesOnReset = TRUE,
+ .DisableBackgroundPolling = TRUE,
+ };
+ struct mnp_nic *mnp = netdev->priv;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Create transmit event */
+ if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+ mnpnet_event, &mnp->tx,
+ &mnp->tx.token.Event ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( mnp, "MNP %s could not create TX event: %s\n",
+ netdev->name, strerror ( rc ) );
+ goto err_tx_event;
+ }
+
+ /* Create receive event */
+ if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+ mnpnet_event, &mnp->rx,
+ &mnp->rx.token.Event ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( mnp, "MNP %s could not create RX event: %s\n",
+ netdev->name, strerror ( rc ) );
+ goto err_rx_event;
+ }
+
+ /* Configure MNP */
+ if ( ( efirc = mnp->mnp->Configure ( mnp->mnp, &config ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( mnp, "MNP %s could not configure: %s\n",
+ netdev->name, strerror ( rc ) );
+ goto err_configure;
+ }
+
+ /* Refill receive token */
+ mnpnet_refill_rx ( netdev );
+
+ return 0;
+
+ mnp->mnp->Configure ( mnp->mnp, NULL );
+ err_configure:
+ bs->CloseEvent ( mnp->rx.token.Event );
+ err_rx_event:
+ bs->CloseEvent ( mnp->tx.token.Event );
+ err_tx_event:
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void mnpnet_close ( struct net_device *netdev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct mnp_nic *mnp = netdev->priv;
+
+ /* Reset MNP (unless whole system shutdown is in progress) */
+ if ( ! efi_shutdown_in_progress )
+ mnp->mnp->Configure ( mnp->mnp, NULL );
+
+ /* Close events */
+ bs->CloseEvent ( mnp->rx.token.Event );
+ bs->CloseEvent ( mnp->tx.token.Event );
+
+ /* Reset tokens */
+ mnp->tx.busy = 0;
+ mnp->rx.busy = 0;
+
+ /* Discard any incomplete I/O buffer */
+ if ( mnp->txbuf ) {
+ netdev_tx_complete_err ( netdev, mnp->txbuf, -ECANCELED );
+ mnp->txbuf = NULL;
+ }
+}
+
+/** MNP network device operations */
+static struct net_device_operations mnpnet_operations = {
+ .open = mnpnet_open,
+ .close = mnpnet_close,
+ .transmit = mnpnet_transmit,
+ .poll = mnpnet_poll,
+};
+
+/**
+ * Attach driver to device
+ *
+ * @v efidev EFI device
+ * @ret rc Return status code
+ */
+int mnpnet_start ( struct efi_device *efidev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_HANDLE device = efidev->device;
+ EFI_GUID *binding = &efi_managed_network_service_binding_protocol_guid;
+ EFI_SIMPLE_NETWORK_MODE mode;
+ union {
+ EFI_MANAGED_NETWORK_PROTOCOL *mnp;
+ void *interface;
+ } u;
+ struct net_device *netdev;
+ struct mnp_nic *mnp;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Allocate and initalise structure */
+ netdev = alloc_etherdev ( sizeof ( *mnp ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &mnpnet_operations );
+ mnp = netdev->priv;
+ mnp->efidev = efidev;
+ efidev_set_drvdata ( efidev, netdev );
+
+ /* Populate underlying device information */
+ efi_device_info ( device, "MNP", &mnp->dev );
+ mnp->dev.driver_name = "MNP";
+ mnp->dev.parent = &efidev->dev;
+ list_add ( &mnp->dev.siblings, &efidev->dev.children );
+ INIT_LIST_HEAD ( &mnp->dev.children );
+ netdev->dev = &mnp->dev;
+
+ /* Create MNP child */
+ if ( ( rc = efi_service_add ( device, binding,
+ &efidev->child ) ) != 0 ) {
+ DBGC ( mnp, "MNP %s could not create child: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_service;
+ }
+
+ /* Open MNP protocol */
+ if ( ( efirc = bs->OpenProtocol ( efidev->child,
+ &efi_managed_network_protocol_guid,
+ &u.interface, efi_image_handle,
+ efidev->child,
+ ( EFI_OPEN_PROTOCOL_BY_DRIVER |
+ EFI_OPEN_PROTOCOL_EXCLUSIVE )))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( mnp, "MNP %s could not open MNP protocol: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_open;
+ }
+ mnp->mnp = u.mnp;
+
+ /* Get configuration */
+ efirc = mnp->mnp->GetModeData ( mnp->mnp, NULL, &mode );
+ if ( ( efirc != 0 ) && ( efirc != EFI_NOT_STARTED ) ) {
+ rc = -EEFI ( efirc );
+ DBGC ( mnp, "MNP %s could not get mode data: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_mode;
+ }
+
+ /* Populate network device parameters */
+ if ( mode.HwAddressSize != netdev->ll_protocol->hw_addr_len ) {
+ DBGC ( device, "MNP %s has invalid hardware address length "
+ "%d\n", efi_handle_name ( device ), mode.HwAddressSize );
+ rc = -ENOTSUP;
+ goto err_hw_addr_len;
+ }
+ memcpy ( netdev->hw_addr, &mode.PermanentAddress,
+ netdev->ll_protocol->hw_addr_len );
+ if ( mode.HwAddressSize != netdev->ll_protocol->ll_addr_len ) {
+ DBGC ( device, "MNP %s has invalid link-layer address length "
+ "%d\n", efi_handle_name ( device ), mode.HwAddressSize );
+ rc = -ENOTSUP;
+ goto err_ll_addr_len;
+ }
+ memcpy ( netdev->ll_addr, &mode.CurrentAddress,
+ netdev->ll_protocol->ll_addr_len );
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register;
+ DBGC ( mnp, "MNP %s registered as %s\n",
+ efi_handle_name ( device ), netdev->name );
+
+ /* Mark as link up: we don't handle link state */
+ netdev_link_up ( netdev );
+
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register:
+ err_ll_addr_len:
+ err_hw_addr_len:
+ err_mode:
+ bs->CloseProtocol ( efidev->child, &efi_managed_network_protocol_guid,
+ efi_image_handle, efidev->child );
+ err_open:
+ efi_service_del ( device, binding, efidev->child );
+ err_service:
+ list_del ( &mnp->dev.siblings );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Detach driver from device
+ *
+ * @v efidev EFI device
+ */
+void mnpnet_stop ( struct efi_device *efidev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_GUID *binding = &efi_managed_network_service_binding_protocol_guid;
+ struct net_device *netdev = efidev_get_drvdata ( efidev );
+ struct mnp_nic *mnp = netdev->priv;
+
+ /* Unregister network device */
+ unregister_netdev ( netdev );
+
+ /* Close MNP protocol */
+ bs->CloseProtocol ( efidev->child, &efi_managed_network_protocol_guid,
+ efi_image_handle, efidev->child );
+
+ /* Remove MNP child (unless whole system shutdown is in progress) */
+ if ( ! efi_shutdown_in_progress )
+ efi_service_del ( efidev->device, binding, efidev->child );
+
+ /* Free network device */
+ list_del ( &mnp->dev.siblings );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/**
+ * Create temporary MNP network device
+ *
+ * @v handle MNP service binding handle
+ * @v netdev Network device to fill in
+ * @ret rc Return status code
+ */
+int mnptemp_create ( EFI_HANDLE handle, struct net_device **netdev ) {
+ struct efi_device *efidev;
+ int rc;
+
+ /* Create temporary EFI device */
+ efidev = efidev_alloc ( handle );
+ if ( ! efidev ) {
+ DBGC ( handle, "MNP %s could not create temporary device\n",
+ efi_handle_name ( handle ) );
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Start temporary network device */
+ if ( ( rc = mnpnet_start ( efidev ) ) != 0 ) {
+ DBGC ( handle, "MNP %s could not start MNP: %s\n",
+ efi_handle_name ( handle ), strerror ( rc ) );
+ goto err_start;
+ }
+
+ /* Fill in network device */
+ *netdev = efidev_get_drvdata ( efidev );
+
+ return 0;
+
+ mnpnet_stop ( efidev );
+ err_start:
+ efidev_free ( efidev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Destroy temporary MNP network device
+ *
+ * @v netdev Network device
+ */
+void mnptemp_destroy ( struct net_device *netdev ) {
+ struct mnp_nic *mnp = netdev->priv;
+ struct efi_device *efidev = mnp->efidev;
+
+ /* Recycle any cached DHCP packet */
+ cachedhcp_recycle ( netdev );
+
+ /* Stop temporary network device */
+ mnpnet_stop ( efidev );
+
+ /* Free temporary EFI device */
+ efidev_free ( efidev );
+}
diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c
index 8dd17e4b..16e9e10d 100644
--- a/src/drivers/net/efi/nii.c
+++ b/src/drivers/net/efi/nii.c
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
+#include <ipxe/if_ether.h>
#include <ipxe/umalloc.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
@@ -998,6 +999,12 @@ static int nii_transmit ( struct net_device *netdev,
return 0;
}
+ /* Pad to minimum Ethernet length, to work around underlying
+ * drivers that do not correctly handle frame padding
+ * themselves.
+ */
+ iob_pad ( iobuf, ETH_ZLEN );
+
/* Construct parameter block */
memset ( &cpb, 0, sizeof ( cpb ) );
cpb.FrameAddr = ( ( intptr_t ) iobuf->data );
diff --git a/src/drivers/net/efi/snp.c b/src/drivers/net/efi/snp.c
index 1920cdbc..cac8b38e 100644
--- a/src/drivers/net/efi/snp.c
+++ b/src/drivers/net/efi/snp.c
@@ -23,11 +23,8 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-#include <errno.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
-#include <ipxe/efi/efi_snp.h>
-#include <ipxe/efi/efi_utils.h>
#include "snpnet.h"
#include "nii.h"
@@ -41,58 +38,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Check to see if driver supports a device
*
* @v device EFI device handle
- * @v protocol Protocol GUID
- * @ret rc Return status code
- */
-static int snp_nii_supported ( EFI_HANDLE device, EFI_GUID *protocol ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- EFI_HANDLE parent;
- EFI_STATUS efirc;
- int rc;
-
- /* Check that this is not a device we are providing ourselves */
- if ( find_snpdev ( device ) != NULL ) {
- DBGCP ( device, "HANDLE %s is provided by this binary\n",
- efi_handle_name ( device ) );
- return -ENOTTY;
- }
-
- /* Test for presence of protocol */
- if ( ( efirc = bs->OpenProtocol ( device, protocol,
- NULL, efi_image_handle, device,
- EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){
- DBGCP ( device, "HANDLE %s is not a %s device\n",
- efi_handle_name ( device ),
- efi_guid_ntoa ( protocol ) );
- return -EEFI ( efirc );
- }
-
- /* Check that there are no instances of this protocol further
- * up this device path.
- */
- if ( ( rc = efi_locate_device ( device, protocol,
- &parent, 1 ) ) == 0 ) {
- DBGC2 ( device, "HANDLE %s has %s-supporting parent ",
- efi_handle_name ( device ),
- efi_guid_ntoa ( protocol ) );
- DBGC2 ( device, "%s\n", efi_handle_name ( parent ) );
- return -ENOTTY;
- }
-
- DBGC ( device, "HANDLE %s is a %s device\n",
- efi_handle_name ( device ), efi_guid_ntoa ( protocol ) );
- return 0;
-}
-
-/**
- * Check to see if driver supports a device
- *
- * @v device EFI device handle
* @ret rc Return status code
*/
static int snp_supported ( EFI_HANDLE device ) {
- return snp_nii_supported ( device, &efi_simple_network_protocol_guid );
+ return snpnet_supported ( device, &efi_simple_network_protocol_guid );
}
/**
@@ -103,7 +53,7 @@ static int snp_supported ( EFI_HANDLE device ) {
*/
static int nii_supported ( EFI_HANDLE device ) {
- return snp_nii_supported ( device, &efi_nii31_protocol_guid );
+ return snpnet_supported ( device, &efi_nii31_protocol_guid );
}
/** EFI SNP driver */
diff --git a/src/drivers/net/efi/snpnet.c b/src/drivers/net/efi/snpnet.c
index 3b09d491..6ce731d7 100644
--- a/src/drivers/net/efi/snpnet.c
+++ b/src/drivers/net/efi/snpnet.c
@@ -26,12 +26,14 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/iobuf.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
+#include <ipxe/if_ether.h>
#include <ipxe/vsprintf.h>
#include <ipxe/timer.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/efi_snp.h>
#include "snpnet.h"
/** @file
@@ -71,6 +73,19 @@ struct snp_nic {
/** Delay between each initialisation retry */
#define SNP_INITIALIZE_RETRY_DELAY_MS 10
+/** Additional padding for receive buffers
+ *
+ * Some SNP implementations seem to require additional space in the
+ * allocated receive buffers, otherwise full-length packets will be
+ * silently dropped.
+ *
+ * The EDK2 MnpDxe driver happens to allocate an additional 8 bytes of
+ * padding (4 for a VLAN tag, 4 for the Ethernet frame checksum).
+ * Match this behaviour since drivers are very likely to have been
+ * tested against MnpDxe.
+ */
+#define SNP_RX_PAD 8
+
/**
* Format SNP MAC address (for debugging)
*
@@ -174,6 +189,12 @@ static int snpnet_transmit ( struct net_device *netdev,
return 0;
}
+ /* Pad to minimum Ethernet length, to work around underlying
+ * drivers that do not correctly handle frame padding
+ * themselves.
+ */
+ iob_pad ( iobuf, ETH_ZLEN );
+
/* Transmit packet */
if ( ( efirc = snp->snp->Transmit ( snp->snp, 0, iob_len ( iobuf ),
iobuf->data, NULL, NULL,
@@ -246,7 +267,7 @@ static void snpnet_poll_rx ( struct net_device *netdev ) {
/* Allocate buffer, if required */
if ( ! snp->rxbuf ) {
- snp->rxbuf = alloc_iob ( snp->mtu );
+ snp->rxbuf = alloc_iob ( snp->mtu + SNP_RX_PAD );
if ( ! snp->rxbuf ) {
/* Leave for next poll */
break;
@@ -465,6 +486,53 @@ static struct net_device_operations snpnet_operations = {
};
/**
+ * Check to see if driver supports a device
+ *
+ * @v device EFI device handle
+ * @v protocol Protocol GUID
+ * @ret rc Return status code
+ */
+int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_HANDLE parent;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Check that this is not a device we are providing ourselves */
+ if ( find_snpdev ( device ) != NULL ) {
+ DBGCP ( device, "HANDLE %s is provided by this binary\n",
+ efi_handle_name ( device ) );
+ return -ENOTTY;
+ }
+
+ /* Test for presence of protocol */
+ if ( ( efirc = bs->OpenProtocol ( device, protocol,
+ NULL, efi_image_handle, device,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){
+ DBGCP ( device, "HANDLE %s is not a %s device\n",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( protocol ) );
+ return -EEFI ( efirc );
+ }
+
+ /* Check that there are no instances of this protocol further
+ * up this device path.
+ */
+ if ( ( rc = efi_locate_device ( device, protocol,
+ &parent, 1 ) ) == 0 ) {
+ DBGC2 ( device, "HANDLE %s has %s-supporting parent ",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( protocol ) );
+ DBGC2 ( device, "%s\n", efi_handle_name ( parent ) );
+ return -ENOTTY;
+ }
+
+ DBGC ( device, "HANDLE %s is a %s device\n",
+ efi_handle_name ( device ), efi_guid_ntoa ( protocol ) );
+ return 0;
+}
+
+/**
* Attach driver to device
*
* @v efidev EFI device
diff --git a/src/drivers/net/efi/snpnet.h b/src/drivers/net/efi/snpnet.h
index e6d31d5e..4699c789 100644
--- a/src/drivers/net/efi/snpnet.h
+++ b/src/drivers/net/efi/snpnet.h
@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
struct efi_device;
+extern int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol );
extern int snpnet_start ( struct efi_device *efidev );
extern void snpnet_stop ( struct efi_device *efidev );
diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c
index 674e0a05..2ae63fc0 100644
--- a/src/drivers/net/efi/snponly.c
+++ b/src/drivers/net/efi/snponly.c
@@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_driver.h>
#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/mnpnet.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#include <ipxe/efi/Protocol/NetworkInterfaceIdentifier.h>
#include "snpnet.h"
@@ -45,14 +46,23 @@ struct chained_protocol {
/** Protocol GUID */
EFI_GUID *protocol;
/**
- * Protocol instance installed on the loaded image's device handle
+ * Target device handle
+ *
+ * This is the uppermost handle on which the same protocol
+ * instance is installed as we find on the loaded image's
+ * device handle.
*
* We match against the protocol instance (rather than simply
* matching against the device handle itself) because some
* systems load us via a child of the underlying device, with
* a duplicate protocol installed on the child handle.
+ *
+ * We record the handle rather than the protocol instance
+ * pointer since the calls to DisconnectController() and
+ * ConnectController() may end up uninstalling and
+ * reinstalling the protocol instance.
*/
- void *interface;
+ EFI_HANDLE device;
};
/** Chainloaded SNP protocol */
@@ -65,50 +75,74 @@ static struct chained_protocol chained_nii = {
.protocol = &efi_nii31_protocol_guid,
};
+/** Chainloaded MNP protocol */
+static struct chained_protocol chained_mnp = {
+ .protocol = &efi_managed_network_service_binding_protocol_guid,
+};
+
/**
- * Locate chainloaded protocol instance
+ * Locate chainloaded protocol
*
* @v chained Chainloaded protocol
- * @ret rc Return status code
*/
-static int chained_locate ( struct chained_protocol *chained ) {
+static void chained_locate ( struct chained_protocol *chained ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
- EFI_HANDLE parent;
+ EFI_HANDLE handle;
+ void *match = NULL;
+ void *interface;
+ unsigned int skip;
EFI_STATUS efirc;
int rc;
- /* Locate handle supporting this protocol */
- if ( ( rc = efi_locate_device ( device, chained->protocol,
- &parent, 0 ) ) != 0 ) {
- DBGC ( device, "CHAINED %s does not support %s: %s\n",
- efi_handle_name ( device ),
- efi_guid_ntoa ( chained->protocol ), strerror ( rc ) );
- goto err_locate_device;
- }
- DBGC ( device, "CHAINED %s found %s on ", efi_handle_name ( device ),
- efi_guid_ntoa ( chained->protocol ) );
- DBGC ( device, "%s\n", efi_handle_name ( parent ) );
+ /* Identify target device handle */
+ for ( skip = 0 ; ; skip++ ) {
- /* Get protocol instance */
- if ( ( efirc = bs->OpenProtocol ( parent, chained->protocol,
- &chained->interface, efi_image_handle,
- device,
- EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
- rc = -EEFI ( efirc );
- DBGC ( device, "CHAINED %s could not open %s on ",
+ /* Locate handle supporting this protocol */
+ if ( ( rc = efi_locate_device ( device, chained->protocol,
+ &handle, skip ) ) != 0 ) {
+ if ( skip == 0 ) {
+ DBGC ( device, "CHAINED %s does not support "
+ "%s: %s\n", efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ),
+ strerror ( rc ) );
+ }
+ break;
+ }
+
+ /* Get protocol instance */
+ if ( ( efirc = bs->OpenProtocol (
+ handle, chained->protocol, &interface,
+ efi_image_handle, handle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL )) != 0){
+ rc = -EEFI ( efirc );
+ DBGC ( device, "CHAINED %s could not open %s on ",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ) );
+ DBGC ( device, "%s: %s\n",
+ efi_handle_name ( handle ), strerror ( rc ) );
+ break;
+ }
+ bs->CloseProtocol ( handle, chained->protocol,
+ efi_image_handle, handle );
+
+ /* Stop if we reach a non-matching protocol instance */
+ if ( match && ( match != interface ) ) {
+ DBGC ( device, "CHAINED %s found non-matching %s on ",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ) );
+ DBGC ( device, "%s\n", efi_handle_name ( handle ) );
+ break;
+ }
+
+ /* Record this handle */
+ chained->device = handle;
+ match = interface;
+ DBGC ( device, "CHAINED %s found %s on ",
efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
- DBGC ( device, "%s: %s\n",
- efi_handle_name ( parent ), strerror ( rc ) );
- goto err_open_protocol;
+ DBGC ( device, "%s\n", efi_handle_name ( chained->device ) );
}
-
- err_locate_device:
- bs->CloseProtocol ( parent, chained->protocol, efi_image_handle,
- device );
- err_open_protocol:
- return rc;
}
/**
@@ -121,8 +155,8 @@ static int chained_locate ( struct chained_protocol *chained ) {
static int chained_supported ( EFI_HANDLE device,
struct chained_protocol *chained ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- EFI_STATUS efirc;
void *interface;
+ EFI_STATUS efirc;
int rc;
/* Get protocol */
@@ -136,19 +170,19 @@ static int chained_supported ( EFI_HANDLE device,
goto err_open_protocol;
}
- /* Test for a match against the chainloading device */
- if ( interface != chained->interface ) {
- DBGC ( device, "CHAINED %s %p is not the chainloaded %s\n",
- efi_handle_name ( device ), interface,
- efi_guid_ntoa ( chained->protocol ) );
+ /* Ignore non-matching handles */
+ if ( device != chained->device ) {
+ DBGC2 ( device, "CHAINED %s is not the chainloaded %s\n",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( chained->protocol ) );
rc = -ENOTTY;
goto err_no_match;
}
/* Success */
rc = 0;
- DBGC ( device, "CHAINED %s %p is the chainloaded %s\n",
- efi_handle_name ( device ), interface,
+ DBGC ( device, "CHAINED %s is the chainloaded %s\n",
+ efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ) );
err_no_match:
@@ -180,6 +214,17 @@ static int niionly_supported ( EFI_HANDLE device ) {
return chained_supported ( device, &chained_nii );
}
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+static int mnponly_supported ( EFI_HANDLE device ) {
+
+ return chained_supported ( device, &chained_mnp );
+}
+
/** EFI SNP chainloading-device-only driver */
struct efi_driver snponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
.name = "SNPONLY",
@@ -196,6 +241,14 @@ struct efi_driver niionly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
.stop = nii_stop,
};
+/** EFI MNP chainloading-device-only driver */
+struct efi_driver mnponly_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+ .name = "MNPONLY",
+ .supported = mnponly_supported,
+ .start = mnpnet_start,
+ .stop = mnpnet_stop,
+};
+
/**
* Initialise EFI chainloaded-device-only driver
*
@@ -204,6 +257,7 @@ static void chained_init ( void ) {
chained_locate ( &chained_snp );
chained_locate ( &chained_nii );
+ chained_locate ( &chained_mnp );
}
/** EFI chainloaded-device-only initialisation function */