aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/ice/ice_fwlog.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/ice/ice_fwlog.c')
-rw-r--r--sys/dev/ice/ice_fwlog.c506
1 files changed, 506 insertions, 0 deletions
diff --git a/sys/dev/ice/ice_fwlog.c b/sys/dev/ice/ice_fwlog.c
new file mode 100644
index 000000000000..07ca94ee003d
--- /dev/null
+++ b/sys/dev/ice/ice_fwlog.c
@@ -0,0 +1,506 @@
+/* SPDX-License-Identifier: BSD-3-Clause */
+/* Copyright (c) 2024, Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "ice_osdep.h"
+#include "ice_common.h"
+#include "ice_fwlog.h"
+
+/**
+ * cache_cfg - Cache FW logging config
+ * @hw: pointer to the HW structure
+ * @cfg: config to cache
+ */
+static void cache_cfg(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
+{
+ hw->fwlog_cfg = *cfg;
+}
+
+/**
+ * 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
+ */
+static bool
+valid_module_entries(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ u16 i;
+
+ if (!entries) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "Null ice_fwlog_module_entry array\n");
+ return false;
+ }
+
+ if (!num_entries) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "num_entries must be non-zero\n");
+ return false;
+ }
+
+ for (i = 0; i < num_entries; i++) {
+ struct ice_fwlog_module_entry *entry = &entries[i];
+
+ if (entry->module_id >= ICE_AQC_FW_LOG_ID_MAX) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "Invalid module_id %u, max valid module_id is %u\n",
+ entry->module_id, ICE_AQC_FW_LOG_ID_MAX - 1);
+ return false;
+ }
+
+ if (entry->log_level >= ICE_FWLOG_LEVEL_INVALID) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "Invalid log_level %u, max valid log_level is %u\n",
+ entry->log_level,
+ ICE_AQC_FW_LOG_ID_MAX - 1);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/**
+ * valid_cfg - validate entire configuration
+ * @hw: pointer to the HW structure
+ * @cfg: config to validate
+ */
+static bool valid_cfg(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
+{
+ if (!cfg) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "Null ice_fwlog_cfg\n");
+ return false;
+ }
+
+ if (cfg->log_resolution < ICE_AQC_FW_LOG_MIN_RESOLUTION ||
+ cfg->log_resolution > ICE_AQC_FW_LOG_MAX_RESOLUTION) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "Unsupported log_resolution %u, must be between %u and %u\n",
+ cfg->log_resolution, ICE_AQC_FW_LOG_MIN_RESOLUTION,
+ ICE_AQC_FW_LOG_MAX_RESOLUTION);
+ return false;
+ }
+
+ if (!valid_module_entries(hw, cfg->module_entries,
+ ICE_AQC_FW_LOG_ID_MAX))
+ return false;
+
+ return true;
+}
+
+/**
+ * ice_fwlog_init - Initialize cached structures for tracking FW logging
+ * @hw: pointer to the HW structure
+ * @cfg: config used to initialize the cached structures
+ *
+ * This function should be called on driver initialization and before calling
+ * ice_init_hw(). Firmware logging will be configured based on these settings
+ * and also the PF will be registered on init.
+ */
+int
+ice_fwlog_init(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
+{
+ if (!valid_cfg(hw, cfg))
+ return ICE_ERR_PARAM;
+
+ cache_cfg(hw, cfg);
+
+ return 0;
+}
+
+/**
+ * ice_aq_fwlog_set - Set FW logging configuration AQ command (0xFF30)
+ * @hw: pointer to the HW structure
+ * @entries: entries to configure
+ * @num_entries: number of @entries
+ * @options: options from ice_fwlog_cfg->options structure
+ * @log_resolution: logging resolution
+ */
+static int
+ice_aq_fwlog_set(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
+ u16 num_entries, u16 options, u16 log_resolution)
+{
+ struct ice_aqc_fw_log_cfg_resp *fw_modules;
+ struct ice_aqc_fw_log *cmd;
+ struct ice_aq_desc desc;
+ int status;
+ u16 i;
+
+ fw_modules = (struct ice_aqc_fw_log_cfg_resp *)
+ ice_calloc(hw, num_entries, sizeof(*fw_modules));
+ if (!fw_modules)
+ return ICE_ERR_NO_MEMORY;
+
+ for (i = 0; i < num_entries; i++) {
+ fw_modules[i].module_identifier =
+ CPU_TO_LE16(entries[i].module_id);
+ fw_modules[i].log_level = entries[i].log_level;
+ }
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_config);
+ desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
+
+ cmd = &desc.params.fw_log;
+
+ cmd->cmd_flags = ICE_AQC_FW_LOG_CONF_SET_VALID;
+ cmd->ops.cfg.log_resolution = CPU_TO_LE16(log_resolution);
+ cmd->ops.cfg.mdl_cnt = CPU_TO_LE16(num_entries);
+
+ if (options & ICE_FWLOG_OPTION_ARQ_ENA)
+ cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_AQ_EN;
+ if (options & ICE_FWLOG_OPTION_UART_ENA)
+ cmd->cmd_flags |= ICE_AQC_FW_LOG_CONF_UART_EN;
+
+ status = ice_aq_send_cmd(hw, &desc, fw_modules,
+ sizeof(*fw_modules) * num_entries,
+ NULL);
+
+ ice_free(hw, fw_modules);
+
+ return status;
+}
+
+/**
+ * ice_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 ice_init_hw(), so it must be
+ * called after ice_init_hw().
+ */
+bool ice_fwlog_supported(struct ice_hw *hw)
+{
+ return hw->fwlog_support_ena;
+}
+
+/**
+ * ice_fwlog_set - Set the firmware logging settings
+ * @hw: pointer to the HW structure
+ * @cfg: config used to set firmware logging
+ *
+ * This function should be called 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
+ * ice_fwlog_register. Note, that ice_fwlog_register does not need to be called
+ * for init.
+ */
+int
+ice_fwlog_set(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
+{
+ int status;
+
+ if (!ice_fwlog_supported(hw))
+ return ICE_ERR_NOT_SUPPORTED;
+
+ if (!valid_cfg(hw, cfg))
+ return ICE_ERR_PARAM;
+
+ status = ice_aq_fwlog_set(hw, cfg->module_entries,
+ ICE_AQC_FW_LOG_ID_MAX, cfg->options,
+ cfg->log_resolution);
+ if (!status)
+ cache_cfg(hw, cfg);
+
+ return status;
+}
+
+/**
+ * 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
+ */
+static void
+update_cached_entries(struct ice_hw *hw, struct ice_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ u16 i;
+
+ for (i = 0; i < num_entries; i++) {
+ struct ice_fwlog_module_entry *updated = &entries[i];
+ u16 j;
+
+ for (j = 0; j < ICE_AQC_FW_LOG_ID_MAX; j++) {
+ struct ice_fwlog_module_entry *cached =
+ &hw->fwlog_cfg.module_entries[j];
+
+ if (cached->module_id == updated->module_id) {
+ cached->log_level = updated->log_level;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * ice_fwlog_update_modules - Update the log level 1 or more FW logging modules
+ * @hw: pointer to the HW structure
+ * @entries: array of ice_fwlog_module_entry(s)
+ * @num_entries: number of entries
+ *
+ * This function should be called to 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.
+ */
+int
+ice_fwlog_update_modules(struct ice_hw *hw,
+ struct ice_fwlog_module_entry *entries,
+ u16 num_entries)
+{
+ struct ice_fwlog_cfg *cfg;
+ int status;
+
+ if (!ice_fwlog_supported(hw))
+ return ICE_ERR_NOT_SUPPORTED;
+
+ if (!valid_module_entries(hw, entries, num_entries))
+ return ICE_ERR_PARAM;
+
+ cfg = (struct ice_fwlog_cfg *)ice_calloc(hw, 1, sizeof(*cfg));
+ if (!cfg)
+ return ICE_ERR_NO_MEMORY;
+
+ status = ice_fwlog_get(hw, cfg);
+ if (status)
+ goto status_out;
+
+ status = ice_aq_fwlog_set(hw, entries, num_entries, cfg->options,
+ cfg->log_resolution);
+ if (!status)
+ update_cached_entries(hw, entries, num_entries);
+
+status_out:
+ ice_free(hw, cfg);
+ return status;
+}
+
+/**
+ * ice_aq_fwlog_register - Register PF for firmware logging events (0xFF31)
+ * @hw: pointer to the HW structure
+ * @reg: true to register and false to unregister
+ */
+static int ice_aq_fwlog_register(struct ice_hw *hw, bool reg)
+{
+ struct ice_aq_desc desc;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_register);
+
+ if (reg)
+ desc.params.fw_log.cmd_flags = ICE_AQC_FW_LOG_AQ_REGISTER;
+
+ return ice_aq_send_cmd(hw, &desc, NULL, 0, NULL);
+}
+
+/**
+ * ice_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 ice_fwlog_set.
+ */
+int ice_fwlog_register(struct ice_hw *hw)
+{
+ int status;
+
+ if (!ice_fwlog_supported(hw))
+ return ICE_ERR_NOT_SUPPORTED;
+
+ status = ice_aq_fwlog_register(hw, true);
+ if (status)
+ ice_debug(hw, ICE_DBG_FW_LOG, "Failed to register for firmware logging events over ARQ\n");
+ else
+ hw->fwlog_cfg.options |= ICE_FWLOG_OPTION_IS_REGISTERED;
+
+ return status;
+}
+
+/**
+ * ice_fwlog_unregister - Unregister the PF from firmware logging
+ * @hw: pointer to the HW structure
+ */
+int ice_fwlog_unregister(struct ice_hw *hw)
+{
+ int status;
+
+ if (!ice_fwlog_supported(hw))
+ return ICE_ERR_NOT_SUPPORTED;
+
+ status = ice_aq_fwlog_register(hw, false);
+ if (status)
+ ice_debug(hw, ICE_DBG_FW_LOG, "Failed to unregister from firmware logging events over ARQ\n");
+ else
+ hw->fwlog_cfg.options &= ~ICE_FWLOG_OPTION_IS_REGISTERED;
+
+ return status;
+}
+
+/**
+ * ice_aq_fwlog_get - Get the current firmware logging configuration (0xFF32)
+ * @hw: pointer to the HW structure
+ * @cfg: firmware logging configuration to populate
+ */
+static int
+ice_aq_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
+{
+ struct ice_aqc_fw_log_cfg_resp *fw_modules;
+ struct ice_aqc_fw_log *cmd;
+ struct ice_aq_desc desc;
+ u16 i, module_id_cnt;
+ int status;
+ void *buf;
+
+ ice_memset(cfg, 0, sizeof(*cfg), ICE_NONDMA_MEM);
+
+ buf = ice_calloc(hw, 1, ICE_AQ_MAX_BUF_LEN);
+ if (!buf)
+ return ICE_ERR_NO_MEMORY;
+
+ ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_fw_logs_query);
+ cmd = &desc.params.fw_log;
+
+ cmd->cmd_flags = ICE_AQC_FW_LOG_AQ_QUERY;
+
+ status = ice_aq_send_cmd(hw, &desc, buf, ICE_AQ_MAX_BUF_LEN, NULL);
+ if (status) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "Failed to get FW log configuration\n");
+ goto status_out;
+ }
+
+ module_id_cnt = LE16_TO_CPU(cmd->ops.cfg.mdl_cnt);
+ if (module_id_cnt < ICE_AQC_FW_LOG_ID_MAX) {
+ ice_debug(hw, ICE_DBG_FW_LOG, "FW returned less than the expected number of FW log module IDs\n");
+ } else {
+ if (module_id_cnt > ICE_AQC_FW_LOG_ID_MAX)
+ ice_debug(hw, ICE_DBG_FW_LOG, "FW returned more than expected number of FW log module IDs, setting module_id_cnt to software expected max %u\n",
+ ICE_AQC_FW_LOG_ID_MAX);
+ module_id_cnt = ICE_AQC_FW_LOG_ID_MAX;
+ }
+
+ cfg->log_resolution = LE16_TO_CPU(cmd->ops.cfg.log_resolution);
+ if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_AQ_EN)
+ cfg->options |= ICE_FWLOG_OPTION_ARQ_ENA;
+ if (cmd->cmd_flags & ICE_AQC_FW_LOG_CONF_UART_EN)
+ cfg->options |= ICE_FWLOG_OPTION_UART_ENA;
+ if (cmd->cmd_flags & ICE_AQC_FW_LOG_QUERY_REGISTERED)
+ cfg->options |= ICE_FWLOG_OPTION_IS_REGISTERED;
+
+ fw_modules = (struct ice_aqc_fw_log_cfg_resp *)buf;
+
+ for (i = 0; i < module_id_cnt; i++) {
+ struct ice_aqc_fw_log_cfg_resp *fw_module = &fw_modules[i];
+
+ cfg->module_entries[i].module_id =
+ LE16_TO_CPU(fw_module->module_identifier);
+ cfg->module_entries[i].log_level = fw_module->log_level;
+ }
+
+status_out:
+ ice_free(hw, buf);
+ return status;
+}
+
+/**
+ * ice_fwlog_set_support_ena - Set if FW logging is supported by FW
+ * @hw: pointer to the HW struct
+ *
+ * If FW returns success to the ice_aq_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.
+ */
+void ice_fwlog_set_support_ena(struct ice_hw *hw)
+{
+ struct ice_fwlog_cfg *cfg;
+ int status;
+
+ hw->fwlog_support_ena = false;
+
+ cfg = (struct ice_fwlog_cfg *)ice_calloc(hw, 1, sizeof(*cfg));
+ if (!cfg)
+ return;
+
+ /* don't call ice_fwlog_get() because that would overwrite the cached
+ * configuration from the call to ice_fwlog_init(), which is expected to
+ * be called prior to this function
+ */
+ status = ice_aq_fwlog_get(hw, cfg);
+ if (status)
+ ice_debug(hw, ICE_DBG_FW_LOG, "ice_fwlog_get failed, FW logging is not supported on this version of FW, status %d\n",
+ status);
+ else
+ hw->fwlog_support_ena = true;
+
+ ice_free(hw, cfg);
+}
+
+/**
+ * ice_fwlog_get - Get the firmware logging settings
+ * @hw: pointer to the HW structure
+ * @cfg: config to populate based on current firmware logging settings
+ */
+int
+ice_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
+{
+ int status;
+
+ if (!ice_fwlog_supported(hw))
+ return ICE_ERR_NOT_SUPPORTED;
+
+ if (!cfg)
+ return ICE_ERR_PARAM;
+
+ status = ice_aq_fwlog_get(hw, cfg);
+ if (status)
+ return status;
+
+ cache_cfg(hw, cfg);
+
+ return 0;
+}
+
+/**
+ * ice_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 ice_aqc_opc_fw_logs_event on the Admin Receive
+ * Queue, then it should call this function to dump the FW log data.
+ */
+void
+ice_fwlog_event_dump(struct ice_hw *hw, struct ice_aq_desc *desc, void *buf)
+{
+ if (!ice_fwlog_supported(hw))
+ return;
+
+ ice_info_fwlog(hw, 32, 1, (u8 *)buf, LE16_TO_CPU(desc->datalen));
+}
+