git: 8d2d6284bb8c - main - e1000: Recover from I210 and I211 memory errors

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Sun, 16 Aug 2026 09:18:10 UTC
The branch main has been updated by kbowling:

URL: https://cgit.FreeBSD.org/src/commit/?id=8d2d6284bb8ca168f6173c8836a40711b5f34e13

commit 8d2d6284bb8ca168f6173c8836a40711b5f34e13
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2026-08-12 18:33:39 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2026-08-16 09:15:28 +0000

    e1000: Recover from I210 and I211 memory errors
    
    I210 and I211 report uncorrectable internal memory errors through
    ICR.FER and identify the affected region in PEIND.  Depending on the
    region, hardware stops transmit or all PCIe and DMA traffic until the
    port is reset and reinitialized.
    
    Enable FER and all regional indication masks.  Discard indication state
    left by firmware before enabling reactions, capture the read-clear
    status in the interrupt filter, and keep the cause masked while recovery
    is pending.  Report the affected regions and expose per-region
    indication counters.  Management-only errors remain under firmware
    control.
    
    PCIe region parity errors require a different recovery order from the
    normal reset path.  Assert the port-local CTRL.RST bit, wait at least
    3 ms, verify reset completion, disable master requests, clear
    PCIEERRSTS, and then enter normal port reinitialization.  Do not use the
    device-wide CTRL.DEV_RST sequence used by I225 and I226.
    
    Hardware validation used an I210 revision 3 and the self-clearing
    LANPERRINJ retransmit-buffer bit 9.  It injected a real parity error
    without synthesizing interrupt or status state.
    Three injections in one boot produced the following result each time:
    
        Observed hardware status          Result
        PEIND 0x1, LANPERRSTS 0x200       Reset and recovered
    
    fatal_lan advanced exactly once per injection.  All tests completed
    without a panic or watchdog, and FER and the LAN parity masks remained
    enabled after every recovery.
    
    MFC after:      2 weeks
    Sponsored by:   BBOX.io
---
 sys/dev/e1000/e1000_defines.h |  20 ++++
 sys/dev/e1000/e1000_regs.h    |   9 +-
 sys/dev/e1000/if_em.c         | 244 +++++++++++++++++++++++++++++++++++++-----
 sys/dev/e1000/if_em.h         |   8 ++
 4 files changed, 254 insertions(+), 27 deletions(-)

diff --git a/sys/dev/e1000/e1000_defines.h b/sys/dev/e1000/e1000_defines.h
index afa8bb4cce0d..fb2332462206 100644
--- a/sys/dev/e1000/e1000_defines.h
+++ b/sys/dev/e1000/e1000_defines.h
@@ -334,6 +334,7 @@
 #define E1000_STATUS_LAN_INIT_DONE	0x00000200 /* Lan Init Compltn by NVM */
 #define E1000_STATUS_PHYRA		0x00000400 /* PHY Reset Asserted */
 #define E1000_STATUS_GIO_MASTER_ENABLE	0x00080000 /* Master request status */
+#define E1000_STATUS_RST_DONE		0x00200000 /* Device reset complete */
 #define E1000_STATUS_PCI66		0x00000800 /* In 66Mhz slot */
 #define E1000_STATUS_BUS64		0x00001000 /* In 64 bit slot */
 #define E1000_STATUS_2P5_SKU		0x00001000 /* Val of 2.5GBE SKU strap */
@@ -527,6 +528,25 @@
 #define E1000_PBECCSTS_UNCORR_ERR_CNT_SHIFT	8
 #define E1000_PBECCSTS_ECC_ENABLE		0x00010000
 
+/* I210/I211 memory error status bits. */
+#define E1000_PEIND_LANPORT_PARITY_FATAL	0x00000001
+#define E1000_PEIND_MNG_PARITY_FATAL	0x00000002
+#define E1000_PEIND_PCIE_PARITY_FATAL	0x00000004
+#define E1000_PEIND_DMA_PARITY_FATAL	0x00000008
+#define E1000_PEIND_FATAL_MASK		0x0000000F
+#define E1000_PEIND_HOST_FATAL_MASK	(E1000_PEIND_LANPORT_PARITY_FATAL | \
+	E1000_PEIND_PCIE_PARITY_FATAL | E1000_PEIND_DMA_PARITY_FATAL)
+
+#define E1000_PBECCSTS_I210_ECC_ENABLE	0x00000001
+#define E1000_PBECCSTS_I210_CORR_ERR	0x00000004
+
+#define E1000_PCIEERRSTS_FATAL_MASK	0x00000078
+#define E1000_PCIEECCSTS_TX_WR_DATA	0x00000010
+#define E1000_PCIEECCSTS_RETRY_BUF	0x00000020
+#define E1000_PCIEECCSTS_CORR_MASK	0x00000030
+
+#define E1000_LANPERRSTS_RETX_BUF		0x00000200
+
 #define IFS_MAX			80
 #define IFS_MIN			40
 #define IFS_RATIO		4
diff --git a/sys/dev/e1000/e1000_regs.h b/sys/dev/e1000/e1000_regs.h
index 138155cfc5bc..fe7b62a9a493 100644
--- a/sys/dev/e1000/e1000_regs.h
+++ b/sys/dev/e1000/e1000_regs.h
@@ -114,6 +114,9 @@
 #define E1000_PBA	0x01000  /* Packet Buffer Allocation - RW */
 #define E1000_PBS	0x01008  /* Packet Buffer Size */
 #define E1000_PBECCSTS	0x0100C  /* Packet Buffer ECC Status - RW */
+#define E1000_PEIND	0x01084  /* Parity and ECC Indication - RC */
+#define E1000_PEINDM	0x01088  /* Parity and ECC Indication Mask - RW */
+#define E1000_PBECCSTS_I210 0x0245C /* I210 Packet Buffer ECC Status */
 #define E1000_IOSFPC	0x00F28  /* TX corrupted data  */
 #define E1000_EEMNGCTL	0x01010  /* MNG EEprom Control */
 #define E1000_EEMNGCTL_I210	0x01010  /* i210 MNG EEprom Mode Control */
@@ -691,8 +694,10 @@
 #define E1000_FCRTC	0x02170 /* Flow Control Rx high watermark */
 #define E1000_PCIEMISC	0x05BB8 /* PCIE misc config register */
 
-/* PCIe Parity Status Register */
-#define E1000_PCIEERRSTS	0x05BA8
+/* Memory error status registers */
+#define E1000_PCIEERRSTS	0x05BA8 /* PCIe Parity Status - RW1C */
+#define E1000_PCIEECCSTS 0x05BAC /* PCIe ECC Status - RW1C */
+#define E1000_LANPERRSTS 0x05F58 /* LAN Port Parity Status - RW1C */
 
 #define E1000_PROXYS	0x5F64 /* Proxying Status */
 #define E1000_PROXYFC	0x5F60 /* Proxying Filter Control */
diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index eb4c17f78769..4cc5dbd7b7ee 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -457,7 +457,9 @@ static int	igb_if_rx_queue_intr_enable(if_ctx_t, uint16_t);
 static int	igb_if_tx_queue_intr_enable(if_ctx_t, uint16_t);
 static void	em_handle_fatal_error_intr(struct e1000_softc *, u32);
 static bool	em_handle_fatal_error_admin(struct e1000_softc *);
+static void	em_prepare_fatal_error_reset(struct e1000_softc *);
 static void	em_finish_fatal_error_reset(struct e1000_softc *);
+static void	em_configure_peind_memory_errors(struct e1000_softc *);
 static void	em_if_multi_set(if_ctx_t);
 static void	em_if_update_admin_status(if_ctx_t);
 static void	em_if_debug(if_ctx_t);
@@ -507,6 +509,7 @@ enum em_fatal_error_state {
 	EM_FATAL_ERROR_CAPTURING,
 	EM_FATAL_ERROR_DETECTED,
 	EM_FATAL_ERROR_RESET_REQUESTED,
+	EM_FATAL_ERROR_RESET_PREPARED,
 };
 
 /* MSI-X handlers */
@@ -1985,6 +1988,7 @@ em_if_init(if_ctx_t ctx)
 		else
 			e1000_set_eee_i350(&sc->hw, true, true);
 	}
+	em_configure_peind_memory_errors(sc);
 	if (sc->vf_ifp)
 		sc->vf_reset_pending = false;
 }
@@ -2176,14 +2180,41 @@ em_has_pch_ecc(const struct e1000_hw *hw)
 	    hw->mac.type < e1000_82575);
 }
 
+static bool
+em_has_i210_memory_errors(const struct e1000_hw *hw)
+{
+
+	return (hw->mac.type == e1000_i210 ||
+	    hw->mac.type == e1000_i211);
+}
+static void
+em_configure_peind_memory_errors(struct e1000_softc *sc)
+{
+	struct e1000_hw *hw;
+	u32 peindm;
+
+	hw = &sc->hw;
+	if (!em_has_i210_memory_errors(hw))
+		return;
+
+	/* Discard indications left by firmware before enabling reactions. */
+	(void)E1000_READ_REG(hw, E1000_PEIND);
+	/* Do not depend on firmware preserving the datasheet defaults. */
+	peindm = E1000_READ_REG(hw, E1000_PEINDM);
+	E1000_WRITE_REG(hw, E1000_PEINDM,
+	    peindm | E1000_PEIND_FATAL_MASK);
+	E1000_WRITE_FLUSH(hw);
+}
+
 static u32
 em_fatal_error_intr_mask(struct e1000_softc *sc)
 {
 
-	if (em_has_pch_ecc(&sc->hw) &&
+	if ((em_has_pch_ecc(&sc->hw) ||
+	    em_has_i210_memory_errors(&sc->hw)) &&
 	    atomic_load_acq_32(&sc->fatal_error_state) ==
 	    EM_FATAL_ERROR_NONE)
-		return (E1000_IMS_ECCER);
+		return (E1000_IMS_FER);
 	return (0);
 }
 
@@ -2199,24 +2230,44 @@ em_update_pch_ecc_stats(struct e1000_softc *sc, u32 pbeccsts)
 }
 
 /*
- * Descriptor-memory ECC errors stop the PCH MAC.  Capture the read-clear
- * status before handing recovery to the iflib admin task.
+ * Fatal internal-memory errors stop part or all of the MAC.  Capture the
+ * read-clear indication before handing recovery to the iflib admin task.
  */
 static void
 em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
 {
 	struct e1000_hw *hw;
+	u32 lanerr, pcieerr, peind;
 
-	if (!em_has_pch_ecc(&sc->hw) || (icr & E1000_ICR_ECCER) == 0)
+	if ((!em_has_pch_ecc(&sc->hw) &&
+	    !em_has_i210_memory_errors(&sc->hw)) ||
+	    (icr & E1000_ICR_FER) == 0)
 		return;
 
 	hw = &sc->hw;
-	E1000_WRITE_REG(hw, E1000_IMC, E1000_IMS_ECCER);
+	E1000_WRITE_REG(hw, E1000_IMC, E1000_IMS_FER);
 	if (!atomic_cmpset_32(&sc->fatal_error_state,
 	    EM_FATAL_ERROR_NONE, EM_FATAL_ERROR_CAPTURING))
 		return;
 
-	sc->fatal_error_pbeccsts = E1000_READ_REG(hw, E1000_PBECCSTS);
+	if (em_has_pch_ecc(hw)) {
+		sc->fatal_error_pbeccsts =
+		    E1000_READ_REG(hw, E1000_PBECCSTS);
+	} else {
+		peind = E1000_READ_REG(hw, E1000_PEIND) &
+		    E1000_PEIND_FATAL_MASK;
+		pcieerr = E1000_READ_REG(hw, E1000_PCIEERRSTS) &
+		    E1000_PCIEERRSTS_FATAL_MASK;
+		lanerr = E1000_READ_REG(hw, E1000_LANPERRSTS) &
+		    E1000_LANPERRSTS_RETX_BUF;
+		if (pcieerr != 0)
+			peind |= E1000_PEIND_PCIE_PARITY_FATAL;
+		if (lanerr != 0)
+			peind |= E1000_PEIND_LANPORT_PARITY_FATAL;
+		sc->fatal_error_peind = peind;
+		sc->fatal_error_pcie = pcieerr;
+		sc->fatal_error_lan = lanerr;
+	}
 	atomic_store_rel_32(&sc->fatal_error_state,
 	    EM_FATAL_ERROR_DETECTED);
 	iflib_admin_intr_deferred(sc->ctx);
@@ -2225,16 +2276,51 @@ em_handle_fatal_error_intr(struct e1000_softc *sc, u32 icr)
 static bool
 em_handle_fatal_error_admin(struct e1000_softc *sc)
 {
+	u32 peind;
 
 	if (!atomic_cmpset_acq_32(&sc->fatal_error_state,
 	    EM_FATAL_ERROR_DETECTED, EM_FATAL_ERROR_RESET_REQUESTED))
 		return (atomic_load_acq_32(&sc->fatal_error_state) !=
 		    EM_FATAL_ERROR_NONE);
 
-	em_update_pch_ecc_stats(sc, sc->fatal_error_pbeccsts);
-	device_printf(sc->dev,
-	    "uncorrectable packet-buffer ECC error: PBECCSTS %#x; "
-	    "requesting reset\n", sc->fatal_error_pbeccsts);
+	if (em_has_pch_ecc(&sc->hw)) {
+		em_update_pch_ecc_stats(sc, sc->fatal_error_pbeccsts);
+		device_printf(sc->dev,
+		    "uncorrectable packet-buffer ECC error: "
+		    "PBECCSTS %#x; requesting reset\n",
+		    sc->fatal_error_pbeccsts);
+	} else {
+		peind = sc->fatal_error_peind;
+		if (peind & E1000_PEIND_LANPORT_PARITY_FATAL)
+			sc->fatal_error_lan_count++;
+		if (peind & E1000_PEIND_MNG_PARITY_FATAL)
+			sc->fatal_error_mng_count++;
+		if (peind & E1000_PEIND_PCIE_PARITY_FATAL)
+			sc->fatal_error_pcie_count++;
+		if (peind & E1000_PEIND_DMA_PARITY_FATAL)
+			sc->fatal_error_dma_count++;
+		if (peind == 0)
+			sc->fatal_error_unknown_count++;
+		device_printf(sc->dev,
+		    "fatal internal memory error: PEIND %#x, "
+		    "PCIEERRSTS %#x, LANPERRSTS %#x\n", peind,
+		    sc->fatal_error_pcie, sc->fatal_error_lan);
+		/* Management-memory recovery belongs to management firmware. */
+		if (peind != 0 &&
+		    (peind & E1000_PEIND_HOST_FATAL_MASK) == 0) {
+			sc->fatal_error_peind = 0;
+			sc->fatal_error_pcie = 0;
+			sc->fatal_error_lan = 0;
+			atomic_store_rel_32(&sc->fatal_error_state,
+			    EM_FATAL_ERROR_NONE);
+			E1000_WRITE_REG(&sc->hw, E1000_IMS,
+			    E1000_IMS_FER);
+			E1000_WRITE_FLUSH(&sc->hw);
+			return (true);
+		}
+		device_printf(sc->dev,
+		    "requesting reset after memory error\n");
+	}
 	sc->fatal_error_reset_count++;
 	iflib_request_reset(sc->ctx);
 	/* Re-enter the admin task so it observes the reset request. */
@@ -2242,14 +2328,94 @@ em_handle_fatal_error_admin(struct e1000_softc *sc)
 	return (true);
 }
 
+/*
+ * A PCIe-region parity failure stops PCIe and DMA traffic.  I210 and I211
+ * require a port reset before master disable in this case, unlike the normal
+ * reset path, which disables the bus master first.
+ */
+static void
+em_prepare_fatal_error_reset(struct e1000_softc *sc)
+{
+	struct e1000_hw *hw;
+	s32 error;
+	u32 ctrl, pcieerr;
+	int i;
+
+	if (!em_has_i210_memory_errors(&sc->hw) ||
+	    atomic_load_acq_32(&sc->fatal_error_state) !=
+	    EM_FATAL_ERROR_RESET_REQUESTED)
+		return;
+
+	pcieerr = sc->fatal_error_pcie |
+	    (E1000_READ_REG(&sc->hw, E1000_PCIEERRSTS) &
+	    E1000_PCIEERRSTS_FATAL_MASK);
+	if ((sc->fatal_error_peind & E1000_PEIND_PCIE_PARITY_FATAL) == 0 &&
+	    pcieerr == 0)
+		return;
+
+	hw = &sc->hw;
+	ctrl = E1000_READ_REG(hw, E1000_CTRL);
+	E1000_WRITE_REG(hw, E1000_CTRL, ctrl | E1000_CTRL_RST);
+	/* Do not access device registers for at least 3 ms after RST. */
+	msec_delay(3);
+	for (i = 0; i < AUTO_READ_DONE_TIMEOUT; i++) {
+		if ((E1000_READ_REG(hw, E1000_EECD) &
+		    E1000_EECD_AUTO_RD) != 0 &&
+		    (E1000_READ_REG(hw, E1000_STATUS) &
+		    E1000_STATUS_RST_DONE) != 0)
+			break;
+		msec_delay(1);
+	}
+	if (i == AUTO_READ_DONE_TIMEOUT)
+		device_printf(sc->dev,
+		    "port reset did not complete during parity recovery\n");
+	error = e1000_disable_pcie_master_generic(hw);
+	if (error != E1000_SUCCESS)
+		device_printf(sc->dev,
+		    "PCIe master disable failed during parity recovery: %d\n",
+		    error);
+	pcieerr |= E1000_READ_REG(hw, E1000_PCIEERRSTS) &
+	    E1000_PCIEERRSTS_FATAL_MASK;
+	if (pcieerr != 0)
+		E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
+	atomic_store_rel_32(&sc->fatal_error_state,
+	    EM_FATAL_ERROR_RESET_PREPARED);
+}
+
 static void
 em_finish_fatal_error_reset(struct e1000_softc *sc)
 {
+	struct e1000_hw *hw;
+	u32 lanerr, pcieerr;
+	u32 state;
 
-	if (atomic_load_acq_32(&sc->fatal_error_state) ==
-	    EM_FATAL_ERROR_NONE)
+	state = atomic_load_acq_32(&sc->fatal_error_state);
+	if (state != EM_FATAL_ERROR_RESET_REQUESTED &&
+	    state != EM_FATAL_ERROR_RESET_PREPARED)
 		return;
 
+	hw = &sc->hw;
+	if (em_has_i210_memory_errors(hw)) {
+		pcieerr = sc->fatal_error_pcie |
+		    (E1000_READ_REG(hw, E1000_PCIEERRSTS) &
+		    E1000_PCIEERRSTS_FATAL_MASK);
+		if (pcieerr != 0)
+			E1000_WRITE_REG(hw, E1000_PCIEERRSTS, pcieerr);
+		lanerr = sc->fatal_error_lan |
+		    (E1000_READ_REG(hw, E1000_LANPERRSTS) &
+		    E1000_LANPERRSTS_RETX_BUF);
+		if (lanerr != 0)
+			E1000_WRITE_REG(hw, E1000_LANPERRSTS, lanerr);
+		/*
+		 * RST can relatch PEIND from a subordinate status register
+		 * before that register is cleared.  Drain the recovered
+		 * indication before unmasking FER.
+		 */
+		(void)E1000_READ_REG(hw, E1000_PEIND);
+		sc->fatal_error_peind = 0;
+		sc->fatal_error_pcie = 0;
+		sc->fatal_error_lan = 0;
+	}
 	sc->fatal_error_pbeccsts = 0;
 	atomic_store_rel_32(&sc->fatal_error_state, EM_FATAL_ERROR_NONE);
 }
@@ -2930,8 +3096,10 @@ em_if_stop(if_ctx_t ctx)
 	igb_iov_reset_prepare(sc);
 	if (!sc->vf_ifp ||
 	    (atomic_load_acq_32(&sc->vf_mbx_ready) != 0 &&
-	    (if_getflags(iflib_get_ifp(ctx)) & IFF_UP) == 0))
+	    (if_getflags(iflib_get_ifp(ctx)) & IFF_UP) == 0)) {
+		em_prepare_fatal_error_reset(sc);
 		e1000_reset_hw(&sc->hw);
+	}
 	if (sc->vf_ifp)
 		atomic_store_rel_32(&sc->vf_mbx_ready, 0);
 	if (sc->hw.mac.type >= e1000_82544 && !sc->vf_ifp)
@@ -3914,6 +4082,7 @@ em_reset(if_ctx_t ctx)
 		em_flush_desc_rings(sc);
 
 	/* Issue a global reset */
+	em_prepare_fatal_error_reset(sc);
 	e1000_reset_hw(hw);
 	if (hw->mac.type >= igb_mac_min) {
 		E1000_WRITE_REG(hw, E1000_WUC, 0);
@@ -6200,7 +6369,8 @@ em_add_hw_stats(struct e1000_softc *sc)
 		SYSCTL_ADD_UQUAD(ctx, eee_list, OID_AUTO, "rx_lpi_count",
 		    CTLFLAG_RD, &stats->rlpic, "RX LPI event count");
 	}
-	if (em_has_pch_ecc(&sc->hw)) {
+	if (em_has_pch_ecc(&sc->hw) ||
+	    em_has_i210_memory_errors(&sc->hw)) {
 		struct sysctl_oid *memerr_node;
 		struct sysctl_oid_list *memerr_list;
 
@@ -6208,17 +6378,41 @@ em_add_hw_stats(struct e1000_softc *sc)
 		    "memory_errors", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL,
 		    "Internal memory error indications");
 		memerr_list = SYSCTL_CHILDREN(memerr_node);
-		SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO, "fatal_resets",
-		    CTLFLAG_RD, &sc->fatal_error_reset_count,
-		    "Resets requested for fatal packet-buffer ECC errors");
 		SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
-		    "corrected_packet_buffer", CTLFLAG_RD,
-		    &sc->corrected_error_packet_buffer_count,
-		    "Corrected packet-buffer ECC errors");
-		SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
-		    "uncorrected_packet_buffer", CTLFLAG_RD,
-		    &sc->uncorrected_error_packet_buffer_count,
-		    "Uncorrected packet-buffer ECC errors");
+		    "fatal_resets", CTLFLAG_RD,
+		    &sc->fatal_error_reset_count,
+		    "Resets requested for fatal internal memory errors");
+		if (em_has_pch_ecc(&sc->hw)) {
+			SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+			    "corrected_packet_buffer", CTLFLAG_RD,
+			    &sc->corrected_error_packet_buffer_count,
+			    "Corrected packet-buffer ECC errors");
+			SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+			    "uncorrected_packet_buffer", CTLFLAG_RD,
+			    &sc->uncorrected_error_packet_buffer_count,
+			    "Uncorrected packet-buffer ECC errors");
+		} else {
+			SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+			    "fatal_lan", CTLFLAG_RD,
+			    &sc->fatal_error_lan_count,
+			    "Fatal LAN-port memory error indications");
+			SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+			    "fatal_management", CTLFLAG_RD,
+			    &sc->fatal_error_mng_count,
+			    "Fatal management-memory error indications");
+			SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+			    "fatal_pcie", CTLFLAG_RD,
+			    &sc->fatal_error_pcie_count,
+			    "Fatal PCIe memory error indications");
+			SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+			    "fatal_dma", CTLFLAG_RD,
+			    &sc->fatal_error_dma_count,
+			    "Fatal DMA memory error indications");
+			SYSCTL_ADD_UQUAD(ctx, memerr_list, OID_AUTO,
+			    "fatal_unknown", CTLFLAG_RD,
+			    &sc->fatal_error_unknown_count,
+			    "Fatal memory errors without a reported region");
+		}
 	}
 
 	SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "excess_coll",
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index b88cb8abefb5..1d1587331555 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -626,7 +626,15 @@ struct e1000_softc {
 	u32			stats_pending;
 	u32			fatal_error_state;
 	u32			fatal_error_pbeccsts;
+	u32			fatal_error_peind;
+	u32			fatal_error_pcie;
+	u32			fatal_error_lan;
 	u64			fatal_error_reset_count;
+	u64			fatal_error_lan_count;
+	u64			fatal_error_mng_count;
+	u64			fatal_error_pcie_count;
+	u64			fatal_error_dma_count;
+	u64			fatal_error_unknown_count;
 	u64			corrected_error_packet_buffer_count;
 	u64			uncorrected_error_packet_buffer_count;