summaryrefslogtreecommitdiffstats
path: root/src/drivers/net/efi
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers/net/efi')
-rw-r--r--src/drivers/net/efi/mnp.c56
-rw-r--r--src/drivers/net/efi/mnpnet.c504
-rw-r--r--src/drivers/net/efi/mnpnet.h17
-rw-r--r--src/drivers/net/efi/snp.c54
-rw-r--r--src/drivers/net/efi/snpnet.c48
-rw-r--r--src/drivers/net/efi/snpnet.h1
-rw-r--r--src/drivers/net/efi/snponly.c26
7 files changed, 654 insertions, 52 deletions
diff --git a/src/drivers/net/efi/mnp.c b/src/drivers/net/efi/mnp.c
new file mode 100644
index 00000000..b79fe188
--- /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 "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
index 00000000..a07eae54
--- /dev/null
+++ b/src/drivers/net/efi/mnpnet.c
@@ -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
index 00000000..afed62aa
--- /dev/null
+++ b/src/drivers/net/efi/mnpnet.h
@@ -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 */
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 b8bf963e..6ce731d7 100644
--- a/src/drivers/net/efi/snpnet.c
+++ b/src/drivers/net/efi/snpnet.c
@@ -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
@@ -485,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 16370463..6786f3e8 100644
--- a/src/drivers/net/efi/snponly.c
+++ b/src/drivers/net/efi/snponly.c
@@ -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 */