[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251117184815.1027271-22-smostafa@google.com>
Date: Mon, 17 Nov 2025 18:48:08 +0000
From: Mostafa Saleh <smostafa@...gle.com>
To: linux-arm-kernel@...ts.infradead.org, linux-kernel@...r.kernel.org,
kvmarm@...ts.linux.dev, iommu@...ts.linux.dev
Cc: catalin.marinas@....com, will@...nel.org, maz@...nel.org,
oliver.upton@...ux.dev, joey.gouly@....com, suzuki.poulose@....com,
yuzenghui@...wei.com, joro@...tes.org, jean-philippe@...aro.org, jgg@...pe.ca,
praan@...gle.com, danielmentz@...gle.com, mark.rutland@....com,
qperret@...gle.com, tabba@...gle.com, Mostafa Saleh <smostafa@...gle.com>
Subject: [PATCH v5 21/27] iommu/arm-smmu-v3-kvm: Emulate CMDQ for host
Don’t allow access to the command queue from the host:
- ARM_SMMU_CMDQ_BASE: Only allowed to be written when CMDQ is disabled, we
use it to keep track of the host command queue base.
Reads return the saved value.
- ARM_SMMU_CMDQ_PROD: Writes trigger command queue emulation which sanitise
and filters the whole range. Reads returns the host copy.
- ARM_SMMU_CMDQ_CONS: Writes move the sw copy of the cons, but the host can’t
skip commands once submitted. Reads return the emulated value and the error
bits in the actual cons.
Signed-off-by: Mostafa Saleh <smostafa@...gle.com>
---
.../iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c | 119 +++++++++++++++++-
1 file changed, 115 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c
index a970b43e6a7e..746ffc4b0a70 100644
--- a/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/pkvm/arm-smmu-v3.c
@@ -60,6 +60,16 @@ static void smmu_reclaim_pages(u64 phys, size_t size)
WARN_ON(__pkvm_hyp_donate_host(phys >> PAGE_SHIFT, size >> PAGE_SHIFT));
}
+static void smmu_copy_from_host(struct hyp_arm_smmu_v3_device *smmu,
+ void *dst_hyp_va, void *src_hyp_va,
+ size_t size)
+{
+ /* Clean and inval DC as the kernel uses NC mapping. */
+ if (!(smmu->features & ARM_SMMU_FEAT_COHERENCY))
+ kvm_flush_dcache_to_poc(src_hyp_va, size);
+ memcpy(dst_hyp_va, src_hyp_va, size);
+}
+
/*
* CMDQ, STE host copies are accessed by the hypervisor, we share them to
* - Prevent the host from passing protected VM memory.
@@ -89,7 +99,6 @@ static int smmu_unshare_pages(phys_addr_t addr, size_t size)
return 0;
}
-__maybe_unused
static bool smmu_cmdq_has_space(struct arm_smmu_queue *cmdq, u32 n)
{
struct arm_smmu_ll_queue *llq = &cmdq->llq;
@@ -327,6 +336,88 @@ static int smmu_init(void)
return ret;
}
+static bool smmu_filter_command(struct hyp_arm_smmu_v3_device *smmu, u64 *command)
+{
+ u64 type = FIELD_GET(CMDQ_0_OP, command[0]);
+
+ switch (type) {
+ case CMDQ_OP_CFGI_STE:
+ /* TBD: SHADOW_STE*/
+ break;
+ case CMDQ_OP_CFGI_ALL:
+ {
+ /*
+ * Linux doesn't use range STE invalidation, and only use this
+ * for CFGI_ALL, which is done on reset and not on an new STE
+ * being used.
+ * Although, this is not architectural we rely on the current Linux
+ * implementation.
+ */
+ WARN_ON((FIELD_GET(CMDQ_CFGI_1_RANGE, command[1]) != 31));
+ break;
+ }
+ case CMDQ_OP_TLBI_NH_ASID:
+ case CMDQ_OP_TLBI_NH_VA:
+ case 0x13: /* CMD_TLBI_NH_VAA: Not used by Linux */
+ {
+ /* Only allow VMID = 0*/
+ if (FIELD_GET(CMDQ_TLBI_0_VMID, command[0]) == 0)
+ break;
+ break;
+ }
+ case 0x10: /* CMD_TLBI_NH_ALL: Not used by Linux */
+ case CMDQ_OP_TLBI_EL2_ALL:
+ case CMDQ_OP_TLBI_EL2_VA:
+ case CMDQ_OP_TLBI_EL2_ASID:
+ case CMDQ_OP_TLBI_S12_VMALL:
+ case 0x23: /* CMD_TLBI_EL2_VAA: Not used by Linux */
+ return WARN_ON(true);
+ case CMDQ_OP_CMD_SYNC:
+ if (FIELD_GET(CMDQ_SYNC_0_CS, command[0]) == CMDQ_SYNC_0_CS_IRQ) {
+ /* Allow it, but let the host timeout, as this should never happen. */
+ command[0] &= ~CMDQ_SYNC_0_CS;
+ command[0] |= FIELD_PREP(CMDQ_SYNC_0_CS, CMDQ_SYNC_0_CS_SEV);
+ command[1] &= ~CMDQ_SYNC_1_MSIADDR_MASK;
+ }
+ break;
+ }
+
+ return false;
+}
+
+static void smmu_emulate_cmdq_insert(struct hyp_arm_smmu_v3_device *smmu)
+{
+ u64 *host_cmdq = hyp_phys_to_virt(smmu->cmdq_host.q_base & Q_BASE_ADDR_MASK);
+ int idx;
+ u64 cmd[CMDQ_ENT_DWORDS];
+ bool skip;
+ u32 space;
+ bool use_wfe = smmu->features & ARM_SMMU_FEAT_SEV;
+
+ if (!is_cmdq_enabled(smmu))
+ return;
+
+ space = (1 << (smmu->cmdq_host.llq.max_n_shift)) - queue_space(&smmu->cmdq_host.llq);
+ /* Wait for the command queue to have some space. */
+ WARN_ON(smmu_wait(use_wfe, smmu_cmdq_has_space(&smmu->cmdq, space)));
+
+ while (space--) {
+ idx = Q_IDX(&smmu->cmdq_host.llq, smmu->cmdq_host.llq.cons);
+ queue_inc_cons(&smmu->cmdq_host.llq);
+
+ smmu_copy_from_host(smmu, cmd, &host_cmdq[idx * CMDQ_ENT_DWORDS],
+ CMDQ_ENT_DWORDS << 3);
+ skip = smmu_filter_command(smmu, cmd);
+ if (skip)
+ continue;
+ smmu_add_cmd_raw(smmu, cmd);
+ }
+
+ writel_relaxed(smmu->cmdq.llq.prod, smmu->cmdq.prod_reg);
+
+ WARN_ON(smmu_wait(use_wfe, smmu_cmdq_empty(&smmu->cmdq)));
+}
+
static void smmu_emulate_cmdq_enable(struct hyp_arm_smmu_v3_device *smmu)
{
smmu->cmdq_host.llq.max_n_shift = smmu->cmdq_host.q_base & Q_BASE_LOG2SIZE;
@@ -360,17 +451,37 @@ static bool smmu_dabt_device(struct hyp_arm_smmu_v3_device *smmu,
mask = read_only & ~(IDR0_S2P | IDR0_VMID16 | IDR0_MSI | IDR0_HYP);
WARN_ON(len != sizeof(u32));
break;
- /* Passthrough the register access for bisectiblity, handled later */
case ARM_SMMU_CMDQ_BASE:
if (is_write) {
/* Not allowed by the architecture */
WARN_ON(is_cmdq_enabled(smmu));
smmu->cmdq_host.q_base = val;
+ } else {
+ regs->regs[rd] = smmu->cmdq_host.q_base;
}
- mask = read_write;
- break;
+ goto out_ret;
case ARM_SMMU_CMDQ_PROD:
+ if (is_write) {
+ smmu->cmdq_host.llq.prod = val;
+ smmu_emulate_cmdq_insert(smmu);
+ } else {
+ regs->regs[rd] = smmu->cmdq_host.llq.prod;
+ }
+ goto out_ret;
case ARM_SMMU_CMDQ_CONS:
+ if (is_write) {
+ /* Not allowed by the architecture */
+ WARN_ON(is_cmdq_enabled(smmu));
+ smmu->cmdq_host.llq.cons = val;
+ } else {
+ /* Propagate errors back to the host.*/
+ u32 cons = readl_relaxed(smmu->base + ARM_SMMU_CMDQ_CONS);
+ u32 err = CMDQ_CONS_ERR & cons;
+
+ regs->regs[rd] = smmu->cmdq_host.llq.cons | err;
+ }
+ goto out_ret;
+ /* Passthrough the register access for bisectiblity, handled later */
case ARM_SMMU_STRTAB_BASE:
case ARM_SMMU_STRTAB_BASE_CFG:
case ARM_SMMU_GBPA:
--
2.52.0.rc1.455.g30608eb744-goog
Powered by blists - more mailing lists