Require NVM, OROM, NetList and Manifest together when firmware reports
eRoT presence. Track the component IDs while sending the PLDM component
tables and reject an incomplete set before erasing a bank. Reject
duplicates only for eRoT updates, leaving ordinary duplicate handling
unchanged.

Read fresh device capabilities for each flash attempt before canceling
a pending update or acquiring the NVM write lock. Use the confirmed eRoT
presence as a per-update snapshot for component selection and validation.
This avoids depending on probe-time state after reset or in recovery,
where normal hardware initialization is skipped. Use the same fresh
query for the unified-update check, retaining its recovery bypass.

Treat an omitted capability as absent, retaining legacy full-image and
fw.mgmt component updates. Enable eRoT-specific restrictions only when
firmware explicitly reports presence. Older eRoT hardware whose firmware
omits the capability is not protected by the early completeness checks.

Propagate discovery errors and reject explicitly reported values other
than 0 or 1 before pending-update cancellation or the NVM write lock.
Neither case reaches package transfer, erase, write or activation.
Recovery mode does not bypass this check or require full hardware
initialization. Pending-update cancellation already discovers device
capabilities, so recovery flashing required that query before this series.

Reject single-component requests up front when eRoT presence is reported.
Keep the completeness check independent of the PLDM END flag: component
filtering can omit the component that would carry END. Also check before
finalization, since an empty applicable component list skips the flash
callback but still reaches bank activation.

Accept Manifest only with confirmed eRoT presence. Its activation flag
is zero: the AQ activation-select mask has bits for NVM, OROM and NetList,
but none for Manifest. This does not establish its activation timing.
Define its bank pointer, without adding an unused bank-size constant.

Document the component rule and the capability-only policy in normal
and recovery modes. These checks neither authenticate image contents
nor make the update atomic.

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

diff --git a/Documentation/networking/devlink/ice.rst 
b/Documentation/networking/devlink/ice.rst
index 792e9f8..e03630a 100644
--- a/Documentation/networking/devlink/ice.rst
+++ b/Documentation/networking/devlink/ice.rst
@@ -205,6 +205,27 @@ 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) as present, the
+image must also contain the ``fw.pqc_manifest`` component, and all four
+components must be updated together. Images missing any of them, or
+containing a component more than once, are rejected before any flash bank
+is erased. An empty applicable component list is rejected before
+activation. Updating a single component (``component fw.mgmt``) is not
+supported when eRoT presence is reported. ``fw.pqc_manifest`` is accepted
+only when firmware reports eRoT presence. These checks do not authenticate
+the image contents or make the update atomic.
+
+Before each flash attempt, the driver reads device capabilities from
+firmware. An omitted eRoT capability is treated as absent, preserving
+legacy full-image and ``component fw.mgmt`` updates. Older eRoT hardware
+whose firmware omits the capability is not protected by the early
+completeness checks. An explicitly reported value other than 0 (absent)
+or 1 (present), or failure to read capabilities, blocks the update before
+any pending update is canceled or a flash write lock is acquired. This
+policy also applies in recovery mode, which only bypasses the separate
+unified-update capability check. Pending-update cancellation already
+requires capability discovery, including in recovery mode.
+
 .. 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..9da7df6 100644
--- a/drivers/net/ethernet/intel/ice/ice_fw_update.c
+++ b/drivers/net/ethernet/intel/ice/ice_fw_update.c
@@ -9,6 +9,13 @@
 #include "ice_lib.h"
 #include "ice_fw_update.h"
 
+#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)
+#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 +36,8 @@ struct ice_fwu_priv {
 
        /* Track if EMP reset is available */
        u8 emp_reset_available;
+       u8 seen_components;
+       bool erot_present;
 };
 
 /**
@@ -108,6 +117,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 +232,27 @@ 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) {
+                       dev_err(dev, "Manifest component is not supported on 
adapters without eRoT\n");
+                       NL_SET_ERR_MSG_MOD(extack,
+                                          "Manifest component is not supported 
on adapters without eRoT");
+                       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 +261,14 @@ ice_send_component_table(struct pldmfw *context, struct 
pldmfw_component *compon
                return -EOPNOTSUPP;
        }
 
+       if (priv->erot_present && (priv->seen_components & comp_bit)) {
+               dev_err(dev, "Duplicate component in PLDM image: component ID 
0x%02x\n",
+                       component->identifier);
+               NL_SET_ERR_MSG_MOD(extack, "Duplicate component in PLDM image");
+               return -EOPNOTSUPP;
+       }
+       priv->seen_components |= comp_bit;
+
        length = struct_size(comp_tbl, cvs, component->version_len);
        comp_tbl = kzalloc(length, GFP_KERNEL);
        if (!comp_tbl)
@@ -648,6 +683,12 @@ ice_flash_component(struct pldmfw *context, struct 
pldmfw_component *component)
        u8 flag;
        int err;
 
+       if (priv->erot_present && priv->seen_components != 
ICE_FWU_COMP_ALL_EROT) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "eRoT adapter requires all four components 
(NVM, OROM, NetList, Manifest) in a single update");
+               return -EINVAL;
+       }
+
        switch (component->identifier) {
        case NVM_COMP_ID_OROM:
                module = ICE_SR_1ST_OROM_BANK_PTR;
@@ -667,6 +708,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 AQ activation-select mask has no Manifest bit. */
+               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.
@@ -706,6 +754,12 @@ static int ice_finalize_update(struct pldmfw *context)
        struct devlink *devlink;
        int err;
 
+       if (priv->erot_present && priv->seen_components != 
ICE_FWU_COMP_ALL_EROT) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "eRoT adapter requires all four components 
(NVM, OROM, NetList, Manifest) in a single update");
+               return -EINVAL;
+       }
+
        /* Finally, notify firmware to activate the written NVM banks */
        err = ice_switch_flash_banks(pf, priv->activate_flags,
                                     &priv->emp_reset_available, extack);
@@ -981,9 +1035,9 @@ 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 the selected firmware
+ * components: "fw.mgmt", "fw.undi", "fw.netlist", and, when firmware reports
+ * eRoT presence, "fw.pqc_manifest". Notify firmware once the data is written.
  *
  * Returns: zero on success or a negative error code on failure.
  */
@@ -1013,12 +1067,37 @@ int ice_devlink_flash_update(struct devlink *devlink,
                return -EOPNOTSUPP;
        }
 
-       if (!hw->dev_caps.common_cap.nvm_unified_update && 
!ice_is_recovery_mode(hw)) {
+       struct ice_hw_dev_caps *dev_caps __free(kfree) =
+               kzalloc_obj(*dev_caps);
+       if (!dev_caps)
+               return -ENOMEM;
+
+       err = ice_discover_dev_caps(hw, dev_caps);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Cannot determine eRoT state: unable to read 
firmware device capabilities");
+               return err;
+       }
+
+       if (dev_caps->common_cap.external_pqc_rot_invalid) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Invalid eRoT capability reported by 
firmware");
+               return -EOPNOTSUPP;
+       }
+
+       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;
        }
 
        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");
+               return -EOPNOTSUPP;
+       }
 
        if (params->component && strcmp(params->component, "fw.mgmt") == 0) {
                priv.context.mode = PLDMFW_UPDATE_MODE_SINGLE_COMPONENT;
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h 
b/drivers/net/ethernet/intel/ice/ice_type.h
index 1fede0f..43f3208 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -1151,6 +1151,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.52.0

Reply via email to