]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ice: support FW Recovery Mode
authorKonrad Knitter <konrad.knitter@intel.com>
Wed, 6 Nov 2024 09:36:43 +0000 (10:36 +0100)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Thu, 16 Jan 2025 21:05:06 +0000 (13:05 -0800)
Recovery Mode is intended to recover from a fatal failure scenario in
which the device is not accessible to the host, meaning the firmware is
non-responsive.

The purpose of the Firmware Recovery Mode is to enable software tools to
update firmware and/or device configuration so the fatal error can be
resolved.

Recovery Mode Firmware supports a limited set of admin commands required
for NVM update.
Recovery Firmware does not support hardware interrupts so a polling mode
is used.

The driver will expose only the minimum set of devlink commands required
for the recovery of the adapter.

Using an appropriate NVM image, the user can recover the adapter using
the devlink flash API.

Prior to 4.20 E810 Adapter Recovery Firmware supports only the update
and erase of the "fw.mgmt" component.

E810 Adapter Recovery Firmware doesn't support selected preservation of
cards settings or identifiers.

The following command can be used to recover the adapter:

$ devlink dev flash <pci-address> <update-image.bin> component fw.mgmt
  overwrite settings overwrite identifier

Newer FW versions (4.20 or newer) supports update of "fw.undi" and
"fw.netlist" components.

$ devlink dev flash <pci-address> <update-image.bin>

Tested on Intel Corporation Ethernet Controller E810-C for SFP
FW revision 3.20 and 4.30.

Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
Signed-off-by: Konrad Knitter <konrad.knitter@intel.com>
Tested-by: Pucha Himasekhar Reddy <himasekharx.reddy.pucha@intel.com> (A Contingent worker at Intel)
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/ice/devlink/devlink.c
drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
drivers/net/ethernet/intel/ice/ice_fw_update.c
drivers/net/ethernet/intel/ice/ice_lib.c
drivers/net/ethernet/intel/ice/ice_lib.h
drivers/net/ethernet/intel/ice/ice_main.c

index d1b9ccec5e05541d1aadbc109a8a0c8dd2f1f169..d116e2b10bceaa4feb3eaac4d8d9b368502f850a 100644 (file)
@@ -368,14 +368,18 @@ static int ice_devlink_info_get(struct devlink *devlink,
                        }
                        break;
                case ICE_VERSION_RUNNING:
-                       err = devlink_info_version_running_put(req, key, ctx->buf);
+                       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(req, key, ctx->buf);
+                       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;
index dd41a4dcc0b9bba86cbeda06626506df14de5646..1f65eedabf7cb93d74932fdfaebed73e3f98ebc2 100644 (file)
@@ -1814,6 +1814,7 @@ struct ice_aqc_nvm_pass_comp_tbl {
 #define ICE_AQ_NVM_PASS_COMP_CAN_BE_UPDATED            0x0
 #define ICE_AQ_NVM_PASS_COMP_CAN_MAY_BE_UPDATEABLE     0x1
 #define ICE_AQ_NVM_PASS_COMP_CAN_NOT_BE_UPDATED                0x2
+#define ICE_AQ_NVM_PASS_COMP_PARTIAL_CHECK             0x3
        u8 component_response_code; /* Response only */
 #define ICE_AQ_NVM_PASS_COMP_CAN_BE_UPDATED_CODE       0x0
 #define ICE_AQ_NVM_PASS_COMP_STAMP_IDENTICAL_CODE      0x1
index 2702a0da5c3e4673175b69e5c882809d8b494fd5..70c201f569ceb54b9243f9fd536ded632aaff33e 100644 (file)
@@ -6,6 +6,7 @@
 #include <linux/crc32.h>
 #include <linux/pldmfw.h>
 #include "ice.h"
+#include "ice_lib.h"
 #include "ice_fw_update.h"
 
 struct ice_fwu_priv {
@@ -125,6 +126,10 @@ ice_check_component_response(struct ice_pf *pf, u16 id, u8 response, u8 code,
        case ICE_AQ_NVM_PASS_COMP_CAN_NOT_BE_UPDATED:
                dev_info(dev, "firmware has rejected updating %s\n", component);
                break;
+       case ICE_AQ_NVM_PASS_COMP_PARTIAL_CHECK:
+               if (ice_is_recovery_mode(&pf->hw))
+                       return 0;
+               break;
        }
 
        switch (code) {
@@ -1004,13 +1009,20 @@ int ice_devlink_flash_update(struct devlink *devlink,
                return -EOPNOTSUPP;
        }
 
-       if (!hw->dev_caps.common_cap.nvm_unified_update) {
+       if (!hw->dev_caps.common_cap.nvm_unified_update && !ice_is_recovery_mode(hw)) {
                NL_SET_ERR_MSG_MOD(extack, "Current firmware does not support unified update");
                return -EOPNOTSUPP;
        }
 
        memset(&priv, 0, sizeof(priv));
 
+       if (params->component && strcmp(params->component, "fw.mgmt") == 0) {
+               priv.context.mode = PLDMFW_UPDATE_MODE_SINGLE_COMPONENT;
+               priv.context.component_identifier = NVM_COMP_ID_NVM;
+       } else if (params->component) {
+               return -EOPNOTSUPP;
+       }
+
        /* the E822 device needs a slightly different ops */
        if (hw->mac_type == ICE_MAC_GENERIC)
                priv.context.ops = &ice_fwu_ops_e822;
index a7d45a8ce7ac0029cd5c5a2e44b61a2050f8a426..38a1c8372180b5abdf64f87d39711bce0d401beb 100644 (file)
@@ -1700,6 +1700,12 @@ bool ice_pf_state_is_nominal(struct ice_pf *pf)
        return true;
 }
 
+#define ICE_FW_MODE_REC_M BIT(1)
+bool ice_is_recovery_mode(struct ice_hw *hw)
+{
+       return rd32(hw, GL_MNG_FWSM) & ICE_FW_MODE_REC_M;
+}
+
 /**
  * ice_update_eth_stats - Update VSI-specific ethernet statistics counters
  * @vsi: the VSI to be updated
index 10d6fc479a32116e52fb310c92382d8ce4bbbf7d..eabb35834a2458f6213be13f634db20b17f9d11e 100644 (file)
@@ -90,6 +90,7 @@ void ice_set_q_vector_intrl(struct ice_q_vector *q_vector);
 
 bool ice_is_safe_mode(struct ice_pf *pf);
 bool ice_is_rdma_ena(struct ice_pf *pf);
+bool ice_is_recovery_mode(struct ice_hw *hw);
 bool ice_is_dflt_vsi_in_use(struct ice_port_info *pi);
 bool ice_is_vsi_dflt_vsi(struct ice_vsi *vsi);
 int ice_set_dflt_vsi(struct ice_vsi *vsi);
index 749b5e29e83af9ea7fb2b3ee6643833258829dda..2205ea871209f5f87f7189d38463871d07a9b7a8 100644 (file)
@@ -2364,6 +2364,18 @@ static void ice_check_media_subtask(struct ice_pf *pf)
        }
 }
 
+static void ice_service_task_recovery_mode(struct work_struct *work)
+{
+       struct ice_pf *pf = container_of(work, struct ice_pf, serv_task);
+
+       set_bit(ICE_ADMINQ_EVENT_PENDING, pf->state);
+       ice_clean_adminq_subtask(pf);
+
+       ice_service_task_complete(pf);
+
+       mod_timer(&pf->serv_tmr, jiffies + msecs_to_jiffies(100));
+}
+
 /**
  * ice_service_task - manage and run subtasks
  * @work: pointer to work_struct contained by the PF struct
@@ -5217,6 +5229,36 @@ void ice_unload(struct ice_pf *pf)
        ice_decfg_netdev(vsi);
 }
 
+static int ice_probe_recovery_mode(struct ice_pf *pf)
+{
+       struct device *dev = ice_pf_to_dev(pf);
+       int err;
+
+       dev_err(dev, "Firmware recovery mode detected. Limiting functionality. Refer to the Intel(R) Ethernet Adapters and Devices User Guide for details on firmware recovery mode\n");
+
+       INIT_HLIST_HEAD(&pf->aq_wait_list);
+       spin_lock_init(&pf->aq_wait_lock);
+       init_waitqueue_head(&pf->aq_wait_queue);
+
+       timer_setup(&pf->serv_tmr, ice_service_timer, 0);
+       pf->serv_tmr_period = HZ;
+       INIT_WORK(&pf->serv_task, ice_service_task_recovery_mode);
+       clear_bit(ICE_SERVICE_SCHED, pf->state);
+       err = ice_create_all_ctrlq(&pf->hw);
+       if (err)
+               return err;
+
+       scoped_guard(devl, priv_to_devlink(pf)) {
+               err = ice_init_devlink(pf);
+               if (err)
+                       return err;
+       }
+
+       ice_service_task_restart(pf);
+
+       return 0;
+}
+
 /**
  * ice_probe - Device initialization routine
  * @pdev: PCI device information struct
@@ -5308,6 +5350,9 @@ ice_probe(struct pci_dev *pdev, const struct pci_device_id __always_unused *ent)
                hw->debug_mask = debug;
 #endif
 
+       if (ice_is_recovery_mode(hw))
+               return ice_probe_recovery_mode(pf);
+
        err = ice_init_hw(hw);
        if (err) {
                dev_err(dev, "ice_init_hw failed: %d\n", err);
@@ -5425,6 +5470,14 @@ static void ice_remove(struct pci_dev *pdev)
                msleep(100);
        }
 
+       if (ice_is_recovery_mode(&pf->hw)) {
+               ice_service_task_stop(pf);
+               scoped_guard(devl, priv_to_devlink(pf)) {
+                       ice_deinit_devlink(pf);
+               }
+               return;
+       }
+
        if (test_bit(ICE_FLAG_SRIOV_ENA, pf->flags)) {
                set_bit(ICE_VF_RESETS_DISABLED, pf->state);
                ice_free_vfs(pf);