// SPDX-License-Identifier: GPL-2.0/* Copyright (c) 2020, Intel Corporation. */ #include <linux/vmalloc.h> #include "ice.h"#include "ice_lib.h"#include "devlink.h"#include "port.h"#include "ice_eswitch.h"#include "ice_fw_update.h"#include "ice_dcb_lib.h"#include "ice_sf_eth.h" /* context for devlink info version reporting */struct ice_info_ctx { char buf[128]; struct ice_orom_info pending_orom; struct ice_nvm_info pending_nvm; struct ice_netlist_info pending_netlist; struct ice_hw_dev_caps dev_caps;}; /* The following functions are used to format specific strings for various * devlink info versions. The ctx parameter is used to provide the storage * buffer, as well as any ancillary information calculated when the info * request was made. * * If a version does not exist, for example when attempting to get the * inactive version of flash when there is no pending update, the function * should leave the buffer in the ctx structure empty. */ static void ice_info_get_dsn(struct ice_pf *pf, struct ice_info_ctx *ctx){ u8 dsn[8]; /* Copy the DSN into an array in Big Endian format */ put_unaligned_be64(pci_get_dsn(pf->pdev), dsn); snprintf(ctx->buf, sizeof(ctx->buf), "%8phD", dsn);} static void ice_info_pba(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_hw *hw = &pf->hw; int status; status = ice_read_pba_string(hw, (u8 *)ctx->buf, sizeof(ctx->buf)); if (status) /* We failed to locate the PBA, so just skip this entry */ dev_dbg(ice_pf_to_dev(pf), "Failed to read Product Board Assembly string, status %d\n", status);} static void ice_info_fw_mgmt(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", hw->fw_maj_ver, hw->fw_min_ver, hw->fw_patch);} static void ice_info_fw_api(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", hw->api_maj_ver, hw->api_min_ver, hw->api_patch);} static void ice_info_fw_build(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", hw->fw_build);} static void ice_info_orom_ver(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_orom_info *orom = &pf->hw.flash.orom; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", orom->major, orom->build, orom->patch);} static voidice_info_pending_orom_ver(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx){ struct ice_orom_info *orom = &ctx->pending_orom; if (ctx->dev_caps.common_cap.nvm_update_pending_orom) snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", orom->major, orom->build, orom->patch);} static void ice_info_nvm_ver(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_nvm_info *nvm = &pf->hw.flash.nvm; snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x", nvm->major, nvm->minor);} static voidice_info_pending_nvm_ver(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx){ struct ice_nvm_info *nvm = &ctx->pending_nvm; if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x", nvm->major, nvm->minor);} static void ice_info_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_nvm_info *nvm = &pf->hw.flash.nvm; snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack);} static voidice_info_pending_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_nvm_info *nvm = &ctx->pending_nvm; if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack);} static void ice_info_ddp_pkg_name(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "%s", hw->active_pkg_name);} static voidice_info_ddp_pkg_version(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_pkg_ver *pkg = &pf->hw.active_pkg_ver; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u.%u", pkg->major, pkg->minor, pkg->update, pkg->draft);} static voidice_info_ddp_pkg_bundle_id(struct ice_pf *pf, struct ice_info_ctx *ctx){ snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", pf->hw.active_track_id);} static void ice_info_netlist_ver(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_netlist_info *netlist = &pf->hw.flash.netlist; /* The netlist version fields are BCD formatted */ snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x", netlist->major, netlist->minor, netlist->type >> 16, netlist->type & 0xFFFF, netlist->rev, netlist->cust_ver);} static void ice_info_netlist_build(struct ice_pf *pf, struct ice_info_ctx *ctx){ struct ice_netlist_info *netlist = &pf->hw.flash.netlist; snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash);} static voidice_info_pending_netlist_ver(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx){ struct ice_netlist_info *netlist = &ctx->pending_netlist; /* The netlist version fields are BCD formatted */ if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x", netlist->major, netlist->minor, netlist->type >> 16, netlist->type & 0xFFFF, netlist->rev, netlist->cust_ver);} static voidice_info_pending_netlist_build(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx){ struct ice_netlist_info *netlist = &ctx->pending_netlist; if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash);} static void ice_info_cgu_fw_build(struct ice_pf *pf, struct ice_info_ctx *ctx){ u32 id, cfg_ver, fw_ver; if (!ice_is_feature_supported(pf, ICE_F_CGU)) return; if (ice_aq_get_cgu_info(&pf->hw, &id, &cfg_ver, &fw_ver)) return; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", id, cfg_ver, fw_ver);} static void ice_info_cgu_id(struct ice_pf *pf, struct ice_info_ctx *ctx){ if (!ice_is_feature_supported(pf, ICE_F_CGU)) return; snprintf(ctx->buf, sizeof(ctx->buf), "%u", pf->hw.cgu_part_number);} #define fixed(key, getter) { ICE_VERSION_FIXED, key, getter, NULL }#define running(key, getter) { ICE_VERSION_RUNNING, key, getter, NULL }#define stored(key, getter, fallback) { ICE_VERSION_STORED, key, getter, fallback } /* The combined() macro inserts both the running entry as well as a stored * entry. The running entry will always report the version from the active * handler. The stored entry will first try the pending handler, and fallback * to the active handler if the pending function does not report a version. * The pending handler should check the status of a pending update for the * relevant flash component. It should only fill in the buffer in the case * where a valid pending version is available. This ensures that the related * stored and running versions remain in sync, and that stored versions are * correctly reported as expected. */#define combined(key, active, pending) \ running(key, active), \ stored(key, pending, active) enum ice_version_type { ICE_VERSION_FIXED, ICE_VERSION_RUNNING, ICE_VERSION_STORED,}; static const struct ice_devlink_version { enum ice_version_type type; const char *key; void (*getter)(struct ice_pf *pf, struct ice_info_ctx *ctx); void (*fallback)(struct ice_pf *pf, struct ice_info_ctx *ctx);} ice_devlink_versions[] = { fixed(DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, ice_info_pba), running(DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, ice_info_fw_mgmt), running("fw.mgmt.api", ice_info_fw_api), running("fw.mgmt.build", ice_info_fw_build), combined(DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, ice_info_orom_ver, ice_info_pending_orom_ver), combined("fw.psid.api", ice_info_nvm_ver, ice_info_pending_nvm_ver), combined(DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, ice_info_eetrack, ice_info_pending_eetrack), running("fw.app.name", ice_info_ddp_pkg_name), running(DEVLINK_INFO_VERSION_GENERIC_FW_APP, ice_info_ddp_pkg_version), running("fw.app.bundle_id", ice_info_ddp_pkg_bundle_id), combined("fw.netlist", ice_info_netlist_ver, ice_info_pending_netlist_ver), combined("fw.netlist.build", ice_info_netlist_build, ice_info_pending_netlist_build), fixed("cgu.id", ice_info_cgu_id), running("fw.cgu", ice_info_cgu_fw_build),}; /** * ice_devlink_info_get - .info_get devlink handler * @devlink: devlink instance structure * @req: the devlink info request * @extack: extended netdev ack structure * * Callback for the devlink .info_get operation. Reports information about the * device. * * Return: zero on success or an error code on failure. */static int ice_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack){ struct ice_pf *pf = devlink_priv(devlink); struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; struct ice_info_ctx *ctx; size_t i; int err; err = ice_wait_for_reset(pf, 10 * HZ); if (err) { NL_SET_ERR_MSG_MOD(extack, "Device is busy resetting"); return err; } ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; /* discover capabilities first */ err = ice_discover_dev_caps(hw, &ctx->dev_caps); if (err) { dev_dbg(dev, "Failed to discover device capabilities, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); NL_SET_ERR_MSG_MOD(extack, "Unable to discover device capabilities"); goto out_free_ctx; } if (ctx->dev_caps.common_cap.nvm_update_pending_orom) { err = ice_get_inactive_orom_ver(hw, &ctx->pending_orom); if (err) { dev_dbg(dev, "Unable to read inactive Option ROM version data, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); /* disable display of pending Option ROM */ ctx->dev_caps.common_cap.nvm_update_pending_orom = false; } } if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) { err = ice_get_inactive_nvm_ver(hw, &ctx->pending_nvm); if (err) { dev_dbg(dev, "Unable to read inactive NVM version data, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); /* disable display of pending Option ROM */ ctx->dev_caps.common_cap.nvm_update_pending_nvm = false; } } if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) { err = ice_get_inactive_netlist_ver(hw, &ctx->pending_netlist); if (err) { dev_dbg(dev, "Unable to read inactive Netlist version data, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); /* disable display of pending Option ROM */ ctx->dev_caps.common_cap.nvm_update_pending_netlist = false; } } ice_info_get_dsn(pf, ctx); err = devlink_info_serial_number_put(req, ctx->buf); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set serial number"); goto out_free_ctx; } for (i = 0; i < ARRAY_SIZE(ice_devlink_versions); i++) { enum ice_version_type type = ice_devlink_versions[i].type; const char *key = ice_devlink_versions[i].key; memset(ctx->buf, 0, sizeof(ctx->buf)); ice_devlink_versions[i].getter(pf, ctx); /* If the default getter doesn't report a version, use the * fallback function. This is primarily useful in the case of * "stored" versions that want to report the same value as the * running version in the normal case of no pending update. */ if (ctx->buf[0] == '\0' && ice_devlink_versions[i].fallback) ice_devlink_versions[i].fallback(pf, ctx); /* Do not report missing versions */ if (ctx->buf[0] == '\0') continue; switch (type) { case ICE_VERSION_FIXED: err = devlink_info_version_fixed_put(req, key, ctx->buf); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set fixed version"); goto out_free_ctx; } break; case ICE_VERSION_RUNNING: err = devlink_info_version_running_put_ext(req, key, ctx->buf, DEVLINK_INFO_VERSION_TYPE_COMPONENT); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set running version"); goto out_free_ctx; } break; case ICE_VERSION_STORED: err = devlink_info_version_stored_put_ext(req, key, ctx->buf, DEVLINK_INFO_VERSION_TYPE_COMPONENT); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set stored version"); goto out_free_ctx; } break; } } out_free_ctx: kfree(ctx); return err;} /** * ice_devlink_reload_empr_start - Start EMP reset to activate new firmware * @pf: pointer to the pf instance * @extack: netlink extended ACK structure * * Allow user to activate new Embedded Management Processor firmware by * issuing device specific EMP reset. Called in response to * a DEVLINK_CMD_RELOAD with the DEVLINK_RELOAD_ACTION_FW_ACTIVATE. * * Note that teardown and rebuild of the driver state happens automatically as * part of an interrupt and watchdog task. This is because all physical * functions on the device must be able to reset when an EMP reset occurs from * any source. */static intice_devlink_reload_empr_start(struct ice_pf *pf, struct netlink_ext_ack *extack){ struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; u8 pending; int err; err = ice_get_pending_updates(pf, &pending, extack); if (err) return err; /* pending is a bitmask of which flash banks have a pending update, * including the main NVM bank, the Option ROM bank, and the netlist * bank. If any of these bits are set, then there is a pending update * waiting to be activated. */ if (!pending) { NL_SET_ERR_MSG_MOD(extack, "No pending firmware update"); return -ECANCELED; } if (pf->fw_emp_reset_disabled) { NL_SET_ERR_MSG_MOD(extack, "EMP reset is not available. To activate firmware, a reboot or power cycle is needed"); return -ECANCELED; } dev_dbg(dev, "Issuing device EMP reset to activate firmware\n"); err = ice_aq_nvm_update_empr(hw); if (err) { dev_err(dev, "Failed to trigger EMP device reset to reload firmware, err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); NL_SET_ERR_MSG_MOD(extack, "Failed to trigger EMP device reset to reload firmware"); return err; } return 0;} /** * ice_devlink_reinit_down - unload given PF * @pf: pointer to the PF struct */static void ice_devlink_reinit_down(struct ice_pf *pf){ /* No need to take devl_lock, it's already taken by devlink API */ ice_unload(pf); rtnl_lock(); ice_vsi_decfg(ice_get_main_vsi(pf)); rtnl_unlock(); ice_deinit_pf(pf); ice_deinit_hw(&pf->hw); ice_deinit_dev(pf);} /** * ice_devlink_reload_down - prepare for reload * @devlink: pointer to the devlink instance to reload * @netns_change: if true, the network namespace is changing * @action: the action to perform * @limit: limits on what reload should do, such as not resetting * @extack: netlink extended ACK structure */static intice_devlink_reload_down(struct devlink *devlink, bool netns_change, enum devlink_reload_action action, enum devlink_reload_limit limit, struct netlink_ext_ack *extack){ struct ice_pf *pf = devlink_priv(devlink); switch (action) { case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: if (ice_is_eswitch_mode_switchdev(pf)) { NL_SET_ERR_MSG_MOD(extack, "Go to legacy mode before doing reinit"); return -EOPNOTSUPP; } if (ice_is_adq_active(pf)) { NL_SET_ERR_MSG_MOD(extack, "Turn off ADQ before doing reinit"); return -EOPNOTSUPP; } if (ice_has_vfs(pf)) { NL_SET_ERR_MSG_MOD(extack, "Remove all VFs before doing reinit"); return -EOPNOTSUPP; } ice_devlink_reinit_down(pf);Lines 1–500 of 2094. Load more
// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2020, Intel Corporation. */ #include <linux/vmalloc.h> #include "ice.h" #include "ice_lib.h" #include "devlink.h" #include "port.h" #include "ice_eswitch.h" #include "ice_fw_update.h" #include "ice_dcb_lib.h" #include "ice_sf_eth.h" /* context for devlink info version reporting */ struct ice_info_ctx { char buf[128]; struct ice_orom_info pending_orom; struct ice_nvm_info pending_nvm; struct ice_netlist_info pending_netlist; struct ice_hw_dev_caps dev_caps; }; /* The following functions are used to format specific strings for various * devlink info versions. The ctx parameter is used to provide the storage * buffer, as well as any ancillary information calculated when the info * request was made. * * If a version does not exist, for example when attempting to get the * inactive version of flash when there is no pending update, the function * should leave the buffer in the ctx structure empty. */ static void ice_info_get_dsn(struct ice_pf *pf, struct ice_info_ctx *ctx) { u8 dsn[8]; /* Copy the DSN into an array in Big Endian format */ put_unaligned_be64(pci_get_dsn(pf->pdev), dsn); snprintf(ctx->buf, sizeof(ctx->buf), "%8phD", dsn); } static void ice_info_pba(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_hw *hw = &pf->hw; int status; status = ice_read_pba_string(hw, (u8 *)ctx->buf, sizeof(ctx->buf)); if (status) /* We failed to locate the PBA, so just skip this entry */ dev_dbg(ice_pf_to_dev(pf), "Failed to read Product Board Assembly string, status %d\n", status); } static void ice_info_fw_mgmt(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", hw->fw_maj_ver, hw->fw_min_ver, hw->fw_patch); } static void ice_info_fw_api(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", hw->api_maj_ver, hw->api_min_ver, hw->api_patch); } static void ice_info_fw_build(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", hw->fw_build); } static void ice_info_orom_ver(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_orom_info *orom = &pf->hw.flash.orom; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", orom->major, orom->build, orom->patch); } static void ice_info_pending_orom_ver(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx) { struct ice_orom_info *orom = &ctx->pending_orom; if (ctx->dev_caps.common_cap.nvm_update_pending_orom) snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", orom->major, orom->build, orom->patch); } static void ice_info_nvm_ver(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_nvm_info *nvm = &pf->hw.flash.nvm; snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x", nvm->major, nvm->minor); } static void ice_info_pending_nvm_ver(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx) { struct ice_nvm_info *nvm = &ctx->pending_nvm; if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) snprintf(ctx->buf, sizeof(ctx->buf), "%x.%02x", nvm->major, nvm->minor); } static void ice_info_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_nvm_info *nvm = &pf->hw.flash.nvm; snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack); } static void ice_info_pending_eetrack(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_nvm_info *nvm = &ctx->pending_nvm; if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", nvm->eetrack); } static void ice_info_ddp_pkg_name(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_hw *hw = &pf->hw; snprintf(ctx->buf, sizeof(ctx->buf), "%s", hw->active_pkg_name); } static void ice_info_ddp_pkg_version(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_pkg_ver *pkg = &pf->hw.active_pkg_ver; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u.%u", pkg->major, pkg->minor, pkg->update, pkg->draft); } static void ice_info_ddp_pkg_bundle_id(struct ice_pf *pf, struct ice_info_ctx *ctx) { snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", pf->hw.active_track_id); } static void ice_info_netlist_ver(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_netlist_info *netlist = &pf->hw.flash.netlist; /* The netlist version fields are BCD formatted */ snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x", netlist->major, netlist->minor, netlist->type >> 16, netlist->type & 0xFFFF, netlist->rev, netlist->cust_ver); } static void ice_info_netlist_build(struct ice_pf *pf, struct ice_info_ctx *ctx) { struct ice_netlist_info *netlist = &pf->hw.flash.netlist; snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash); } static void ice_info_pending_netlist_ver(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx) { struct ice_netlist_info *netlist = &ctx->pending_netlist; /* The netlist version fields are BCD formatted */ if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) snprintf(ctx->buf, sizeof(ctx->buf), "%x.%x.%x-%x.%x.%x", netlist->major, netlist->minor, netlist->type >> 16, netlist->type & 0xFFFF, netlist->rev, netlist->cust_ver); } static void ice_info_pending_netlist_build(struct ice_pf __always_unused *pf, struct ice_info_ctx *ctx) { struct ice_netlist_info *netlist = &ctx->pending_netlist; if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) snprintf(ctx->buf, sizeof(ctx->buf), "0x%08x", netlist->hash); } static void ice_info_cgu_fw_build(struct ice_pf *pf, struct ice_info_ctx *ctx) { u32 id, cfg_ver, fw_ver; if (!ice_is_feature_supported(pf, ICE_F_CGU)) return; if (ice_aq_get_cgu_info(&pf->hw, &id, &cfg_ver, &fw_ver)) return; snprintf(ctx->buf, sizeof(ctx->buf), "%u.%u.%u", id, cfg_ver, fw_ver); } static void ice_info_cgu_id(struct ice_pf *pf, struct ice_info_ctx *ctx) { if (!ice_is_feature_supported(pf, ICE_F_CGU)) return; snprintf(ctx->buf, sizeof(ctx->buf), "%u", pf->hw.cgu_part_number); } #define fixed(key, getter) { ICE_VERSION_FIXED, key, getter, NULL } #define running(key, getter) { ICE_VERSION_RUNNING, key, getter, NULL } #define stored(key, getter, fallback) { ICE_VERSION_STORED, key, getter, fallback } /* The combined() macro inserts both the running entry as well as a stored * entry. The running entry will always report the version from the active * handler. The stored entry will first try the pending handler, and fallback * to the active handler if the pending function does not report a version. * The pending handler should check the status of a pending update for the * relevant flash component. It should only fill in the buffer in the case * where a valid pending version is available. This ensures that the related * stored and running versions remain in sync, and that stored versions are * correctly reported as expected. */ #define combined(key, active, pending) \ running(key, active), \ stored(key, pending, active) enum ice_version_type { ICE_VERSION_FIXED, ICE_VERSION_RUNNING, ICE_VERSION_STORED, }; static const struct ice_devlink_version { enum ice_version_type type; const char *key; void (*getter)(struct ice_pf *pf, struct ice_info_ctx *ctx); void (*fallback)(struct ice_pf *pf, struct ice_info_ctx *ctx); } ice_devlink_versions[] = { fixed(DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, ice_info_pba), running(DEVLINK_INFO_VERSION_GENERIC_FW_MGMT, ice_info_fw_mgmt), running("fw.mgmt.api", ice_info_fw_api), running("fw.mgmt.build", ice_info_fw_build), combined(DEVLINK_INFO_VERSION_GENERIC_FW_UNDI, ice_info_orom_ver, ice_info_pending_orom_ver), combined("fw.psid.api", ice_info_nvm_ver, ice_info_pending_nvm_ver), combined(DEVLINK_INFO_VERSION_GENERIC_FW_BUNDLE_ID, ice_info_eetrack, ice_info_pending_eetrack), running("fw.app.name", ice_info_ddp_pkg_name), running(DEVLINK_INFO_VERSION_GENERIC_FW_APP, ice_info_ddp_pkg_version), running("fw.app.bundle_id", ice_info_ddp_pkg_bundle_id), combined("fw.netlist", ice_info_netlist_ver, ice_info_pending_netlist_ver), combined("fw.netlist.build", ice_info_netlist_build, ice_info_pending_netlist_build), fixed("cgu.id", ice_info_cgu_id), running("fw.cgu", ice_info_cgu_fw_build), }; /** * ice_devlink_info_get - .info_get devlink handler * @devlink: devlink instance structure * @req: the devlink info request * @extack: extended netdev ack structure * * Callback for the devlink .info_get operation. Reports information about the * device. * * Return: zero on success or an error code on failure. */ static int ice_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, struct netlink_ext_ack *extack) { struct ice_pf *pf = devlink_priv(devlink); struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; struct ice_info_ctx *ctx; size_t i; int err; err = ice_wait_for_reset(pf, 10 * HZ); if (err) { NL_SET_ERR_MSG_MOD(extack, "Device is busy resetting"); return err; } ctx = kzalloc_obj(*ctx); if (!ctx) return -ENOMEM; /* discover capabilities first */ err = ice_discover_dev_caps(hw, &ctx->dev_caps); if (err) { dev_dbg(dev, "Failed to discover device capabilities, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); NL_SET_ERR_MSG_MOD(extack, "Unable to discover device capabilities"); goto out_free_ctx; } if (ctx->dev_caps.common_cap.nvm_update_pending_orom) { err = ice_get_inactive_orom_ver(hw, &ctx->pending_orom); if (err) { dev_dbg(dev, "Unable to read inactive Option ROM version data, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); /* disable display of pending Option ROM */ ctx->dev_caps.common_cap.nvm_update_pending_orom = false; } } if (ctx->dev_caps.common_cap.nvm_update_pending_nvm) { err = ice_get_inactive_nvm_ver(hw, &ctx->pending_nvm); if (err) { dev_dbg(dev, "Unable to read inactive NVM version data, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); /* disable display of pending Option ROM */ ctx->dev_caps.common_cap.nvm_update_pending_nvm = false; } } if (ctx->dev_caps.common_cap.nvm_update_pending_netlist) { err = ice_get_inactive_netlist_ver(hw, &ctx->pending_netlist); if (err) { dev_dbg(dev, "Unable to read inactive Netlist version data, status %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); /* disable display of pending Option ROM */ ctx->dev_caps.common_cap.nvm_update_pending_netlist = false; } } ice_info_get_dsn(pf, ctx); err = devlink_info_serial_number_put(req, ctx->buf); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set serial number"); goto out_free_ctx; } for (i = 0; i < ARRAY_SIZE(ice_devlink_versions); i++) { enum ice_version_type type = ice_devlink_versions[i].type; const char *key = ice_devlink_versions[i].key; memset(ctx->buf, 0, sizeof(ctx->buf)); ice_devlink_versions[i].getter(pf, ctx); /* If the default getter doesn't report a version, use the * fallback function. This is primarily useful in the case of * "stored" versions that want to report the same value as the * running version in the normal case of no pending update. */ if (ctx->buf[0] == '\0' && ice_devlink_versions[i].fallback) ice_devlink_versions[i].fallback(pf, ctx); /* Do not report missing versions */ if (ctx->buf[0] == '\0') continue; switch (type) { case ICE_VERSION_FIXED: err = devlink_info_version_fixed_put(req, key, ctx->buf); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set fixed version"); goto out_free_ctx; } break; case ICE_VERSION_RUNNING: err = devlink_info_version_running_put_ext(req, key, ctx->buf, DEVLINK_INFO_VERSION_TYPE_COMPONENT); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set running version"); goto out_free_ctx; } break; case ICE_VERSION_STORED: err = devlink_info_version_stored_put_ext(req, key, ctx->buf, DEVLINK_INFO_VERSION_TYPE_COMPONENT); if (err) { NL_SET_ERR_MSG_MOD(extack, "Unable to set stored version"); goto out_free_ctx; } break; } } out_free_ctx: kfree(ctx); return err; } /** * ice_devlink_reload_empr_start - Start EMP reset to activate new firmware * @pf: pointer to the pf instance * @extack: netlink extended ACK structure * * Allow user to activate new Embedded Management Processor firmware by * issuing device specific EMP reset. Called in response to * a DEVLINK_CMD_RELOAD with the DEVLINK_RELOAD_ACTION_FW_ACTIVATE. * * Note that teardown and rebuild of the driver state happens automatically as * part of an interrupt and watchdog task. This is because all physical * functions on the device must be able to reset when an EMP reset occurs from * any source. */ static int ice_devlink_reload_empr_start(struct ice_pf *pf, struct netlink_ext_ack *extack) { struct device *dev = ice_pf_to_dev(pf); struct ice_hw *hw = &pf->hw; u8 pending; int err; err = ice_get_pending_updates(pf, &pending, extack); if (err) return err; /* pending is a bitmask of which flash banks have a pending update, * including the main NVM bank, the Option ROM bank, and the netlist * bank. If any of these bits are set, then there is a pending update * waiting to be activated. */ if (!pending) { NL_SET_ERR_MSG_MOD(extack, "No pending firmware update"); return -ECANCELED; } if (pf->fw_emp_reset_disabled) { NL_SET_ERR_MSG_MOD(extack, "EMP reset is not available. To activate firmware, a reboot or power cycle is needed"); return -ECANCELED; } dev_dbg(dev, "Issuing device EMP reset to activate firmware\n"); err = ice_aq_nvm_update_empr(hw); if (err) { dev_err(dev, "Failed to trigger EMP device reset to reload firmware, err %d aq_err %s\n", err, libie_aq_str(hw->adminq.sq_last_status)); NL_SET_ERR_MSG_MOD(extack, "Failed to trigger EMP device reset to reload firmware"); return err; } return 0; } /** * ice_devlink_reinit_down - unload given PF * @pf: pointer to the PF struct */ static void ice_devlink_reinit_down(struct ice_pf *pf) { /* No need to take devl_lock, it's already taken by devlink API */ ice_unload(pf); rtnl_lock(); ice_vsi_decfg(ice_get_main_vsi(pf)); rtnl_unlock(); ice_deinit_pf(pf); ice_deinit_hw(&pf->hw); ice_deinit_dev(pf); } /** * ice_devlink_reload_down - prepare for reload * @devlink: pointer to the devlink instance to reload * @netns_change: if true, the network namespace is changing * @action: the action to perform * @limit: limits on what reload should do, such as not resetting * @extack: netlink extended ACK structure */ static int ice_devlink_reload_down(struct devlink *devlink, bool netns_change, enum devlink_reload_action action, enum devlink_reload_limit limit, struct netlink_ext_ack *extack) { struct ice_pf *pf = devlink_priv(devlink); switch (action) { case DEVLINK_RELOAD_ACTION_DRIVER_REINIT: if (ice_is_eswitch_mode_switchdev(pf)) { NL_SET_ERR_MSG_MOD(extack, "Go to legacy mode before doing reinit"); return -EOPNOTSUPP; } if (ice_is_adq_active(pf)) { NL_SET_ERR_MSG_MOD(extack, "Turn off ADQ before doing reinit"); return -EOPNOTSUPP; } if (ice_has_vfs(pf)) { NL_SET_ERR_MSG_MOD(extack, "Remove all VFs before doing reinit"); return -EOPNOTSUPP; } ice_devlink_reinit_down(pf);