]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Add support for driving EFI_MANAGED_NETWORK_PROTOCOL devices
authorMichael Brown <mcb30@ipxe.org>
Fri, 22 Mar 2024 15:30:45 +0000 (15:30 +0000)
committerMichael Brown <mcb30@ipxe.org>
Mon, 25 Mar 2024 17:58:33 +0000 (17:58 +0000)
We want exclusive access to the network device, both for performance
reasons and because we perform operations such as EAPoL that affect
the entire link.  We currently drive the network card via either a
native hardware driver or via the SNP or NII/UNDI interfaces, both of
which grant us this exclusive access.

Add an alternative driver that drives the network card non-exclusively
via the EFI_MANAGED_NETWORK_PROTOCOL interface.  This can function as
a fallback for situations where neither SNP nor NII/UNDI interfaces
are functional, and also opens up the possibility of non-destructively
installing a temporary network device over which to download the
autoexec.ipxe script.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/Makefile.efi
src/drivers/net/efi/mnp.c [new file with mode: 0644]
src/drivers/net/efi/mnpnet.c [new file with mode: 0644]
src/drivers/net/efi/mnpnet.h [new file with mode: 0644]
src/drivers/net/efi/snp.c
src/drivers/net/efi/snpnet.c
src/drivers/net/efi/snpnet.h
src/drivers/net/efi/snponly.c
src/include/ipxe/errfile.h

index 6e8ad46bc05923bcdd7bc8edb9b95a6ae9d4be78..95ecf3863c523642f6799f3335648a137bc8b755 100644 (file)
@@ -23,9 +23,9 @@ NON_AUTO_MEDIA        += efidrv
 NON_AUTO_MEDIA += drv.efi
 NON_AUTO_MEDIA += efirom
 
-# Include SNP driver in the all-drivers build
+# Include SNP and MNP drivers in the all-drivers build
 #
-DRIVERS_net += snp
+DRIVERS_net += snp mnp
 
 # Rules for building EFI files
 #
diff --git a/src/drivers/net/efi/mnp.c b/src/drivers/net/efi/mnp.c
new file mode 100644 (file)
index 0000000..b79fe18
--- /dev/null
@@ -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 "snpnet.h"
+#include "mnpnet.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 (file)
index 0000000..a07eae5
--- /dev/null
@@ -0,0 +1,504 @@
+/*
+ * 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/efi/efi.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_service.h>
+#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/Protocol/ManagedNetwork.h>
+#include "mnpnet.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 );
+}
diff --git a/src/drivers/net/efi/mnpnet.h b/src/drivers/net/efi/mnpnet.h
new file mode 100644 (file)
index 0000000..afed62a
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _MNPNET_H
+#define _MNPNET_H
+
+/** @file
+ *
+ * MNP NIC driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+struct efi_device;
+
+extern int mnpnet_start ( struct efi_device *efidev );
+extern void mnpnet_stop ( struct efi_device *efidev );
+
+#endif /* _MNPNET_H */
index 1920cdbc53cb8716297efb5af55168b7cd845714..cac8b38e2c07821df272cfc6daebd1e6465e2cda 100644 (file)
 
 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"
 
@@ -37,53 +34,6 @@ 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
  *
@@ -92,7 +42,7 @@ static int snp_nii_supported ( EFI_HANDLE device, EFI_GUID *protocol ) {
  */
 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 */
index b8bf963e91ec3a870d862154cfc96534bfe4e73e..6ce731d7891f88d263c95dc54d7f34677c2b2e0c 100644 (file)
@@ -33,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #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
@@ -484,6 +485,53 @@ static struct net_device_operations snpnet_operations = {
        .poll = snpnet_poll,
 };
 
+/**
+ * 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
  *
index e6d31d5e4c70f3ee41db6ad8aafbde2700c0755f..4699c7892f8d59da46cb46f9f7d269242d28bc0f 100644 (file)
@@ -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 );
 
index 16370463663a62703a81cc5ece3d2c5aa01bd9b8..6786f3e83c6f114ef9d7870bb8eb9ff470a18ff1 100644 (file)
@@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <ipxe/efi/Protocol/SimpleNetwork.h>
 #include <ipxe/efi/Protocol/NetworkInterfaceIdentifier.h>
 #include "snpnet.h"
+#include "mnpnet.h"
 #include "nii.h"
 
 /** @file
@@ -74,6 +75,11 @@ 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
  *
@@ -208,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",
@@ -224,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
  *
@@ -232,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 */
index 083c77e1f3a38a494b59893614b2c713c55d052c..0ab37230a31032930ebf6772246fab81b2729c51 100644 (file)
@@ -223,6 +223,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define ERRFILE_ice                 ( ERRFILE_DRIVER | 0x00d20000 )
 #define ERRFILE_ecam                ( ERRFILE_DRIVER | 0x00d30000 )
 #define ERRFILE_pcibridge           ( ERRFILE_DRIVER | 0x00d40000 )
+#define ERRFILE_mnpnet              ( ERRFILE_DRIVER | 0x00d50000 )
 
 #define ERRFILE_aoe                    ( ERRFILE_NET | 0x00000000 )
 #define ERRFILE_arp                    ( ERRFILE_NET | 0x00010000 )