git: 6b58d10fc6d5 - main - ix(4): Add support for firmware logging for E610 adapters
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 09 Apr 2026 18:47:34 UTC
The branch main has been updated by kgalazka:
URL: https://cgit.FreeBSD.org/src/commit/?id=6b58d10fc6d51ddcf5ee81628ead74d3dadb9bf6
commit 6b58d10fc6d51ddcf5ee81628ead74d3dadb9bf6
Author: Bhosale, Yogesh <yogesh.bhosale@intel.com>
AuthorDate: 2026-04-09 18:05:29 +0000
Commit: Krzysztof Galazka <kgalazka@FreeBSD.org>
CommitDate: 2026-04-09 18:43:43 +0000
ix(4): Add support for firmware logging for E610 adapters
This is part 3 of the support for the new Intel Ethernet E610
family of devices
The ix driver now enables firmware logging on Intel E610 devices
for debugging with Customer Support. Logs are enabled by default
and generated in binary format that requires decoding by support
teams. The collected data is firmware and hardware related for
debugging purposes only.
When the driver loads, it creates a fw_log sysctl node under the
debug section. Events are organized into categories (modules) for
targeted logging, and users can adjust verbosity levels as needed.
This adds sysctl support for the firmware logging feature and
updates the ix(4) manual page with documentation.
Signed-off-by: Yogesh Bhosale <yogesh.bhosale@intel.com>
Co-developed-by: Krzysztof Galazka <krzysztof.galazka@intel.com>
Reviewed by: ziaee, kbowling
Tested by: Mateusz Moga <mateusz.moga@intel.com>
MFC after: 1 weeks
Sponsored by: Intel Corporation
Differential Revision: https://reviews.freebsd.org/D53973
---
share/man/man4/ix.4 | 34 ++-
sys/conf/files | 2 +
sys/dev/ixgbe/if_ix.c | 22 +-
sys/dev/ixgbe/ixgbe.h | 5 +
sys/dev/ixgbe/ixgbe_e610.c | 486 +++++++++++++++++++++++++++++++++++++++
sys/dev/ixgbe/ixgbe_e610.h | 13 ++
sys/dev/ixgbe/ixgbe_features.h | 1 +
sys/dev/ixgbe/ixgbe_fw_logging.c | 467 +++++++++++++++++++++++++++++++++++++
sys/dev/ixgbe/ixgbe_osdep.c | 36 +++
sys/dev/ixgbe/ixgbe_osdep.h | 3 +
sys/modules/ix/Makefile | 1 +
sys/modules/ixv/Makefile | 1 +
12 files changed, 1066 insertions(+), 5 deletions(-)
diff --git a/share/man/man4/ix.4 b/share/man/man4/ix.4
index 09af85f5c4a7..39ed49aa8dfc 100644
--- a/share/man/man4/ix.4
+++ b/share/man/man4/ix.4
@@ -138,7 +138,7 @@ The
driver supports the following
.Xr sysctl 8
variables:
-.Bl -tag -width "dev.ix.?.debug.dump.clusters"
+.Bl -tag -width "dev.ix.?.debug.fw_log.severity.<module>"
.It Va dev.ix.?.debug.dump.clusters
Specify a bitmask to select firmware event clusters
to be included in the debug dump.
@@ -160,6 +160,38 @@ Output must be redirected to a file
and decoded by Intel Customer Support.
.Pp
This feature is only supported on E610.
+.It Va dev.ix.?.debug.fw_log.severity.<module>
+Specify firmware logging verbosity level for the specified module.
+Available levels include:
+.Pp
+.Bl -tag -compact
+.It 0
+none
+.It 1
+error
+.It 2
+warning
+.It 3
+normal
+.It 4
+verbose
+.El
+.Pp
+Supported modules: general, ctrl, link, link_topo, dnl, i2c, sdp, mdio,
+adminq, hdma, lldp, dcbx, dcb, xlr, nvm, auth, vpd, iosf, parser, sw,
+scheduler, txq, acl, post, watchdog, task_dispatch, mng, synce, health,
+tsdrv, pfreg, mdlver.
+.Pp
+This feature is only supported on E610 devices.
+.It Va dev.ix.?.debug.fw_log.register
+Specify 1 to apply per-device firmware logging configuration.
+.Pp
+This feature is only supported on E610 devices.
+.It Va dev.ix.?.debug.fw_log.on_load
+Enable firmware logging during driver initialization when set via
+.Xr kenv 1 .
+.Pp
+This feature is only supported on E610 devices.
.El
.Sh DIAGNOSTICS
.Bl -diag
diff --git a/sys/conf/files b/sys/conf/files
index 66999f9c5325..b44fb46ef764 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -2315,6 +2315,8 @@ dev/ixgbe/ixgbe_api.c optional ix inet | ixv inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/ixgbe_common.c optional ix inet | ixv inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe"
+dev/ixgbe/ixgbe_fw_logging.c optional ix inet | ixv inet \
+ compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/ixgbe_mbx.c optional ix inet | ixv inet \
compile-with "${NORMAL_C} -I$S/dev/ixgbe"
dev/ixgbe/ixgbe_vf.c optional ix inet | ixv inet \
diff --git a/sys/dev/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c
index 88cbeb418ec6..9a1ed66ad6a8 100644
--- a/sys/dev/ixgbe/if_ix.c
+++ b/sys/dev/ixgbe/if_ix.c
@@ -1122,10 +1122,13 @@ ixgbe_if_attach_pre(if_ctx_t ctx)
break;
}
- /* Check the FW API version */
- if (hw->mac.type == ixgbe_mac_E610 && ixgbe_check_fw_api_version(sc)) {
- error = EIO;
- goto err_pci;
+ /* Check the FW API version and enable FW logging support for E610 */
+ if (hw->mac.type == ixgbe_mac_E610) {
+ if (ixgbe_check_fw_api_version(sc)) {
+ error = EIO;
+ goto err_pci;
+ }
+ ixgbe_fwlog_set_support_ena(hw);
}
/* Most of the iflib initialization... */
@@ -3399,6 +3402,9 @@ ixgbe_add_debug_sysctls(struct ixgbe_softc *sc)
if (sc->feat_en & IXGBE_FEATURE_DBG_DUMP)
ixgbe_add_debug_dump_sysctls(sc);
+
+ if (sc->feat_en & IXGBE_FEATURE_FW_LOGGING)
+ ixgbe_add_fw_logging_tunables(sc, sc->debug_sysctls);
} /* ixgbe_add_debug_sysctls */
/************************************************************************
@@ -4495,6 +4501,10 @@ ixgbe_handle_fw_event(void *context)
sc->task_requests |= IXGBE_REQUEST_TASK_LSC;
break;
+ case ixgbe_aci_opc_fw_logs_event:
+ ixgbe_fwlog_event_dump(&sc->hw, &event.desc, event.msg_buf);
+ break;
+
case ixgbe_aci_opc_temp_tca_event:
if (hw->adapter_stopped == FALSE)
ixgbe_if_stop(ctx);
@@ -5731,6 +5741,7 @@ ixgbe_init_device_features(struct ixgbe_softc *sc)
case ixgbe_mac_E610:
sc->feat_cap |= IXGBE_FEATURE_RECOVERY_MODE;
sc->feat_cap |= IXGBE_FEATURE_DBG_DUMP;
+ sc->feat_cap |= IXGBE_FEATURE_FW_LOGGING;
error = ixgbe_get_caps(&sc->hw);
if (error == 0 && sc->hw.func_caps.common_cap.eee_support != 0)
sc->feat_cap |= IXGBE_FEATURE_EEE;
@@ -5758,6 +5769,9 @@ ixgbe_init_device_features(struct ixgbe_softc *sc)
/* FW Debug Dump */
if (sc->feat_cap & IXGBE_FEATURE_DBG_DUMP)
sc->feat_en |= IXGBE_FEATURE_DBG_DUMP;
+ /* FW Logging */
+ if (sc->feat_cap & IXGBE_FEATURE_FW_LOGGING)
+ sc->feat_en |= IXGBE_FEATURE_FW_LOGGING;
/* Enabled via global sysctl... */
/* Flow Director */
diff --git a/sys/dev/ixgbe/ixgbe.h b/sys/dev/ixgbe/ixgbe.h
index 624b71acabea..9120ca5a37ff 100644
--- a/sys/dev/ixgbe/ixgbe.h
+++ b/sys/dev/ixgbe/ixgbe.h
@@ -607,6 +607,11 @@ int ixgbe_setup_receive_structures(struct ixgbe_softc *);
void ixgbe_free_receive_structures(struct ixgbe_softc *);
int ixgbe_get_regs(SYSCTL_HANDLER_ARGS);
+void ixgbe_add_fw_logging_tunables(struct ixgbe_softc *sc,
+ struct sysctl_oid *parent);
+
+#define IXGBE_STR_BUF_LEN 32
+
#include "ixgbe_bypass.h"
#include "ixgbe_fdir.h"
#include "ixgbe_rss.h"
diff --git a/sys/dev/ixgbe/ixgbe_e610.c b/sys/dev/ixgbe/ixgbe_e610.c
index b76d96814933..21066f95a16e 100644
--- a/sys/dev/ixgbe/ixgbe_e610.c
+++ b/sys/dev/ixgbe/ixgbe_e610.c
@@ -3829,6 +3829,492 @@ s32 ixgbe_handle_nvm_access(struct ixgbe_hw *hw,
}
}
+/**
+ * ixgbe_fwlog_cache_cfg - Cache FW logging config
+ * @hw: pointer to the HW structure
+ * @cfg: config to cache
+ *
+ * Cache FW logging config.
+ */
+static void ixgbe_fwlog_cache_cfg(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_cfg *cfg)
+{
+ hw->fwlog_cfg = *cfg;
+}
+
+/**
+ * ixgbe_fwlog_valid_module_entries - validate all the module entry IDs and
+ * log levels
+ * @hw: pointer to the HW structure
+ * @entries: entries to validate
+ * @num_entries: number of entries to validate
+ *
+ * Checks if all the module entry IDs and log levels are valid.
+ *
+ * Return: true if all the module entry IDs and log levels are valid,
+ * otherwise false.
+ */
+static bool ixgbe_fwlog_valid_module_entries(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ u16 i;
+
+ UNREFERENCED_1PARAMETER(hw);
+
+ if (!entries) {
+ return false;
+ }
+
+ if (!num_entries) {
+ return false;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ struct ixgbe_fwlog_module_entry *entry = &entries[i];
+
+ if (entry->module_id >= IXGBE_ACI_FW_LOG_ID_MAX) {
+ return false;
+ }
+
+ if (entry->log_level >= IXGBE_FWLOG_LEVEL_INVALID) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * ixgbe_fwlog_valid_cfg - validate configuration
+ * @hw: pointer to the HW structure
+ * @cfg: config to validate
+ *
+ * Validate the entire configuration.
+ *
+ * Return: true if the entire configuration is valid, otherwise false.
+ */
+static bool ixgbe_fwlog_valid_cfg(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_cfg *cfg)
+{
+ if (!cfg) {
+ return false;
+ }
+
+ if (cfg->log_resolution < IXGBE_ACI_FW_LOG_MIN_RESOLUTION ||
+ cfg->log_resolution > IXGBE_ACI_FW_LOG_MAX_RESOLUTION) {
+ return false;
+ }
+
+ if (!ixgbe_fwlog_valid_module_entries(hw, cfg->module_entries,
+ IXGBE_ACI_FW_LOG_ID_MAX))
+ return false;
+
+ return true;
+}
+
+/**
+ * ixgbe_fwlog_init - Initialize cached structures for tracking FW logging
+ * @hw: pointer to the HW structure
+ * @cfg: config used to initialize the cached structures
+ *
+ * Initialize cached structures for tracking FW logging
+ * Called on driver initialization and before calling
+ * ixgbe_init_hw(). Firmware logging will be configured based on these settings
+ * and also the PF will be registered on init.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_init(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ if (!ixgbe_fwlog_valid_cfg(hw, cfg))
+ return IXGBE_ERR_PARAM;
+
+ ixgbe_fwlog_cache_cfg(hw, cfg);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_aci_fwlog_set - Set FW logging configuration
+ * @hw: pointer to the HW structure
+ * @entries: entries to configure
+ * @num_entries: number of @entries
+ * @options: options from ixgbe_fwlog_cfg->options structure
+ * @log_resolution: logging resolution
+ *
+ * Set FW logging configuration using ACI command (0xFF30).
+ *
+ * Return: the exit code of the operation.
+ */
+static s32 ixgbe_aci_fwlog_set(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries, u16 options, u16 log_resolution)
+{
+ struct ixgbe_aci_cmd_fw_log_cfg_resp fw_modules[IXGBE_ACI_FW_LOG_ID_MAX];
+ struct ixgbe_aci_cmd_fw_log *cmd;
+ struct ixgbe_aci_desc desc;
+ s32 status;
+ u16 i;
+
+ if (num_entries > IXGBE_ACI_FW_LOG_ID_MAX)
+ return IXGBE_ERR_PARAM;
+
+ for (i = 0; i < num_entries; i++) {
+ fw_modules[i].module_identifier =
+ IXGBE_CPU_TO_LE16(entries[i].module_id);
+ fw_modules[i].log_level = entries[i].log_level;
+ }
+
+ ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_fw_logs_config);
+ desc.flags |= IXGBE_CPU_TO_LE16(IXGBE_ACI_FLAG_RD);
+
+ cmd = &desc.params.fw_log;
+
+ cmd->cmd_flags = IXGBE_ACI_FW_LOG_CONF_SET_VALID;
+ cmd->ops.cfg.log_resolution = IXGBE_CPU_TO_LE16(log_resolution);
+ cmd->ops.cfg.mdl_cnt = IXGBE_CPU_TO_LE16(num_entries);
+
+ if (options & IXGBE_FWLOG_OPTION_ARQ_ENA)
+ cmd->cmd_flags |= IXGBE_ACI_FW_LOG_CONF_AQ_EN;
+ if (options & IXGBE_FWLOG_OPTION_UART_ENA)
+ cmd->cmd_flags |= IXGBE_ACI_FW_LOG_CONF_UART_EN;
+
+ status = ixgbe_aci_send_cmd(hw, &desc, fw_modules,
+ sizeof(*fw_modules) * num_entries);
+
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_supported - Cached for whether FW supports FW logging or not
+ * @hw: pointer to the HW structure
+ *
+ * This will always return false if called before ixgbe_init_hw(), so it must be
+ * called after ixgbe_init_hw().
+ *
+ * Return: true if FW supports FW logging.
+ * If this function is called before ixgbe_init_hw(), return false.
+ */
+bool ixgbe_fwlog_supported(struct ixgbe_hw *hw)
+{
+ return hw->fwlog_support_ena;
+}
+
+/**
+ * ixgbe_fwlog_set - Set the firmware logging settings
+ * @hw: pointer to the HW structure
+ * @cfg: config used to set firmware logging
+ *
+ * Call this function whenever the driver needs to set the firmware
+ * logging configuration. It can be called on initialization, reset, or during
+ * runtime.
+ *
+ * If the PF wishes to receive FW logging then it must register via
+ * ixgbe_fwlog_register. Note, that ixgbe_fwlog_register does not need to
+ * be called for init.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_set(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ if (!ixgbe_fwlog_valid_cfg(hw, cfg))
+ return IXGBE_ERR_PARAM;
+
+ status = ixgbe_aci_fwlog_set(hw, cfg->module_entries,
+ IXGBE_ACI_FW_LOG_ID_MAX, cfg->options,
+ cfg->log_resolution);
+ if (!status)
+ ixgbe_fwlog_cache_cfg(hw, cfg);
+
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_update_cached_entries - Update module entries in cached
+ * FW logging config
+ * @hw: pointer to the HW structure
+ * @entries: entries to cache
+ * @num_entries: number of @entries
+ *
+ * Update module entries in cached FW logging config.
+ */
+static void ixgbe_fwlog_update_cached_entries(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ u16 i;
+
+ for (i = 0; i < num_entries; i++) {
+ struct ixgbe_fwlog_module_entry *updated = &entries[i];
+ u16 j;
+
+ for (j = 0; j < IXGBE_ACI_FW_LOG_ID_MAX; j++) {
+ struct ixgbe_fwlog_module_entry *cached =
+ &hw->fwlog_cfg.module_entries[j];
+
+ if (cached->module_id == updated->module_id) {
+ cached->log_level = updated->log_level;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * ixgbe_fwlog_update_modules - Update the log level 1 or more
+ * FW logging modules
+ * @hw: pointer to the HW structure
+ * @entries: array of ixgbe_fwlog_module_entry(s)
+ * @num_entries: number of entries
+ *
+ * Update the log level of 1 or more FW logging modules via module ID.
+ *
+ * Only the entries passed in will be affected. All other firmware logging
+ * settings will be unaffected.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_update_modules(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ struct ixgbe_fwlog_cfg cfg;
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ if (num_entries > IXGBE_ACI_FW_LOG_ID_MAX)
+ return IXGBE_ERR_PARAM;
+
+ if (!ixgbe_fwlog_valid_module_entries(hw, entries, num_entries))
+ return IXGBE_ERR_PARAM;
+
+ status = ixgbe_fwlog_get(hw, &cfg);
+ if (status)
+ goto status_out;
+
+ status = ixgbe_aci_fwlog_set(hw, entries, num_entries, cfg.options,
+ cfg.log_resolution);
+ if (!status)
+ ixgbe_fwlog_update_cached_entries(hw, entries, num_entries);
+
+status_out:
+ return status;
+}
+
+/**
+ * ixgbe_aci_fwlog_register - Register PF for firmware logging events.
+ * @hw: pointer to the HW structure
+ * @reg: true to register and false to unregister
+ *
+ * Register a PF for firmware logging events using ACI command (0xFF31).
+ *
+ * Return: the exit code of the operation.
+ */
+static s32 ixgbe_aci_fwlog_register(struct ixgbe_hw *hw, bool reg)
+{
+ struct ixgbe_aci_desc desc;
+
+ ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_fw_logs_register);
+
+ if (reg)
+ desc.params.fw_log.cmd_flags = IXGBE_ACI_FW_LOG_AQ_REGISTER;
+
+ return ixgbe_aci_send_cmd(hw, &desc, NULL, 0);
+}
+
+/**
+ * ixgbe_fwlog_register - Register the PF for firmware logging
+ * @hw: pointer to the HW structure
+ *
+ * After this call the PF will start to receive firmware logging based on the
+ * configuration set in ixgbe_fwlog_set.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_register(struct ixgbe_hw *hw)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ status = ixgbe_aci_fwlog_register(hw, true);
+
+ if (!status)
+ hw->fwlog_cfg.options |= IXGBE_FWLOG_OPTION_IS_REGISTERED;
+
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_unregister - Unregister the PF from firmware logging
+ * @hw: pointer to the HW structure
+ *
+ * Make an attempt to unregister the PF from firmware logging.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_unregister(struct ixgbe_hw *hw)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ status = ixgbe_aci_fwlog_register(hw, false);
+ if (!status)
+ hw->fwlog_cfg.options &= ~IXGBE_FWLOG_OPTION_IS_REGISTERED;
+
+ return status;
+}
+
+/**
+ * ixgbe_aci_fwlog_get - Get the current firmware logging configuration
+ * @hw: pointer to the HW structure
+ * @cfg: firmware logging configuration to populate
+ *
+ * Make an attempt to get the current firmware logging
+ * configuration using ACI command (0xFF32).
+ *
+ * Return: the exit code of the operation.
+ */
+static s32 ixgbe_aci_fwlog_get(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ struct ixgbe_aci_cmd_fw_log_cfg_resp *fw_modules;
+ struct ixgbe_aci_cmd_fw_log *cmd;
+ struct ixgbe_aci_desc desc;
+ u16 i, module_id_cnt;
+ u8 *buf = NULL;
+ s32 status;
+
+ memset(cfg, 0, sizeof(*cfg));
+
+ ixgbe_fill_dflt_direct_cmd_desc(&desc, ixgbe_aci_opc_fw_logs_query);
+ cmd = &desc.params.fw_log;
+
+ cmd->cmd_flags = IXGBE_ACI_FW_LOG_AQ_QUERY;
+
+ buf = (u8 *)ixgbe_malloc(hw, IXGBE_ACI_MAX_BUFFER_SIZE);
+ if (!buf)
+ return IXGBE_ERR_OUT_OF_MEM;
+
+ status = ixgbe_aci_send_cmd(hw, &desc, buf, IXGBE_ACI_MAX_BUFFER_SIZE);
+ if (status) {
+ goto status_out;
+ }
+
+ module_id_cnt = IXGBE_LE16_TO_CPU(cmd->ops.cfg.mdl_cnt);
+ if (module_id_cnt > IXGBE_ACI_FW_LOG_ID_MAX) {
+ module_id_cnt = IXGBE_ACI_FW_LOG_ID_MAX;
+ }
+
+ cfg->log_resolution = (u8)IXGBE_LE16_TO_CPU(cmd->ops.cfg.log_resolution);
+ if (cmd->cmd_flags & IXGBE_ACI_FW_LOG_CONF_AQ_EN)
+ cfg->options |= IXGBE_FWLOG_OPTION_ARQ_ENA;
+ if (cmd->cmd_flags & IXGBE_ACI_FW_LOG_CONF_UART_EN)
+ cfg->options |= IXGBE_FWLOG_OPTION_UART_ENA;
+ if (cmd->cmd_flags & IXGBE_ACI_FW_LOG_QUERY_REGISTERED)
+ cfg->options |= IXGBE_FWLOG_OPTION_IS_REGISTERED;
+
+ fw_modules = (struct ixgbe_aci_cmd_fw_log_cfg_resp *)buf;
+
+ for (i = 0; i < module_id_cnt; i++) {
+ struct ixgbe_aci_cmd_fw_log_cfg_resp *fw_module = &fw_modules[i];
+
+ cfg->module_entries[i].module_id =
+ IXGBE_LE16_TO_CPU(fw_module->module_identifier);
+ cfg->module_entries[i].log_level = fw_module->log_level;
+ }
+
+status_out:
+ if (buf)
+ ixgbe_free(hw, buf);
+ return status;
+}
+
+/**
+ * ixgbe_fwlog_set_support_ena - Set if FW logging is supported by FW
+ * @hw: pointer to the HW struct
+ *
+ * If FW returns success to the ixgbe_aci_fwlog_get call then it supports FW
+ * logging, else it doesn't. Set the fwlog_support_ena flag accordingly.
+ *
+ * This function is only meant to be called during driver init to determine if
+ * the FW support FW logging.
+ *
+ * Return: the exit code of the operation.
+ */
+void ixgbe_fwlog_set_support_ena(struct ixgbe_hw *hw)
+{
+ struct ixgbe_fwlog_cfg cfg;
+ s32 status;
+
+ hw->fwlog_support_ena = false;
+
+ /* don't call ixgbe_fwlog_get() because that would overwrite the cached
+ * configuration from the call to ixgbe_fwlog_init(), which is expected
+ * to be called prior to this function
+ */
+ status = ixgbe_aci_fwlog_get(hw, &cfg);
+ if (!status)
+ hw->fwlog_support_ena = true;
+}
+
+/**
+ * ixgbe_fwlog_get - Get the firmware logging settings
+ * @hw: pointer to the HW structure
+ * @cfg: config to populate based on current firmware logging settings
+ *
+ * Get the current firmware logging settings.
+ *
+ * Return: the exit code of the operation.
+ */
+s32 ixgbe_fwlog_get(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg)
+{
+ s32 status;
+
+ if (!ixgbe_fwlog_supported(hw))
+ return IXGBE_ERR_NOT_SUPPORTED;
+
+ if (!cfg)
+ return IXGBE_ERR_PARAM;
+
+ status = ixgbe_aci_fwlog_get(hw, cfg);
+ if (status)
+ return status;
+
+ ixgbe_fwlog_cache_cfg(hw, cfg);
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_fwlog_event_dump - Dump the event received over the Admin Receive Queue
+ * @hw: pointer to the HW structure
+ * @desc: Admin Receive Queue descriptor
+ * @buf: buffer that contains the FW log event data
+ *
+ * If the driver receives the ixgbe_aci_opc_fw_logs_event on the Admin Receive
+ * Queue, then it should call this function to dump the FW log data.
+ */
+void ixgbe_fwlog_event_dump(struct ixgbe_hw *hw,
+ struct ixgbe_aci_desc *desc, void *buf)
+{
+ if (!ixgbe_fwlog_supported(hw))
+ return;
+
+ ixgbe_info_fwlog(hw, 32, 1, (u8 *)buf,
+ IXGBE_LE16_TO_CPU(desc->datalen));
+}
+
/**
* ixgbe_aci_set_health_status_config - Configure FW health events
* @hw: pointer to the HW struct
diff --git a/sys/dev/ixgbe/ixgbe_e610.h b/sys/dev/ixgbe/ixgbe_e610.h
index 94e600139499..7af5506d85e8 100644
--- a/sys/dev/ixgbe/ixgbe_e610.h
+++ b/sys/dev/ixgbe/ixgbe_e610.h
@@ -169,6 +169,19 @@ s32 ixgbe_handle_nvm_access(struct ixgbe_hw *hw,
s32 ixgbe_aci_set_health_status_config(struct ixgbe_hw *hw, u8 event_source);
+s32 ixgbe_fwlog_init(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg);
+bool ixgbe_fwlog_supported(struct ixgbe_hw *hw);
+s32 ixgbe_fwlog_set(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg);
+s32 ixgbe_fwlog_update_modules(struct ixgbe_hw *hw,
+ struct ixgbe_fwlog_module_entry *entries,
+ u16 num_entries);
+s32 ixgbe_fwlog_register(struct ixgbe_hw *hw);
+s32 ixgbe_fwlog_unregister(struct ixgbe_hw *hw);
+void ixgbe_fwlog_set_support_ena(struct ixgbe_hw *hw);
+s32 ixgbe_fwlog_get(struct ixgbe_hw *hw, struct ixgbe_fwlog_cfg *cfg);
+void ixgbe_fwlog_event_dump(struct ixgbe_hw *hw,
+ struct ixgbe_aci_desc *desc, void *buf);
+
/* E610 operations */
s32 ixgbe_init_ops_E610(struct ixgbe_hw *hw);
s32 ixgbe_reset_hw_E610(struct ixgbe_hw *hw);
diff --git a/sys/dev/ixgbe/ixgbe_features.h b/sys/dev/ixgbe/ixgbe_features.h
index bee9040319d8..bbc7507b29ac 100644
--- a/sys/dev/ixgbe/ixgbe_features.h
+++ b/sys/dev/ixgbe/ixgbe_features.h
@@ -58,6 +58,7 @@
#define IXGBE_FEATURE_NEEDS_CTXD (u32)(1 << 13)
#define IXGBE_FEATURE_RECOVERY_MODE (u32)(1 << 15)
#define IXGBE_FEATURE_DBG_DUMP (u32)(1 << 16)
+#define IXGBE_FEATURE_FW_LOGGING (u32)(1 << 17)
/* Check for OS support. Undefine features if not included in the OS */
#ifndef PCI_IOV
diff --git a/sys/dev/ixgbe/ixgbe_fw_logging.c b/sys/dev/ixgbe/ixgbe_fw_logging.c
new file mode 100644
index 000000000000..6202d504423f
--- /dev/null
+++ b/sys/dev/ixgbe/ixgbe_fw_logging.c
@@ -0,0 +1,467 @@
+/**
+ * @file ixgbe_fw_logging.c
+ * @brief firmware logging sysctls
+ *
+ * Contains sysctls to enable and configure firmware logging debug support.
+ */
+
+ #include "ixgbe.h"
+
+ /**
+ * ixgbe_reconfig_fw_log - Re-program firmware logging configuration
+ * @sc: private softc structure
+ * @cfg: firmware log configuration to latch
+ *
+ * If the adminq is currently active, ask firmware to update the logging
+ * configuration. If the adminq is currently down, then do nothing. In this
+ * case, ixgbe_init_hw() will re-configure firmware logging as soon as it brings
+ * up the adminq.
+ */
+ static int
+ ixgbe_reconfig_fw_log(struct ixgbe_softc *sc, struct ixgbe_fwlog_cfg *cfg)
+ {
+ int status;
+
+ ixgbe_fwlog_init(&sc->hw, cfg);
+
+ if (!ixgbe_fwlog_supported(&sc->hw))
+ return (0);
+
+ status = ixgbe_fwlog_set(&sc->hw, cfg);
+ if (status != IXGBE_SUCCESS) {
+ DEBUGOUT1("Failed to reconfigure firmware logging, status %d\n",
+ status);
+ return (ENODEV);
+ }
+
+ return (0);
+ }
+
+ /**
+ * ixgbe_sysctl_fwlog_set_cfg_options - Sysctl for setting fwlog cfg options
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: option to adjust
+ * @req: sysctl request pointer
+ *
+ * On read: displays whether firmware logging was reported during attachment
+ * On write: enables/disables firmware logging during attach phase
+ *
+ * This has no effect on the legacy (V1) version of firmware logging.
+ */
+ static int
+ ixgbe_sysctl_fwlog_set_cfg_options(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ int error;
+ u16 option = (u16)arg2;
+ bool enabled;
+
+ enabled = !!(cfg->options & option);
+
+ error = sysctl_handle_bool(oidp, &enabled, 0, req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if (enabled)
+ cfg->options |= option;
+ else
+ cfg->options &= ~option;
+
+ return ixgbe_reconfig_fw_log(sc, cfg);
+ }
+
+ /**
+ * ixgbe_sysctl_fwlog_log_resolution - Sysctl for setting log message resolution
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: __unused__
+ * @req: sysctl request pointer
+ *
+ * On read: displays message queue limit before posting
+ * On write: sets message queue limit before posting
+ *
+ * This has no effect on the legacy (V1) version of firmware logging.
+ */
+ static int
+ ixgbe_sysctl_fwlog_log_resolution(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ int error;
+ u8 resolution;
+
+ UNREFERENCED_PARAMETER(arg2);
+
+ resolution = cfg->log_resolution;
+
+ error = sysctl_handle_8(oidp, &resolution, 0, req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if ((resolution < IXGBE_ACI_FW_LOG_MIN_RESOLUTION) ||
+ (resolution > IXGBE_ACI_FW_LOG_MAX_RESOLUTION)) {
+ DEBUGOUT("Log resolution out-of-bounds\n");
+ return (EINVAL);
+ }
+
+ cfg->log_resolution = resolution;
+
+ return ixgbe_reconfig_fw_log(sc, cfg);
+ }
+
+ /**
+ * ixgbe_sysctl_fwlog_register - Sysctl for (de)registering firmware logs
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: __unused__
+ * @req: sysctl request pointer
+ *
+ * On read: displays whether firmware logging is registered
+ * On write: (de)registers firmware logging.
+ */
+ static int
+ ixgbe_sysctl_fwlog_register(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ int status;
+ int error;
+ u8 enabled;
+
+ UNREFERENCED_PARAMETER(arg2);
+
+ if (cfg->options & IXGBE_FWLOG_OPTION_IS_REGISTERED)
+ enabled = true;
+ else
+ enabled = false;
+
+ error = sysctl_handle_bool(oidp, &enabled, 0, req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if (enabled) {
+ status = ixgbe_fwlog_register(&sc->hw);
+ if (status == IXGBE_SUCCESS)
+ sc->feat_en |= IXGBE_FEATURE_FW_LOGGING;
+ } else {
+ status = ixgbe_fwlog_unregister(&sc->hw);
+ if (status == IXGBE_SUCCESS)
+ sc->feat_en &= ~IXGBE_FEATURE_FW_LOGGING;
+ }
+
+ if (status != IXGBE_SUCCESS)
+ return (EIO);
+
+ return (0);
+ }
+
+ /**
+ * ixgbe_log_sev_str - Convert log level to a string
+ * @log_level: the log level to convert
+ *
+ * Convert the u8 log level of a FW logging module into a readable
+ * string for outputting in a sysctl.
+ */
+ struct ixgbe_str_buf {
+ char str[IXGBE_STR_BUF_LEN];
+ };
+
+ static struct ixgbe_str_buf
+ _ixgbe_log_sev_str(u8 log_level)
+ {
+ struct ixgbe_str_buf buf = { .str = "" };
+ const char *str = NULL;
+
+ switch (log_level) {
+ case IXGBE_FWLOG_LEVEL_NONE:
+ str = "none";
+ break;
+ case IXGBE_FWLOG_LEVEL_ERROR:
+ str = "error";
+ break;
+ case IXGBE_FWLOG_LEVEL_WARNING:
+ str = "warning";
+ break;
+ case IXGBE_FWLOG_LEVEL_NORMAL:
+ str = "normal";
+ break;
+ case IXGBE_FWLOG_LEVEL_VERBOSE:
+ str = "verbose";
+ break;
+ default:
+ break;
+ }
+
+ if (str)
+ snprintf(buf.str, IXGBE_STR_BUF_LEN, "%s", str);
+ else
+ snprintf(buf.str, IXGBE_STR_BUF_LEN, "%u", log_level);
+
+ return buf;
+ }
+
+ #define ixgbe_log_sev_str(log_level) _ixgbe_log_sev_str(log_level).str
+
+ /**
+ * ixgbe_sysctl_fwlog_module_log_severity - Add tunables for a FW logging module
+ * @oidp: sysctl oid structure
+ * @arg1: private softc structure
+ * @arg2: index to logging module
+ * @req: sysctl request pointer
+ */
+ static int
+ ixgbe_sysctl_fwlog_module_log_severity(SYSCTL_HANDLER_ARGS)
+ {
+ struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1;
+ struct ixgbe_fwlog_cfg *cfg = &sc->hw.fwlog_cfg;
+ struct sbuf *sbuf;
+ char *sev_str_end;
+ enum ixgbe_aci_fw_logging_mod module = (enum ixgbe_aci_fw_logging_mod)arg2;
+ int error, ll_num;
+ u8 log_level;
+ char sev_str[16];
+ bool sev_set = false;
+
+ log_level = cfg->module_entries[module].log_level;
+ sbuf = sbuf_new(NULL, sev_str, sizeof(sev_str), SBUF_FIXEDLEN);
+ sbuf_printf(sbuf, "%d<%s>", log_level, ixgbe_log_sev_str(log_level));
+ sbuf_finish(sbuf);
+ sbuf_delete(sbuf);
+
+ error = sysctl_handle_string(oidp, sev_str, sizeof(sev_str), req);
+ if ((error) || (req->newptr == NULL))
+ return (error);
+
+ if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_VERBOSE), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_VERBOSE;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_NORMAL), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_NORMAL;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_WARNING), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_WARNING;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_ERROR), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_ERROR;
+ sev_set = true;
+ } else if (strcasecmp(ixgbe_log_sev_str(IXGBE_FWLOG_LEVEL_NONE), sev_str) == 0) {
+ log_level = IXGBE_FWLOG_LEVEL_NONE;
+ sev_set = true;
+ }
+
+ if (!sev_set) {
*** 294 LINES SKIPPED ***