summaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/block/scsi.c4
-rw-r--r--src/drivers/bus/eisa.c8
-rw-r--r--src/drivers/bus/pci.c1
-rw-r--r--src/drivers/bus/pcibackup.c10
-rw-r--r--src/drivers/bus/pciextra.c9
-rw-r--r--src/drivers/infiniband/arbel.c4
-rwxr-xr-xsrc/drivers/infiniband/golan.c1
-rw-r--r--src/drivers/infiniband/hermon.c5
-rw-r--r--src/drivers/infiniband/qib7322.c4
-rw-r--r--src/drivers/net/ecm.c5
-rw-r--r--src/drivers/net/efi/nii.c59
-rw-r--r--src/drivers/net/efi/snp.c66
-rw-r--r--src/drivers/net/efi/snponly.c2
-rw-r--r--src/drivers/net/ena.c93
-rw-r--r--src/drivers/net/ena.h46
-rw-r--r--src/drivers/net/intel.c20
-rw-r--r--src/drivers/net/intel.h10
-rw-r--r--src/drivers/net/intelx.c1
-rw-r--r--src/drivers/net/netfront.c49
-rw-r--r--src/drivers/net/netfront.h5
-rw-r--r--src/drivers/net/realtek.c4
-rw-r--r--src/drivers/net/realtek.h5
22 files changed, 350 insertions, 61 deletions
diff --git a/src/drivers/block/scsi.c b/src/drivers/block/scsi.c
index f765c9762..ff415f5c6 100644
--- a/src/drivers/block/scsi.c
+++ b/src/drivers/block/scsi.c
@@ -609,6 +609,7 @@ static void scsicmd_read_capacity_cmd ( struct scsi_command *scsicmd,
*/
static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
int rc ) {
+ struct scsi_device *scsidev = scsicmd->scsidev;
struct scsi_read_capacity_private *priv = scsicmd_priv ( scsicmd );
struct scsi_capacity_16 *capacity16 = &priv->capacity.capacity16;
struct scsi_capacity_10 *capacity10 = &priv->capacity.capacity10;
@@ -645,6 +646,9 @@ static void scsicmd_read_capacity_done ( struct scsi_command *scsicmd,
}
capacity.max_count = -1U;
+ /* Allow transport layer to update capacity */
+ block_capacity ( &scsidev->scsi, &capacity );
+
/* Return capacity to caller */
block_capacity ( &scsicmd->block, &capacity );
diff --git a/src/drivers/bus/eisa.c b/src/drivers/bus/eisa.c
index a4efe2621..68837eb4d 100644
--- a/src/drivers/bus/eisa.c
+++ b/src/drivers/bus/eisa.c
@@ -98,8 +98,16 @@ static void eisa_remove ( struct eisa_device *eisa ) {
static int eisabus_probe ( struct root_device *rootdev ) {
struct eisa_device *eisa = NULL;
unsigned int slot;
+ uint8_t system;
int rc;
+ /* Check for EISA system board */
+ system = inb ( EISA_VENDOR_ID );
+ if ( system & 0x80 ) {
+ DBG ( "No EISA system board (read %02x)\n", system );
+ return -ENODEV;
+ }
+
for ( slot = EISA_MIN_SLOT ; slot <= EISA_MAX_SLOT ; slot++ ) {
/* Allocate struct eisa_device */
if ( ! eisa )
diff --git a/src/drivers/bus/pci.c b/src/drivers/bus/pci.c
index 7953aaedd..92b389641 100644
--- a/src/drivers/bus/pci.c
+++ b/src/drivers/bus/pci.c
@@ -205,6 +205,7 @@ int pci_read_config ( struct pci_device *pci ) {
pci_read_config_dword ( pci, PCI_REVISION, &tmp );
pci->class = ( tmp >> 8 );
pci_read_config_byte ( pci, PCI_INTERRUPT_LINE, &pci->irq );
+ pci_read_config_byte ( pci, PCI_HEADER_TYPE, &pci->hdrtype );
pci_read_bases ( pci );
/* Initialise generic device component */
diff --git a/src/drivers/bus/pcibackup.c b/src/drivers/bus/pcibackup.c
index fecad8192..4cf126f83 100644
--- a/src/drivers/bus/pcibackup.c
+++ b/src/drivers/bus/pcibackup.c
@@ -61,14 +61,15 @@ pci_backup_excluded ( struct pci_device *pci, unsigned int offset,
*
* @v pci PCI device
* @v backup PCI configuration space backup
+ * @v limit Maximum offset in PCI configuration space
* @v exclude PCI configuration space backup exclusion list, or NULL
*/
void pci_backup ( struct pci_device *pci, struct pci_config_backup *backup,
- const uint8_t *exclude ) {
+ unsigned int limit, const uint8_t *exclude ) {
unsigned int offset;
uint32_t *dword;
- for ( offset = 0, dword = backup->dwords ; offset < 0x100 ;
+ for ( offset = 0, dword = backup->dwords ; offset < limit ;
offset += sizeof ( *dword ) , dword++ ) {
if ( ! pci_backup_excluded ( pci, offset, exclude ) )
pci_read_config_dword ( pci, offset, dword );
@@ -80,14 +81,15 @@ void pci_backup ( struct pci_device *pci, struct pci_config_backup *backup,
*
* @v pci PCI device
* @v backup PCI configuration space backup
+ * @v limit Maximum offset in PCI configuration space
* @v exclude PCI configuration space backup exclusion list, or NULL
*/
void pci_restore ( struct pci_device *pci, struct pci_config_backup *backup,
- const uint8_t *exclude ) {
+ unsigned int limit, const uint8_t *exclude ) {
unsigned int offset;
uint32_t *dword;
- for ( offset = 0, dword = backup->dwords ; offset < 0x100 ;
+ for ( offset = 0, dword = backup->dwords ; offset < limit ;
offset += sizeof ( *dword ) , dword++ ) {
if ( ! pci_backup_excluded ( pci, offset, exclude ) )
pci_write_config_dword ( pci, offset, *dword );
diff --git a/src/drivers/bus/pciextra.c b/src/drivers/bus/pciextra.c
index 23617bc9a..1eeb9b2a0 100644
--- a/src/drivers/bus/pciextra.c
+++ b/src/drivers/bus/pciextra.c
@@ -3,6 +3,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/timer.h>
#include <ipxe/pci.h>
+#include <ipxe/pcibackup.h>
static int pci_find_capability_common ( struct pci_device *pci,
uint8_t pos, int cap ) {
@@ -121,8 +122,12 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) {
* @v exp PCI Express Capability address
*/
void pci_reset ( struct pci_device *pci, unsigned int exp ) {
+ struct pci_config_backup backup;
uint16_t control;
+ /* Back up configuration space */
+ pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL );
+
/* Perform a PCIe function-level reset */
pci_read_config_word ( pci, ( exp + PCI_EXP_DEVCTL ), &control );
control |= PCI_EXP_DEVCTL_FLR;
@@ -131,6 +136,6 @@ void pci_reset ( struct pci_device *pci, unsigned int exp ) {
/* Allow time for reset to complete */
mdelay ( PCI_EXP_FLR_DELAY_MS );
- /* Re-enable device */
- adjust_pci_device ( pci );
+ /* Restore configuration */
+ pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_STANDARD, NULL );
}
diff --git a/src/drivers/infiniband/arbel.c b/src/drivers/infiniband/arbel.c
index fbef3f8a6..293c1b647 100644
--- a/src/drivers/infiniband/arbel.c
+++ b/src/drivers/infiniband/arbel.c
@@ -2561,7 +2561,7 @@ static void arbel_reset ( struct arbel *arbel ) {
unsigned int i;
/* Perform device reset and preserve PCI configuration */
- pci_backup ( pci, &backup, backup_exclude );
+ pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_ALL, backup_exclude );
writel ( ARBEL_RESET_MAGIC,
( arbel->config + ARBEL_RESET_OFFSET ) );
for ( i = 0 ; i < ARBEL_RESET_WAIT_TIME_MS ; i++ ) {
@@ -2570,7 +2570,7 @@ static void arbel_reset ( struct arbel *arbel ) {
if ( vendor != 0xffff )
break;
}
- pci_restore ( pci, &backup, backup_exclude );
+ pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_ALL, backup_exclude );
}
/**
diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c
index 2f1ab2357..ce02a867f 100755
--- a/src/drivers/infiniband/golan.c
+++ b/src/drivers/infiniband/golan.c
@@ -2647,6 +2647,7 @@ static struct pci_device_id golan_nics[] = {
PCI_ROM ( 0x15b3, 0x1021, "ConnectX-7", "ConnectX-7 HCA driver, DevID 4129", 0 ),
PCI_ROM ( 0x15b3, 0xa2d2, "BlueField", "BlueField integrated ConnectX-5 network controller HCA driver, DevID 41682", 0 ),
PCI_ROM ( 0x15b3, 0xa2d6, "BlueField-2", "BlueField-2 network controller HCA driver, DevID 41686", 0 ),
+ PCI_ROM ( 0x15b3, 0xa2dc, "BlueField-3", "BlueField-3 network controller HCA driver, DevID 41692", 0 ),
};
struct pci_driver golan_driver __pci_driver = {
diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c
index 2afaaf991..c09baf7ae 100644
--- a/src/drivers/infiniband/hermon.c
+++ b/src/drivers/infiniband/hermon.c
@@ -2840,7 +2840,7 @@ static int hermon_reset ( struct hermon *hermon ) {
hermon->toggle = 0;
/* Perform device reset and preserve PCI configuration */
- pci_backup ( pci, &backup, backup_exclude );
+ pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_ALL, backup_exclude );
writel ( HERMON_RESET_MAGIC,
( hermon->config + HERMON_RESET_OFFSET ) );
@@ -2852,7 +2852,8 @@ static int hermon_reset ( struct hermon *hermon ) {
if ( vendor == pci->vendor ) {
/* Restore PCI configuration */
- pci_restore ( pci, &backup, backup_exclude );
+ pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_ALL,
+ backup_exclude );
DBGC ( hermon, "Hermon %p reset after %dms\n",
hermon, i );
diff --git a/src/drivers/infiniband/qib7322.c b/src/drivers/infiniband/qib7322.c
index a4b51db05..da055b744 100644
--- a/src/drivers/infiniband/qib7322.c
+++ b/src/drivers/infiniband/qib7322.c
@@ -2256,7 +2256,7 @@ static void qib7322_reset ( struct qib7322 *qib7322, struct pci_device *pci ) {
struct pci_config_backup backup;
/* Back up PCI configuration space */
- pci_backup ( pci, &backup, NULL );
+ pci_backup ( pci, &backup, PCI_CONFIG_BACKUP_ALL, NULL );
/* Assert reset */
memset ( &control, 0, sizeof ( control ) );
@@ -2267,7 +2267,7 @@ static void qib7322_reset ( struct qib7322 *qib7322, struct pci_device *pci ) {
mdelay ( 1000 );
/* Restore PCI configuration space */
- pci_restore ( pci, &backup, NULL );
+ pci_restore ( pci, &backup, PCI_CONFIG_BACKUP_ALL, NULL );
}
/**
diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c
index 68ac962ab..ab1f98370 100644
--- a/src/drivers/net/ecm.c
+++ b/src/drivers/net/ecm.c
@@ -121,10 +121,9 @@ int ecm_fetch_mac ( struct usb_function *func,
}
/* Apply system-specific MAC address as current link-layer
- * address, if present and not already used.
+ * address, if present.
*/
- if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) &&
- ! find_netdev_by_ll_addr ( &ethernet_protocol, amac ) ) {
+ if ( ( rc = acpi_mac ( amac ) ) == 0 ) {
memcpy ( netdev->ll_addr, amac, ETH_ALEN );
DBGC ( usb, "USB %s using system-specific MAC %s\n",
func->name, eth_ntoa ( netdev->ll_addr ) );
diff --git a/src/drivers/net/efi/nii.c b/src/drivers/net/efi/nii.c
index 833462e77..be5bce4b4 100644
--- a/src/drivers/net/efi/nii.c
+++ b/src/drivers/net/efi/nii.c
@@ -222,7 +222,7 @@ static int nii_pci_open ( struct nii_nic *nii ) {
/* Locate PCI I/O protocol */
if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid,
- &pci_device ) ) != 0 ) {
+ &pci_device, 0 ) ) != 0 ) {
DBGC ( nii, "NII %s could not locate PCI I/O protocol: %s\n",
nii->dev.name, strerror ( rc ) );
goto err_locate;
@@ -921,18 +921,17 @@ static int nii_set_station_address ( struct nii_nic *nii,
* Set receive filters
*
* @v nii NII NIC
+ * @v flags Flags
* @ret rc Return status code
*/
-static int nii_set_rx_filters ( struct nii_nic *nii ) {
+static int nii_set_rx_filters ( struct nii_nic *nii, unsigned int flags ) {
uint32_t implementation = nii->undi->Implementation;
- unsigned int flags;
unsigned int op;
int stat;
int rc;
/* Construct receive filter set */
- flags = ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE |
- PXE_OPFLAGS_RECEIVE_FILTER_UNICAST );
+ flags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST;
if ( implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED )
flags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED )
@@ -944,8 +943,12 @@ static int nii_set_rx_filters ( struct nii_nic *nii ) {
op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, flags );
if ( ( stat = nii_issue ( nii, op ) ) < 0 ) {
rc = -EIO_STAT ( stat );
- DBGC ( nii, "NII %s could not set receive filters %#04x: %s\n",
- nii->dev.name, flags, strerror ( rc ) );
+ DBGC ( nii, "NII %s could not %s%sable receive filters "
+ "%#04x: %s\n", nii->dev.name,
+ ( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_ENABLE ) ?
+ "en" : "" ),
+ ( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_DISABLE ) ?
+ "dis" : "" ), flags, strerror ( rc ) );
return rc;
}
@@ -953,6 +956,28 @@ static int nii_set_rx_filters ( struct nii_nic *nii ) {
}
/**
+ * Enable receive filters
+ *
+ * @v nii NII NIC
+ * @ret rc Return status code
+ */
+static int nii_enable_rx_filters ( struct nii_nic *nii ) {
+
+ return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_ENABLE );
+}
+
+/**
+ * Disable receive filters
+ *
+ * @v nii NII NIC
+ * @ret rc Return status code
+ */
+static int nii_disable_rx_filters ( struct nii_nic *nii ) {
+
+ return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_DISABLE );
+}
+
+/**
* Transmit packet
*
* @v netdev Network device
@@ -1175,13 +1200,25 @@ static int nii_open ( struct net_device *netdev ) {
/* Treat as non-fatal */
}
- /* Set receive filters */
- if ( ( rc = nii_set_rx_filters ( nii ) ) != 0 )
- goto err_set_rx_filters;
+ /* Disable receive filters
+ *
+ * We have no reason to disable receive filters here (or
+ * anywhere), but some NII drivers have a bug which prevents
+ * packets from being received unless we attempt to disable
+ * the receive filters.
+ *
+ * Ignore any failures, since we genuinely don't care if the
+ * NII driver cannot disable the filters.
+ */
+ nii_disable_rx_filters ( nii );
+
+ /* Enable receive filters */
+ if ( ( rc = nii_enable_rx_filters ( nii ) ) != 0 )
+ goto err_enable_rx_filters;
return 0;
- err_set_rx_filters:
+ err_enable_rx_filters:
nii_shutdown ( nii );
err_initialise:
return rc;
diff --git a/src/drivers/net/efi/snp.c b/src/drivers/net/efi/snp.c
index fbd606902..1920cdbc5 100644
--- a/src/drivers/net/efi/snp.c
+++ b/src/drivers/net/efi/snp.c
@@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#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"
@@ -40,31 +41,46 @@ 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_supported ( EFI_HANDLE device ) {
+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, "SNP %s is provided by this binary\n",
+ DBGCP ( device, "HANDLE %s is provided by this binary\n",
efi_handle_name ( device ) );
return -ENOTTY;
}
- /* Test for presence of simple network protocol */
- if ( ( efirc = bs->OpenProtocol ( device,
- &efi_simple_network_protocol_guid,
+ /* Test for presence of protocol */
+ if ( ( efirc = bs->OpenProtocol ( device, protocol,
NULL, efi_image_handle, device,
EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){
- DBGCP ( device, "SNP %s is not an SNP device\n",
- efi_handle_name ( device ) );
+ DBGCP ( device, "HANDLE %s is not a %s device\n",
+ efi_handle_name ( device ),
+ efi_guid_ntoa ( protocol ) );
return -EEFI ( efirc );
}
- DBGC ( device, "SNP %s is an SNP device\n",
- efi_handle_name ( device ) );
+ /* 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;
}
@@ -74,30 +90,20 @@ static int snp_supported ( EFI_HANDLE device ) {
* @v device EFI device handle
* @ret rc Return status code
*/
-static int nii_supported ( EFI_HANDLE device ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- EFI_STATUS efirc;
+static int snp_supported ( EFI_HANDLE device ) {
- /* Check that this is not a device we are providing ourselves */
- if ( find_snpdev ( device ) != NULL ) {
- DBGCP ( device, "NII %s is provided by this binary\n",
- efi_handle_name ( device ) );
- return -ENOTTY;
- }
+ return snp_nii_supported ( device, &efi_simple_network_protocol_guid );
+}
- /* Test for presence of NII protocol */
- if ( ( efirc = bs->OpenProtocol ( device,
- &efi_nii31_protocol_guid,
- NULL, efi_image_handle, device,
- EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){
- DBGCP ( device, "NII %s is not an NII device\n",
- efi_handle_name ( device ) );
- return -EEFI ( efirc );
- }
- DBGC ( device, "NII %s is an NII device\n",
- efi_handle_name ( device ) );
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+static int nii_supported ( EFI_HANDLE device ) {
- return 0;
+ return snp_nii_supported ( device, &efi_nii31_protocol_guid );
}
/** EFI SNP driver */
diff --git a/src/drivers/net/efi/snponly.c b/src/drivers/net/efi/snponly.c
index cb7ea1bbc..674e0a050 100644
--- a/src/drivers/net/efi/snponly.c
+++ b/src/drivers/net/efi/snponly.c
@@ -80,7 +80,7 @@ static int chained_locate ( struct chained_protocol *chained ) {
/* Locate handle supporting this protocol */
if ( ( rc = efi_locate_device ( device, chained->protocol,
- &parent ) ) != 0 ) {
+ &parent, 0 ) ) != 0 ) {
DBGC ( device, "CHAINED %s does not support %s: %s\n",
efi_handle_name ( device ),
efi_guid_ntoa ( chained->protocol ), strerror ( rc ) );
diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c
index 22e7e1e30..7ce5b9eb9 100644
--- a/src/drivers/net/ena.c
+++ b/src/drivers/net/ena.c
@@ -351,6 +351,90 @@ static int ena_admin ( struct ena_nic *ena, union ena_aq_req *req,
}
/**
+ * Set async event notification queue config
+ *
+ * @v ena ENA device
+ * @v enabled Bitmask of the groups to enable
+ * @ret rc Return status code
+ */
+static int ena_set_aenq_config ( struct ena_nic *ena, uint32_t enabled ) {
+ union ena_aq_req *req;
+ union ena_acq_rsp *rsp;
+ union ena_feature *feature;
+ int rc;
+
+ /* Construct request */
+ req = ena_admin_req ( ena );
+ req->header.opcode = ENA_SET_FEATURE;
+ req->set_feature.id = ENA_AENQ_CONFIG;
+ feature = &req->set_feature.feature;
+ feature->aenq.enabled = cpu_to_le32 ( enabled );
+
+ /* Issue request */
+ if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Create async event notification queue
+ *
+ * @v ena ENA device
+ * @ret rc Return status code
+ */
+static int ena_create_async ( struct ena_nic *ena ) {
+ size_t aenq_len = ( ENA_AENQ_COUNT * sizeof ( ena->aenq.evt[0] ) );
+ int rc;
+
+ /* Allocate async event notification queue */
+ ena->aenq.evt = malloc_phys ( aenq_len, aenq_len );
+ if ( ! ena->aenq.evt ) {
+ rc = -ENOMEM;
+ goto err_alloc_aenq;
+ }
+ memset ( ena->aenq.evt, 0, aenq_len );
+
+ /* Program queue address and capabilities */
+ ena_set_base ( ena, ENA_AENQ_BASE, ena->aenq.evt );
+ ena_set_caps ( ena, ENA_AENQ_CAPS, ENA_AENQ_COUNT,
+ sizeof ( ena->aenq.evt[0] ) );
+
+ DBGC ( ena, "ENA %p AENQ [%08lx,%08lx)\n",
+ ena, virt_to_phys ( ena->aenq.evt ),
+ ( virt_to_phys ( ena->aenq.evt ) + aenq_len ) );
+
+ /* Disable all events */
+ if ( ( rc = ena_set_aenq_config ( ena, 0 ) ) != 0 )
+ goto err_set_aenq_config;
+
+ return 0;
+
+ err_set_aenq_config:
+ ena_clear_caps ( ena, ENA_AENQ_CAPS );
+ free_phys ( ena->aenq.evt, aenq_len );
+ err_alloc_aenq:
+ return rc;
+}
+
+/**
+ * Destroy async event notification queue
+ *
+ * @v ena ENA device
+ */
+static void ena_destroy_async ( struct ena_nic *ena ) {
+ size_t aenq_len = ( ENA_AENQ_COUNT * sizeof ( ena->aenq.evt[0] ) );
+
+ /* Clear queue capabilities */
+ ena_clear_caps ( ena, ENA_AENQ_CAPS );
+ wmb();
+
+ /* Free queue */
+ free_phys ( ena->aenq.evt, aenq_len );
+ DBGC ( ena, "ENA %p AENQ destroyed\n", ena );
+}
+
+/**
* Create submission queue
*
* @v ena ENA device
@@ -1098,6 +1182,10 @@ static int ena_probe ( struct pci_device *pci ) {
if ( ( rc = ena_create_admin ( ena ) ) != 0 )
goto err_create_admin;
+ /* Create async event notification queue */
+ if ( ( rc = ena_create_async ( ena ) ) != 0 )
+ goto err_create_async;
+
/* Set host attributes */
if ( ( rc = ena_set_host_attributes ( ena ) ) != 0 )
goto err_set_host_attributes;
@@ -1121,6 +1209,8 @@ static int ena_probe ( struct pci_device *pci ) {
err_register_netdev:
err_get_device_attributes:
err_set_host_attributes:
+ ena_destroy_async ( ena );
+ err_create_async:
ena_destroy_admin ( ena );
err_create_admin:
ena_reset ( ena );
@@ -1148,6 +1238,9 @@ static void ena_remove ( struct pci_device *pci ) {
/* Unregister network device */
unregister_netdev ( netdev );
+ /* Destroy async event notification queue */
+ ena_destroy_async ( ena );
+
/* Destroy admin queues */
ena_destroy_admin ( ena );
diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h
index 4e1896e86..0f280c700 100644
--- a/src/drivers/net/ena.h
+++ b/src/drivers/net/ena.h
@@ -24,6 +24,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Number of admin completion queue entries */
#define ENA_ACQ_COUNT 2
+/** Number of async event notification queue entries */
+#define ENA_AENQ_COUNT 2
+
/** Number of transmit queue entries */
#define ENA_TX_COUNT 16
@@ -60,6 +63,12 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Maximum time to wait for admin requests */
#define ENA_ADMIN_MAX_WAIT_MS 5000
+/** Async event notification queue capabilities register */
+#define ENA_AENQ_CAPS 0x34
+
+/** Async event notification queue base address register */
+#define ENA_AENQ_BASE 0x38
+
/** Device control register */
#define ENA_CTRL 0x54
#define ENA_CTRL_RESET 0x00000001UL /**< Reset */
@@ -130,6 +139,17 @@ struct ena_device_attributes {
uint32_t mtu;
} __attribute__ (( packed ));
+/** Async event notification queue config */
+#define ENA_AENQ_CONFIG 26
+
+/** Async event notification queue config */
+struct ena_aenq_config {
+ /** Bitmask of supported AENQ groups (device -> host) */
+ uint32_t supported;
+ /** Bitmask of enabled AENQ groups (host -> device) */
+ uint32_t enabled;
+} __attribute__ (( packed ));
+
/** Host attributes */
#define ENA_HOST_ATTRIBUTES 28
@@ -208,6 +228,8 @@ struct ena_host_info {
union ena_feature {
/** Device attributes */
struct ena_device_attributes device;
+ /** Async event notification queue config */
+ struct ena_aenq_config aenq;
/** Host attributes */
struct ena_host_attributes host;
};
@@ -506,6 +528,28 @@ struct ena_acq {
unsigned int phase;
};
+/** Async event notification queue event */
+struct ena_aenq_event {
+ /** Type of event */
+ uint16_t group;
+ /** ID of event */
+ uint16_t syndrome;
+ /** Phase */
+ uint8_t flags;
+ /** Reserved */
+ uint8_t reserved[3];
+ /** Timestamp */
+ uint64_t timestamp;
+ /** Additional event data */
+ uint8_t data[48];
+} __attribute__ (( packed ));
+
+/** Async event notification queue */
+struct ena_aenq {
+ /** Events */
+ struct ena_aenq_event *evt;
+};
+
/** Transmit submission queue entry */
struct ena_tx_sqe {
/** Length */
@@ -702,6 +746,8 @@ struct ena_nic {
struct ena_aq aq;
/** Admin completion queue */
struct ena_acq acq;
+ /** Async event notification queue */
+ struct ena_aenq aenq;
/** Transmit queue */
struct ena_qp tx;
/** Receive queue */
diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c
index ea3ebf68d..46527bdbb 100644
--- a/src/drivers/net/intel.c
+++ b/src/drivers/net/intel.c
@@ -290,6 +290,18 @@ static int intel_reset ( struct intel_nic *intel ) {
pba, readl ( intel->regs + INTEL_PBA ) );
}
+ /* The Intel I210's packet buffer size registers reset only on
+ * power up. If an operating system changes these but then
+ * the computer recieves a reset signal without losing power,
+ * the registers will stay the same (but be incompatible with
+ * other register defaults), thus making the device unable to
+ * pass traffic.
+ */
+ if ( intel->flags & INTEL_PBSIZE_RST ) {
+ writel ( INTEL_RXPBS_I210, intel->regs + INTEL_RXPBS );
+ writel ( INTEL_TXPBS_I210, intel->regs + INTEL_TXPBS );
+ }
+
/* Always reset MAC. Required to reset the TX and RX rings. */
writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL );
mdelay ( INTEL_RESET_DELAY_MS );
@@ -1139,7 +1151,7 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x1525, "82567v-4", "82567V-4", 0 ),
PCI_ROM ( 0x8086, 0x1526, "82576-5", "82576", 0 ),
PCI_ROM ( 0x8086, 0x1527, "82580-f2", "82580 Fiber", 0 ),
- PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ),
+ PCI_ROM ( 0x8086, 0x1533, "i210", "I210", INTEL_PBSIZE_RST ),
PCI_ROM ( 0x8086, 0x1539, "i211", "I211", 0 ),
PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ),
PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ),
@@ -1147,7 +1159,7 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", INTEL_NO_PHY_RST ),
PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x1570, "i219v", "I219-V", INTEL_I219 ),
- PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", 0 ),
+ PCI_ROM ( 0x8086, 0x157b, "i210-2", "I210", INTEL_PBSIZE_RST ),
PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", INTEL_NO_PHY_RST ),
PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ),
PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", INTEL_NO_PHY_RST ),
@@ -1173,6 +1185,10 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x15fa, "i219v-14", "I219-V (14)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15fb, "i219lm-13", "I219-LM (13)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15fc, "i219v-13", "I219-V (13)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x1a1c, "i219lm-17", "I219-LM (17)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x1a1d, "i219v-17", "I219-V (17)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x1a1e, "i219lm-16", "I219-LM (16)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x1a1f, "i219v-16", "I219-V (16)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ),
PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ),
PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ),
diff --git a/src/drivers/net/intel.h b/src/drivers/net/intel.h
index 4f51a80f6..29cf3a7d8 100644
--- a/src/drivers/net/intel.h
+++ b/src/drivers/net/intel.h
@@ -138,6 +138,10 @@ struct intel_descriptor {
/** Packet Buffer Size */
#define INTEL_PBS 0x01008UL
+/** Receive packet buffer size */
+#define INTEL_RXPBS 0x02404UL
+#define INTEL_RXPBS_I210 0x000000a2UL /**< I210 power-up default */
+
/** Receive Descriptor register block */
#define INTEL_RD 0x02800UL
@@ -154,6 +158,10 @@ struct intel_descriptor {
/** Receive buffer length */
#define INTEL_RX_MAX_LEN 2048
+/** Transmit packet buffer size */
+#define INTEL_TXPBS 0x03404UL
+#define INTEL_TXPBS_I210 0x04000014UL /**< I210 power-up default */
+
/** Transmit Descriptor register block */
#define INTEL_TD 0x03800UL
@@ -319,6 +327,8 @@ enum intel_flags {
INTEL_NO_ASDE = 0x0008,
/** Reset may cause a complete device hang */
INTEL_RST_HANG = 0x0010,
+ /** PBSIZE registers must be explicitly reset */
+ INTEL_PBSIZE_RST = 0x0020,
};
/** The i219 has a seriously broken reset mechanism */
diff --git a/src/drivers/net/intelx.c b/src/drivers/net/intelx.c
index f4dad8859..343d01374 100644
--- a/src/drivers/net/intelx.c
+++ b/src/drivers/net/intelx.c
@@ -473,6 +473,7 @@ static struct pci_device_id intelx_nics[] = {
PCI_ROM ( 0x8086, 0x10f9, "82599-cx4", "82599 (CX4)", 0 ),
PCI_ROM ( 0x8086, 0x10fb, "82599-sfp", "82599 (SFI/SFP+)", 0 ),
PCI_ROM ( 0x8086, 0x10fc, "82599-xaui", "82599 (XAUI/BX4)", 0 ),
+ PCI_ROM ( 0x8086, 0x151c, "82599-tn", "82599 (TN)", 0 ),
PCI_ROM ( 0x8086, 0x1528, "x540t", "X540-AT2/X540-BT2", 0 ),
PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ),
PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ),
diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c
index 1203e585c..90930a5a3 100644
--- a/src/drivers/net/netfront.c
+++ b/src/drivers/net/netfront.c
@@ -59,6 +59,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
EUNIQ ( EINFO_EIO, ( -(status) & 0x1f ), \
EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED )
+/** List of netfront devices */
+static LIST_HEAD ( netfront_devices );
+
/******************************************************************************
*
* XenStore interface
@@ -952,6 +955,7 @@ static int netfront_probe ( struct xen_device *xendev ) {
netdev->dev = &xendev->dev;
netfront = netdev->priv;
netfront->xendev = xendev;
+ netfront->netdev = netdev;
INIT_LIST_HEAD ( &netfront->rx_partial );
DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n",
xendev->key, xendev->backend, xendev->backend_id );
@@ -991,9 +995,13 @@ static int netfront_probe ( struct xen_device *xendev ) {
/* Set initial link state */
netdev_link_down ( netdev );
+ /* Add to list of netfront devices */
+ list_add_tail ( &netfront->list, &netfront_devices );
+
xen_set_drvdata ( xendev, netdev );
return 0;
+ list_del ( &netfront->list );
unregister_netdev ( netdev );
err_register_netdev:
err_read_mac:
@@ -1015,6 +1023,9 @@ static void netfront_remove ( struct xen_device *xendev ) {
struct netfront_nic *netfront = netdev->priv;
struct xen_hypervisor *xen = xendev->xen;
+ /* Remove from list of netfront devices */
+ list_del ( &netfront->list );
+
/* Unregister network device */
unregister_netdev ( netdev );
@@ -1033,3 +1044,41 @@ struct xen_driver netfront_driver __xen_driver = {
.probe = netfront_probe,
.remove = netfront_remove,
};
+
+/******************************************************************************
+ *
+ * Emulated PCI device inhibitor
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Inhibit emulated PCI devices
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int netfront_net_probe ( struct net_device *netdev ) {
+ struct netfront_nic *netfront;
+
+ /* Inhibit emulated PCI devices matching an existing netfront device */
+ list_for_each_entry ( netfront, &netfront_devices, list ) {
+ if ( ( netdev->dev != netfront->netdev->dev ) &&
+ ( netdev->ll_protocol->ll_addr_len == ETH_ALEN ) &&
+ ( memcmp ( netdev->hw_addr, netfront->netdev->hw_addr,
+ ETH_ALEN ) == 0 ) ) {
+ DBGC ( netfront, "NETFRONT %s inhibiting emulated %s "
+ "%s\n", netfront->xendev->key,
+ netdev->dev->driver_name, netdev->dev->name );
+ return -EEXIST;
+ }
+ }
+
+ return 0;
+}
+
+/** Emulated PCI device inhibitor driver */
+struct net_driver netfront_net_driver __net_driver = {
+ .name = "netfront",
+ .probe = netfront_net_probe,
+};
diff --git a/src/drivers/net/netfront.h b/src/drivers/net/netfront.h
index dca3ff1c5..de16d5291 100644
--- a/src/drivers/net/netfront.h
+++ b/src/drivers/net/netfront.h
@@ -159,6 +159,11 @@ struct netfront_nic {
/** Grant references */
grant_ref_t refs[NETFRONT_REF_COUNT];
+ /** Network device */
+ struct net_device *netdev;
+ /** List of netfront NICs */
+ struct list_head list;
+
/** Transmit ring */
struct netfront_ring tx;
/** Transmit front ring */
diff --git a/src/drivers/net/realtek.c b/src/drivers/net/realtek.c
index a43efb68b..80442ab83 100644
--- a/src/drivers/net/realtek.c
+++ b/src/drivers/net/realtek.c
@@ -1067,11 +1067,15 @@ static void realtek_detect ( struct realtek_nic *rtl ) {
* Note that enabling DAC seems to cause bizarre behaviour
* (lockups, garbage data on the wire) on some systems, even
* if only 32-bit addresses are used.
+ *
+ * Disable VLAN offload, since some cards seem to have it
+ * enabled by default.
*/
cpcr = readw ( rtl->regs + RTL_CPCR );
cpcr |= ( RTL_CPCR_MULRW | RTL_CPCR_CPRX | RTL_CPCR_CPTX );
if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
cpcr |= RTL_CPCR_DAC;
+ cpcr &= ~RTL_CPCR_VLAN;
writew ( cpcr, rtl->regs + RTL_CPCR );
check_cpcr = readw ( rtl->regs + RTL_CPCR );
diff --git a/src/drivers/net/realtek.h b/src/drivers/net/realtek.h
index d4642fd76..d50e349b0 100644
--- a/src/drivers/net/realtek.h
+++ b/src/drivers/net/realtek.h
@@ -228,8 +228,9 @@ enum realtek_legacy_status {
/** C+ Command Register (word) */
#define RTL_CPCR 0xe0
-#define RTL_CPCR_DAC 0x0010 /**< PCI Dual Address Cycle Enable */
-#define RTL_CPCR_MULRW 0x0008 /**< PCI Multiple Read/Write Enable */
+#define RTL_CPCR_VLAN 0x0040 /**< VLAN tag stripping enable */
+#define RTL_CPCR_DAC 0x0010 /**< PCI Dual Address Cycle enable */
+#define RTL_CPCR_MULRW 0x0008 /**< PCI Multiple Read/Write enable */
#define RTL_CPCR_CPRX 0x0002 /**< C+ receive enable */
#define RTL_CPCR_CPTX 0x0001 /**< C+ transmit enable */