diff options
Diffstat (limited to 'gpxe/src/interface/efi/efi_snp.c')
-rw-r--r-- | gpxe/src/interface/efi/efi_snp.c | 1148 |
1 files changed, 0 insertions, 1148 deletions
diff --git a/gpxe/src/interface/efi/efi_snp.c b/gpxe/src/interface/efi/efi_snp.c deleted file mode 100644 index b5241e54..00000000 --- a/gpxe/src/interface/efi/efi_snp.c +++ /dev/null @@ -1,1148 +0,0 @@ -/* - * Copyright (C) 2008 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <gpxe/netdevice.h> -#include <gpxe/iobuf.h> -#include <gpxe/in.h> -#include <gpxe/pci.h> -#include <gpxe/efi/efi.h> -#include <gpxe/efi/Protocol/DriverBinding.h> -#include <gpxe/efi/Protocol/PciIo.h> -#include <gpxe/efi/Protocol/SimpleNetwork.h> -#include <gpxe/efi/Protocol/ComponentName2.h> -#include <gpxe/efi/Protocol/NetworkInterfaceIdentifier.h> -#include <config/general.h> - -/** @file - * - * gPXE EFI SNP interface - * - */ - -/** An SNP device */ -struct efi_snp_device { - /** The underlying gPXE network device */ - struct net_device *netdev; - /** EFI device handle */ - EFI_HANDLE handle; - /** The SNP structure itself */ - EFI_SIMPLE_NETWORK_PROTOCOL snp; - /** The SNP "mode" (parameters) */ - EFI_SIMPLE_NETWORK_MODE mode; - /** Outstanding TX packet count (via "interrupt status") - * - * Used in order to generate TX completions. - */ - unsigned int tx_count_interrupts; - /** Outstanding TX packet count (via "recycled tx buffers") - * - * Used in order to generate TX completions. - */ - unsigned int tx_count_txbufs; - /** Outstanding RX packet count (via "interrupt status") */ - unsigned int rx_count_interrupts; - /** Outstanding RX packet count (via WaitForPacket event) */ - unsigned int rx_count_events; - /** The network interface identifier */ - EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii; - /** Device name */ - wchar_t name[ sizeof ( ( ( struct net_device * ) NULL )->name ) ]; - /** The device path - * - * This field is variable in size and must appear at the end - * of the structure. - */ - EFI_DEVICE_PATH_PROTOCOL path; -}; - -/** EFI simple network protocol GUID */ -static EFI_GUID efi_simple_network_protocol_guid - = EFI_SIMPLE_NETWORK_PROTOCOL_GUID; - -/** EFI driver binding protocol GUID */ -static EFI_GUID efi_driver_binding_protocol_guid - = EFI_DRIVER_BINDING_PROTOCOL_GUID; - -/** EFI component name protocol GUID */ -static EFI_GUID efi_component_name2_protocol_guid - = EFI_COMPONENT_NAME2_PROTOCOL_GUID; - -/** EFI device path protocol GUID */ -static EFI_GUID efi_device_path_protocol_guid - = EFI_DEVICE_PATH_PROTOCOL_GUID; - -/** EFI network interface identifier GUID */ -static EFI_GUID efi_nii_protocol_guid - = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID; - -/** EFI network interface identifier GUID (extra special version) */ -static EFI_GUID efi_nii31_protocol_guid = { - /* At some point, it seems that someone decided to change the - * GUID. Current EFI builds ignore the older GUID, older EFI - * builds ignore the newer GUID, so we have to expose both. - */ - 0x1ACED566, 0x76ED, 0x4218, - { 0xBC, 0x81, 0x76, 0x7F, 0x1F, 0x97, 0x7A, 0x89 } -}; - -/** EFI PCI I/O protocol GUID */ -static EFI_GUID efi_pci_io_protocol_guid - = EFI_PCI_IO_PROTOCOL_GUID; - -/** - * Set EFI SNP mode based on gPXE net device parameters - * - * @v snp SNP interface - */ -static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) { - struct net_device *netdev = snpdev->netdev; - EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode; - struct ll_protocol *ll_protocol = netdev->ll_protocol; - unsigned int ll_addr_len = ll_protocol->ll_addr_len; - - mode->HwAddressSize = ll_addr_len; - mode->MediaHeaderSize = ll_protocol->ll_header_len; - mode->MaxPacketSize = netdev->max_pkt_len; - mode->ReceiveFilterMask = ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST | - EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST | - EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST ); - assert ( ll_addr_len <= sizeof ( mode->CurrentAddress ) ); - memcpy ( &mode->CurrentAddress, netdev->ll_addr, ll_addr_len ); - memcpy ( &mode->BroadcastAddress, netdev->ll_broadcast, ll_addr_len ); - ll_protocol->init_addr ( netdev->hw_addr, &mode->PermanentAddress ); - mode->IfType = ntohs ( ll_protocol->ll_proto ); - mode->MacAddressChangeable = TRUE; - mode->MediaPresentSupported = TRUE; - mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE ); -} - -/** - * Poll net device and count received packets - * - * @v snpdev SNP device - */ -static void efi_snp_poll ( struct efi_snp_device *snpdev ) { - struct io_buffer *iobuf; - unsigned int before = 0; - unsigned int after = 0; - unsigned int arrived; - - /* We have to report packet arrivals, and this is the easiest - * way to fake it. - */ - list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) - before++; - netdev_poll ( snpdev->netdev ); - list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) - after++; - arrived = ( after - before ); - - snpdev->rx_count_interrupts += arrived; - snpdev->rx_count_events += arrived; -} - -/** - * Change SNP state from "stopped" to "started" - * - * @v snp SNP interface - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - - DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev ); - - snpdev->mode.State = EfiSimpleNetworkStarted; - return 0; -} - -/** - * Change SNP state from "started" to "stopped" - * - * @v snp SNP interface - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - - DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev ); - - snpdev->mode.State = EfiSimpleNetworkStopped; - return 0; -} - -/** - * Open the network device - * - * @v snp SNP interface - * @v extra_rx_bufsize Extra RX buffer size, in bytes - * @v extra_tx_bufsize Extra TX buffer size, in bytes - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, - UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - int rc; - - DBGC2 ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n", - snpdev, ( ( unsigned long ) extra_rx_bufsize ), - ( ( unsigned long ) extra_tx_bufsize ) ); - - if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { - DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n", - snpdev, snpdev->netdev->name, strerror ( rc ) ); - return RC_TO_EFIRC ( rc ); - } - - snpdev->mode.State = EfiSimpleNetworkInitialized; - return 0; -} - -/** - * Reset the network device - * - * @v snp SNP interface - * @v ext_verify Extended verification required - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - int rc; - - DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n", - snpdev, ( ext_verify ? "with" : "without" ) ); - - netdev_close ( snpdev->netdev ); - snpdev->mode.State = EfiSimpleNetworkStarted; - - if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { - DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n", - snpdev, snpdev->netdev->name, strerror ( rc ) ); - return RC_TO_EFIRC ( rc ); - } - - snpdev->mode.State = EfiSimpleNetworkInitialized; - return 0; -} - -/** - * Shut down the network device - * - * @v snp SNP interface - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - - DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev ); - - netdev_close ( snpdev->netdev ); - snpdev->mode.State = EfiSimpleNetworkStarted; - return 0; -} - -/** - * Manage receive filters - * - * @v snp SNP interface - * @v enable Receive filters to enable - * @v disable Receive filters to disable - * @v mcast_reset Reset multicast filters - * @v mcast_count Number of multicast filters - * @v mcast Multicast filters - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable, - UINT32 disable, BOOLEAN mcast_reset, - UINTN mcast_count, EFI_MAC_ADDRESS *mcast ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - unsigned int i; - - DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n", - snpdev, enable, disable, ( mcast_reset ? " reset" : "" ), - ( ( unsigned long ) mcast_count ) ); - for ( i = 0 ; i < mcast_count ; i++ ) { - DBGC2_HDA ( snpdev, i, &mcast[i], - snpdev->netdev->ll_protocol->ll_addr_len ); - } - - /* Lie through our teeth, otherwise MNP refuses to accept us */ - return 0; -} - -/** - * Set station address - * - * @v snp SNP interface - * @v reset Reset to permanent address - * @v new New station address - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, - EFI_MAC_ADDRESS *new ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; - - DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev, - ( reset ? "reset" : ll_protocol->ntoa ( new ) ) ); - - /* Set the MAC address */ - if ( reset ) - new = &snpdev->mode.PermanentAddress; - memcpy ( snpdev->netdev->ll_addr, new, ll_protocol->ll_addr_len ); - - /* MAC address changes take effect only on netdev_open() */ - if ( snpdev->netdev->state & NETDEV_OPEN ) { - DBGC ( snpdev, "SNPDEV %p MAC address changed while net " - "devive open\n", snpdev ); - } - - return 0; -} - -/** - * Get (or reset) statistics - * - * @v snp SNP interface - * @v reset Reset statistics - * @v stats_len Size of statistics table - * @v stats Statistics table - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset, - UINTN *stats_len, EFI_NETWORK_STATISTICS *stats ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - EFI_NETWORK_STATISTICS stats_buf; - - DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev, - ( reset ? " reset" : "" ) ); - - /* Gather statistics */ - memset ( &stats_buf, 0, sizeof ( stats_buf ) ); - stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good; - stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad; - stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good + - snpdev->netdev->tx_stats.bad ); - stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good; - stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad; - stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good + - snpdev->netdev->rx_stats.bad ); - if ( *stats_len > sizeof ( stats_buf ) ) - *stats_len = sizeof ( stats_buf ); - if ( stats ) - memcpy ( stats, &stats_buf, *stats_len ); - - /* Reset statistics if requested to do so */ - if ( reset ) { - memset ( &snpdev->netdev->tx_stats, 0, - sizeof ( snpdev->netdev->tx_stats ) ); - memset ( &snpdev->netdev->rx_stats, 0, - sizeof ( snpdev->netdev->rx_stats ) ); - } - - return 0; -} - -/** - * Convert multicast IP address to MAC address - * - * @v snp SNP interface - * @v ipv6 Address is IPv6 - * @v ip IP address - * @v mac MAC address - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6, - EFI_IP_ADDRESS *ip, EFI_MAC_ADDRESS *mac ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; - const char *ip_str; - int rc; - - ip_str = ( ipv6 ? "(IPv6)" /* FIXME when we have inet6_ntoa() */ : - inet_ntoa ( *( ( struct in_addr * ) ip ) ) ); - DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str ); - - /* Try to hash the address */ - if ( ( rc = ll_protocol->mc_hash ( ( ipv6 ? AF_INET6 : AF_INET ), - ip, mac ) ) != 0 ) { - DBGC ( snpdev, "SNPDEV %p could not hash %s: %s\n", - snpdev, ip_str, strerror ( rc ) ); - return RC_TO_EFIRC ( rc ); - } - - return 0; -} - -/** - * Read or write non-volatile storage - * - * @v snp SNP interface - * @v read Operation is a read - * @v offset Starting offset within NVRAM - * @v len Length of data buffer - * @v data Data buffer - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, - UINTN offset, UINTN len, VOID *data ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - - DBGC2 ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev, - ( read ? "read" : "write" ), ( ( unsigned long ) offset ), - ( ( unsigned long ) len ) ); - if ( ! read ) - DBGC2_HDA ( snpdev, offset, data, len ); - - return EFI_UNSUPPORTED; -} - -/** - * Read interrupt status and TX recycled buffer status - * - * @v snp SNP interface - * @v interrupts Interrupt status, or NULL - * @v txbufs Recycled transmit buffer address, or NULL - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, - UINT32 *interrupts, VOID **txbufs ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - - DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); - - /* Poll the network device */ - efi_snp_poll ( snpdev ); - - /* Interrupt status. In practice, this seems to be used only - * to detect TX completions. - */ - if ( interrupts ) { - *interrupts = 0; - /* Report TX completions once queue is empty; this - * avoids having to add hooks in the net device layer. - */ - if ( snpdev->tx_count_interrupts && - list_empty ( &snpdev->netdev->tx_queue ) ) { - *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; - snpdev->tx_count_interrupts--; - } - /* Report RX */ - if ( snpdev->rx_count_interrupts ) { - *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; - snpdev->rx_count_interrupts--; - } - DBGC2 ( snpdev, " INTS:%02x", *interrupts ); - } - - /* TX completions. It would be possible to design a more - * idiotic scheme for this, but it would be a challenge. - * According to the UEFI header file, txbufs will be filled in - * with a list of "recycled transmit buffers" (i.e. completed - * TX buffers). Observant readers may care to note that - * *txbufs is a void pointer. Precisely how a list of - * completed transmit buffers is meant to be represented as an - * array of voids is left as an exercise for the reader. - * - * The only users of this interface (MnpDxe/MnpIo.c and - * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until - * seeing a non-NULL result return in txbufs. This is valid - * provided that they do not ever attempt to transmit more - * than one packet concurrently (and that TX never times out). - */ - if ( txbufs ) { - if ( snpdev->tx_count_txbufs && - list_empty ( &snpdev->netdev->tx_queue ) ) { - *txbufs = "Which idiot designed this API?"; - snpdev->tx_count_txbufs--; - } else { - *txbufs = NULL; - } - DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) ); - } - - DBGC2 ( snpdev, "\n" ); - return 0; -} - -/** - * Start packet transmission - * - * @v snp SNP interface - * @v ll_header_len Link-layer header length, if to be filled in - * @v len Length of data buffer - * @v data Data buffer - * @v ll_src Link-layer source address, if specified - * @v ll_dest Link-layer destination address, if specified - * @v net_proto Network-layer protocol (in host order) - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, - UINTN ll_header_len, UINTN len, VOID *data, - EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, - UINT16 *net_proto ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; - struct io_buffer *iobuf; - int rc; - EFI_STATUS efirc; - - DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, - ( ( unsigned long ) len ) ); - if ( ll_header_len ) { - if ( ll_src ) { - DBGC2 ( snpdev, " src %s", - ll_protocol->ntoa ( ll_src ) ); - } - if ( ll_dest ) { - DBGC2 ( snpdev, " dest %s", - ll_protocol->ntoa ( ll_dest ) ); - } - if ( net_proto ) { - DBGC2 ( snpdev, " proto %04x", *net_proto ); - } - } - DBGC2 ( snpdev, "\n" ); - - /* Sanity checks */ - if ( ll_header_len ) { - if ( ll_header_len != ll_protocol->ll_header_len ) { - DBGC ( snpdev, "SNPDEV %p TX invalid header length " - "%ld\n", snpdev, - ( ( unsigned long ) ll_header_len ) ); - efirc = EFI_INVALID_PARAMETER; - goto err_sanity; - } - if ( len < ll_header_len ) { - DBGC ( snpdev, "SNPDEV %p invalid packet length %ld\n", - snpdev, ( ( unsigned long ) len ) ); - efirc = EFI_BUFFER_TOO_SMALL; - goto err_sanity; - } - if ( ! ll_dest ) { - DBGC ( snpdev, "SNPDEV %p TX missing destination " - "address\n", snpdev ); - efirc = EFI_INVALID_PARAMETER; - goto err_sanity; - } - if ( ! net_proto ) { - DBGC ( snpdev, "SNPDEV %p TX missing network " - "protocol\n", snpdev ); - efirc = EFI_INVALID_PARAMETER; - goto err_sanity; - } - if ( ! ll_src ) - ll_src = &snpdev->mode.CurrentAddress; - } - - /* Allocate buffer */ - iobuf = alloc_iob ( len ); - if ( ! iobuf ) { - DBGC ( snpdev, "SNPDEV %p TX could not allocate %ld-byte " - "buffer\n", snpdev, ( ( unsigned long ) len ) ); - efirc = EFI_DEVICE_ERROR; - goto err_alloc_iob; - } - memcpy ( iob_put ( iobuf, len ), data, len ); - - /* Create link-layer header, if specified */ - if ( ll_header_len ) { - iob_pull ( iobuf, ll_header_len ); - if ( ( rc = ll_protocol->push ( snpdev->netdev, - iobuf, ll_dest, ll_src, - htons ( *net_proto ) )) != 0 ){ - DBGC ( snpdev, "SNPDEV %p TX could not construct " - "header: %s\n", snpdev, strerror ( rc ) ); - efirc = RC_TO_EFIRC ( rc ); - goto err_ll_push; - } - } - - /* Transmit packet */ - if ( ( rc = netdev_tx ( snpdev->netdev, iob_disown ( iobuf ) ) ) != 0){ - DBGC ( snpdev, "SNPDEV %p TX could not transmit: %s\n", - snpdev, strerror ( rc ) ); - efirc = RC_TO_EFIRC ( rc ); - goto err_tx; - } - - /* Record transmission as outstanding */ - snpdev->tx_count_interrupts++; - snpdev->tx_count_txbufs++; - - return 0; - - err_tx: - err_ll_push: - free_iob ( iobuf ); - err_alloc_iob: - err_sanity: - return efirc; -} - -/** - * Receive packet - * - * @v snp SNP interface - * @v ll_header_len Link-layer header length, if to be filled in - * @v len Length of data buffer - * @v data Data buffer - * @v ll_src Link-layer source address, if specified - * @v ll_dest Link-layer destination address, if specified - * @v net_proto Network-layer protocol (in host order) - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, - UINTN *ll_header_len, UINTN *len, VOID *data, - EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest, - UINT16 *net_proto ) { - struct efi_snp_device *snpdev = - container_of ( snp, struct efi_snp_device, snp ); - struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; - struct io_buffer *iobuf; - const void *iob_ll_dest; - const void *iob_ll_src; - uint16_t iob_net_proto; - int rc; - EFI_STATUS efirc; - - DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data, - ( ( unsigned long ) *len ) ); - - /* Poll the network device */ - efi_snp_poll ( snpdev ); - - /* Dequeue a packet, if one is available */ - iobuf = netdev_rx_dequeue ( snpdev->netdev ); - if ( ! iobuf ) { - DBGC2 ( snpdev, "\n" ); - efirc = EFI_NOT_READY; - goto out_no_packet; - } - DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); - - /* Return packet to caller */ - memcpy ( data, iobuf->data, iob_len ( iobuf ) ); - *len = iob_len ( iobuf ); - - /* Attempt to decode link-layer header */ - if ( ( rc = ll_protocol->pull ( snpdev->netdev, iobuf, &iob_ll_dest, - &iob_ll_src, &iob_net_proto ) ) != 0 ){ - DBGC ( snpdev, "SNPDEV %p could not parse header: %s\n", - snpdev, strerror ( rc ) ); - efirc = RC_TO_EFIRC ( rc ); - goto out_bad_ll_header; - } - - /* Return link-layer header parameters to caller, if required */ - if ( ll_header_len ) - *ll_header_len = ll_protocol->ll_header_len; - if ( ll_src ) - memcpy ( ll_src, iob_ll_src, ll_protocol->ll_addr_len ); - if ( ll_dest ) - memcpy ( ll_dest, iob_ll_dest, ll_protocol->ll_addr_len ); - if ( net_proto ) - *net_proto = ntohs ( iob_net_proto ); - - efirc = 0; - - out_bad_ll_header: - free_iob ( iobuf ); -out_no_packet: - return efirc; -} - -/** - * Poll event - * - * @v event Event - * @v context Event context - */ -static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, - VOID *context ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_snp_device *snpdev = context; - - DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); - - /* Do nothing unless the net device is open */ - if ( ! ( snpdev->netdev->state & NETDEV_OPEN ) ) - return; - - /* Poll the network device */ - efi_snp_poll ( snpdev ); - - /* Fire event if packets have been received */ - if ( snpdev->rx_count_events != 0 ) { - DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n", - snpdev ); - bs->SignalEvent ( event ); - snpdev->rx_count_events--; - } -} - -/** SNP interface */ -static EFI_SIMPLE_NETWORK_PROTOCOL efi_snp_device_snp = { - .Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION, - .Start = efi_snp_start, - .Stop = efi_snp_stop, - .Initialize = efi_snp_initialize, - .Reset = efi_snp_reset, - .Shutdown = efi_snp_shutdown, - .ReceiveFilters = efi_snp_receive_filters, - .StationAddress = efi_snp_station_address, - .Statistics = efi_snp_statistics, - .MCastIpToMac = efi_snp_mcast_ip_to_mac, - .NvData = efi_snp_nvdata, - .GetStatus = efi_snp_get_status, - .Transmit = efi_snp_transmit, - .Receive = efi_snp_receive, -}; - -/** - * Locate net device corresponding to EFI device - * - * @v driver EFI driver - * @v device EFI device - * @ret netdev Net device, or NULL if not found - */ -static struct net_device * -efi_snp_netdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_PCI_IO_PROTOCOL *pci; - void *interface; - } u; - UINTN pci_segment, pci_bus, pci_dev, pci_fn; - unsigned int pci_busdevfn; - struct net_device *netdev = NULL; - EFI_STATUS efirc; - - /* See if device is a PCI device */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_pci_io_protocol_guid, - &u.interface, - driver->DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ - DBGCP ( driver, "SNPDRV %p device %p is not a PCI device\n", - driver, device ); - goto out_no_pci_io; - } - - /* Get PCI bus:dev.fn address */ - if ( ( efirc = u.pci->GetLocation ( u.pci, &pci_segment, &pci_bus, - &pci_dev, &pci_fn ) ) != 0 ) { - DBGC ( driver, "SNPDRV %p device %p could not get PCI " - "location: %s\n", - driver, device, efi_strerror ( efirc ) ); - goto out_no_pci_location; - } - DBGCP ( driver, "SNPDRV %p device %p is PCI %04lx:%02lx:%02lx.%lx\n", - driver, device, ( ( unsigned long ) pci_segment ), - ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ), - ( ( unsigned long ) pci_fn ) ); - - /* Look up corresponding network device */ - pci_busdevfn = PCI_BUSDEVFN ( pci_bus, PCI_DEVFN ( pci_dev, pci_fn ) ); - if ( ( netdev = find_netdev_by_location ( BUS_TYPE_PCI, - pci_busdevfn ) ) == NULL ) { - DBGCP ( driver, "SNPDRV %p device %p is not a gPXE network " - "device\n", driver, device ); - goto out_no_netdev; - } - DBGC ( driver, "SNPDRV %p device %p is %s\n", - driver, device, netdev->name ); - - out_no_netdev: - out_no_pci_location: - bs->CloseProtocol ( device, &efi_pci_io_protocol_guid, - driver->DriverBindingHandle, device ); - out_no_pci_io: - return netdev; -} - -/** - * Locate SNP corresponding to EFI device - * - * @v driver EFI driver - * @v device EFI device - * @ret snp EFI SNP, or NULL if not found - */ -static struct efi_snp_device * -efi_snp_snpdev ( EFI_DRIVER_BINDING_PROTOCOL *driver, EFI_HANDLE device ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - union { - EFI_SIMPLE_NETWORK_PROTOCOL *snp; - void *interface; - } u; - struct efi_snp_device *snpdev = NULL; - EFI_STATUS efirc; - - if ( ( efirc = bs->OpenProtocol ( device, - &efi_simple_network_protocol_guid, - &u.interface, - driver->DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_GET_PROTOCOL))!=0){ - DBGC ( driver, "SNPDRV %p device %p could not locate SNP: " - "%s\n", driver, device, efi_strerror ( efirc ) ); - goto err_no_snp; - } - - snpdev = container_of ( u.snp, struct efi_snp_device, snp ); - DBGCP ( driver, "SNPDRV %p device %p is SNPDEV %p\n", - driver, device, snpdev ); - - bs->CloseProtocol ( device, &efi_simple_network_protocol_guid, - driver->DriverBindingHandle, device ); - err_no_snp: - return snpdev; -} - -/** - * Check to see if driver supports a device - * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - struct net_device *netdev; - - DBGCP ( driver, "SNPDRV %p DRIVER_SUPPORTED %p (%p)\n", - driver, device, child ); - - netdev = efi_snp_netdev ( driver, device ); - return ( netdev ? 0 : EFI_UNSUPPORTED ); -} - -/** - * Attach driver to device - * - * @v driver EFI driver - * @v device EFI device - * @v child Path to child device, if any - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - EFI_DEVICE_PATH_PROTOCOL *child ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DEVICE_PATH_PROTOCOL *path; - EFI_DEVICE_PATH_PROTOCOL *subpath; - MAC_ADDR_DEVICE_PATH *macpath; - struct efi_snp_device *snpdev; - struct net_device *netdev; - size_t subpath_len; - size_t path_prefix_len = 0; - unsigned int i; - EFI_STATUS efirc; - - DBGCP ( driver, "SNPDRV %p DRIVER_START %p (%p)\n", - driver, device, child ); - - /* Determine device path prefix length */ - if ( ( efirc = bs->OpenProtocol ( device, - &efi_device_path_protocol_guid, - ( void * ) &path, - driver->DriverBindingHandle, - device, - EFI_OPEN_PROTOCOL_BY_DRIVER )) !=0 ){ - DBGCP ( driver, "SNPDRV %p device %p has no device path\n", - driver, device ); - goto err_no_device_path; - } - subpath = path; - while ( subpath->Type != END_DEVICE_PATH_TYPE ) { - subpath_len = ( ( subpath->Length[1] << 8 ) | - subpath->Length[0] ); - path_prefix_len += subpath_len; - subpath = ( ( ( void * ) subpath ) + subpath_len ); - } - - /* Allocate the SNP device */ - snpdev = zalloc ( sizeof ( *snpdev ) + path_prefix_len + - sizeof ( *macpath ) ); - if ( ! snpdev ) { - efirc = EFI_OUT_OF_RESOURCES; - goto err_alloc_snp; - } - - /* Identify the net device */ - netdev = efi_snp_netdev ( driver, device ); - if ( ! netdev ) { - DBGC ( snpdev, "SNPDEV %p cannot find netdev for device %p\n", - snpdev, device ); - efirc = EFI_UNSUPPORTED; - goto err_no_netdev; - } - snpdev->netdev = netdev_get ( netdev ); - - /* Sanity check */ - if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) { - DBGC ( snpdev, "SNPDEV %p cannot support link-layer address " - "length %d for %s\n", snpdev, - netdev->ll_protocol->ll_addr_len, netdev->name ); - efirc = EFI_INVALID_PARAMETER; - goto err_ll_addr_len; - } - - /* Populate the SNP structure */ - memcpy ( &snpdev->snp, &efi_snp_device_snp, sizeof ( snpdev->snp ) ); - snpdev->snp.Mode = &snpdev->mode; - if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY, - efi_snp_wait_for_packet, snpdev, - &snpdev->snp.WaitForPacket ) ) != 0 ){ - DBGC ( snpdev, "SNPDEV %p could not create event: %s\n", - snpdev, efi_strerror ( efirc ) ); - goto err_create_event; - } - - /* Populate the SNP mode structure */ - snpdev->mode.State = EfiSimpleNetworkStopped; - efi_snp_set_mode ( snpdev ); - - /* Populate the NII structure */ - snpdev->nii.Revision = - EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION; - strncpy ( snpdev->nii.StringId, "gPXE", - sizeof ( snpdev->nii.StringId ) ); - - /* Populate the device name */ - for ( i = 0 ; i < sizeof ( netdev->name ) ; i++ ) { - /* Damn Unicode names */ - assert ( i < ( sizeof ( snpdev->name ) / - sizeof ( snpdev->name[0] ) ) ); - snpdev->name[i] = netdev->name[i]; - } - - /* Populate the device path */ - memcpy ( &snpdev->path, path, path_prefix_len ); - macpath = ( ( ( void * ) &snpdev->path ) + path_prefix_len ); - subpath = ( ( void * ) ( macpath + 1 ) ); - memset ( macpath, 0, sizeof ( *macpath ) ); - macpath->Header.Type = MESSAGING_DEVICE_PATH; - macpath->Header.SubType = MSG_MAC_ADDR_DP; - macpath->Header.Length[0] = sizeof ( *macpath ); - memcpy ( &macpath->MacAddress, netdev->ll_addr, - sizeof ( macpath->MacAddress ) ); - macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto ); - memset ( subpath, 0, sizeof ( *subpath ) ); - subpath->Type = END_DEVICE_PATH_TYPE; - subpath->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE; - subpath->Length[0] = sizeof ( *subpath ); - - /* Install the SNP */ - if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &snpdev->handle, - &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, - &efi_nii_protocol_guid, &snpdev->nii, - &efi_nii31_protocol_guid, &snpdev->nii, - NULL ) ) != 0 ) { - DBGC ( snpdev, "SNPDEV %p could not install protocols: " - "%s\n", snpdev, efi_strerror ( efirc ) ); - goto err_install_protocol_interface; - } - - DBGC ( snpdev, "SNPDEV %p installed for %s as device %p\n", - snpdev, netdev->name, snpdev->handle ); - return 0; - - bs->UninstallMultipleProtocolInterfaces ( - snpdev->handle, - &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, - &efi_nii_protocol_guid, &snpdev->nii, - &efi_nii31_protocol_guid, &snpdev->nii, - NULL ); - err_install_protocol_interface: - bs->CloseEvent ( snpdev->snp.WaitForPacket ); - err_create_event: - err_ll_addr_len: - netdev_put ( netdev ); - err_no_netdev: - free ( snpdev ); - err_alloc_snp: - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - driver->DriverBindingHandle, device ); - err_no_device_path: - return efirc; -} - -/** - * Detach driver from device - * - * @v driver EFI driver - * @v device EFI device - * @v num_children Number of child devices - * @v children List of child devices - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver, - EFI_HANDLE device, - UINTN num_children, - EFI_HANDLE *children ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - struct efi_snp_device *snpdev; - - DBGCP ( driver, "SNPDRV %p DRIVER_STOP %p (%ld %p)\n", - driver, device, ( ( unsigned long ) num_children ), children ); - - /* Locate SNP device */ - snpdev = efi_snp_snpdev ( driver, device ); - if ( ! snpdev ) { - DBGC ( driver, "SNPDRV %p device %p could not find SNPDEV\n", - driver, device ); - return EFI_DEVICE_ERROR; - } - - /* Uninstall the SNP */ - bs->UninstallMultipleProtocolInterfaces ( - snpdev->handle, - &efi_simple_network_protocol_guid, &snpdev->snp, - &efi_device_path_protocol_guid, &snpdev->path, - &efi_nii_protocol_guid, &snpdev->nii, - &efi_nii31_protocol_guid, &snpdev->nii, - NULL ); - bs->CloseEvent ( snpdev->snp.WaitForPacket ); - netdev_put ( snpdev->netdev ); - free ( snpdev ); - bs->CloseProtocol ( device, &efi_device_path_protocol_guid, - driver->DriverBindingHandle, device ); - return 0; -} - -/** EFI SNP driver binding */ -static EFI_DRIVER_BINDING_PROTOCOL efi_snp_binding = { - efi_snp_driver_supported, - efi_snp_driver_start, - efi_snp_driver_stop, - 0x10, - NULL, - NULL -}; - -/** - * Look up driver name - * - * @v wtf Component name protocol - * @v language Language to use - * @v driver_name Driver name to fill in - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, - CHAR8 *language __unused, CHAR16 **driver_name ) { - - *driver_name = L"" PRODUCT_SHORT_NAME " Driver"; - return 0; -} - -/** - * Look up controller name - * - * @v wtf Component name protocol - * @v device Device - * @v child Child device, or NULL - * @v language Language to use - * @v driver_name Device name to fill in - * @ret efirc EFI status code - */ -static EFI_STATUS EFIAPI -efi_snp_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused, - EFI_HANDLE device __unused, - EFI_HANDLE child __unused, - CHAR8 *language __unused, - CHAR16 **controller_name __unused ) { - - /* Just let EFI use the default Device Path Name */ - return EFI_UNSUPPORTED; -} - -/** EFI SNP component name protocol */ -static EFI_COMPONENT_NAME2_PROTOCOL efi_snp_name = { - efi_snp_get_driver_name, - efi_snp_get_controller_name, - "en" -}; - -/** - * Install EFI SNP driver - * - * @ret rc Return status code - */ -int efi_snp_install ( void ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; - EFI_DRIVER_BINDING_PROTOCOL *driver = &efi_snp_binding; - EFI_STATUS efirc; - - driver->ImageHandle = efi_image_handle; - if ( ( efirc = bs->InstallMultipleProtocolInterfaces ( - &driver->DriverBindingHandle, - &efi_driver_binding_protocol_guid, driver, - &efi_component_name2_protocol_guid, &efi_snp_name, - NULL ) ) != 0 ) { - DBGC ( driver, "SNPDRV %p could not install protocols: " - "%s\n", driver, efi_strerror ( efirc ) ); - return EFIRC_TO_RC ( efirc ); - } - - DBGC ( driver, "SNPDRV %p driver binding installed as %p\n", - driver, driver->DriverBindingHandle ); - return 0; -} |