freebsd-src/sys/dev/ice/ice_fwlog.c
Eric Joyner 015f8cc5b0
ice(4): Update copyright year to 2024
Signed-off-by: Eric Joyner <erj@FreeBSD.org>

MFC after:	3 days
Sponsored by:	Intel Corporation
Differential Revision:	https://reviews.freebsd.org/D44003
2024-04-18 16:14:03 -07:00

506 lines
14 KiB
C

/* 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_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.
*/
enum ice_status
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 ICE_SUCCESS;
}
/**
* 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 enum ice_status
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;
enum ice_status 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.
*/
enum ice_status
ice_fwlog_set(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
enum ice_status 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.
*/
enum ice_status
ice_fwlog_update_modules(struct ice_hw *hw,
struct ice_fwlog_module_entry *entries,
u16 num_entries)
{
struct ice_fwlog_cfg *cfg;
enum ice_status 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 enum ice_status 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.
*/
enum ice_status ice_fwlog_register(struct ice_hw *hw)
{
enum ice_status 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
*/
enum ice_status ice_fwlog_unregister(struct ice_hw *hw)
{
enum ice_status 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 enum ice_status
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;
enum ice_status status;
u16 i, module_id_cnt;
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;
enum ice_status 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
*/
enum ice_status
ice_fwlog_get(struct ice_hw *hw, struct ice_fwlog_cfg *cfg)
{
enum ice_status 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 ICE_SUCCESS;
}
/**
* 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));
}