* [PATCH iwl-next v3 0/2] ice: eRoT adapter NVM update guard
@ 2026-10-27 17:45 Aleksandr Loktionov
2026-10-27 17:45 ` [PATCH iwl-next v3 1/2] ice: parse external Root of Trust capability Aleksandr Loktionov
2026-10-27 17:45 ` [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters Aleksandr Loktionov
0 siblings, 2 replies; 6+ messages in thread
From: Aleksandr Loktionov @ 2026-10-27 17:45 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov
Parse the external Root of Trust capability and reject a PLDM capsule
that does not carry every component an eRoT adapter authenticates,
before any flash bank is erased.
Changes in this v3 respin:
- Act on device capabilities read once per flash attempt instead of
on the cached copy: a driver loaded while firmware was already in
recovery mode has no cached capabilities, and a reset re-parses
them in place. The same read feeds the pending update check that
the attempt already makes, so an update still costs one List Device
Capabilities round trip.
- Reject duplicates and components without data while the component
tables are sent, and require all four components before the first
bank is erased and again before activation.
- Name the components the way the user names them: fw.mgmt, fw.undi,
fw.netlist, fw.pqc_manifest.
- Document the invalid capability policy, and that the driver can
neither activate nor roll back fw.pqc_manifest.
- Report the new rejections through the netlink extended ACK only.
- Drop __free(kfree) and the mid-function declaration.
- One errno for a malformed image, and -EIO for an invalid capability
value instead of -EOPNOTSUPP.
- Make the capability verdicts sticky, so a duplicated capability
entry cannot undo an earlier one.
- Shorten both commit messages and the devlink section. The review
tags from earlier rounds are not carried, since the update path
changed; a fresh review is requested.
On the Manifest: the AQ activation select mask and the pending update
capabilities have no Manifest entry, so the driver writes that bank and
requests no activation for it. It can neither activate nor roll back
the bank, and the devlink docs now say so. Two points still need an
answer from the firmware side, and a Tested-by from a named adapter
would carry the rest: whether shadow RAM word 0x4E and component ID
0x12 are the right values for the PQC Manifest, and whether the eRoT
authenticates a Manifest against the active image, which decides
whether writing it before the other banks are activated is safe.
Two defects next to this path are not part of this series because they
predate it: an ice_aq_task left linked in pf->aq_wait_list when an AQ
command submission fails, because ice_aq_prep_for_event() has no undo
helper, and unchecked lengths in lib/pldmfw (a 32-bit size_t wrap in
pldm_check_fw_space(), and a component bitmap whose length is never
checked against the component count). Fixes go out separately; the new
module type ID only widens the first one.
Aleksandr Loktionov (2):
ice: parse external Root of Trust capability
ice: reject partial NVM update on eRoT adapters
Documentation/networking/devlink/ice.rst | 24 +++
.../net/ethernet/intel/ice/ice_adminq_cmd.h | 1 +
drivers/net/ethernet/intel/ice/ice_common.c | 7 +
.../net/ethernet/intel/ice/ice_fw_update.c | 193 +++++++++++++++---
drivers/net/ethernet/intel/ice/ice_type.h | 4 +
include/linux/net/intel/libie/adminq.h | 1 +
6 files changed, 203 insertions(+), 27 deletions(-)
--
2.56.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH iwl-next v3 1/2] ice: parse external Root of Trust capability
2026-10-27 17:45 [PATCH iwl-next v3 0/2] ice: eRoT adapter NVM update guard Aleksandr Loktionov
@ 2026-10-27 17:45 ` Aleksandr Loktionov
2026-10-29 14:43 ` Tomasz Lichwala
2026-10-27 17:45 ` [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters Aleksandr Loktionov
1 sibling, 1 reply; 6+ messages in thread
From: Aleksandr Loktionov @ 2026-10-27 17:45 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov
Reserve the external PQC Root of Trust capability ID in the shared libie
capability list and parse its value into the device capabilities.
Firmware reports 1 when an external Root of Trust (eRoT) controller is
present and 0 when it is not. Keep a separate bit for an explicit value
other than 0 or 1, so the flash path can reject an unknown answer
instead of reading it as absence: an image the eRoT turns down leaves an
erased flash bank behind, so guessing is worse than not flashing. Set
both bits with |=, so a duplicated capability entry cannot undo a verdict
already taken from an earlier one: an invalid value always rejects the
update, and a mix of 0 and 1 keeps the eRoT checks enabled.
Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
---
drivers/net/ethernet/intel/ice/ice_common.c | 7 +++++++
drivers/net/ethernet/intel/ice/ice_type.h | 3 +++
include/linux/net/intel/libie/adminq.h | 1 +
3 files changed, 11 insertions(+)
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index 81187214..cc387c6 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -2482,6 +2482,13 @@ ice_parse_common_caps(struct ice_hw *hw, struct ice_hw_common_caps *caps,
case LIBIE_AQC_CAPS_TX_SCHED_TOPO_COMP_MODE:
caps->tx_sched_topo_comp_mode_en = (number == 1);
break;
+ case LIBIE_AQC_CAPS_EXTERNAL_PQC_ROT_PRESENT:
+ /* sticky, so a duplicated entry cannot clear an earlier verdict */
+ caps->external_pqc_rot_present |= (number == 1);
+ caps->external_pqc_rot_invalid |= (number != 0 && number != 1);
+ ice_debug(hw, ICE_DBG_INIT, "%s: external_pqc_rot = %u\n",
+ prefix, number);
+ break;
default:
/* Not one of the recognized common capabilities */
found = false;
diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
index 4b6ed42..1ff69f7 100644
--- a/drivers/net/ethernet/intel/ice/ice_type.h
+++ b/drivers/net/ethernet/intel/ice/ice_type.h
@@ -311,6 +311,9 @@ struct ice_hw_common_caps {
/* Post update reset restriction */
bool reset_restrict_support;
bool tx_sched_topo_comp_mode_en;
+ /* External Root of Trust (eRoT) controller present */
+ bool external_pqc_rot_present;
+ bool external_pqc_rot_invalid;
};
/* IEEE 1588 TIME_SYNC specific info */
diff --git a/include/linux/net/intel/libie/adminq.h b/include/linux/net/intel/libie/adminq.h
index 839114d..df42f5b 100644
--- a/include/linux/net/intel/libie/adminq.h
+++ b/include/linux/net/intel/libie/adminq.h
@@ -175,6 +175,7 @@ LIBIE_CHECK_STRUCT_LEN(16, libie_aqc_list_caps);
#define LIBIE_AQC_CAPS_PENDING_OROM_VER 0x004B
#define LIBIE_AQC_CAPS_NET_VER 0x004C
#define LIBIE_AQC_CAPS_PENDING_NET_VER 0x004D
+#define LIBIE_AQC_CAPS_EXTERNAL_PQC_ROT_PRESENT 0x004E
#define LIBIE_AQC_CAPS_RDMA 0x0051
#define LIBIE_AQC_CAPS_LED 0x0061
#define LIBIE_AQC_CAPS_SDP 0x0062
--
2.56.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters
2026-10-27 17:45 [PATCH iwl-next v3 0/2] ice: eRoT adapter NVM update guard Aleksandr Loktionov
2026-10-27 17:45 ` [PATCH iwl-next v3 1/2] ice: parse external Root of Trust capability Aleksandr Loktionov
@ 2026-10-27 17:45 ` Aleksandr Loktionov
2026-10-28 12:11 ` Przemek Kitszel
2026-10-29 14:43 ` Tomasz Lichwala
1 sibling, 2 replies; 6+ messages in thread
From: Aleksandr Loktionov @ 2026-10-27 17:45 UTC (permalink / raw)
To: intel-wired-lan, anthony.l.nguyen, aleksandr.loktionov
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 <aleksandr.loktionov@intel.com>
---
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
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters
2026-10-27 17:45 ` [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters Aleksandr Loktionov
@ 2026-10-28 12:11 ` Przemek Kitszel
2026-10-29 14:43 ` Tomasz Lichwala
1 sibling, 0 replies; 6+ messages in thread
From: Przemek Kitszel @ 2026-10-28 12:11 UTC (permalink / raw)
To: Aleksandr Loktionov, intel-wired-lan, anthony.l.nguyen
On 10/27/26 6:45 PM, Aleksandr Loktionov wrote:
> 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 <aleksandr.loktionov@intel.com>
> ---
> 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(-)
two nitpicks, otherwise the whole series is fine, feel free to add my RB
to both patches:
Reviewed-by: Przemek Kitszel <przemyslaw.kitszel@intel.com>
> @@ -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;
RCT
> 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;
goto free_caps instead
> + }
> +
> + 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;
> + }
[...]
> @@ -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;
> }
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH iwl-next v3 1/2] ice: parse external Root of Trust capability
2026-10-27 17:45 ` [PATCH iwl-next v3 1/2] ice: parse external Root of Trust capability Aleksandr Loktionov
@ 2026-10-29 14:43 ` Tomasz Lichwala
0 siblings, 0 replies; 6+ messages in thread
From: Tomasz Lichwala @ 2026-10-29 14:43 UTC (permalink / raw)
To: Aleksandr Loktionov, intel-wired-lan, anthony.l.nguyen
On 27.10.2026 18:45, Aleksandr Loktionov wrote:
> Reserve the external PQC Root of Trust capability ID in the shared libie
> capability list and parse its value into the device capabilities.
>
> Firmware reports 1 when an external Root of Trust (eRoT) controller is
> present and 0 when it is not. Keep a separate bit for an explicit value
> other than 0 or 1, so the flash path can reject an unknown answer
> instead of reading it as absence: an image the eRoT turns down leaves an
> erased flash bank behind, so guessing is worse than not flashing. Set
> both bits with |=, so a duplicated capability entry cannot undo a verdict
> already taken from an earlier one: an invalid value always rejects the
> update, and a mix of 0 and 1 keeps the eRoT checks enabled.
>
> Signed-off-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
> ---
> drivers/net/ethernet/intel/ice/ice_common.c | 7 +++++++
> drivers/net/ethernet/intel/ice/ice_type.h | 3 +++
> include/linux/net/intel/libie/adminq.h | 1 +
> 3 files changed, 11 insertions(+)
>
> diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
> index 81187214..cc387c6 100644
> --- a/drivers/net/ethernet/intel/ice/ice_common.c
> +++ b/drivers/net/ethernet/intel/ice/ice_common.c
> @@ -2482,6 +2482,13 @@ ice_parse_common_caps(struct ice_hw *hw, struct ice_hw_common_caps *caps,
> case LIBIE_AQC_CAPS_TX_SCHED_TOPO_COMP_MODE:
> caps->tx_sched_topo_comp_mode_en = (number == 1);
> break;
> + case LIBIE_AQC_CAPS_EXTERNAL_PQC_ROT_PRESENT:
> + /* sticky, so a duplicated entry cannot clear an earlier verdict */
> + caps->external_pqc_rot_present |= (number == 1);
> + caps->external_pqc_rot_invalid |= (number != 0 && number != 1);
> + ice_debug(hw, ICE_DBG_INIT, "%s: external_pqc_rot = %u\n",
> + prefix, number);
> + break;
> default:
> /* Not one of the recognized common capabilities */
> found = false;
> diff --git a/drivers/net/ethernet/intel/ice/ice_type.h b/drivers/net/ethernet/intel/ice/ice_type.h
> index 4b6ed42..1ff69f7 100644
> --- a/drivers/net/ethernet/intel/ice/ice_type.h
> +++ b/drivers/net/ethernet/intel/ice/ice_type.h
> @@ -311,6 +311,9 @@ struct ice_hw_common_caps {
> /* Post update reset restriction */
> bool reset_restrict_support;
> bool tx_sched_topo_comp_mode_en;
> + /* External Root of Trust (eRoT) controller present */
> + bool external_pqc_rot_present;
> + bool external_pqc_rot_invalid;
> };
>
> /* IEEE 1588 TIME_SYNC specific info */
> diff --git a/include/linux/net/intel/libie/adminq.h b/include/linux/net/intel/libie/adminq.h
> index 839114d..df42f5b 100644
> --- a/include/linux/net/intel/libie/adminq.h
> +++ b/include/linux/net/intel/libie/adminq.h
> @@ -175,6 +175,7 @@ LIBIE_CHECK_STRUCT_LEN(16, libie_aqc_list_caps);
> #define LIBIE_AQC_CAPS_PENDING_OROM_VER 0x004B
> #define LIBIE_AQC_CAPS_NET_VER 0x004C
> #define LIBIE_AQC_CAPS_PENDING_NET_VER 0x004D
> +#define LIBIE_AQC_CAPS_EXTERNAL_PQC_ROT_PRESENT 0x004E
> #define LIBIE_AQC_CAPS_RDMA 0x0051
> #define LIBIE_AQC_CAPS_LED 0x0061
> #define LIBIE_AQC_CAPS_SDP 0x0062
Reviewed-by: Tomasz Lichwala <tomasz.lichwala@linux.intel.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters
2026-10-27 17:45 ` [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters Aleksandr Loktionov
2026-10-28 12:11 ` Przemek Kitszel
@ 2026-10-29 14:43 ` Tomasz Lichwala
1 sibling, 0 replies; 6+ messages in thread
From: Tomasz Lichwala @ 2026-10-29 14:43 UTC (permalink / raw)
To: Aleksandr Loktionov, intel-wired-lan, anthony.l.nguyen
On 27.10.2026 18:45, Aleksandr Loktionov wrote:
> 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 <aleksandr.loktionov@intel.com>
> ---
> 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 */
Reviewed-by: Tomasz Lichwala <tomasz.lichwala@linux.intel.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-10-29 14:43 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 17:45 [PATCH iwl-next v3 0/2] ice: eRoT adapter NVM update guard Aleksandr Loktionov
2026-10-27 17:45 ` [PATCH iwl-next v3 1/2] ice: parse external Root of Trust capability Aleksandr Loktionov
2026-10-29 14:43 ` Tomasz Lichwala
2026-10-27 17:45 ` [PATCH iwl-next v3 2/2] ice: reject partial NVM update on eRoT adapters Aleksandr Loktionov
2026-10-28 12:11 ` Przemek Kitszel
2026-10-29 14:43 ` Tomasz Lichwala
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox