/
githubmirror
/
ipxe
Обзор
Документация
Войти
/
githubmirror
/
ipxe
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/drivers/net/efi/snpnet.c
871 строка
24 KB
Michael Brown
[build] Mark core files as permitted for UEFI Secure Boot
14 янв 2026, 16:25
14 янв 2026, 16:25
6cccb3b
Код
Авторство
О чём код?
/* * Copyright (C) 2014 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. */ FILE_LICENCE ( GPL2_OR_LATER ); FILE_SECBOOT ( PERMITTED ); #include <stdlib.h> #include <stdio.h> #include <string.h> #include <errno.h> #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 * * SNP NIC driver * */ /** An SNP NIC */ struct snp_nic { /** EFI device */ struct efi_device *efidev; /** Simple network protocol */ EFI_SIMPLE_NETWORK_PROTOCOL *snp; /** Generic device */ struct device dev; /** Maximum packet size * * This is calculated as the sum of MediaHeaderSize and * MaxPacketSize, and may therefore be an overestimate. */ size_t mtu; /** Current transmit buffer */ struct io_buffer *txbuf; /** Current receive buffer */ struct io_buffer *rxbuf; }; /** Maximum number of received packets per poll */ #define SNP_RX_QUOTA 4 /** Maximum initialisation retry count */ #define SNP_INITIALIZE_RETRY_MAX 10 /** 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 /** An SNP interface patch to inhibit shutdown for insomniac devices */ struct snp_insomniac_patch { /** Original Shutdown() method */ EFI_SIMPLE_NETWORK_SHUTDOWN shutdown; /** Original Stop() method */ EFI_SIMPLE_NETWORK_STOP stop; }; /** * Format SNP MAC address (for debugging) * * @v mac MAC address * @v len Length of MAC address * @ret text MAC address as text */ static const char * snpnet_mac_text ( EFI_MAC_ADDRESS *mac, size_t len ) { static char buf[ sizeof ( *mac ) * 3 /* "xx:" or "xx\0" */ ]; size_t used = 0; unsigned int i; for ( i = 0 ; i < len ; i++ ) { used += ssnprintf ( &buf[used], ( sizeof ( buf ) - used ), "%s%02x", ( used ? ":" : "" ), mac->Addr[i] ); } return buf; } /** * Dump SNP mode information (for debugging) * * @v netdev Network device */ static void snpnet_dump_mode ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; EFI_SIMPLE_NETWORK_MODE *mode = snp->snp->Mode; size_t mac_len = mode->HwAddressSize; unsigned int i; /* Do nothing unless debugging is enabled */ if ( ! DBG_EXTRA ) return; DBGC2 ( snp, "SNP %s st %d type %d hdr %d pkt %d rxflt %#x/%#x%s " "nvram %d acc %d mcast %d/%d\n", netdev->name, mode->State, mode->IfType, mode->MediaHeaderSize, mode->MaxPacketSize, mode->ReceiveFilterSetting, mode->ReceiveFilterMask, ( mode->MultipleTxSupported ? " multitx" : "" ), mode->NvRamSize, mode->NvRamAccessSize, mode->MCastFilterCount, mode->MaxMCastFilterCount ); DBGC2 ( snp, "SNP %s hw %s", netdev->name, snpnet_mac_text ( &mode->PermanentAddress, mac_len ) ); DBGC2 ( snp, " addr %s%s", snpnet_mac_text ( &mode->CurrentAddress, mac_len ), ( mode->MacAddressChangeable ? "" : "(f)" ) ); DBGC2 ( snp, " bcast %s\n", snpnet_mac_text ( &mode->BroadcastAddress, mac_len ) ); for ( i = 0 ; i < mode->MCastFilterCount ; i++ ) { DBGC2 ( snp, "SNP %s mcast %s\n", netdev->name, snpnet_mac_text ( &mode->MCastFilter[i], mac_len ) ); } DBGC2 ( snp, "SNP %s media %s\n", netdev->name, ( mode->MediaPresentSupported ? ( mode->MediaPresent ? "present" : "not present" ) : "presence not supported" ) ); } /** * Check link state * * @v netdev Network device */ static void snpnet_check_link ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; EFI_SIMPLE_NETWORK_MODE *mode = snp->snp->Mode; /* Do nothing unless media presence detection is supported */ if ( ! mode->MediaPresentSupported ) return; /* Report any link status change */ if ( mode->MediaPresent && ( ! netdev_link_ok ( netdev ) ) ) { netdev_link_up ( netdev ); } else if ( ( ! mode->MediaPresent ) && netdev_link_ok ( netdev ) ) { netdev_link_down ( netdev ); } } /** * Transmit packet * * @v netdev Network device * @v iobuf I/O buffer * @ret rc Return status code */ static int snpnet_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct snp_nic *snp = netdev->priv; 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 ( snp->txbuf ) { netdev_tx_defer ( netdev, iobuf ); 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, NULL ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not transmit: %s\n", netdev->name, strerror ( rc ) ); return rc; } snp->txbuf = iobuf; return 0; } /** * Poll for completed packets * * @v netdev Network device */ static void snpnet_poll_tx ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; struct io_buffer *iobuf; UINT32 irq; VOID *txbuf; EFI_STATUS efirc; int rc; /* Get status */ txbuf = NULL; if ( ( efirc = snp->snp->GetStatus ( snp->snp, &irq, &txbuf ) ) != 0 ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not get status: %s\n", netdev->name, strerror ( rc ) ); netdev_rx_err ( netdev, NULL, rc ); return; } /* Do nothing unless we have a completion */ if ( ! txbuf ) return; /* Sanity check */ if ( ! snp->txbuf ) { DBGC ( snp, "SNP %s reported spurious TX completion\n", netdev->name ); netdev_tx_err ( netdev, NULL, -EPIPE ); return; } /* Complete transmission */ iobuf = snp->txbuf; snp->txbuf = NULL; netdev_tx_complete ( netdev, iobuf ); } /** * Poll for received packets * * @v netdev Network device */ static void snpnet_poll_rx ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; UINTN len; unsigned int quota; EFI_STATUS efirc; int rc; /* Retrieve up to SNP_RX_QUOTA packets */ for ( quota = SNP_RX_QUOTA ; quota ; quota-- ) { /* Allocate buffer, if required */ if ( ! snp->rxbuf ) { snp->rxbuf = alloc_iob ( snp->mtu + SNP_RX_PAD ); if ( ! snp->rxbuf ) { /* Leave for next poll */ break; } } /* Receive packet */ len = iob_tailroom ( snp->rxbuf ); if ( ( efirc = snp->snp->Receive ( snp->snp, NULL, &len, snp->rxbuf->data, NULL, NULL, NULL ) ) != 0 ) { /* EFI_NOT_READY is just the usual "no packet" * status indication; ignore it. */ if ( efirc == EFI_NOT_READY ) break; /* Anything else is an error */ rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not receive: %s\n", netdev->name, strerror ( rc ) ); netdev_rx_err ( netdev, NULL, rc ); break; } /* Hand off to network stack */ iob_put ( snp->rxbuf, len ); netdev_rx ( netdev, snp->rxbuf ); snp->rxbuf = NULL; } } /** * Poll for completed packets * * @v netdev Network device */ static void snpnet_poll ( struct net_device *netdev ) { /* Do nothing if shutdown is in progress */ if ( efi_shutdown_in_progress ) return; /* Process any TX completions */ snpnet_poll_tx ( netdev ); /* Process any RX completions */ snpnet_poll_rx ( netdev ); /* Check for link state changes */ snpnet_check_link ( netdev ); } /** * Set receive filters * * @v netdev Network device * @ret rc Return status code */ static int snpnet_rx_filters ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; UINT32 filters[] = { snp->snp->Mode->ReceiveFilterMask, ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ), ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | EFI_SIMPLE_NETWORK_RECEIVE_PROMISCUOUS_MULTICAST | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ), ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ), ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST ), }; unsigned int i; EFI_STATUS efirc; int rc; /* Try possible receive filters in turn */ for ( i = 0; i < ( sizeof ( filters ) / sizeof ( filters[0] ) ); i++ ) { efirc = snp->snp->ReceiveFilters ( snp->snp, filters[i], EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST, TRUE, 0, NULL ); if ( efirc == 0 ) return 0; rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not set receive filters %#02x (have " "%#02x): %s\n", netdev->name, filters[i], snp->snp->Mode->ReceiveFilterSetting, strerror ( rc ) ); } return rc; } /** * Open network device * * @v netdev Network device * @ret rc Return status code */ static int snpnet_open ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; EFI_MAC_ADDRESS *mac = ( ( void * ) netdev->ll_addr ); EFI_SIMPLE_NETWORK_MODE *mode = snp->snp->Mode; EFI_STATUS efirc; unsigned int retry; int rc; /* Try setting MAC address (before initialising) */ if ( ( efirc = snp->snp->StationAddress ( snp->snp, FALSE, mac ) ) !=0){ rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not set station address before " "initialising: %s\n", netdev->name, strerror ( rc ) ); /* Ignore error */ } /* Initialise NIC, retrying multiple times if link stays down */ for ( retry = 0 ; ; ) { /* Initialise NIC, if not already initialised */ if ( ( mode->State != EfiSimpleNetworkInitialized ) && ( ( efirc = snp->snp->Initialize ( snp->snp, 0, 0 ) ) != 0 ) ) { rc = -EEFI ( efirc ); snpnet_dump_mode ( netdev ); DBGC ( snp, "SNP %s could not initialise: %s\n", netdev->name, strerror ( rc ) ); return rc; } /* Stop if we have link up (or no link detection capability) */ if ( ( ! mode->MediaPresentSupported ) || mode->MediaPresent ) break; /* Stop if we have exceeded our retry count. This is * not a failure; it is plausible that we genuinely do * not have link up. */ if ( ++retry >= SNP_INITIALIZE_RETRY_MAX ) break; DBGC ( snp, "SNP %s retrying initialisation (retry %d)\n", netdev->name, retry ); /* Delay to allow time for link to establish */ mdelay ( SNP_INITIALIZE_RETRY_DELAY_MS ); /* Shut down and retry (unless device is insomniac); * this is sometimes necessary in order to persuade * the underlying SNP driver to actually update the * link state. */ if ( ( ! netdev_insomniac ( netdev ) ) && ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); snpnet_dump_mode ( netdev ); DBGC ( snp, "SNP %s could not shut down: %s\n", netdev->name, strerror ( rc ) ); return rc; } } /* Try setting MAC address (after initialising) */ if ( ( efirc = snp->snp->StationAddress ( snp->snp, FALSE, mac ) ) !=0){ rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not set station address after " "initialising: %s\n", netdev->name, strerror ( rc ) ); /* Ignore error */ } /* Set receive filters */ if ( ( rc = snpnet_rx_filters ( netdev ) ) != 0 ) { /* Ignore error */ } /* Dump mode information (for debugging) */ snpnet_dump_mode ( netdev ); return 0; } /** * Close network device * * @v netdev Network device */ static void snpnet_close ( struct net_device *netdev ) { struct snp_nic *snp = netdev->priv; EFI_STATUS efirc; int rc; /* Shut down NIC (unless whole system shutdown is in progress, * or device is insomniac). */ if ( ( ! efi_shutdown_in_progress ) && ( ! netdev_insomniac ( netdev ) ) && ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( snp, "SNP %s could not shut down: %s\n", netdev->name, strerror ( rc ) ); /* Nothing we can do about this */ } /* Discard transmit buffer, if applicable */ if ( snp->txbuf ) { netdev_tx_complete_err ( netdev, snp->txbuf, -ECANCELED ); snp->txbuf = NULL; } /* Discard receive buffer, if applicable */ if ( snp->rxbuf ) { free_iob ( snp->rxbuf ); snp->rxbuf = NULL; } } /** SNP network device operations */ static struct net_device_operations snpnet_operations = { .open = snpnet_open, .close = snpnet_close, .transmit = snpnet_transmit, .poll = snpnet_poll, }; /** * Check to see if driver supports a device * * @v device EFI device handle * @v protocol Protocol GUID * @v inhibit_wifi Inhibit wireless devices * @ret rc Return status code */ int snpnet_supported ( EFI_HANDLE device, EFI_GUID *protocol, int inhibit_wifi ) { EFI_HANDLE parent; 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 ( ( rc = efi_test ( device, protocol ) ) != 0 ) { DBGCP ( device, "HANDLE %s is not a %s device\n", efi_handle_name ( device ), efi_guid_ntoa ( protocol ) ); return rc; } /* 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 ) ); /* Check for wireless devices, if applicable */ if ( inhibit_wifi && ( ( efi_test ( device, &efi_wifi2_protocol_guid ) ) == 0 ) ) { DBGC ( device, "HANDLE %s is wireless: assuming vendor %s " "driver is too unreliable to use\n", efi_handle_name ( device ), efi_guid_ntoa ( protocol ) ); return -ENOTTY; } return 0; } /** * Check if device must be insomniac * * @v device EFI device handle * @v is_insomniac Device must be insomniac */ static int snpnet_is_insomniac ( EFI_HANDLE device ) { int rc; /* Check for wireless devices * * The UEFI model for wireless network configuration is * somewhat underdefined. At the time of writing, the EDK2 * "UEFI WiFi Connection Manager" driver provides only one way * to configure wireless network credentials, which is to * enter them interactively via an HII form. Credentials are * not stored (or exposed via any protocol interface), and so * any temporary disconnection from the wireless network will * inevitably leave the interface in an unusable state that * cannot be recovered without user intervention. * * Experimentation shows that at least some wireless network * drivers will disconnect from the wireless network when the * SNP Shutdown() method is called, or if the device is not * polled sufficiently frequently to maintain its association * to the network. We therefore inhibit calls to Shutdown() * and Stop() for any such SNP protocol interfaces, and mark * our network device as insomniac so that it will be polled * even when closed. */ if ( ( rc = efi_test ( device, &efi_wifi2_protocol_guid ) ) == 0 ) { DBGC ( device, "SNP %s is wireless: assuming insomniac\n", efi_handle_name ( device ) ); return 1; } return 0; } /** * Ignore shutdown attempt * * @v snp SNP interface * @ret efirc EFI status code */ static EFI_STATUS EFIAPI snpnet_do_nothing ( EFI_SIMPLE_NETWORK_PROTOCOL *snp __unused ) { return 0; } /** * Patch SNP protocol interface to prevent shutdown * * @v device EFI device handle * @v patch Interface patch * @ret rc Return status code */ static int snpnet_insomniac_patch ( EFI_HANDLE device, struct snp_insomniac_patch *patch ) { EFI_SIMPLE_NETWORK_PROTOCOL *interface; int rc; /* Open interface for ephemeral use */ if ( ( rc = efi_open ( device, &efi_simple_network_protocol_guid, &interface ) ) != 0 ) { DBGC ( device, "SNP %s cannot open SNP protocol for patching: " "%s\n", efi_handle_name ( device ), strerror ( rc ) ); return rc; } /* Record original Shutdown() and Stop() methods */ patch->shutdown = interface->Shutdown; patch->stop = interface->Stop; /* Inhibit other UEFI drivers' calls to Shutdown() and Stop() * * This is necessary since disconnecting the MnpDxe driver * will attempt to shut down the SNP device, which would leave * us with an unusable device. */ interface->Shutdown = snpnet_do_nothing; interface->Stop = snpnet_do_nothing; DBGC ( device, "SNP %s patched to inhibit shutdown\n", efi_handle_name ( device ) ); return 0; } /** * Restore patched SNP protocol interface * * @v device EFI device handle * @v patch Interface patch to fill in * @ret rc Return status code */ static int snpnet_insomniac_restore ( EFI_HANDLE device, struct snp_insomniac_patch *patch ) { EFI_SIMPLE_NETWORK_PROTOCOL *interface; int rc; /* Avoid returning uninitialised data on error */ memset ( patch, 0, sizeof ( *patch ) ); /* Open interface for ephemeral use */ if ( ( rc = efi_open ( device, &efi_simple_network_protocol_guid, &interface ) ) != 0 ) { DBGC ( device, "SNP %s cannot open patched SNP protocol: %s\n", efi_handle_name ( device ), strerror ( rc ) ); return rc; } /* Restore original Shutdown() and Stop() methods, if possible */ if ( interface->Shutdown == snpnet_do_nothing ) interface->Shutdown = patch->shutdown; if ( interface->Stop == snpnet_do_nothing ) interface->Stop = patch->stop; /* Check that original methods were restored (by us or others) */ if ( ( interface->Shutdown != patch->shutdown ) || ( interface->Stop != patch->stop ) ) { DBGC ( device, "SNP %s could not restore patched SNP " "protocol\n", efi_handle_name ( device ) ); return -EBUSY; } return 0; } /** * Exclude existing drivers * * @v device EFI device handle * @ret rc Return status code */ int snpnet_exclude ( EFI_HANDLE device ) { EFI_GUID *protocol = &efi_simple_network_protocol_guid; struct snp_insomniac_patch patch; int insomniac; int rc; /* Check if this is a device that must not ever be shut down */ insomniac = snpnet_is_insomniac ( device ); /* Inhibit calls to Shutdown() and Stop(), if applicable */ if ( insomniac && ( ( rc = snpnet_insomniac_patch ( device, &patch ) ) != 0 ) ) { goto err_patch; } /* Exclude existing SNP drivers */ if ( ( rc = efi_driver_exclude ( device, protocol ) ) != 0 ) { DBGC ( device, "SNP %s could not exclude drivers: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_exclude; } err_exclude: if ( insomniac ) snpnet_insomniac_restore ( device, &patch ); err_patch: return rc; } /** * Attach driver to device * * @v efidev EFI device * @ret rc Return status code */ int snpnet_start ( struct efi_device *efidev ) { EFI_HANDLE device = efidev->device; EFI_SIMPLE_NETWORK_PROTOCOL *interface; EFI_SIMPLE_NETWORK_MODE *mode; struct net_device *netdev; struct snp_nic *snp; EFI_STATUS efirc; int rc; /* Open SNP protocol */ if ( ( rc = efi_open_by_driver ( device, &efi_simple_network_protocol_guid, &interface ) ) != 0 ) { DBGC ( device, "SNP %s cannot open SNP protocol: %s\n", efi_handle_name ( device ), strerror ( rc ) ); DBGC_EFI_OPENERS ( device, device, &efi_simple_network_protocol_guid ); goto err_open_protocol; } /* Allocate and initialise structure */ netdev = alloc_etherdev ( sizeof ( *snp ) ); if ( ! netdev ) { rc = -ENOMEM; goto err_alloc; } netdev_init ( netdev, &snpnet_operations ); snp = netdev->priv; snp->efidev = efidev; snp->snp = interface; mode = snp->snp->Mode; efidev_set_drvdata ( efidev, netdev ); /* Populate underlying device information */ efi_device_info ( device, "SNP", &snp->dev ); snp->dev.driver_name = "SNP"; snp->dev.parent = &efidev->dev; list_add ( &snp->dev.siblings, &efidev->dev.children ); INIT_LIST_HEAD ( &snp->dev.children ); netdev->dev = &snp->dev; /* Check if device is insomniac */ if ( snpnet_is_insomniac ( device ) ) netdev->state |= NETDEV_INSOMNIAC; /* Bring to the correct state for a closed interface */ if ( ( mode->State == EfiSimpleNetworkStopped ) && ( ( efirc = snp->snp->Start ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( device, "SNP %s could not start: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_start; } if ( ( mode->State == EfiSimpleNetworkInitialized ) && ( ! netdev_insomniac ( netdev ) ) && ( ( efirc = snp->snp->Shutdown ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( device, "SNP %s could not shut down: %s\n", efi_handle_name ( device ), strerror ( rc ) ); goto err_shutdown; } /* Populate network device parameters */ if ( mode->HwAddressSize != netdev->ll_protocol->hw_addr_len ) { DBGC ( device, "SNP %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, "SNP %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 ); snp->mtu = ( snp->snp->Mode->MaxPacketSize + snp->snp->Mode->MediaHeaderSize ); /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; DBGC ( device, "SNP %s registered as %s\n", efi_handle_name ( device ), netdev->name ); /* Set initial link state */ if ( snp->snp->Mode->MediaPresentSupported ) { snpnet_check_link ( netdev ); } else { netdev_link_up ( netdev ); } return 0; unregister_netdev ( netdev ); err_register_netdev: err_ll_addr_len: err_hw_addr_len: err_shutdown: err_start: list_del ( &snp->dev.siblings ); netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: efi_close_by_driver ( device, &efi_simple_network_protocol_guid ); err_open_protocol: return rc; } /** * Detach driver from device * * @v efidev EFI device */ void snpnet_stop ( struct efi_device *efidev ) { struct net_device *netdev = efidev_get_drvdata ( efidev ); struct snp_nic *snp = netdev->priv; EFI_HANDLE device = efidev->device; EFI_STATUS efirc; int rc; /* Unregister network device */ unregister_netdev ( netdev ); /* Stop SNP protocol (unless whole system shutdown is in progress) */ if ( ( ! efi_shutdown_in_progress ) && ( ( efirc = snp->snp->Stop ( snp->snp ) ) != 0 ) ) { rc = -EEFI ( efirc ); DBGC ( device, "SNP %s could not stop: %s\n", efi_handle_name ( device ), strerror ( rc ) ); /* Nothing we can do about this */ } /* Free network device */ list_del ( &snp->dev.siblings ); netdev_nullify ( netdev ); netdev_put ( netdev ); /* Close SNP protocol */ efi_close_by_driver ( device, &efi_simple_network_protocol_guid ); }