git: 73922769f0c2 - stable/13 - e1000: print EEPROM/NVM/OROM versions

From: Kevin Bowling <kbowling_at_FreeBSD.org>
Date: Wed, 13 Oct 2021 02:11:53 UTC
The branch stable/13 has been updated by kbowling (ports committer):

URL: https://cgit.FreeBSD.org/src/commit/?id=73922769f0c2f70da78f680eed6b3c91911235da

commit 73922769f0c2f70da78f680eed6b3c91911235da
Author:     Kevin Bowling <kbowling@FreeBSD.org>
AuthorDate: 2021-10-06 23:25:03 +0000
Commit:     Kevin Bowling <kbowling@FreeBSD.org>
CommitDate: 2021-10-13 02:11:18 +0000

    e1000: print EEPROM/NVM/OROM versions
    
    This is useful for diagnosing problems. In particular, the errata
    sheets identify the EEPROM version for many fixes.
    
    Reviewed by:    gallatin
    MFC after:      1 week
    Differential Revision:  https://reviews.freebsd.org/D32333
    
    (cherry picked from commit 293663f4da9e8b8aeb106ce3b73a8ed2aa2a8a90)
---
 sys/dev/e1000/if_em.c | 185 ++++++++++++++++++++++++++++++++++++++++++--------
 sys/dev/e1000/if_em.h |   3 +
 2 files changed, 158 insertions(+), 30 deletions(-)

diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c
index 52eff393e848..b3775b351c70 100644
--- a/sys/dev/e1000/if_em.c
+++ b/sys/dev/e1000/if_em.c
@@ -305,6 +305,10 @@ static void	em_if_vlan_filter_write(struct e1000_softc *);
 static void	em_setup_vlan_hw_support(struct e1000_softc *);
 static int	em_sysctl_nvm_info(SYSCTL_HANDLER_ARGS);
 static void	em_print_nvm_info(struct e1000_softc *);
+static void	em_fw_version_locked(if_ctx_t);
+static void	em_sbuf_fw_version(struct e1000_fw_version *, struct sbuf *);
+static void	em_print_fw_version(struct e1000_softc *);
+static int	em_sysctl_print_fw_version(SYSCTL_HANDLER_ARGS);
 static int	em_sysctl_debug_info(SYSCTL_HANDLER_ARGS);
 static int	em_get_rs(SYSCTL_HANDLER_ARGS);
 static void	em_print_debug_info(struct e1000_softc *);
@@ -792,6 +796,8 @@ em_if_attach_pre(if_ctx_t ctx)
 	if_softc_ctx_t scctx;
 	device_t dev;
 	struct e1000_hw *hw;
+	struct sysctl_oid_list *child;
+	struct sysctl_ctx_list *ctx_list;
 	int error = 0;
 
 	INIT_DEBUGOUT("em_if_attach_pre: begin");
@@ -806,37 +812,38 @@ em_if_attach_pre(if_ctx_t ctx)
 
 	sc->tx_process_limit = scctx->isc_ntxd[0];
 
+	/* Determine hardware and mac info */
+	em_identify_hardware(ctx);
+
 	/* SYSCTL stuff */
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
-	    OID_AUTO, "nvm", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
-	    sc, 0, em_sysctl_nvm_info, "I", "NVM Information");
-
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
-	    OID_AUTO, "debug", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
-	    sc, 0, em_sysctl_debug_info, "I", "Debug Information");
-
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
-	    OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
-	    sc, 0, em_set_flowcntl, "I", "Flow Control");
-
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
-	    OID_AUTO, "reg_dump",
+	ctx_list = device_get_sysctl_ctx(dev);
+	child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+
+	SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "nvm",
+	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+	    em_sysctl_nvm_info, "I", "NVM Information");
+
+	SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fw_version",
+	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
+	    em_sysctl_print_fw_version, "A",
+	    "Prints FW/NVM Versions");
+
+	SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "debug",
+	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+	    em_sysctl_debug_info, "I", "Debug Information");
+
+	SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fc",
+	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+	    em_set_flowcntl, "I", "Flow Control");
+
+	SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "reg_dump",
 	    CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0,
 	    em_get_regs, "A", "Dump Registers");
 
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
-	    OID_AUTO, "rs_dump",
+	SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "rs_dump",
 	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
 	    em_get_rs, "I", "Dump RS indexes");
 
-	/* Determine hardware and mac info */
-	em_identify_hardware(ctx);
-
 	scctx->isc_tx_nsegments = EM_MAX_SCATTER;
 	scctx->isc_nrxqsets_max = scctx->isc_ntxqsets_max = em_set_num_queues(ctx);
 	if (bootverbose)
@@ -1045,12 +1052,9 @@ em_if_attach_pre(if_ctx_t ctx)
 
 	/* Sysctl for setting Energy Efficient Ethernet */
 	hw->dev_spec.ich8lan.eee_disable = eee_setting;
-	SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev),
-	    SYSCTL_CHILDREN(device_get_sysctl_tree(dev)),
-	    OID_AUTO, "eee_control",
-	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
-	    sc, 0, em_sysctl_eee, "I",
-	    "Disable Energy Efficient Ethernet");
+	SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "eee_control",
+	    CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0,
+	    em_sysctl_eee, "I", "Disable Energy Efficient Ethernet");
 
 	/*
 	** Start from a known state, this is
@@ -1093,6 +1097,11 @@ em_if_attach_pre(if_ctx_t ctx)
 		}
 	}
 
+	/* Save the EEPROM/NVM versions, must be done under IFLIB_CTX_LOCK */
+	em_fw_version_locked(ctx);
+
+	em_print_fw_version(sc);
+
 	/* Disable ULP support */
 	e1000_disable_ulp_lpt_lp(hw, true);
 
@@ -4476,6 +4485,122 @@ em_add_hw_stats(struct e1000_softc *sc)
 			"Interrupt Cause Receiver Overrun Count");
 }
 
+static void
+em_fw_version_locked(if_ctx_t ctx)
+{
+	struct e1000_softc *sc = iflib_get_softc(ctx);
+	struct e1000_hw *hw = &sc->hw;
+	struct e1000_fw_version *fw_ver = &sc->fw_ver;
+	uint16_t eep = 0;
+
+	/*
+	 * em_fw_version_locked() must run under the IFLIB_CTX_LOCK to meet the
+	 * NVM locking model, so we do it in em_if_attach_pre() and store the
+	 * info in the softc
+	 */
+	ASSERT_CTX_LOCK_HELD(hw);
+
+	*fw_ver = (struct e1000_fw_version){0};
+
+	if (hw->mac.type >= igb_mac_min) {
+		/*
+		 * Use the Shared Code for igb(4)
+		 */
+		e1000_get_fw_version(hw, fw_ver);
+	} else {
+		/*
+		 * Otherwise, EEPROM version should be present on (almost?) all
+		 * devices here
+		 */
+		if(e1000_read_nvm(hw, NVM_VERSION, 1, &eep)) {
+			INIT_DEBUGOUT("can't get EEPROM version");
+			return;
+		}
+
+		fw_ver->eep_major = (eep & NVM_MAJOR_MASK) >> NVM_MAJOR_SHIFT;
+		fw_ver->eep_minor = (eep & NVM_MINOR_MASK) >> NVM_MINOR_SHIFT;
+		fw_ver->eep_build = (eep & NVM_IMAGE_ID_MASK);
+	}
+}
+
+static void
+em_sbuf_fw_version(struct e1000_fw_version *fw_ver, struct sbuf *buf)
+{
+	const char *space = "";
+
+	if (fw_ver->eep_major || fw_ver->eep_minor || fw_ver->eep_build) {
+		sbuf_printf(buf, "EEPROM V%d.%d-%d", fw_ver->eep_major,
+			    fw_ver->eep_minor, fw_ver->eep_build);
+		space = " ";
+	}
+
+	if (fw_ver->invm_major || fw_ver->invm_minor || fw_ver->invm_img_type) {
+		sbuf_printf(buf, "%sNVM V%d.%d imgtype%d",
+			    space, fw_ver->invm_major, fw_ver->invm_minor,
+			    fw_ver->invm_img_type);
+		space = " ";
+	}
+
+	if (fw_ver->or_valid) {
+		sbuf_printf(buf, "%sOption ROM V%d-b%d-p%d",
+			    space, fw_ver->or_major, fw_ver->or_build,
+			    fw_ver->or_patch);
+		space = " ";
+	}
+
+	if (fw_ver->etrack_id)
+		sbuf_printf(buf, "%seTrack 0x%08x", space, fw_ver->etrack_id);
+}
+
+static void
+em_print_fw_version(struct e1000_softc *sc )
+{
+	device_t dev = sc->dev;
+	struct sbuf *buf;
+	int error = 0;
+
+	buf = sbuf_new_auto();
+	if (!buf) {
+		device_printf(dev, "Could not allocate sbuf for output.\n");
+		return;
+	}
+
+	em_sbuf_fw_version(&sc->fw_ver, buf);
+
+	error = sbuf_finish(buf);
+	if (error)
+		device_printf(dev, "Error finishing sbuf: %d\n", error);
+	else if (sbuf_len(buf))
+		device_printf(dev, "%s\n", sbuf_data(buf));
+
+	sbuf_delete(buf);
+}
+
+static int
+em_sysctl_print_fw_version(SYSCTL_HANDLER_ARGS)
+{
+	struct e1000_softc *sc = (struct e1000_softc *)arg1;
+	device_t dev = sc->dev;
+	struct sbuf *buf;
+	int error = 0;
+
+	buf = sbuf_new_for_sysctl(NULL, NULL, 128, req);
+	if (!buf) {
+		device_printf(dev, "Could not allocate sbuf for output.\n");
+		return (ENOMEM);
+	}
+
+	em_sbuf_fw_version(&sc->fw_ver, buf);
+
+	error = sbuf_finish(buf);
+	if (error)
+		device_printf(dev, "Error finishing sbuf: %d\n", error);
+
+	sbuf_delete(buf);
+
+	return (0);
+}
+
 /**********************************************************************
  *
  *  This routine provides a way to dump out the adapter eeprom,
diff --git a/sys/dev/e1000/if_em.h b/sys/dev/e1000/if_em.h
index 41deecb69bf3..33ba22a88552 100644
--- a/sys/dev/e1000/if_em.h
+++ b/sys/dev/e1000/if_em.h
@@ -510,6 +510,9 @@ struct e1000_softc {
 
 	u64			que_mask;
 
+	/* We need to store this at attach due to e1000 hw/sw locking model */
+	struct e1000_fw_version	fw_ver;
+
 	struct em_int_delay_info tx_int_delay;
 	struct em_int_delay_info tx_abs_int_delay;
 	struct em_int_delay_info rx_int_delay;