diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c
index 7cb268ac..dad8941a 100644
--- a/src/drivers/net/intel.c
+++ b/src/drivers/net/intel.c
@@ -15,6 +15,10 @@
  * 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 );
@@ -233,27 +237,6 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) {
 	return -ENOENT;
 }
 
-/******************************************************************************
- *
- * Diagnostics
- *
- ******************************************************************************
- */
-
-/**
- * Dump diagnostic information
- *
- * @v intel		Intel device
- */
-static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) {
-
-	DBGC ( intel, "INTEL %p TDH=%04x TDT=%04x RDH=%04x RDT=%04x\n", intel,
-	       readl ( intel->regs + intel->tx.reg + INTEL_xDH ),
-	       readl ( intel->regs + intel->tx.reg + INTEL_xDT ),
-	       readl ( intel->regs + intel->rx.reg + INTEL_xDH ),
-	       readl ( intel->regs + intel->rx.reg + INTEL_xDT ) );
-}
-
 /******************************************************************************
  *
  * Device reset
@@ -269,39 +252,61 @@ static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) {
  */
 static int intel_reset ( struct intel_nic *intel ) {
 	uint32_t pbs;
+	uint32_t pba;
 	uint32_t ctrl;
 	uint32_t status;
+	uint32_t orig_ctrl;
+	uint32_t orig_status;
+
+	/* Record initial control and status register values */
+	orig_ctrl = ctrl = readl ( intel->regs + INTEL_CTRL );
+	orig_status = readl ( intel->regs + INTEL_STATUS );
 
 	/* Force RX and TX packet buffer allocation, to work around an
 	 * errata in ICH devices.
 	 */
-	pbs = readl ( intel->regs + INTEL_PBS );
-	if ( ( pbs == 0x14 ) || ( pbs == 0x18 ) ) {
+	if ( intel->flags & INTEL_PBS_ERRATA ) {
 		DBGC ( intel, "INTEL %p WARNING: applying ICH PBS/PBA errata\n",
 		       intel );
+		pbs = readl ( intel->regs + INTEL_PBS );
+		pba = readl ( intel->regs + INTEL_PBA );
 		writel ( 0x08, intel->regs + INTEL_PBA );
 		writel ( 0x10, intel->regs + INTEL_PBS );
+		DBGC ( intel, "INTEL %p PBS %#08x->%#08x PBA %#08x->%#08x\n",
+		       intel, pbs, readl ( intel->regs + INTEL_PBS ),
+		       pba, readl ( intel->regs + INTEL_PBA ) );
 	}
 
 	/* Always reset MAC.  Required to reset the TX and RX rings. */
-	ctrl = readl ( intel->regs + INTEL_CTRL );
 	writel ( ( ctrl | INTEL_CTRL_RST ), intel->regs + INTEL_CTRL );
 	mdelay ( INTEL_RESET_DELAY_MS );
 
 	/* Set a sensible default configuration */
-	ctrl |= ( INTEL_CTRL_SLU | INTEL_CTRL_ASDE );
+	if ( ! ( intel->flags & INTEL_NO_ASDE ) )
+		ctrl |= INTEL_CTRL_ASDE;
+	ctrl |= INTEL_CTRL_SLU;
 	ctrl &= ~( INTEL_CTRL_LRST | INTEL_CTRL_FRCSPD | INTEL_CTRL_FRCDPLX );
 	writel ( ctrl, intel->regs + INTEL_CTRL );
 	mdelay ( INTEL_RESET_DELAY_MS );
 
-	/* If link is already up, do not attempt to reset the PHY.  On
-	 * some models (notably ICH), performing a PHY reset seems to
-	 * drop the link speed to 10Mbps.
+	/* On some models (notably ICH), the PHY reset mechanism
+	 * appears to be broken.  In particular, the PHY_CTRL register
+	 * will be correctly loaded from NVM but the values will not
+	 * be propagated to the "OEM bits" PHY register.  This
+	 * typically has the effect of dropping the link speed to
+	 * 10Mbps.
+	 *
+	 * Work around this problem by skipping the PHY reset if
+	 * either (a) the link is already up, or (b) this particular
+	 * NIC is known to be broken.
 	 */
 	status = readl ( intel->regs + INTEL_STATUS );
-	if ( status & INTEL_STATUS_LU ) {
-		DBGC ( intel, "INTEL %p MAC reset (ctrl %08x)\n",
-		       intel, ctrl );
+	if ( ( intel->flags & INTEL_NO_PHY_RST ) ||
+	     ( status & INTEL_STATUS_LU ) ) {
+		DBGC ( intel, "INTEL %p %sMAC reset (%08x/%08x was "
+		       "%08x/%08x)\n", intel,
+		       ( ( intel->flags & INTEL_NO_PHY_RST ) ? "forced " : "" ),
+		       ctrl, status, orig_ctrl, orig_status );
 		return 0;
 	}
 
@@ -316,8 +321,10 @@ static int intel_reset ( struct intel_nic *intel ) {
 	/* PHY reset is not self-clearing on all models */
 	writel ( ctrl, intel->regs + INTEL_CTRL );
 	mdelay ( INTEL_RESET_DELAY_MS );
+	status = readl ( intel->regs + INTEL_STATUS );
 
-	DBGC ( intel, "INTEL %p MAC+PHY reset (ctrl %08x)\n", intel, ctrl );
+	DBGC ( intel, "INTEL %p MAC+PHY reset (%08x/%08x was %08x/%08x)\n",
+	       intel, ctrl, status, orig_ctrl, orig_status );
 	return 0;
 }
 
@@ -349,6 +356,67 @@ static void intel_check_link ( struct net_device *netdev ) {
 	}
 }
 
+/******************************************************************************
+ *
+ * Descriptors
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Populate transmit descriptor
+ *
+ * @v tx		Transmit descriptor
+ * @v addr		Data buffer address
+ * @v len		Length of data
+ */
+void intel_describe_tx ( struct intel_descriptor *tx, physaddr_t addr,
+			 size_t len ) {
+
+	/* Populate transmit descriptor */
+	tx->address = cpu_to_le64 ( addr );
+	tx->length = cpu_to_le16 ( len );
+	tx->flags = 0;
+	tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS |
+			INTEL_DESC_CMD_EOP );
+	tx->status = 0;
+}
+
+/**
+ * Populate advanced transmit descriptor
+ *
+ * @v tx		Transmit descriptor
+ * @v addr		Data buffer address
+ * @v len		Length of data
+ */
+void intel_describe_tx_adv ( struct intel_descriptor *tx, physaddr_t addr,
+			     size_t len ) {
+
+	/* Populate advanced transmit descriptor */
+	tx->address = cpu_to_le64 ( addr );
+	tx->length = cpu_to_le16 ( len );
+	tx->flags = INTEL_DESC_FL_DTYP_DATA;
+	tx->command = ( INTEL_DESC_CMD_DEXT | INTEL_DESC_CMD_RS |
+			INTEL_DESC_CMD_IFCS | INTEL_DESC_CMD_EOP );
+	tx->status = cpu_to_le32 ( INTEL_DESC_STATUS_PAYLEN ( len ) );
+}
+
+/**
+ * Populate receive descriptor
+ *
+ * @v rx		Receive descriptor
+ * @v addr		Data buffer address
+ * @v len		Length of data
+ */
+void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr,
+			 size_t len __unused ) {
+
+	/* Populate transmit descriptor */
+	rx->address = cpu_to_le64 ( addr );
+	rx->length = 0;
+	rx->status = 0;
+}
+
 /******************************************************************************
  *
  * Network device interface
@@ -356,6 +424,61 @@ static void intel_check_link ( struct net_device *netdev ) {
  ******************************************************************************
  */
 
+/**
+ * Disable descriptor ring
+ *
+ * @v intel		Intel device
+ * @v reg		Register block
+ * @ret rc		Return status code
+ */
+static int intel_disable_ring ( struct intel_nic *intel, unsigned int reg ) {
+	uint32_t dctl;
+	unsigned int i;
+
+	/* Disable ring */
+	writel ( 0, ( intel->regs + reg + INTEL_xDCTL ) );
+
+	/* Wait for disable to complete */
+	for ( i = 0 ; i < INTEL_DISABLE_MAX_WAIT_MS ; i++ ) {
+
+		/* Check if ring is disabled */
+		dctl = readl ( intel->regs + reg + INTEL_xDCTL );
+		if ( ! ( dctl & INTEL_xDCTL_ENABLE ) )
+			return 0;
+
+		/* Delay */
+		mdelay ( 1 );
+	}
+
+	DBGC ( intel, "INTEL %p ring %05x timed out waiting for disable "
+	       "(dctl %08x)\n", intel, reg, dctl );
+	return -ETIMEDOUT;
+}
+
+/**
+ * Reset descriptor ring
+ *
+ * @v intel		Intel device
+ * @v reg		Register block
+ * @ret rc		Return status code
+ */
+void intel_reset_ring ( struct intel_nic *intel, unsigned int reg ) {
+
+	/* Disable ring.  Ignore errors and continue to reset the ring anyway */
+	intel_disable_ring ( intel, reg );
+
+	/* Clear ring length */
+	writel ( 0, ( intel->regs + reg + INTEL_xDLEN ) );
+
+	/* Clear ring address */
+	writel ( 0, ( intel->regs + reg + INTEL_xDBAH ) );
+	writel ( 0, ( intel->regs + reg + INTEL_xDBAL ) );
+
+	/* Reset head and tail pointers */
+	writel ( 0, ( intel->regs + reg + INTEL_xDH ) );
+	writel ( 0, ( intel->regs + reg + INTEL_xDT ) );
+}
+
 /**
  * Create descriptor ring
  *
@@ -416,12 +539,8 @@ int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ) {
  */
 void intel_destroy_ring ( struct intel_nic *intel, struct intel_ring *ring ) {
 
-	/* Clear ring length */
-	writel ( 0, ( intel->regs + ring->reg + INTEL_xDLEN ) );
-
-	/* Clear ring address */
-	writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAL ) );
-	writel ( 0, ( intel->regs + ring->reg + INTEL_xDBAH ) );
+	/* Reset ring */
+	intel_reset_ring ( intel, ring->reg );
 
 	/* Free descriptor ring */
 	free_dma ( ring->desc, ring->len );
@@ -441,39 +560,41 @@ void intel_refill_rx ( struct intel_nic *intel ) {
 	unsigned int rx_idx;
 	unsigned int rx_tail;
 	physaddr_t address;
+	unsigned int refilled = 0;
 
+	/* Refill ring */
 	while ( ( intel->rx.prod - intel->rx.cons ) < INTEL_RX_FILL ) {
 
 		/* Allocate I/O buffer */
 		iobuf = alloc_iob ( INTEL_RX_MAX_LEN );
 		if ( ! iobuf ) {
 			/* Wait for next refill */
-			return;
+			break;
 		}
 
 		/* Get next receive descriptor */
 		rx_idx = ( intel->rx.prod++ % INTEL_NUM_RX_DESC );
-		rx_tail = ( intel->rx.prod % INTEL_NUM_RX_DESC );
 		rx = &intel->rx.desc[rx_idx];
 
 		/* Populate receive descriptor */
 		address = virt_to_bus ( iobuf->data );
-		rx->address = cpu_to_le64 ( address );
-		rx->length = 0;
-		rx->status = 0;
-		rx->errors = 0;
-		wmb();
+		intel->rx.describe ( rx, address, 0 );
 
 		/* Record I/O buffer */
 		assert ( intel->rx_iobuf[rx_idx] == NULL );
 		intel->rx_iobuf[rx_idx] = iobuf;
 
-		/* Push descriptor to card */
-		writel ( rx_tail, intel->regs + intel->rx.reg + INTEL_xDT );
-
 		DBGC2 ( intel, "INTEL %p RX %d is [%llx,%llx)\n", intel, rx_idx,
 			( ( unsigned long long ) address ),
 			( ( unsigned long long ) address + INTEL_RX_MAX_LEN ) );
+		refilled++;
+	}
+
+	/* Push descriptors to card, if applicable */
+	if ( refilled ) {
+		wmb();
+		rx_tail = ( intel->rx.prod % INTEL_NUM_RX_DESC );
+		writel ( rx_tail, intel->regs + intel->rx.reg + INTEL_xDT );
 	}
 }
 
@@ -501,10 +622,23 @@ void intel_empty_rx ( struct intel_nic *intel ) {
 static int intel_open ( struct net_device *netdev ) {
 	struct intel_nic *intel = netdev->priv;
 	union intel_receive_address mac;
+	uint32_t fextnvm11;
 	uint32_t tctl;
 	uint32_t rctl;
 	int rc;
 
+	/* Set undocumented bit in FEXTNVM11 to work around an errata
+	 * in i219 devices that will otherwise cause a complete
+	 * datapath hang at the next device reset.
+	 */
+	if ( intel->flags & INTEL_RST_HANG ) {
+		DBGC ( intel, "INTEL %p WARNING: applying reset hang "
+		       "workaround\n", intel );
+		fextnvm11 = readl ( intel->regs + INTEL_FEXTNVM11 );
+		fextnvm11 |= INTEL_FEXTNVM11_WTF;
+		writel ( fextnvm11, intel->regs + INTEL_FEXTNVM11 );
+	}
+
 	/* Create transmit descriptor ring */
 	if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 )
 		goto err_create_tx;
@@ -540,6 +674,13 @@ static int intel_open ( struct net_device *netdev ) {
 	/* Update link state */
 	intel_check_link ( netdev );
 
+	/* Apply required errata */
+	if ( intel->flags & INTEL_VMWARE ) {
+		DBGC ( intel, "INTEL %p applying VMware errata workaround\n",
+		       intel );
+		intel->force_icr = INTEL_IRQ_RXT0;
+	}
+
 	return 0;
 
 	intel_destroy_ring ( intel, &intel->rx );
@@ -589,9 +730,10 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
 	unsigned int tx_idx;
 	unsigned int tx_tail;
 	physaddr_t address;
+	size_t len;
 
 	/* Get next transmit descriptor */
-	if ( ( intel->tx.prod - intel->tx.cons ) >= INTEL_NUM_TX_DESC ) {
+	if ( ( intel->tx.prod - intel->tx.cons ) >= INTEL_TX_FILL ) {
 		DBGC ( intel, "INTEL %p out of transmit descriptors\n", intel );
 		return -ENOBUFS;
 	}
@@ -601,11 +743,8 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
 
 	/* Populate transmit descriptor */
 	address = virt_to_bus ( iobuf->data );
-	tx->address = cpu_to_le64 ( address );
-	tx->length = cpu_to_le16 ( iob_len ( iobuf ) );
-	tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS |
-			INTEL_DESC_CMD_EOP );
-	tx->status = 0;
+	len = iob_len ( iobuf );
+	intel->tx.describe ( tx, address, len );
 	wmb();
 
 	/* Notify card that there are packets ready to transmit */
@@ -613,7 +752,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
 
 	DBGC2 ( intel, "INTEL %p TX %d is [%llx,%llx)\n", intel, tx_idx,
 		( ( unsigned long long ) address ),
-		( ( unsigned long long ) address + iob_len ( iobuf ) ) );
+		( ( unsigned long long ) address + len ) );
 
 	return 0;
 }
@@ -636,7 +775,7 @@ void intel_poll_tx ( struct net_device *netdev ) {
 		tx = &intel->tx.desc[tx_idx];
 
 		/* Stop if descriptor is still in use */
-		if ( ! ( tx->status & INTEL_DESC_STATUS_DD ) )
+		if ( ! ( tx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) )
 			return;
 
 		DBGC2 ( intel, "INTEL %p TX %d complete\n", intel, tx_idx );
@@ -667,7 +806,7 @@ void intel_poll_rx ( struct net_device *netdev ) {
 		rx = &intel->rx.desc[rx_idx];
 
 		/* Stop if descriptor is still in use */
-		if ( ! ( rx->status & INTEL_DESC_STATUS_DD ) )
+		if ( ! ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) )
 			return;
 
 		/* Populate I/O buffer */
@@ -677,10 +816,10 @@ void intel_poll_rx ( struct net_device *netdev ) {
 		iob_put ( iobuf, len );
 
 		/* Hand off to network stack */
-		if ( rx->errors ) {
+		if ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_RXE ) ) {
 			DBGC ( intel, "INTEL %p RX %d error (length %zd, "
-			       "errors %02x)\n",
-			       intel, rx_idx, len, rx->errors );
+			       "status %08x)\n", intel, rx_idx, len,
+			       le32_to_cpu ( rx->status ) );
 			netdev_rx_err ( netdev, iobuf, -EIO );
 		} else {
 			DBGC2 ( intel, "INTEL %p RX %d complete (length %zd)\n",
@@ -721,6 +860,14 @@ static void intel_poll ( struct net_device *netdev ) {
 	if ( icr & INTEL_IRQ_LSC )
 		intel_check_link ( netdev );
 
+	/* Check for unexpected interrupts */
+	if ( icr & ~( INTEL_IRQ_TXDW | INTEL_IRQ_TXQE | INTEL_IRQ_LSC |
+		      INTEL_IRQ_RXDMT0 | INTEL_IRQ_RXT0 | INTEL_IRQ_RXO ) ) {
+		DBGC ( intel, "INTEL %p unexpected ICR %08x\n", intel, icr );
+		/* Report as a TX error */
+		netdev_tx_err ( netdev, NULL, -ENOTSUP );
+	}
+
 	/* Refill RX ring */
 	intel_refill_rx ( intel );
 }
@@ -782,14 +929,21 @@ static int intel_probe ( struct pci_device *pci ) {
 	netdev->dev = &pci->dev;
 	memset ( intel, 0, sizeof ( *intel ) );
 	intel->port = PCI_FUNC ( pci->busdevfn );
-	intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD );
-	intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD );
+	intel->flags = pci->id->driver_data;
+	intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD,
+			  intel_describe_tx );
+	intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD,
+			  intel_describe_rx );
 
 	/* Fix up PCI device */
 	adjust_pci_device ( pci );
 
 	/* Map registers */
 	intel->regs = ioremap ( pci->membase, INTEL_BAR_SIZE );
+	if ( ! intel->regs ) {
+		rc = -ENODEV;
+		goto err_ioremap;
+	}
 
 	/* Reset the NIC */
 	if ( ( rc = intel_reset ( intel ) ) != 0 )
@@ -814,6 +968,7 @@ static int intel_probe ( struct pci_device *pci ) {
 	intel_reset ( intel );
  err_reset:
 	iounmap ( intel->regs );
+ err_ioremap:
 	netdev_nullify ( netdev );
 	netdev_put ( netdev );
  err_alloc:
@@ -855,7 +1010,7 @@ static struct pci_device_id intel_nics[] = {
 	PCI_ROM ( 0x8086, 0x100c, "82544gc", "82544GC (Copper)", 0 ),
 	PCI_ROM ( 0x8086, 0x100d, "82544gc-l", "82544GC (LOM)", 0 ),
 	PCI_ROM ( 0x8086, 0x100e, "82540em", "82540EM", 0 ),
-	PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", 0 ),
+	PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", INTEL_VMWARE ),
 	PCI_ROM ( 0x8086, 0x1010, "82546eb", "82546EB (Copper)", 0 ),
 	PCI_ROM ( 0x8086, 0x1011, "82545em-f", "82545EM (Fiber)", 0 ),
 	PCI_ROM ( 0x8086, 0x1012, "82546eb-f", "82546EB (Fiber)", 0 ),
@@ -872,11 +1027,11 @@ static struct pci_device_id intel_nics[] = {
 	PCI_ROM ( 0x8086, 0x1026, "82545gm", "82545GM", 0 ),
 	PCI_ROM ( 0x8086, 0x1027, "82545gm-1", "82545GM", 0 ),
 	PCI_ROM ( 0x8086, 0x1028, "82545gm-2", "82545GM", 0 ),
-	PCI_ROM ( 0x8086, 0x1049, "82566mm", "82566MM", 0 ),
-	PCI_ROM ( 0x8086, 0x104a, "82566dm", "82566DM", 0 ),
-	PCI_ROM ( 0x8086, 0x104b, "82566dc", "82566DC", 0 ),
-	PCI_ROM ( 0x8086, 0x104c, "82562v", "82562V 10/100", 0 ),
-	PCI_ROM ( 0x8086, 0x104d, "82566mc", "82566MC", 0 ),
+	PCI_ROM ( 0x8086, 0x1049, "82566mm", "82566MM", INTEL_PBS_ERRATA ),
+	PCI_ROM ( 0x8086, 0x104a, "82566dm", "82566DM", INTEL_PBS_ERRATA ),
+	PCI_ROM ( 0x8086, 0x104b, "82566dc", "82566DC", INTEL_PBS_ERRATA ),
+	PCI_ROM ( 0x8086, 0x104c, "82562v", "82562V", INTEL_PBS_ERRATA ),
+	PCI_ROM ( 0x8086, 0x104d, "82566mc", "82566MC", INTEL_PBS_ERRATA ),
 	PCI_ROM ( 0x8086, 0x105e, "82571eb", "82571EB", 0 ),
 	PCI_ROM ( 0x8086, 0x105f, "82571eb-1", "82571EB", 0 ),
 	PCI_ROM ( 0x8086, 0x1060, "82571eb-2", "82571EB", 0 ),
@@ -909,11 +1064,11 @@ static struct pci_device_id intel_nics[] = {
 	PCI_ROM ( 0x8086, 0x10bc, "82571eb", "82571EB (Copper)", 0 ),
 	PCI_ROM ( 0x8086, 0x10bd, "82566dm-2", "82566DM-2", 0 ),
 	PCI_ROM ( 0x8086, 0x10bf, "82567lf", "82567LF", 0 ),
-	PCI_ROM ( 0x8086, 0x10c0, "82562v-2", "82562V-2 10/100", 0 ),
-	PCI_ROM ( 0x8086, 0x10c2, "82562g-2", "82562G-2 10/100", 0 ),
-	PCI_ROM ( 0x8086, 0x10c3, "82562gt-2", "82562GT-2 10/100", 0 ),
-	PCI_ROM ( 0x8086, 0x10c4, "82562gt", "82562GT 10/100", 0 ),
-	PCI_ROM ( 0x8086, 0x10c5, "82562g", "82562G 10/100", 0 ),
+	PCI_ROM ( 0x8086, 0x10c0, "82562v-2", "82562V-2", 0 ),
+	PCI_ROM ( 0x8086, 0x10c2, "82562g-2", "82562G-2", 0 ),
+	PCI_ROM ( 0x8086, 0x10c3, "82562gt-2", "82562GT-2", 0 ),
+	PCI_ROM ( 0x8086, 0x10c4, "82562gt", "82562GT", INTEL_PBS_ERRATA ),
+	PCI_ROM ( 0x8086, 0x10c5, "82562g", "82562G", INTEL_PBS_ERRATA ),
 	PCI_ROM ( 0x8086, 0x10c9, "82576", "82576", 0 ),
 	PCI_ROM ( 0x8086, 0x10cb, "82567v", "82567V", 0 ),
 	PCI_ROM ( 0x8086, 0x10cc, "82567lm-2", "82567LM-2", 0 ),
@@ -936,8 +1091,8 @@ static struct pci_device_id intel_nics[] = {
 	PCI_ROM ( 0x8086, 0x10f0, "82578dc", "82578DC", 0 ),
 	PCI_ROM ( 0x8086, 0x10f5, "82567lm", "82567LM", 0 ),
 	PCI_ROM ( 0x8086, 0x10f6, "82574l", "82574L", 0 ),
-	PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", 0 ),
-	PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", 0 ),
+	PCI_ROM ( 0x8086, 0x1501, "82567v-3", "82567V-3", INTEL_PBS_ERRATA ),
+	PCI_ROM ( 0x8086, 0x1502, "82579lm", "82579LM", INTEL_NO_PHY_RST ),
 	PCI_ROM ( 0x8086, 0x1503, "82579v", "82579V", 0 ),
 	PCI_ROM ( 0x8086, 0x150a, "82576ns", "82576NS", 0 ),
 	PCI_ROM ( 0x8086, 0x150c, "82583v", "82583V", 0 ),
@@ -950,24 +1105,40 @@ static struct pci_device_id intel_nics[] = {
 	PCI_ROM ( 0x8086, 0x1518, "82576ns", "82576NS SerDes", 0 ),
 	PCI_ROM ( 0x8086, 0x1521, "i350", "I350", 0 ),
 	PCI_ROM ( 0x8086, 0x1522, "i350-f", "I350 Fiber", 0 ),
-	PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", 0 ),
+	PCI_ROM ( 0x8086, 0x1523, "i350-b", "I350 Backplane", INTEL_NO_ASDE ),
 	PCI_ROM ( 0x8086, 0x1524, "i350-2", "I350", 0 ),
 	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, 0x1539, "i211", "I211", 0 ),
-	PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217LM", 0 ),
-	PCI_ROM ( 0x8086, 0x1559, "i218v", "I218V", 0 ),
-	PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218LM", 0 ),
-	PCI_ROM ( 0x8086, 0x15a1, "i218v", "I218V", 0 ),
-	PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ),
-	PCI_ROM ( 0x8086, 0x156f, "i219lm", "I219-LM", 0 ),
-	PCI_ROM ( 0x8086, 0x15b7, "i219lm", "I219-LM", 0 ),
-	PCI_ROM ( 0x8086, 0x15d7, "i219lm", "I219-LM", 0 ),
-	PCI_ROM ( 0x8086, 0x15e3, "i219lm", "I219-LM", 0 ),
-	PCI_ROM ( 0x8086, 0x1570, "i219v", "I219V", 0 ),
+	PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", INTEL_NO_PHY_RST ),
+	PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ),
+	PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0),
+	PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0),
+	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, 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 ),
+	PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", INTEL_NO_PHY_RST ),
+	PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15bb, "i219lm-7", "I219-LM (7)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15bc, "i219v-7", "I219-V (7)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15bd, "i219lm-6", "I219-LM (6)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15be, "i219v-6", "I219-V (6)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15df, "i219lm-8", "I219-LM (8)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15e0, "i219v-8", "I219-V (8)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ),
+	PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", 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 2a11e1df..088a9fec 100644
--- a/src/drivers/net/intel.h
+++ b/src/drivers/net/intel.h
@@ -22,33 +22,38 @@ struct intel_descriptor {
 	uint64_t address;
 	/** Length */
 	uint16_t length;
-	/** Reserved */
-	uint8_t reserved_a;
+	/** Flags */
+	uint8_t flags;
 	/** Command */
 	uint8_t command;
 	/** Status */
-	uint8_t status;
-	/** Errors */
-	uint8_t errors;
-	/** Reserved */
-	uint16_t reserved_b;
+	uint32_t status;
 } __attribute__ (( packed ));
 
-/** Packet descriptor command bits */
-enum intel_descriptor_command {
-	/** Report status */
-	INTEL_DESC_CMD_RS = 0x08,
-	/** Insert frame checksum (CRC) */
-	INTEL_DESC_CMD_IFCS = 0x02,
-	/** End of packet */
-	INTEL_DESC_CMD_EOP = 0x01,
-};
-
-/** Packet descriptor status bits */
-enum intel_descriptor_status {
-	/** Descriptor done */
-	INTEL_DESC_STATUS_DD = 0x01,
-};
+/** Descriptor type */
+#define INTEL_DESC_FL_DTYP( dtyp ) ( (dtyp) << 4 )
+#define INTEL_DESC_FL_DTYP_DATA INTEL_DESC_FL_DTYP ( 0x03 )
+
+/** Descriptor extension */
+#define INTEL_DESC_CMD_DEXT 0x20
+
+/** Report status */
+#define INTEL_DESC_CMD_RS 0x08
+
+/** Insert frame checksum (CRC) */
+#define INTEL_DESC_CMD_IFCS 0x02
+
+/** End of packet */
+#define INTEL_DESC_CMD_EOP 0x01
+
+/** Descriptor done */
+#define INTEL_DESC_STATUS_DD 0x00000001UL
+
+/** Receive error */
+#define INTEL_DESC_STATUS_RXE 0x00000100UL
+
+/** Payload length */
+#define INTEL_DESC_STATUS_PAYLEN( len ) ( (len) << 14 )
 
 /** Device Control Register */
 #define INTEL_CTRL 0x00000UL
@@ -91,9 +96,11 @@ enum intel_descriptor_status {
 /** Interrupt Cause Read Register */
 #define INTEL_ICR 0x000c0UL
 #define INTEL_IRQ_TXDW		0x00000001UL	/**< Transmit descriptor done */
+#define INTEL_IRQ_TXQE		0x00000002UL	/**< Transmit queue empty */
 #define INTEL_IRQ_LSC		0x00000004UL	/**< Link status change */
+#define INTEL_IRQ_RXDMT0	0x00000010UL	/**< Receive queue low */
+#define INTEL_IRQ_RXO		0x00000040UL	/**< Receive overrun */
 #define INTEL_IRQ_RXT0		0x00000080UL	/**< Receive timer */
-#define INTEL_IRQ_RXO		0x00000400UL	/**< Receive overrun */
 
 /** Interrupt Mask Set/Read Register */
 #define INTEL_IMS 0x000d0UL
@@ -156,6 +163,9 @@ enum intel_descriptor_status {
  */
 #define INTEL_NUM_TX_DESC 256
 
+/** Transmit descriptor ring maximum fill level */
+#define INTEL_TX_FILL ( INTEL_NUM_TX_DESC - 1 )
+
 /** Receive/Transmit Descriptor Base Address Low (offset) */
 #define INTEL_xDBAL 0x00
 
@@ -175,6 +185,9 @@ enum intel_descriptor_status {
 #define INTEL_xDCTL 0x28
 #define INTEL_xDCTL_ENABLE	0x02000000UL	/**< Queue enable */
 
+/** Maximum time to wait for queue disable, in milliseconds */
+#define INTEL_DISABLE_MAX_WAIT_MS 100
+
 /** Receive Address Low */
 #define INTEL_RAL0 0x05400UL
 
@@ -182,6 +195,10 @@ enum intel_descriptor_status {
 #define INTEL_RAH0 0x05404UL
 #define INTEL_RAH0_AV		0x80000000UL	/**< Address valid */
 
+/** Future Extended NVM register 11 */
+#define INTEL_FEXTNVM11 0x05bbcUL
+#define INTEL_FEXTNVM11_WTF	0x00002000UL	/**< Don't ask */
+
 /** Receive address */
 union intel_receive_address {
 	struct {
@@ -204,6 +221,15 @@ struct intel_ring {
 	unsigned int reg;
 	/** Length (in bytes) */
 	size_t len;
+
+	/** Populate descriptor
+	 *
+	 * @v desc		Descriptor
+	 * @v addr		Data buffer address
+	 * @v len		Length of data
+	 */
+	void ( * describe ) ( struct intel_descriptor *desc, physaddr_t addr,
+			      size_t len );
 };
 
 /**
@@ -212,12 +238,39 @@ struct intel_ring {
  * @v ring		Descriptor ring
  * @v count		Number of descriptors
  * @v reg		Descriptor register block
+ * @v describe		Method to populate descriptor
  */
 static inline __attribute__ (( always_inline)) void
-intel_init_ring ( struct intel_ring *ring, unsigned int count,
-		  unsigned int reg ) {
+intel_init_ring ( struct intel_ring *ring, unsigned int count, unsigned int reg,
+		  void ( * describe ) ( struct intel_descriptor *desc,
+					physaddr_t addr, size_t len ) ) {
+
 	ring->len = ( count * sizeof ( ring->desc[0] ) );
 	ring->reg = reg;
+	ring->describe = describe;
+}
+
+/** An Intel virtual function mailbox */
+struct intel_mailbox {
+	/** Mailbox control register */
+	unsigned int ctrl;
+	/** Mailbox memory base */
+	unsigned int mem;
+};
+
+/**
+ * Initialise mailbox
+ *
+ * @v mbox		Mailbox
+ * @v ctrl		Mailbox control register
+ * @v mem		Mailbox memory register base
+ */
+static inline __attribute__ (( always_inline )) void
+intel_init_mbox ( struct intel_mailbox *mbox, unsigned int ctrl,
+		  unsigned int mem ) {
+
+	mbox->ctrl = ctrl;
+	mbox->mem = mem;
 }
 
 /** An Intel network card */
@@ -226,6 +279,10 @@ struct intel_nic {
 	void *regs;
 	/** Port number (for multi-port devices) */
 	unsigned int port;
+	/** Flags */
+	unsigned int flags;
+	/** Forced interrupts */
+	unsigned int force_icr;
 
 	/** EEPROM */
 	struct nvs_device eeprom;
@@ -234,6 +291,9 @@ struct intel_nic {
 	/** EEPROM address shift */
 	unsigned int eerd_addr_shift;
 
+	/** Mailbox */
+	struct intel_mailbox mbox;
+
 	/** Transmit descriptor ring */
 	struct intel_ring tx;
 	/** Receive descriptor ring */
@@ -242,6 +302,49 @@ struct intel_nic {
 	struct io_buffer *rx_iobuf[INTEL_NUM_RX_DESC];
 };
 
+/** Driver flags */
+enum intel_flags {
+	/** PBS/PBA errata workaround required */
+	INTEL_PBS_ERRATA = 0x0001,
+	/** VMware missing interrupt workaround required */
+	INTEL_VMWARE = 0x0002,
+	/** PHY reset is broken */
+	INTEL_NO_PHY_RST = 0x0004,
+	/** ASDE is broken */
+	INTEL_NO_ASDE = 0x0008,
+	/** Reset may cause a complete device hang */
+	INTEL_RST_HANG = 0x0010,
+};
+
+/** The i219 has a seriously broken reset mechanism */
+#define INTEL_I219 ( INTEL_NO_PHY_RST | INTEL_RST_HANG )
+
+/**
+ * Dump diagnostic information
+ *
+ * @v intel		Intel device
+ */
+static inline void intel_diag ( struct intel_nic *intel ) {
+
+	DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) "
+	       "RX %04x(%02x)/%04x(%02x)\n", intel,
+	       ( intel->tx.cons & 0xffff ),
+	       readl ( intel->regs + intel->tx.reg + INTEL_xDH ),
+	       ( intel->tx.prod & 0xffff ),
+	       readl ( intel->regs + intel->tx.reg + INTEL_xDT ),
+	       ( intel->rx.cons & 0xffff ),
+	       readl ( intel->regs + intel->rx.reg + INTEL_xDH ),
+	       ( intel->rx.prod & 0xffff ),
+	       readl ( intel->regs + intel->rx.reg + INTEL_xDT ) );
+}
+
+extern void intel_describe_tx ( struct intel_descriptor *tx,
+				physaddr_t addr, size_t len );
+extern void intel_describe_tx_adv ( struct intel_descriptor *tx,
+				    physaddr_t addr, size_t len );
+extern void intel_describe_rx ( struct intel_descriptor *rx,
+				physaddr_t addr, size_t len );
+extern void intel_reset_ring ( struct intel_nic *intel, unsigned int reg );
 extern int intel_create_ring ( struct intel_nic *intel,
 			       struct intel_ring *ring );
 extern void intel_destroy_ring ( struct intel_nic *intel,
