From: Mostafa Saleh <smostafa@google.com>
To: linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org, kvmarm@lists.linux.dev,
iommu@lists.linux.dev
Cc: catalin.marinas@arm.com, will@kernel.org, maz@kernel.org,
oliver.upton@linux.dev, joey.gouly@arm.com,
suzuki.poulose@arm.com, yuzenghui@huawei.com, joro@8bytes.org,
jgg@ziepe.ca, mark.rutland@arm.com, qperret@google.com,
tabba@google.com, vdonnefort@google.com, nicolinc@nvidia.com,
Mostafa Saleh <smostafa@google.com>
Subject: [PATCH v9 04/25] iommu/arm-smmu-v3: Move hitless machinery to common code
Date: Thu, 15 Oct 2026 20:08:35 +0000 [thread overview]
Message-ID: <20261015200903.2735565-5-smostafa@google.com> (raw)
In-Reply-To: <20261015200903.2735565-1-smostafa@google.com>
Move the hitless STE functions to the common file so they can be
reused by the hypervisor.
No functional change.
Signed-off-by: Mostafa Saleh <smostafa@google.com>
Reviewed-by: Nicolin Chen <nicolinc@nvidia.com>
---
.../arm/arm-smmu-v3/arm-smmu-v3-common-lib.c | 246 +++++++++++++++++
.../arm/arm-smmu-v3/arm-smmu-v3-common-lib.h | 6 +
.../iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c | 2 +-
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 249 ------------------
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h | 6 +-
5 files changed, 254 insertions(+), 255 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.c
index 22467aaca191..cf9287cdbd19 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.c
@@ -5,6 +5,7 @@
*/
#include <asm/cpufeature.h>
+#include <kunit/visibility.h>
#include "arm-smmu-v3.h"
#include "arm-smmu-v3-common-lib.h"
@@ -238,3 +239,248 @@ u32 arm_smmu_device_idr5_probe(ARM_SMMU_OBJ *smmu)
return reg;
}
+
+/*
+ * Based on the value of ent report which bits of the STE the HW will access. It
+ * would be nice if this was complete according to the spec, but minimally it
+ * has to capture the bits this driver uses.
+ */
+void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits)
+{
+ unsigned int cfg = FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(ent[0]));
+
+ used_bits[0] = cpu_to_le64(STRTAB_STE_0_V);
+ if (!(ent[0] & cpu_to_le64(STRTAB_STE_0_V)))
+ return;
+
+ used_bits[0] |= cpu_to_le64(STRTAB_STE_0_CFG);
+
+ /* S1 translates */
+ if (cfg & BIT(0)) {
+ used_bits[0] |= cpu_to_le64(STRTAB_STE_0_S1FMT |
+ STRTAB_STE_0_S1CTXPTR_MASK |
+ STRTAB_STE_0_S1CDMAX);
+ used_bits[1] |=
+ cpu_to_le64(STRTAB_STE_1_S1DSS | STRTAB_STE_1_S1CIR |
+ STRTAB_STE_1_S1COR | STRTAB_STE_1_S1CSH |
+ STRTAB_STE_1_S1STALLD | STRTAB_STE_1_STRW |
+ STRTAB_STE_1_EATS | STRTAB_STE_1_MEV |
+ STRTAB_STE_1_INSTCFG);
+ used_bits[2] |= cpu_to_le64(STRTAB_STE_2_S2VMID);
+
+ /*
+ * See 13.5 Summary of attribute/permission configuration fields
+ * for the SHCFG behavior.
+ */
+ if (FIELD_GET(STRTAB_STE_1_S1DSS, le64_to_cpu(ent[1])) ==
+ STRTAB_STE_1_S1DSS_BYPASS)
+ used_bits[1] |= cpu_to_le64(STRTAB_STE_1_SHCFG);
+ }
+
+ /* S2 translates */
+ if (cfg & BIT(1)) {
+ used_bits[1] |=
+ cpu_to_le64(STRTAB_STE_1_S2FWB | STRTAB_STE_1_EATS |
+ STRTAB_STE_1_SHCFG | STRTAB_STE_1_MEV |
+ STRTAB_STE_1_INSTCFG);
+ used_bits[2] |=
+ cpu_to_le64(STRTAB_STE_2_S2VMID | STRTAB_STE_2_VTCR |
+ STRTAB_STE_2_S2AA64 | STRTAB_STE_2_S2ENDI |
+ STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2S |
+ STRTAB_STE_2_S2R);
+ used_bits[3] |= cpu_to_le64(STRTAB_STE_3_S2TTB_MASK);
+ }
+
+ if (cfg == STRTAB_STE_0_CFG_BYPASS)
+ used_bits[1] |= cpu_to_le64(STRTAB_STE_1_SHCFG);
+}
+EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_used);
+
+void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
+ __le64 *safe_bits)
+{
+ const u64 eats_s1chk =
+ FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_S1CHK);
+ const u64 eats_trans =
+ FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_TRANS);
+
+ /*
+ * When an STE changes EATS_TRANS, the sequencing code in the attach
+ * logic already will have the PCI cap for ATS disabled. Thus at this
+ * moment we can expect that the device will not generate ATS queries
+ * and so we don't care about the sequencing of EATS. The purpose of
+ * EATS_TRANS is to protect the system from hostile untrusted devices
+ * that issue ATS when the PCI config space is disabled. However, if
+ * EATS_TRANS is being changed, then we must have already trusted the
+ * device as the EATS_TRANS security block is being disabled.
+ *
+ * Note: now the EATS_TRANS update is moved to the first entry_set().
+ * Changing S2S and EATS might transiently result in S2S=1 and EATS=1
+ * which is a bad STE (see "5.2 Stream Table Entry"). In such a case,
+ * we can't do a hitless update. Also, it should not be added to the
+ * safe bits with STRTAB_STE_1_EATS_S1CHK, because EATS=0b11 would be
+ * effectively an errant 0b00 configuration.
+ */
+ if (!((cur[1] | target[1]) & cpu_to_le64(eats_s1chk)) &&
+ !((cur[2] | target[2]) & cpu_to_le64(STRTAB_STE_2_S2S)))
+ safe_bits[1] |= cpu_to_le64(eats_trans);
+
+ /*
+ * MEV does not meaningfully impact the operation of the HW, it only
+ * changes how many fault events are generated, thus we can relax it
+ * when computing the ordering. The spec notes the device can act like
+ * MEV=1 anyhow:
+ *
+ * Note: Software must expect, and be able to deal with, coalesced
+ * fault records even when MEV == 0.
+ */
+ safe_bits[1] |= cpu_to_le64(STRTAB_STE_1_MEV);
+}
+EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_update_safe);
+
+/*
+ * Figure out if we can do a hitless update of entry to become target. Returns a
+ * bit mask where 1 indicates that qword needs to be set disruptively.
+ * unused_update is an intermediate value of entry that has unused bits set to
+ * their new values.
+ */
+static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
+ const __le64 *entry, const __le64 *target,
+ __le64 *unused_update)
+{
+ __le64 target_used[NUM_ENTRY_QWORDS] = {};
+ __le64 cur_used[NUM_ENTRY_QWORDS] = {};
+ __le64 safe[NUM_ENTRY_QWORDS] = {};
+ u8 used_qword_diff = 0;
+ unsigned int i;
+
+ writer->ops->get_used(entry, cur_used);
+ writer->ops->get_used(target, target_used);
+ if (writer->ops->get_update_safe)
+ writer->ops->get_update_safe(entry, target, safe);
+
+ for (i = 0; i != NUM_ENTRY_QWORDS; i++) {
+ /*
+ * Safe is only used for bits that are used by both entries,
+ * otherwise it is sequenced according to the unused entry.
+ */
+ safe[i] &= target_used[i] & cur_used[i];
+
+ /*
+ * Check that masks are up to date, the make functions are not
+ * allowed to set a bit to 1 if the used function doesn't say it
+ * is used.
+ */
+ WARN_ON_ONCE(target[i] & ~target_used[i]);
+
+ /* Bits can change because they are not currently being used */
+ cur_used[i] &= ~safe[i];
+ unused_update[i] = (entry[i] & cur_used[i]) |
+ (target[i] & ~cur_used[i]);
+ /*
+ * Each bit indicates that a used bit in a qword needs to be
+ * changed after unused_update is applied.
+ */
+ if ((unused_update[i] & target_used[i]) != target[i])
+ used_qword_diff |= 1 << i;
+ }
+ return used_qword_diff;
+}
+
+static void entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
+ const __le64 *target, unsigned int start,
+ unsigned int len)
+{
+ bool changed = false;
+ unsigned int i;
+
+ for (i = start; len != 0; len--, i++) {
+ if (entry[i] != target[i]) {
+ WRITE_ONCE(entry[i], target[i]);
+ changed = true;
+ }
+ }
+
+ if (changed)
+ writer->ops->sync(writer);
+}
+
+/*
+ * Update the STE/CD to the target configuration. The transition from the
+ * current entry to the target entry takes place over multiple steps that
+ * attempts to make the transition hitless if possible. This function takes care
+ * not to create a situation where the HW can perceive a corrupted entry. HW is
+ * only required to have a 64 bit atomicity with stores from the CPU, while
+ * entries are many 64 bit values big.
+ *
+ * The difference between the current value and the target value is analyzed to
+ * determine which of three updates are required - disruptive, hitless or no
+ * change.
+ *
+ * In the most general disruptive case we can make any update in three steps:
+ * - Disrupting the entry (V=0)
+ * - Fill now unused qwords, execpt qword 0 which contains V
+ * - Make qword 0 have the final value and valid (V=1) with a single 64
+ * bit store
+ *
+ * However this disrupts the HW while it is happening. There are several
+ * interesting cases where a STE/CD can be updated without disturbing the HW
+ * because only a small number of bits are changing (S1DSS, CONFIG, etc) or
+ * because the used bits don't intersect. We can detect this by calculating how
+ * many 64 bit values need update after adjusting the unused bits and skip the
+ * V=0 process. This relies on the IGNORED behavior described in the
+ * specification.
+ */
+void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *entry,
+ const __le64 *target)
+{
+ __le64 unused_update[NUM_ENTRY_QWORDS];
+ u8 used_qword_diff;
+
+ /*
+ * Many of the entry structures have pointers to other structures that
+ * need to have their updates be visible before any writes of the entry
+ * happen.
+ */
+ dma_wmb();
+
+ used_qword_diff =
+ arm_smmu_entry_qword_diff(writer, entry, target, unused_update);
+ if (hweight8(used_qword_diff) == 1) {
+ /*
+ * Only one qword needs its used bits to be changed. This is a
+ * hitless update, update all bits the current STE/CD is
+ * ignoring to their new values, then update a single "critical
+ * qword" to change the STE/CD and finally 0 out any bits that
+ * are now unused in the target configuration.
+ */
+ unsigned int critical_qword_index = ffs(used_qword_diff) - 1;
+
+ /*
+ * Skip writing unused bits in the critical qword since we'll be
+ * writing it in the next step anyways. This can save a sync
+ * when the only change is in that qword.
+ */
+ unused_update[critical_qword_index] =
+ entry[critical_qword_index];
+ entry_set(writer, entry, unused_update, 0, NUM_ENTRY_QWORDS);
+ entry_set(writer, entry, target, critical_qword_index, 1);
+ entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS);
+ } else if (used_qword_diff) {
+ /*
+ * At least two qwords need their inuse bits to be changed. This
+ * requires a breaking update, zero the V bit, write all qwords
+ * but 0, then set qword 0
+ */
+ unused_update[0] = 0;
+ entry_set(writer, entry, unused_update, 0, 1);
+ entry_set(writer, entry, target, 1, NUM_ENTRY_QWORDS - 1);
+ entry_set(writer, entry, target, 0, 1);
+ } else {
+ /*
+ * No inuse bit changed, though safe bits may have changed.
+ */
+ entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS);
+ }
+}
+EXPORT_SYMBOL_IF_KUNIT(arm_smmu_write_entry);
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.h
index 926a84b11a82..55ffa880b093 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-common-lib.h
@@ -36,4 +36,10 @@ int arm_smmu_device_idr1_probe(ARM_SMMU_OBJ *smmu, u32 *idr1);
void arm_smmu_device_idr3_probe(ARM_SMMU_OBJ *smmu);
u32 arm_smmu_device_idr5_probe(ARM_SMMU_OBJ *smmu);
+void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits);
+void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
+ __le64 *safe_bits);
+void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *cur,
+ const __le64 *target);
+
#endif /* __ARM_SMMU_V3_COMMON_LIB_H */
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
index 5a4e45e15125..d9df9adc14ea 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-test.c
@@ -6,6 +6,7 @@
#include <linux/io-pgtable.h>
#include "arm-smmu-v3.h"
+#include "arm-smmu-v3-common-lib.h"
struct arm_smmu_test_writer {
struct arm_smmu_entry_writer writer;
@@ -18,7 +19,6 @@ struct arm_smmu_test_writer {
unsigned int num_syncs;
};
-#define NUM_ENTRY_QWORDS 8
#define NUM_EXPECTED_SYNCS(x) x
static struct arm_smmu_ste bypass_ste;
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 8cbf1e7354e2..5d7de893865e 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -64,7 +64,6 @@ enum arm_smmu_msi_index {
ARM_SMMU_MAX_MSIS,
};
-#define NUM_ENTRY_QWORDS 8
static_assert(sizeof(struct arm_smmu_ste) == NUM_ENTRY_QWORDS * sizeof(u64));
static_assert(sizeof(struct arm_smmu_cd) == NUM_ENTRY_QWORDS * sizeof(u64));
@@ -1216,254 +1215,6 @@ EXPORT_SYMBOL_IF_KUNIT(arm_smmu_invs_purge);
/* Context descriptor manipulation functions */
-/*
- * Based on the value of ent report which bits of the STE the HW will access. It
- * would be nice if this was complete according to the spec, but minimally it
- * has to capture the bits this driver uses.
- */
-VISIBLE_IF_KUNIT
-void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits)
-{
- unsigned int cfg = FIELD_GET(STRTAB_STE_0_CFG, le64_to_cpu(ent[0]));
-
- used_bits[0] = cpu_to_le64(STRTAB_STE_0_V);
- if (!(ent[0] & cpu_to_le64(STRTAB_STE_0_V)))
- return;
-
- used_bits[0] |= cpu_to_le64(STRTAB_STE_0_CFG);
-
- /* S1 translates */
- if (cfg & BIT(0)) {
- used_bits[0] |= cpu_to_le64(STRTAB_STE_0_S1FMT |
- STRTAB_STE_0_S1CTXPTR_MASK |
- STRTAB_STE_0_S1CDMAX);
- used_bits[1] |=
- cpu_to_le64(STRTAB_STE_1_S1DSS | STRTAB_STE_1_S1CIR |
- STRTAB_STE_1_S1COR | STRTAB_STE_1_S1CSH |
- STRTAB_STE_1_S1STALLD | STRTAB_STE_1_STRW |
- STRTAB_STE_1_EATS | STRTAB_STE_1_MEV |
- STRTAB_STE_1_INSTCFG);
- used_bits[2] |= cpu_to_le64(STRTAB_STE_2_S2VMID);
-
- /*
- * See 13.5 Summary of attribute/permission configuration fields
- * for the SHCFG behavior.
- */
- if (FIELD_GET(STRTAB_STE_1_S1DSS, le64_to_cpu(ent[1])) ==
- STRTAB_STE_1_S1DSS_BYPASS)
- used_bits[1] |= cpu_to_le64(STRTAB_STE_1_SHCFG);
- }
-
- /* S2 translates */
- if (cfg & BIT(1)) {
- used_bits[1] |=
- cpu_to_le64(STRTAB_STE_1_S2FWB | STRTAB_STE_1_EATS |
- STRTAB_STE_1_SHCFG | STRTAB_STE_1_MEV |
- STRTAB_STE_1_INSTCFG);
- used_bits[2] |=
- cpu_to_le64(STRTAB_STE_2_S2VMID | STRTAB_STE_2_VTCR |
- STRTAB_STE_2_S2AA64 | STRTAB_STE_2_S2ENDI |
- STRTAB_STE_2_S2PTW | STRTAB_STE_2_S2S |
- STRTAB_STE_2_S2R);
- used_bits[3] |= cpu_to_le64(STRTAB_STE_3_S2TTB_MASK);
- }
-
- if (cfg == STRTAB_STE_0_CFG_BYPASS)
- used_bits[1] |= cpu_to_le64(STRTAB_STE_1_SHCFG);
-}
-EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_used);
-
-VISIBLE_IF_KUNIT
-void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
- __le64 *safe_bits)
-{
- const u64 eats_s1chk =
- FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_S1CHK);
- const u64 eats_trans =
- FIELD_PREP(STRTAB_STE_1_EATS, STRTAB_STE_1_EATS_TRANS);
-
- /*
- * When an STE changes EATS_TRANS, the sequencing code in the attach
- * logic already will have the PCI cap for ATS disabled. Thus at this
- * moment we can expect that the device will not generate ATS queries
- * and so we don't care about the sequencing of EATS. The purpose of
- * EATS_TRANS is to protect the system from hostile untrusted devices
- * that issue ATS when the PCI config space is disabled. However, if
- * EATS_TRANS is being changed, then we must have already trusted the
- * device as the EATS_TRANS security block is being disabled.
- *
- * Note: now the EATS_TRANS update is moved to the first entry_set().
- * Changing S2S and EATS might transiently result in S2S=1 and EATS=1
- * which is a bad STE (see "5.2 Stream Table Entry"). In such a case,
- * we can't do a hitless update. Also, it should not be added to the
- * safe bits with STRTAB_STE_1_EATS_S1CHK, because EATS=0b11 would be
- * effectively an errant 0b00 configuration.
- */
- if (!((cur[1] | target[1]) & cpu_to_le64(eats_s1chk)) &&
- !((cur[2] | target[2]) & cpu_to_le64(STRTAB_STE_2_S2S)))
- safe_bits[1] |= cpu_to_le64(eats_trans);
-
- /*
- * MEV does not meaningfully impact the operation of the HW, it only
- * changes how many fault events are generated, thus we can relax it
- * when computing the ordering. The spec notes the device can act like
- * MEV=1 anyhow:
- *
- * Note: Software must expect, and be able to deal with, coalesced
- * fault records even when MEV == 0.
- */
- safe_bits[1] |= cpu_to_le64(STRTAB_STE_1_MEV);
-}
-EXPORT_SYMBOL_IF_KUNIT(arm_smmu_get_ste_update_safe);
-
-/*
- * Figure out if we can do a hitless update of entry to become target. Returns a
- * bit mask where 1 indicates that qword needs to be set disruptively.
- * unused_update is an intermediate value of entry that has unused bits set to
- * their new values.
- */
-static u8 arm_smmu_entry_qword_diff(struct arm_smmu_entry_writer *writer,
- const __le64 *entry, const __le64 *target,
- __le64 *unused_update)
-{
- __le64 target_used[NUM_ENTRY_QWORDS] = {};
- __le64 cur_used[NUM_ENTRY_QWORDS] = {};
- __le64 safe[NUM_ENTRY_QWORDS] = {};
- u8 used_qword_diff = 0;
- unsigned int i;
-
- writer->ops->get_used(entry, cur_used);
- writer->ops->get_used(target, target_used);
- if (writer->ops->get_update_safe)
- writer->ops->get_update_safe(entry, target, safe);
-
- for (i = 0; i != NUM_ENTRY_QWORDS; i++) {
- /*
- * Safe is only used for bits that are used by both entries,
- * otherwise it is sequenced according to the unused entry.
- */
- safe[i] &= target_used[i] & cur_used[i];
-
- /*
- * Check that masks are up to date, the make functions are not
- * allowed to set a bit to 1 if the used function doesn't say it
- * is used.
- */
- WARN_ON_ONCE(target[i] & ~target_used[i]);
-
- /* Bits can change because they are not currently being used */
- cur_used[i] &= ~safe[i];
- unused_update[i] = (entry[i] & cur_used[i]) |
- (target[i] & ~cur_used[i]);
- /*
- * Each bit indicates that a used bit in a qword needs to be
- * changed after unused_update is applied.
- */
- if ((unused_update[i] & target_used[i]) != target[i])
- used_qword_diff |= 1 << i;
- }
- return used_qword_diff;
-}
-
-static void entry_set(struct arm_smmu_entry_writer *writer, __le64 *entry,
- const __le64 *target, unsigned int start,
- unsigned int len)
-{
- bool changed = false;
- unsigned int i;
-
- for (i = start; len != 0; len--, i++) {
- if (entry[i] != target[i]) {
- WRITE_ONCE(entry[i], target[i]);
- changed = true;
- }
- }
-
- if (changed)
- writer->ops->sync(writer);
-}
-
-/*
- * Update the STE/CD to the target configuration. The transition from the
- * current entry to the target entry takes place over multiple steps that
- * attempts to make the transition hitless if possible. This function takes care
- * not to create a situation where the HW can perceive a corrupted entry. HW is
- * only required to have a 64 bit atomicity with stores from the CPU, while
- * entries are many 64 bit values big.
- *
- * The difference between the current value and the target value is analyzed to
- * determine which of three updates are required - disruptive, hitless or no
- * change.
- *
- * In the most general disruptive case we can make any update in three steps:
- * - Disrupting the entry (V=0)
- * - Fill now unused qwords, execpt qword 0 which contains V
- * - Make qword 0 have the final value and valid (V=1) with a single 64
- * bit store
- *
- * However this disrupts the HW while it is happening. There are several
- * interesting cases where a STE/CD can be updated without disturbing the HW
- * because only a small number of bits are changing (S1DSS, CONFIG, etc) or
- * because the used bits don't intersect. We can detect this by calculating how
- * many 64 bit values need update after adjusting the unused bits and skip the
- * V=0 process. This relies on the IGNORED behavior described in the
- * specification.
- */
-VISIBLE_IF_KUNIT
-void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *entry,
- const __le64 *target)
-{
- __le64 unused_update[NUM_ENTRY_QWORDS];
- u8 used_qword_diff;
-
- /*
- * Many of the entry structures have pointers to other structures that
- * need to have their updates be visible before any writes of the entry
- * happen.
- */
- dma_wmb();
-
- used_qword_diff =
- arm_smmu_entry_qword_diff(writer, entry, target, unused_update);
- if (hweight8(used_qword_diff) == 1) {
- /*
- * Only one qword needs its used bits to be changed. This is a
- * hitless update, update all bits the current STE/CD is
- * ignoring to their new values, then update a single "critical
- * qword" to change the STE/CD and finally 0 out any bits that
- * are now unused in the target configuration.
- */
- unsigned int critical_qword_index = ffs(used_qword_diff) - 1;
-
- /*
- * Skip writing unused bits in the critical qword since we'll be
- * writing it in the next step anyways. This can save a sync
- * when the only change is in that qword.
- */
- unused_update[critical_qword_index] =
- entry[critical_qword_index];
- entry_set(writer, entry, unused_update, 0, NUM_ENTRY_QWORDS);
- entry_set(writer, entry, target, critical_qword_index, 1);
- entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS);
- } else if (used_qword_diff) {
- /*
- * At least two qwords need their inuse bits to be changed. This
- * requires a breaking update, zero the V bit, write all qwords
- * but 0, then set qword 0
- */
- unused_update[0] = 0;
- entry_set(writer, entry, unused_update, 0, 1);
- entry_set(writer, entry, target, 1, NUM_ENTRY_QWORDS - 1);
- entry_set(writer, entry, target, 0, 1);
- } else {
- /*
- * No inuse bit changed, though safe bits may have changed.
- */
- entry_set(writer, entry, target, 0, NUM_ENTRY_QWORDS);
- }
-}
-EXPORT_SYMBOL_IF_KUNIT(arm_smmu_write_entry);
-
static void arm_smmu_sync_cd(struct arm_smmu_master *master,
int ssid, bool leaf)
{
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
index bc4c25421835..d07fca3d1cf5 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
@@ -226,6 +226,7 @@ struct arm_vsmmu;
#define STRTAB_L1_DESC_L2PTR_MASK GENMASK_ULL(51, 6)
#define STRTAB_STE_DWORDS 8
+#define NUM_ENTRY_QWORDS 8
struct arm_smmu_ste {
__le64 data[STRTAB_STE_DWORDS];
@@ -1140,11 +1141,6 @@ void arm_smmu_make_s2_domain_ste(struct arm_smmu_ste *target,
bool ats_enabled);
#if IS_ENABLED(CONFIG_KUNIT)
-void arm_smmu_get_ste_used(const __le64 *ent, __le64 *used_bits);
-void arm_smmu_get_ste_update_safe(const __le64 *cur, const __le64 *target,
- __le64 *safe_bits);
-void arm_smmu_write_entry(struct arm_smmu_entry_writer *writer, __le64 *cur,
- const __le64 *target);
void arm_smmu_get_cd_used(const __le64 *ent, __le64 *used_bits);
void arm_smmu_make_bypass_ste(struct arm_smmu_device *smmu,
struct arm_smmu_ste *target);
--
2.56.0.176.g3575e65682-goog
next prev parent reply other threads:[~2026-10-15 20:09 UTC|newest]
Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-15 20:08 [PATCH v9 00/25] KVM: arm64: SMMUv3 driver for pKVM (trap and emulate) Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 01/25] KVM: arm64: Donate MMIO to the hypervisor Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 02/25] iommu/arm-smmu-v3: Move Queue and STE functions to header Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 03/25] iommu/arm-smmu-v3: Move IDR parsing to common functions Mostafa Saleh
2026-10-15 20:08 ` Mostafa Saleh [this message]
2026-10-15 20:08 ` [PATCH v9 05/25] iommu/arm-smmu-v3: Move RangeInval to common code Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 06/25] KVM: arm64: iommu: Introduce IOMMU driver infrastructure Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 07/25] KVM: arm64: iommu: Shadow host stage-2 page table Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 08/25] KVM: arm64: iommu: Add memory pool Mostafa Saleh
2026-10-15 20:18 ` sashiko-bot
2026-10-15 20:40 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 09/25] KVM: arm64: iommu: Support DABT for IOMMU Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 10/25] iommu/arm-smmu-v3-kvm: Add SMMUv3 driver Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 11/25] iommu/arm-smmu-v3-kvm: Add the kernel driver Mostafa Saleh
2026-10-15 20:35 ` sashiko-bot
2026-10-15 20:52 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 12/25] iommu/arm-smmu-v3-kvm: Probe SMMU HW Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 13/25] iommu/arm-smmu-v3-kvm: Add MMIO emulation Mostafa Saleh
2026-10-15 20:30 ` sashiko-bot
2026-10-15 21:00 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 14/25] iommu/arm-smmu-v3-kvm: Shadow the command queue Mostafa Saleh
2026-10-15 20:28 ` sashiko-bot
2026-10-15 21:01 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 15/25] iommu/arm-smmu-v3-kvm: Add CMDQ functions Mostafa Saleh
2026-10-15 20:30 ` sashiko-bot
2026-10-15 21:11 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 16/25] iommu/arm-smmu-v3-kvm: Emulate CMDQ for host Mostafa Saleh
2026-10-15 20:30 ` sashiko-bot
2026-10-15 21:17 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 17/25] iommu/arm-smmu-v3-kvm: Shadow stream table Mostafa Saleh
2026-10-15 20:30 ` sashiko-bot
2026-10-15 21:20 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 18/25] iommu/arm-smmu-v3-kvm: Shadow STEs Mostafa Saleh
2026-10-15 20:30 ` sashiko-bot
2026-10-15 21:22 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 19/25] iommu/arm-smmu-v3-kvm: Share other queues Mostafa Saleh
2026-10-15 20:29 ` sashiko-bot
2026-10-15 21:23 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 20/25] iommu/arm-smmu-v3-kvm: Emulate GBPA Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 21/25] iommu/io-pgtable-arm: Support io-pgtable-arm in the hypervisor Mostafa Saleh
2026-10-15 20:26 ` sashiko-bot
2026-10-15 21:24 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 22/25] iommu/arm-smmu-v3-kvm: Shadow the CPU stage-2 page table Mostafa Saleh
2026-10-15 20:32 ` sashiko-bot
2026-10-15 21:32 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 23/25] iommu/arm-smmu-v3-kvm: Invalidate the SMMU TLBs Mostafa Saleh
2026-10-15 20:28 ` sashiko-bot
2026-10-15 21:35 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 24/25] iommu/arm-smmu-v3-kvm: Enable nesting Mostafa Saleh
2026-10-15 20:34 ` sashiko-bot
2026-10-15 21:37 ` Mostafa Saleh
2026-10-15 20:08 ` [PATCH v9 25/25] KVM: arm64: Add documentation for pKVM DMA isolation Mostafa Saleh
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261015200903.2735565-5-smostafa@google.com \
--to=smostafa@google.com \
--cc=catalin.marinas@arm.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@ziepe.ca \
--cc=joey.gouly@arm.com \
--cc=joro@8bytes.org \
--cc=kvmarm@lists.linux.dev \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mark.rutland@arm.com \
--cc=maz@kernel.org \
--cc=nicolinc@nvidia.com \
--cc=oliver.upton@linux.dev \
--cc=qperret@google.com \
--cc=suzuki.poulose@arm.com \
--cc=tabba@google.com \
--cc=vdonnefort@google.com \
--cc=will@kernel.org \
--cc=yuzenghui@huawei.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.