An eRoT authenticates the complete NVM image, so a PLDM capsule that
carries only part of the components is not a valid update on such an
adapter. Report that from the driver: it knows the components an eRoT
adapter needs, so it can reject the image with a reason in the extended
ACK before anything is written to the device.

Reject a repeated component and a component without data while the
component tables are sent, and require all four components before the
first bank is erased. Check the component set again before activation,
because an image whose device record selects no component at all skips
the flash path and still reaches that step. Reject a single component
request up front, and accept the Manifest component only when firmware
reports eRoT presence. Adapters whose firmware does not report eRoT
keep their current handling.

Read the device capabilities once per flash attempt and act on that
snapshot, then reuse it for the pending update check the attempt
already makes, so an update still costs one List Device Capabilities
round trip. The cached copy in hw->dev_caps cannot decide this: a
driver loaded while firmware was already in recovery mode never reads
it, and a reset re-parses it in place. An explicit capability value
other than 0 or 1 rejects every update, because an answer the driver
cannot interpret is not evidence that the image is complete.

No activation is requested for the Manifest: the AQ activation select
mask and the pending update capabilities have no Manifest entry, so the
driver writes the bank and leaves the activation flag zero. It can
neither activate nor roll back that bank, which is documented along
with the other eRoT update limits.

Signed-off-by: Aleksandr Loktionov <[email protected]>
---
 Documentation/networking/devlink/ice.rst      |  24 +++
 .../net/ethernet/intel/ice/ice_adminq_cmd.h   |   1 +
 .../net/ethernet/intel/ice/ice_fw_update.c    | 193 +++++++++++++++---
 drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
 4 files changed, 192 insertions(+), 27 deletions(-)

diff --git a/Documentation/networking/devlink/ice.rst 
b/Documentation/networking/devlink/ice.rst
index 792e9f8..a8d8cb5 100644
--- a/Documentation/networking/devlink/ice.rst
+++ b/Documentation/networking/devlink/ice.rst
@@ -205,6 +205,30 @@ The ``ice`` driver implements support for flash update 
using the
 combined flash image that contains the ``fw.mgmt``, ``fw.undi``, and
 ``fw.netlist`` components.
 
+When firmware reports an external Root of Trust (eRoT), the image must
+also contain the ``fw.pqc_manifest`` component and all four components
+must be updated together. A single component request is rejected before
+anything is written to the device. An image that misses one of the four
+components, repeats one, or carries a component without data is
+rejected before any flash bank is erased, but by then any previously
+pending update has already been canceled. The driver queries the device
+capabilities for every flash attempt, so these restrictions follow what
+the firmware reports at that moment, including on a driver loaded while
+the firmware was already in recovery mode. Adapters whose firmware does
+not report the eRoT capability keep their previous handling, and a
+``fw.pqc_manifest`` component always requires a reported eRoT
+capability. A reported eRoT capability value other than 0 or 1 makes
+every flash update fail with ``-EIO``; update such firmware out of band.
+
+These checks reject images the adapter cannot use. They do not
+authenticate image contents and they do not make the update atomic. The
+driver cannot activate or roll back the ``fw.pqc_manifest`` component:
+the AQ activation select mask names only ``fw.mgmt``, ``fw.undi`` and
+``fw.netlist``, and no pending update capability describes a Manifest.
+If an update fails after the Manifest bank has been written, the state
+of that bank is defined by the firmware, not by the driver, and
+recovering the adapter then needs out-of-band tooling.
+
 .. list-table:: List of supported overwrite modes
    :widths: 5 95
 
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h 
b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 848ac58..072cd0b 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -1748,6 +1748,7 @@ struct ice_aqc_nvm_comp_tbl {
 #define NVM_COMP_ID_OROM       0x5
 #define NVM_COMP_ID_NVM                0x6
 #define NVM_COMP_ID_NETLIST    0x8
+#define NVM_COMP_ID_MANIFEST   0x12
 
        u8 comp_class_idx;
 #define FWU_COMP_CLASS_IDX_NOT_USE 0x0
diff --git a/drivers/net/ethernet/intel/ice/ice_fw_update.c 
b/drivers/net/ethernet/intel/ice/ice_fw_update.c
index 3631461..93ddcbe 100644
--- a/drivers/net/ethernet/intel/ice/ice_fw_update.c
+++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c
@@ -9,6 +9,15 @@
 #include "ice_lib.h"
 #include "ice_fw_update.h"
 
+/* Bitmask values for ice_fwu_priv::seen_components */
+#define ICE_FWU_COMP_NVM               BIT(0)
+#define ICE_FWU_COMP_OROM              BIT(1)
+#define ICE_FWU_COMP_NETLIST           BIT(2)
+#define ICE_FWU_COMP_MANIFEST          BIT(3)
+/* Components an eRoT adapter requires in a single update */
+#define ICE_FWU_COMP_ALL_EROT          (ICE_FWU_COMP_NVM | ICE_FWU_COMP_OROM | 
\
+                                        ICE_FWU_COMP_NETLIST | 
ICE_FWU_COMP_MANIFEST)
+
 struct ice_fwu_priv {
        struct pldmfw context;
 
@@ -29,6 +38,10 @@ struct ice_fwu_priv {
 
        /* Track if EMP reset is available */
        u8 emp_reset_available;
+
+       /* Bitmask of components seen in this image (ICE_FWU_COMP_*) */
+       u8 seen_components;
+       bool erot_present;
 };
 
 /**
@@ -108,6 +121,9 @@ ice_check_component_response(struct ice_pf *pf, u16 id, u8 
response, u8 code,
        case NVM_COMP_ID_NETLIST:
                component = "fw.netlist";
                break;
+       case NVM_COMP_ID_MANIFEST:
+               component = "fw.pqc_manifest";
+               break;
        default:
                WARN(1, "Unexpected unknown component identifier 0x%02x", id);
                return -EINVAL;
@@ -220,12 +236,26 @@ ice_send_component_table(struct pldmfw *context, struct 
pldmfw_component *compon
        struct ice_pf *pf = priv->pf;
        struct ice_hw *hw = &pf->hw;
        size_t length;
+       u8 comp_bit;
        int status;
 
        switch (component->identifier) {
        case NVM_COMP_ID_OROM:
+               comp_bit = ICE_FWU_COMP_OROM;
+               break;
        case NVM_COMP_ID_NVM:
+               comp_bit = ICE_FWU_COMP_NVM;
+               break;
        case NVM_COMP_ID_NETLIST:
+               comp_bit = ICE_FWU_COMP_NETLIST;
+               break;
+       case NVM_COMP_ID_MANIFEST:
+               if (!priv->erot_present) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "Manifest component requires an eRoT 
capable adapter");
+                       return -EOPNOTSUPP;
+               }
+               comp_bit = ICE_FWU_COMP_MANIFEST;
                break;
        default:
                dev_err(dev, "Unable to update due to a firmware component with 
unknown ID %u\n",
@@ -234,6 +264,19 @@ ice_send_component_table(struct pldmfw *context, struct 
pldmfw_component *compon
                return -EOPNOTSUPP;
        }
 
+       if (priv->erot_present) {
+               if (priv->seen_components & comp_bit) {
+                       NL_SET_ERR_MSG_MOD(extack, "Duplicate component in PLDM 
image");
+                       return -EINVAL;
+               }
+               /* the PLDM parser accepts a component without any data */
+               if (!component->component_size) {
+                       NL_SET_ERR_MSG_MOD(extack, "Firmware component carries 
no data to flash");
+                       return -EINVAL;
+               }
+               priv->seen_components |= comp_bit;
+       }
+
        length = struct_size(comp_tbl, cvs, component->version_len);
        comp_tbl = kzalloc(length, GFP_KERNEL);
        if (!comp_tbl)
@@ -623,6 +666,25 @@ ice_switch_flash_banks(struct ice_pf *pf, u8 
activate_flags,
        return 0;
 }
 
+/**
+ * ice_fwu_check_erot - reject an eRoT update that misses a component
+ * @priv: PLDM firmware update private data
+ *
+ * An eRoT authenticates the complete NVM image, so all four components
+ * must be updated together.
+ *
+ * Returns: zero when the update may proceed, -EINVAL otherwise.
+ */
+static int ice_fwu_check_erot(struct ice_fwu_priv *priv)
+{
+       if (!priv->erot_present || priv->seen_components == 
ICE_FWU_COMP_ALL_EROT)
+               return 0;
+
+       NL_SET_ERR_MSG_MOD(priv->extack,
+                          "eRoT adapter requires fw.mgmt, fw.undi, fw.netlist 
and fw.pqc_manifest");
+       return -EINVAL;
+}
+
 /**
  * ice_flash_component - Flash a component of the NVM
  * @context: PLDM fw update structure
@@ -667,6 +729,13 @@ ice_flash_component(struct pldmfw *context, struct 
pldmfw_component *component)
                reset_level = NULL;
                name = "fw.netlist";
                break;
+       case NVM_COMP_ID_MANIFEST:
+               module = ICE_SR_PQC_MANIFEST_BANK_PTR;
+               /* the activation select mask has no bit for the manifest */
+               flag = 0;
+               reset_level = NULL;
+               name = "fw.pqc_manifest";
+               break;
        default:
                /* This should not trigger, since we check the id before
                 * sending the component table to firmware.
@@ -676,6 +745,13 @@ ice_flash_component(struct pldmfw *context, struct 
pldmfw_component *component)
                return -EINVAL;
        }
 
+       /* every component table is sent before the first component is flashed,
+        * so all components of the image are already known here
+        */
+       err = ice_fwu_check_erot(priv);
+       if (err)
+               return err;
+
        /* Mark this component for activating at the end */
        priv->activate_flags |= flag;
 
@@ -706,6 +782,11 @@ static int ice_finalize_update(struct pldmfw *context)
        struct devlink *devlink;
        int err;
 
+       /* an image without any applicable component still reaches this point */
+       err = ice_fwu_check_erot(priv);
+       if (err)
+               return err;
+
        /* Finally, notify firmware to activate the written NVM banks */
        err = ice_switch_flash_banks(pf, priv->activate_flags,
                                     &priv->emp_reset_available, extack);
@@ -843,6 +924,39 @@ static const struct pldmfw_ops ice_fwu_ops_e822 = {
        .finalize_update = &ice_finalize_update,
 };
 
+/**
+ * ice_pending_updates_from_caps - Report the pending updates of a caps read
+ * @pf: the PF driver structure
+ * @pending: on return, bitmap of updates pending
+ * @dev_caps: device capabilities holding the pending flags
+ *
+ * Build the bitmap of flash components with an update pending, from an
+ * already read set of device capabilities.
+ */
+static void
+ice_pending_updates_from_caps(struct ice_pf *pf, u8 *pending,
+                             const struct ice_hw_dev_caps *dev_caps)
+{
+       struct device *dev = ice_pf_to_dev(pf);
+
+       *pending = 0;
+
+       if (dev_caps->common_cap.nvm_update_pending_nvm) {
+               dev_info(dev, "The fw.mgmt flash component has a pending 
update\n");
+               *pending |= ICE_AQC_NVM_ACTIV_SEL_NVM;
+       }
+
+       if (dev_caps->common_cap.nvm_update_pending_orom) {
+               dev_info(dev, "The fw.undi flash component has a pending 
update\n");
+               *pending |= ICE_AQC_NVM_ACTIV_SEL_OROM;
+       }
+
+       if (dev_caps->common_cap.nvm_update_pending_netlist) {
+               dev_info(dev, "The fw.netlist flash component has a pending 
update\n");
+               *pending |= ICE_AQC_NVM_ACTIV_SEL_NETLIST;
+       }
+}
+
 /**
  * ice_get_pending_updates - Check if the component has a pending update
  * @pf: the PF driver structure
@@ -857,7 +971,6 @@ static const struct pldmfw_ops ice_fwu_ops_e822 = {
 int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
                            struct netlink_ext_ack *extack)
 {
-       struct device *dev = ice_pf_to_dev(pf);
        struct ice_hw_dev_caps *dev_caps;
        struct ice_hw *hw = &pf->hw;
        int err;
@@ -878,22 +991,7 @@ int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
                return err;
        }
 
-       *pending = 0;
-
-       if (dev_caps->common_cap.nvm_update_pending_nvm) {
-               dev_info(dev, "The fw.mgmt flash component has a pending 
update\n");
-               *pending |= ICE_AQC_NVM_ACTIV_SEL_NVM;
-       }
-
-       if (dev_caps->common_cap.nvm_update_pending_orom) {
-               dev_info(dev, "The fw.undi flash component has a pending 
update\n");
-               *pending |= ICE_AQC_NVM_ACTIV_SEL_OROM;
-       }
-
-       if (dev_caps->common_cap.nvm_update_pending_netlist) {
-               dev_info(dev, "The fw.netlist flash component has a pending 
update\n");
-               *pending |= ICE_AQC_NVM_ACTIV_SEL_NETLIST;
-       }
+       ice_pending_updates_from_caps(pf, pending, dev_caps);
 
        kfree(dev_caps);
 
@@ -904,6 +1002,7 @@ int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
  * ice_cancel_pending_update - Cancel any pending update for a component
  * @pf: the PF driver structure
  * @component: if not NULL, the name of the component being updated
+ * @dev_caps: device capabilities to read the pending updates from
  * @extack: Netlink extended ACK structure
  *
  * Cancel any pending update for the specified component. If component is
@@ -913,6 +1012,7 @@ int ice_get_pending_updates(struct ice_pf *pf, u8 *pending,
  */
 static int
 ice_cancel_pending_update(struct ice_pf *pf, const char *component,
+                         const struct ice_hw_dev_caps *dev_caps,
                          struct netlink_ext_ack *extack)
 {
        struct devlink *devlink = priv_to_devlink(pf);
@@ -921,9 +1021,7 @@ ice_cancel_pending_update(struct ice_pf *pf, const char 
*component,
        u8 pending;
        int err;
 
-       err = ice_get_pending_updates(pf, &pending, extack);
-       if (err)
-               return err;
+       ice_pending_updates_from_caps(pf, &pending, dev_caps);
 
        /* If the flash_update request is for a specific component, ignore all
         * of the other components.
@@ -981,9 +1079,10 @@ ice_cancel_pending_update(struct ice_pf *pf, const char 
*component,
  * formatted image that matches this device.
  *
  * Extract the device record Package Data and Component Tables and send them
- * to the firmware. Extract and write the flash data for each of the three
- * main flash components, "fw.mgmt", "fw.undi", and "fw.netlist". Notify
- * firmware once the data is written to the inactive banks.
+ * to the firmware. Extract and write the flash data for each of the main
+ * flash components, "fw.mgmt", "fw.undi", and "fw.netlist", plus
+ * "fw.pqc_manifest" on eRoT adapters. Notify firmware once the data is
+ * written to the inactive banks.
  *
  * Returns: zero on success or a negative error code on failure.
  */
@@ -994,6 +1093,7 @@ int ice_devlink_flash_update(struct devlink *devlink,
        struct ice_pf *pf = devlink_priv(devlink);
        struct device *dev = ice_pf_to_dev(pf);
        struct ice_hw *hw = &pf->hw;
+       struct ice_hw_dev_caps *dev_caps;
        struct ice_fwu_priv priv;
        u8 preservation;
        int err;
@@ -1013,18 +1113,52 @@ int ice_devlink_flash_update(struct devlink *devlink,
                return -EOPNOTSUPP;
        }
 
-       if (!hw->dev_caps.common_cap.nvm_unified_update && 
!ice_is_recovery_mode(hw)) {
+       dev_caps = kzalloc_obj(*dev_caps);
+       if (!dev_caps)
+               return -ENOMEM;
+
+       /* Read the most recent device capabilities from firmware. Do not use
+        * the cached values in hw->dev_caps: a driver loaded while firmware
+        * was already in recovery mode never reads them, and a reset may be
+        * rebuilding them while this request runs. The same read serves the
+        * pending update check below, so this costs one AQ command per flash
+        * attempt, as it did before.
+        */
+       err = ice_discover_dev_caps(hw, dev_caps);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack, "Unable to read device 
capabilities");
+               kfree(dev_caps);
+               return err;
+       }
+
+       if (dev_caps->common_cap.external_pqc_rot_invalid) {
+               NL_SET_ERR_MSG_MOD(extack, "Invalid eRoT capability reported by 
firmware");
+               err = -EIO;
+               goto free_caps;
+       }
+
+       if (!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;
+               err = -EOPNOTSUPP;
+               goto free_caps;
        }
 
        memset(&priv, 0, sizeof(priv));
+       priv.erot_present = dev_caps->common_cap.external_pqc_rot_present;
+
+       if (params->component && priv.erot_present) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "eRoT adapter does not support single 
component update");
+               err = -EOPNOTSUPP;
+               goto free_caps;
+       }
 
        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;
+               err = -EOPNOTSUPP;
+               goto free_caps;
        }
 
        /* the E822 device needs a slightly different ops */
@@ -1039,7 +1173,8 @@ int ice_devlink_flash_update(struct devlink *devlink,
 
        devlink_flash_update_status_notify(devlink, "Preparing to flash", NULL, 
0, 0);
 
-       err = ice_cancel_pending_update(pf, NULL, extack);
+       err = ice_cancel_pending_update(pf, NULL, dev_caps, extack);
+       kfree(dev_caps);
        if (err)
                return err;
 
@@ -1066,4 +1201,8 @@ int ice_devlink_flash_update(struct devlink *devlink,
        ice_release_nvm(hw);
 
        return err;
+
+free_caps:
+       kfree(dev_caps);
+       return err;
 }
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h 
b/drivers/net/ethernet/intel/ice/ice_type.h
index 1ff69f7..0fed29d 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -1152,6 +1152,7 @@ struct ice_aq_get_set_rss_lut_params {
 #define ICE_SR_OROM_BANK_SIZE          0x45
 #define ICE_SR_NETLIST_BANK_PTR                0x46
 #define ICE_SR_NETLIST_BANK_SIZE       0x47
+#define ICE_SR_PQC_MANIFEST_BANK_PTR   0x4E
 #define ICE_SR_SECTOR_SIZE_IN_WORDS    0x800
 
 /* CSS Header words */
-- 
2.56.0

Reply via email to