From 94c64f177175b0f90138e9e0a4e7962832dd89f6 Mon Sep 17 00:00:00 2001 From: Mostafa Saleh Date: Thu, 12 Dec 2024 18:04:16 +0000 Subject: [PATCH] BACKPORT: FROMLIST: iommu/arm-smmu-v3-kvm: Add IRQs for the driver Handle IRQs from the KVM kernel driver, it should be safe to do it from the kernel as this a debug feature. Only GERROR and EVTQ irqs are handled. Unlike the kernel driver, we don't do much here (no rest in SMMU or interaction of cmdq) but just printing. Link: https://lore.kernel.org/all/20241212180423.1578358-53-smostafa@google.com/ Bug: 357781595 Bug: 384432312 Change-Id: I49b946675c61395db9c8e371c8d4da06d3c20fc9 Signed-off-by: Mostafa Saleh --- arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c | 3 +- .../iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c | 138 ++++++++++++++++++ 2 files changed, 139 insertions(+), 2 deletions(-) diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c b/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c index 047602281463..22e4b021cf48 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/arm-smmu-v3.c @@ -365,7 +365,6 @@ static int smmu_init_registers(struct hyp_arm_smmu_v3_device *smmu) FIELD_PREP(CR1_QUEUE_IC, CR1_CACHE_WB); writel_relaxed(val, smmu->base + ARM_SMMU_CR1); writel_relaxed(CR2_PTM, smmu->base + ARM_SMMU_CR2); - writel_relaxed(0, smmu->base + ARM_SMMU_IRQ_CTRL); val = readl_relaxed(smmu->base + ARM_SMMU_GERROR); old = readl_relaxed(smmu->base + ARM_SMMU_GERRORN); @@ -545,7 +544,7 @@ static int smmu_reset_device(struct hyp_arm_smmu_v3_device *smmu) goto err_disable_cmdq; /* Enable translation */ - return smmu_write_cr0(smmu, CR0_SMMUEN | CR0_CMDQEN | CR0_ATSCHK); + return smmu_write_cr0(smmu, CR0_SMMUEN | CR0_CMDQEN | CR0_ATSCHK | CR0_EVTQEN); err_disable_cmdq: return smmu_write_cr0(smmu, 0); diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c index cbcd8a75d562..674ce2b02a4b 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-kvm.c @@ -496,11 +496,107 @@ static bool kvm_arm_smmu_validate_features(struct arm_smmu_device *smmu) return true; } +static irqreturn_t kvm_arm_smmu_evt_handler(int irq, void *dev) +{ + int i; + struct arm_smmu_device *smmu = dev; + struct arm_smmu_queue *q = &smmu->evtq.q; + struct arm_smmu_ll_queue *llq = &q->llq; + static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + u64 evt[EVTQ_ENT_DWORDS]; + + do { + while (!queue_remove_raw(q, evt)) { + u8 id = FIELD_GET(EVTQ_0_ID, evt[0]); + + if (!__ratelimit(&rs)) + continue; + + dev_info(smmu->dev, "event 0x%02x received:\n", id); + for (i = 0; i < ARRAY_SIZE(evt); ++i) + dev_info(smmu->dev, "\t0x%016llx\n", + (unsigned long long)evt[i]); + + cond_resched(); + } + + /* + * Not much we can do on overflow, so scream and pretend we're + * trying harder. + */ + if (queue_sync_prod_in(q) == -EOVERFLOW) + dev_err(smmu->dev, "EVTQ overflow detected -- events lost\n"); + } while (!queue_empty(llq)); + + /* Sync our overflow flag, as we believe we're up to speed */ + queue_sync_cons_ovf(q); + return IRQ_HANDLED; +} + +static irqreturn_t kvm_arm_smmu_gerror_handler(int irq, void *dev) +{ + u32 gerror, gerrorn, active; + struct arm_smmu_device *smmu = dev; + + gerror = readl_relaxed(smmu->base + ARM_SMMU_GERROR); + gerrorn = readl_relaxed(smmu->base + ARM_SMMU_GERRORN); + + active = gerror ^ gerrorn; + if (!(active & GERROR_ERR_MASK)) + return IRQ_NONE; /* No errors pending */ + + dev_warn(smmu->dev, + "unexpected global error reported (0x%08x), this could be serious\n", + active); + + /* There is no API to reconfigure the device at the moment.*/ + if (active & GERROR_SFM_ERR) + dev_err(smmu->dev, "device has entered Service Failure Mode!\n"); + + if (active & GERROR_MSI_GERROR_ABT_ERR) + dev_warn(smmu->dev, "GERROR MSI write aborted\n"); + + if (active & GERROR_MSI_PRIQ_ABT_ERR) + dev_warn(smmu->dev, "PRIQ MSI write aborted\n"); + + if (active & GERROR_MSI_EVTQ_ABT_ERR) + dev_warn(smmu->dev, "EVTQ MSI write aborted\n"); + + if (active & GERROR_MSI_CMDQ_ABT_ERR) + dev_warn(smmu->dev, "CMDQ MSI write aborted\n"); + + if (active & GERROR_PRIQ_ABT_ERR) + dev_err(smmu->dev, "PRIQ write aborted -- events may have been lost\n"); + + if (active & GERROR_EVTQ_ABT_ERR) + dev_err(smmu->dev, "EVTQ write aborted -- events may have been lost\n"); + + if (active & GERROR_CMDQ_ERR) { + dev_err(smmu->dev, "CMDQ ERR -- Hypervisor cmdq corrupted?\n"); + BUG(); + } + + writel(gerror, smmu->base + ARM_SMMU_GERRORN); + + return IRQ_HANDLED; +} + +static irqreturn_t kvm_arm_smmu_pri_handler(int irq, void *dev) +{ + struct arm_smmu_device *smmu = dev; + + dev_err(smmu->dev, "PRI not supported in KVM driver!\n"); + + return IRQ_HANDLED; +} + static int kvm_arm_smmu_device_reset(struct host_arm_smmu_device *host_smmu) { int ret; u32 reg; struct arm_smmu_device *smmu = &host_smmu->smmu; + u32 irqen_flags = IRQ_CTRL_EVTQ_IRQEN | IRQ_CTRL_GERROR_IRQEN; reg = readl_relaxed(smmu->base + ARM_SMMU_CR0); if (reg & CR0_SMMUEN) @@ -522,6 +618,39 @@ static int kvm_arm_smmu_device_reset(struct host_arm_smmu_device *host_smmu) /* Command queue */ writeq_relaxed(smmu->cmdq.q.q_base, smmu->base + ARM_SMMU_CMDQ_BASE); + /* Event queue */ + writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE); + writel_relaxed(smmu->evtq.q.llq.prod, smmu->base + SZ_64K + ARM_SMMU_EVTQ_PROD); + writel_relaxed(smmu->evtq.q.llq.cons, smmu->base + SZ_64K + ARM_SMMU_EVTQ_CONS); + + /* Disable IRQs first */ + ret = arm_smmu_write_reg_sync(smmu, 0, ARM_SMMU_IRQ_CTRL, + ARM_SMMU_IRQ_CTRLACK); + if (ret) { + dev_err(smmu->dev, "failed to disable irqs\n"); + return ret; + } + + /* + * We don't support combined irqs for now, no specific reason, they are uncommon + * so we just try to avoid bloating the code. + */ + if (smmu->combined_irq) + dev_err(smmu->dev, "Combined irqs not supported by this driver\n"); + else + arm_smmu_setup_unique_irqs(smmu, kvm_arm_smmu_evt_handler, + kvm_arm_smmu_gerror_handler, + kvm_arm_smmu_pri_handler); + + if (smmu->features & ARM_SMMU_FEAT_PRI) + irqen_flags |= IRQ_CTRL_PRIQ_IRQEN; + + /* Enable interrupt generation on the SMMU */ + ret = arm_smmu_write_reg_sync(smmu, irqen_flags, + ARM_SMMU_IRQ_CTRL, ARM_SMMU_IRQ_CTRLACK); + if (ret) + dev_warn(smmu->dev, "failed to enable irqs\n"); + return 0; } @@ -565,6 +694,8 @@ static int kvm_arm_smmu_probe(struct platform_device *pdev) if (IS_ERR(smmu->base)) return PTR_ERR(smmu->base); + arm_smmu_probe_irq(pdev, smmu); + ret = arm_smmu_device_hw_probe(smmu); if (ret) return ret; @@ -583,6 +714,13 @@ static int kvm_arm_smmu_probe(struct platform_device *pdev) if (ret) return ret; + /* evtq */ + ret = arm_smmu_init_one_queue(smmu, &smmu->evtq.q, smmu->base + SZ_64K, + ARM_SMMU_EVTQ_PROD, ARM_SMMU_EVTQ_CONS, + EVTQ_ENT_DWORDS, "evtq"); + if (ret) + return ret; + ret = arm_smmu_init_strtab(smmu); if (ret) return ret;