[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v4 08/22] xen/arm: vsmmuv3: Add support for registers emulation


  • To: "xen-devel@xxxxxxxxxxxxxxxxxxxx" <xen-devel@xxxxxxxxxxxxxxxxxxxx>
  • From: Milan Djokic <milan_djokic@xxxxxxxx>
  • Date: Wed, 7 Oct 2026 00:06:56 +0000
  • Accept-language: en-US
  • Arc-authentication-results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=epam.com; dmarc=pass action=none header.from=epam.com; dkim=pass header.d=epam.com; arc=none
  • Arc-message-signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=PnOfyH+o7v/rCLTZfw3zej+ceiQDAH62Oq1z5jMnbx8=; b=p09Rcf8MUbyolSuq6Dbju7rc+negX1ganFCgv1cm5uqh5kUTV37IHXVL4w/rb1B+byR8Xlp7pNPnMJCxdhadousBnG3jZgBxItZ8IJjXTh/Vqw7BbQmDA4HwAVEyVUMG2ERwQD1cUbtxi8/gcxqZOZPlptrihwf/0FNhlJVocoF5LgaWmL9p5Aqu1Xahm4xZN8QXKvAfj/AteBMlQ5VC7Q8ZCRmr8OLEcTwUxEi4Zlemtz7i3jiuTASUZyNrngyHjtBhO8ZtdvFH/sreXTkHp9XLhHvjxwygGuEREEzGHnFCdM1p8IjLmvOnLf22SFOkMytHwblKhD8O6Ls1fycfJQ==
  • Arc-seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=V5D0Z/7v/rx3f8OR+yxhDAFSTNCBt/XHm9nQrzSIJNDaUwyaHYHg6qhCwZ1wlccB0BMB1NtfYQHIWMMoOz4cMx/Fszxhb4JfvUTQV6OZABLuybJA4IteJ1fORqnKrwj8S3lKg/WUl390adIBDP/83dRH5sm0soYMIiMVjMn/tmyLgWI1MKhue5nO0vVaL1SkAUh5ili3DwwCvmC5VseL7xJq2yvWdYrGkGA0jbz8KUx+Uo4e6nMM+ug/SB2/Pu06grVcm0M9tQsWQqlrx7lshaPuqDPDNhg2FemgCwsAczlUueOzFRkWD3Lj1iVu5tOYTEMun02U2vOoswN8UEeOyw==
  • Authentication-results: eu.smtp.expurgate.cloud; dkim=pass header.s=selector1 header.d=epam.com header.i="@epam.com" header.h="From:Date:Subject:Message-ID:Content-Type:MIME-Version:x-ms-exchange-senderadcheck"
  • Authentication-results: mx.microsoft.com 1; dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=epam.com;
  • Cc: Rahul Singh <rahul.singh@xxxxxxx>, Stefano Stabellini <sstabellini@xxxxxxxxxx>, Julien Grall <julien@xxxxxxx>, Bertrand Marquis <bertrand.marquis@xxxxxxx>, Michal Orzel <michal.orzel@xxxxxxx>, Volodymyr Babchuk <Volodymyr_Babchuk@xxxxxxxx>, Milan Djokic <milan_djokic@xxxxxxxx>
  • Delivery-date: Wed, 07 Oct 2026 00:07:00 +0000
  • List-id: Xen developer discussion <xen-devel.lists.xenproject.org>
  • Thread-index: AQHdVe/D9k36RA9JcEOa7HpFq89euA==
  • Thread-topic: [PATCH v4 08/22] xen/arm: vsmmuv3: Add support for registers emulation

From: Rahul Singh <rahul.singh@xxxxxxx>

Add initial support for various emulated registers for virtual SMMUv3
for guests and also add support for virtual cmdq and eventq.

Signed-off-by: Rahul Singh <rahul.singh@xxxxxxx>
Signed-off-by: Milan Djokic <milan_djokic@xxxxxxxx>
---
 xen/drivers/passthrough/arm/vsmmu-v3.c | 573 ++++++++++++++++++++++++-
 1 file changed, 571 insertions(+), 2 deletions(-)

diff --git a/xen/drivers/passthrough/arm/vsmmu-v3.c 
b/xen/drivers/passthrough/arm/vsmmu-v3.c
index 7001f8c23f..c4fef6e8bd 100644
--- a/xen/drivers/passthrough/arm/vsmmu-v3.c
+++ b/xen/drivers/passthrough/arm/vsmmu-v3.c
@@ -1,30 +1,588 @@
 /* SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) */
 
+/*
+ * IOMMU stage-1 emulation for ARM SMMUv3.
+ *
+ * TODO: Using ARM generic implementer/product/variant to avoid activating
+ * errata workarounds in the guest driver due to emulation layer.
+ * This needs to be fully handled in the future.
+ */
+
 #include <xen/param.h>
 #include <xen/sched.h>
 #include <asm/mmio.h>
+#include <asm/vgic-emul.h>
 #include <asm/viommu.h>
+#include <asm/vreg.h>
 
 #include "smmu-v3.h"
 #include "vsmmu-v3.h"
 
+/* Register Definition */
+#define ARM_SMMU_IDR2       0x8
+#define ARM_SMMU_IDR3       0xc
+#define ARM_SMMU_IDR4       0x10
+#define IDR0_TERM_MODEL     (1 << 26)
+#define IDR3_RIL            (1 << 10)
+/* Only CMDQ, EVENTQ and SMMUEN supported */
+#define CR0_RESERVED        0xFFFFFFF2U
+/* TODO: Right now only 16-bit SID size is supported */
+#define SMMU_IDR1_SIDSIZE   16
+/* The CMDQ/EVTQ sizes are currently set to the architectural maximum. */
+#define SMMU_CMDQS          19
+#define SMMU_EVTQS          19
+#define DWORDS_BYTES        8
+
+/* virtual smmu queue */
+struct arm_vsmmu_queue {
+    uint64_t    q_base; /* base register */
+    uint32_t    prod;
+    uint32_t    cons;
+    uint8_t     ent_size;
+    uint8_t     max_n_shift;
+};
+
 struct virt_smmu {
     struct      domain *d;
     struct      list_head viommu_list;
     uint32_t    features;
+    paddr_t     addr;
+    uint32_t    cr[3];
+    uint32_t    cr0ack;
+    uint32_t    gerror;
+    uint32_t    gerrorn;
+    uint32_t    strtab_base_cfg;
+    uint64_t    strtab_base;
+    uint32_t    irq_ctrl;
+    uint64_t    gerror_irq_cfg0;
+    uint64_t    evtq_irq_cfg0;
+    struct      arm_vsmmu_queue evtq, cmdq;
+    spinlock_t  cmd_queue_lock;
+    spinlock_t  evt_queue_lock;
+    spinlock_t  gerror_lock;
+    spinlock_t  cr0_lock;
+    spinlock_t  cr1_lock;
+    spinlock_t  cr2_lock;
+    spinlock_t  strtab_cfg_lock;
+    spinlock_t  irq_cfg_lock;
 };
 
+/* Helper functions */
+static inline bool smmu_get_smmu_enabled(struct virt_smmu *smmu)
+{
+    bool enabled;
+
+    spin_lock(&smmu->cr0_lock);
+    enabled = FIELD_GET(CR0_SMMUEN, smmu->cr[0]);
+    spin_unlock(&smmu->cr0_lock);
+
+    return enabled;
+}
+
+static inline bool smmu_get_cmdq_enabled(struct virt_smmu *smmu)
+{
+    bool enabled;
+
+    spin_lock(&smmu->cr0_lock);
+    enabled = FIELD_GET(CR0_CMDQEN, smmu->cr[0]);
+    spin_unlock(&smmu->cr0_lock);
+
+    return enabled;
+}
+
+static inline bool smmu_get_evtq_enabled(struct virt_smmu *smmu)
+{
+    bool enabled;
+
+    spin_lock(&smmu->cr0_lock);
+    enabled = FIELD_GET(CR0_EVTQEN, smmu->cr[0]);
+    spin_unlock(&smmu->cr0_lock);
+
+    return enabled;
+}
+
 static int vsmmuv3_mmio_write(struct vcpu *v, mmio_info_t *info,
                               register_t r, void *priv)
 {
-    BUG_ON("unimplemented");
+    struct virt_smmu *smmu = priv;
+    uint64_t reg;
+    uint32_t reg32;
+    struct hsr_dabt dabt = info->dabt;
+    uint64_t offset = info->gpa - smmu->addr;
+
+    if ( offset & 0x10000 )
+    {
+        /* page 1 */
+        switch ( info->gpa & 0xffff )
+        {
+        case VREG32(ARM_SMMU_EVTQ_PROD):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            /*  Write allowed only when SMMU_CR0.EVTQEN == 0 */
+            if ( smmu_get_evtq_enabled(smmu) )
+                break;
+            spin_lock(&smmu->evt_queue_lock);
+            reg32 = smmu->evtq.prod;
+            vreg_reg32_update(&reg32, r, info);
+            smmu->evtq.prod = reg32;
+            spin_unlock(&smmu->evt_queue_lock);
+            break;
+        case VREG32(ARM_SMMU_EVTQ_CONS):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->evt_queue_lock);
+            reg32 = smmu->evtq.cons;
+            vreg_reg32_update(&reg32, r, info);
+            smmu->evtq.cons = reg32;
+            spin_unlock(&smmu->evt_queue_lock);
+            break;
+        default:
+            printk(XENLOG_G_ERR
+                "%pd: vSMMUv3: unhandled write r%d offset %"PRIpaddr"\n",
+                v->domain, info->dabt.reg, (unsigned long)info->gpa & 0xffff);
+            return IO_HANDLED;
+        }
+    }
+    else
+    {
+        /* page 0 */
+        switch ( info->gpa & 0xffff )
+        {
+        case VREG32(ARM_SMMU_CR0):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cr0_lock);
+            reg32 = smmu->cr[0];
+            vreg_reg32_update(&reg32, r, info);
+            smmu->cr[0] = reg32;
+            smmu->cr0ack = reg32 & ~CR0_RESERVED;
+            spin_unlock(&smmu->cr0_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CR1):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cr1_lock);
+            reg32 = smmu->cr[1];
+            vreg_reg32_update(&reg32, r, info);
+            smmu->cr[1] = reg32;
+            spin_unlock(&smmu->cr1_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CR2):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cr2_lock);
+            reg32 = smmu->cr[2];
+            vreg_reg32_update(&reg32, r, info);
+            smmu->cr[2] = reg32;
+            spin_unlock(&smmu->cr2_lock);
+            break;
+
+        case VREG64(ARM_SMMU_STRTAB_BASE):
+            /*  Must only be written when SMMU_CR0.SMMUEN == 0, otherwise 
ignore */
+            if ( smmu_get_smmu_enabled(smmu) )
+                break;
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->strtab_cfg_lock);
+            reg = smmu->strtab_base;
+            vreg_reg64_update(&reg, r, info);
+            smmu->strtab_base = reg;
+            spin_unlock(&smmu->strtab_cfg_lock);
+            break;
+
+        case VREG32(ARM_SMMU_STRTAB_BASE_CFG):
+            /*  Must only be written when SMMU_CR0.SMMUEN == 0, otherwise 
ignore */
+            if ( smmu_get_smmu_enabled(smmu) )
+                break;
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->strtab_cfg_lock);
+            reg32 = smmu->strtab_base_cfg;
+            vreg_reg32_update(&reg32, r, info);
+            smmu->strtab_base_cfg = reg32;
+            spin_unlock(&smmu->strtab_cfg_lock);
+            break;
+
+        case VREG64(ARM_SMMU_CMDQ_BASE):
+            /*  Must only be written when SMMU_CR0.CMDQEN == 0, otherwise 
ignore */
+            if ( smmu_get_cmdq_enabled(smmu) )
+                break;
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->cmd_queue_lock);
+            reg = smmu->cmdq.q_base;
+            vreg_reg64_update(&reg, r, info);
+            smmu->cmdq.q_base = reg;
+            smmu->cmdq.max_n_shift = FIELD_GET(Q_BASE_LOG2SIZE, 
smmu->cmdq.q_base);
+            if ( smmu->cmdq.max_n_shift > SMMU_CMDQS )
+                smmu->cmdq.max_n_shift = SMMU_CMDQS;
+            spin_unlock(&smmu->cmd_queue_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CMDQ_PROD):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cmd_queue_lock);
+            reg32 = smmu->cmdq.prod;
+            vreg_reg32_update(&reg32, r, info);
+            smmu->cmdq.prod = reg32;
+            spin_unlock(&smmu->cmd_queue_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CMDQ_CONS):
+            /*  Must only be written when SMMU_CR0.CMDQEN == 0, otherwise 
ignore */
+            if ( smmu_get_cmdq_enabled(smmu) )
+                break;
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cmd_queue_lock);
+            reg32 = smmu->cmdq.cons;
+            vreg_reg32_update(&reg32, r, info);
+            smmu->cmdq.cons = reg32;
+            spin_unlock(&smmu->cmd_queue_lock);
+            break;
+
+        case VREG64(ARM_SMMU_EVTQ_BASE):
+            /*  Must only be written when SMMU_CR0.EVTQEN == 0, otherwise 
ignore */
+            if ( smmu_get_evtq_enabled(smmu) )
+                break;
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->evt_queue_lock);
+            reg = smmu->evtq.q_base;
+            vreg_reg64_update(&reg, r, info);
+            smmu->evtq.q_base = reg;
+            smmu->evtq.max_n_shift = FIELD_GET(Q_BASE_LOG2SIZE, 
smmu->evtq.q_base);
+            if ( smmu->evtq.max_n_shift > SMMU_EVTQS )
+                smmu->evtq.max_n_shift = SMMU_EVTQS;
+            spin_unlock(&smmu->evt_queue_lock);
+            break;
+
+        case VREG32(ARM_SMMU_IRQ_CTRL):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->irq_cfg_lock);
+            reg32 = smmu->irq_ctrl;
+            vreg_reg32_update(&reg32, r, info);
+            smmu->irq_ctrl = reg32;
+            spin_unlock(&smmu->irq_cfg_lock);
+            break;
+
+        case VREG64(ARM_SMMU_GERROR_IRQ_CFG0):
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->irq_cfg_lock);
+            reg = smmu->gerror_irq_cfg0;
+            vreg_reg64_update(&reg, r, info);
+            smmu->gerror_irq_cfg0 = reg;
+            spin_unlock(&smmu->irq_cfg_lock);
+            break;
+
+        case VREG64(ARM_SMMU_EVTQ_IRQ_CFG0):
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->irq_cfg_lock);
+            reg = smmu->evtq_irq_cfg0;
+            vreg_reg64_update(&reg, r, info);
+            smmu->evtq_irq_cfg0 = reg;
+            spin_unlock(&smmu->irq_cfg_lock);
+            break;
+
+        case VREG32(ARM_SMMU_GERRORN):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->gerror_lock);
+            reg32 = smmu->gerrorn;
+            vreg_reg32_update(&reg32, r, info);
+            smmu->gerrorn = reg32;
+            spin_unlock(&smmu->gerror_lock);
+            break;
+
+        default:
+            printk(XENLOG_G_ERR
+                "%pd: vSMMUv3: unhandled write r%d offset %"PRIpaddr"\n",
+                v->domain, info->dabt.reg, (unsigned long)info->gpa & 0xffff);
+            return IO_HANDLED;
+        }
+    }
+
+    return IO_HANDLED;
+
+ bad_width:
+    gprintk(XENLOG_G_ERR,
+            "%pd: vSMMUv3: bad write width %d r%d offset %"PRIpaddr"\n",
+            v->domain, dabt.size, dabt.reg, info->gpa & 0xffff);
     return IO_HANDLED;
 }
 
 static int vsmmuv3_mmio_read(struct vcpu *v, mmio_info_t *info,
                              register_t *r, void *priv)
 {
-    BUG_ON("unimplemented");
+    struct virt_smmu *smmu = priv;
+    uint64_t reg;
+    struct hsr_dabt dabt = info->dabt;
+    uint64_t offset = info->gpa - smmu->addr;
+
+    if ( offset & 0x10000 )
+    {
+        /* page 1 */
+        switch ( info->gpa & 0xffff )
+        {
+            case VREG32(ARM_SMMU_EVTQ_PROD):
+                if ( dabt.size != DABT_WORD ) goto bad_width;
+                spin_lock(&smmu->evt_queue_lock);
+                *r = vreg_reg32_extract(smmu->evtq.prod, info);
+                spin_unlock(&smmu->evt_queue_lock);
+                break;
+
+            case VREG32(ARM_SMMU_EVTQ_CONS):
+                if ( dabt.size != DABT_WORD ) goto bad_width;
+                spin_lock(&smmu->evt_queue_lock);
+                *r = vreg_reg32_extract(smmu->evtq.cons, info);
+                spin_unlock(&smmu->evt_queue_lock);
+                break;
+
+            default:
+                printk(XENLOG_G_ERR
+                    "%pd: vSMMUv3: unhandled read r%d offset %"PRIpaddr"\n",
+                    v->domain, info->dabt.reg, (unsigned long)info->gpa & 
0xffff);
+                return IO_HANDLED;
+        }
+    }
+    else
+    {
+        /* page 0 */
+        switch ( info->gpa & 0xffff )
+        {
+        case VREG32(ARM_SMMU_IDR0):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            /*
+             * Emulated SMMU capabilities.
+             *
+             * Some architectural capabilities are currently not implemented
+             * by the stage-1 emulation layer or underlying Xen SMMU support,
+             * therefore they are not advertised atm even if supported by the 
HW
+             */
+            reg  = FIELD_PREP(IDR0_S1P, 1) |
+                /*
+                 * TODO: Coherent access not supported by the emulation atm.
+                 * Guest must perform explicit cache clean
+                 */
+                FIELD_PREP(IDR0_COHACC, 0) |
+                /* Transaction stall on fault not implemented */
+                FIELD_PREP(IDR0_STALL_MODEL, 1) |
+                /* Terminating a transaction with RAZ/WI behavior not 
implemented */
+                FIELD_PREP(IDR0_TERM_MODEL, 1);
+
+            /* HW reflected capabilities */
+            reg |= FIELD_PREP(IDR0_ST_LVL,
+                            smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB ?
+                            1 : 0);
+            reg |= FIELD_PREP(IDR0_TTF,
+                            smmu->features & ARM_SMMU_FEAT_TTF_AARCH32_64 ?
+                            IDR0_TTF_AARCH32_64 : IDR0_TTF_AARCH64);
+
+            reg |= FIELD_PREP(IDR0_ASID16,
+                            smmu->features & ARM_SMMU_FEAT_ASID_16 ? 1 : 0);
+            if ( (smmu->features & ARM_SMMU_FEAT_TT_LE) &&
+                (smmu->features & ARM_SMMU_FEAT_TT_BE) )
+                reg |= FIELD_PREP(IDR0_TTENDIAN, IDR0_TTENDIAN_MIXED);
+            else if ( smmu->features & ARM_SMMU_FEAT_TT_LE )
+                reg |= FIELD_PREP(IDR0_TTENDIAN, IDR0_TTENDIAN_LE);
+    #ifdef __BIG_ENDIAN
+            else if ( smmu->features & ARM_SMMU_FEAT_TT_BE )
+                reg |= FIELD_PREP(IDR0_TTENDIAN, IDR0_TTENDIAN_BE);
+    #endif
+            *r = vreg_reg32_extract(reg, info);
+            break;
+
+        case VREG32(ARM_SMMU_IDR1):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            reg  = FIELD_PREP(IDR1_SIDSIZE, SMMU_IDR1_SIDSIZE) |
+                FIELD_PREP(IDR1_CMDQS, SMMU_CMDQS) |
+                FIELD_PREP(IDR1_EVTQS, SMMU_EVTQS);
+            *r = vreg_reg32_extract(reg, info);
+            break;
+
+        case VREG32(ARM_SMMU_IDR2):
+            goto read_reserved;
+
+        case VREG32(ARM_SMMU_IDR3):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            reg = 0;
+            *r = vreg_reg32_extract(reg, info);
+            break;
+
+        case VREG32(ARM_SMMU_IDR4):
+            goto read_impl_defined;
+
+        case VREG32(ARM_SMMU_IDR5):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            reg = 0;
+            if (smmu->features & ARM_SMMU_FEAT_GRAN64K)
+                reg |= IDR5_GRAN64K;
+            if (smmu->features & ARM_SMMU_FEAT_GRAN16K)
+                reg |= IDR5_GRAN16K;
+            if (smmu->features & ARM_SMMU_FEAT_GRAN4K)
+                reg |= IDR5_GRAN4K;
+
+            if (smmu->features & ARM_SMMU_FEAT_OAS_32_BIT)
+                reg |= IDR5_OAS_32_BIT;
+            if (smmu->features & ARM_SMMU_FEAT_OAS_36_BIT)
+                reg |= IDR5_OAS_36_BIT;
+            if (smmu->features & ARM_SMMU_FEAT_OAS_40_BIT)
+                reg |= IDR5_OAS_40_BIT;
+            if (smmu->features & ARM_SMMU_FEAT_OAS_42_BIT)
+                reg |= IDR5_OAS_42_BIT;
+            if (smmu->features & ARM_SMMU_FEAT_OAS_44_BIT)
+                reg |= IDR5_OAS_44_BIT;
+            if (smmu->features & ARM_SMMU_FEAT_OAS_48_BIT)
+                reg |= IDR5_OAS_48_BIT;
+            if (smmu->features & ARM_SMMU_FEAT_OAS_52_BIT)
+                reg |= IDR5_OAS_52_BIT;
+
+            *r = vreg_reg32_extract(reg, info);
+            break;
+
+        case VREG32(ARM_SMMU_IIDR):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            /* Use generic ARM IIDR value to avoid activating errata 
workarounds */
+            reg  = FIELD_PREP(IIDR_IMPLEMENTER, IIDR_IMPLEMENTER_ARM) |
+                FIELD_PREP(IIDR_REVISION, 0) |
+                FIELD_PREP(IIDR_VARIANT, 0) |
+                FIELD_PREP(IIDR_PRODUCTID, 0);
+            *r = vreg_reg32_extract(reg, info);
+            break;
+
+        case VREG32(ARM_SMMU_CR0):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cr0_lock);
+            *r = vreg_reg32_extract(smmu->cr[0], info);
+            spin_unlock(&smmu->cr0_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CR0ACK):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cr0_lock);
+            *r = vreg_reg32_extract(smmu->cr0ack, info);
+            spin_unlock(&smmu->cr0_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CR1):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cr1_lock);
+            *r = vreg_reg32_extract(smmu->cr[1], info);
+            spin_unlock(&smmu->cr1_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CR2):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cr2_lock);
+            *r = vreg_reg32_extract(smmu->cr[2], info);
+            spin_unlock(&smmu->cr2_lock);
+            break;
+
+        case VREG64(ARM_SMMU_STRTAB_BASE):
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->strtab_cfg_lock);
+            *r = vreg_reg64_extract(smmu->strtab_base, info);
+            spin_unlock(&smmu->strtab_cfg_lock);
+            break;
+
+        case VREG32(ARM_SMMU_STRTAB_BASE_CFG):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->strtab_cfg_lock);
+            *r = vreg_reg32_extract(smmu->strtab_base_cfg, info);
+            spin_unlock(&smmu->strtab_cfg_lock);
+            break;
+
+        case VREG64(ARM_SMMU_CMDQ_BASE):
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->cmd_queue_lock);
+            *r = vreg_reg64_extract(smmu->cmdq.q_base, info);
+            spin_unlock(&smmu->cmd_queue_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CMDQ_PROD):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cmd_queue_lock);
+            *r = vreg_reg32_extract(smmu->cmdq.prod, info);
+            spin_unlock(&smmu->cmd_queue_lock);
+            break;
+
+        case VREG32(ARM_SMMU_CMDQ_CONS):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->cmd_queue_lock);
+            *r = vreg_reg32_extract(smmu->cmdq.cons, info);
+            spin_unlock(&smmu->cmd_queue_lock);
+            break;
+
+        case VREG64(ARM_SMMU_EVTQ_BASE):
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->evt_queue_lock);
+            *r = vreg_reg64_extract(smmu->evtq.q_base, info);
+            spin_unlock(&smmu->evt_queue_lock);
+            break;
+
+        case VREG32(ARM_SMMU_IRQ_CTRL):
+        case VREG32(ARM_SMMU_IRQ_CTRLACK):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->irq_cfg_lock);
+            *r = vreg_reg32_extract(smmu->irq_ctrl, info);
+            spin_unlock(&smmu->irq_cfg_lock);
+            break;
+
+        case VREG64(ARM_SMMU_GERROR_IRQ_CFG0):
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->irq_cfg_lock);
+            *r = vreg_reg64_extract(smmu->gerror_irq_cfg0, info);
+            spin_unlock(&smmu->irq_cfg_lock);
+            break;
+
+        case VREG64(ARM_SMMU_EVTQ_IRQ_CFG0):
+            if ( dabt.size != DABT_DOUBLE_WORD && dabt.size != DABT_WORD )
+                goto bad_width;
+            spin_lock(&smmu->irq_cfg_lock);
+            *r = vreg_reg64_extract(smmu->evtq_irq_cfg0, info);
+            spin_unlock(&smmu->irq_cfg_lock);
+            break;
+
+        case VREG32(ARM_SMMU_GERROR):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->gerror_lock);
+            *r = vreg_reg32_extract(smmu->gerror, info);
+            spin_unlock(&smmu->gerror_lock);
+            break;
+
+        case VREG32(ARM_SMMU_GERRORN):
+            if ( dabt.size != DABT_WORD ) goto bad_width;
+            spin_lock(&smmu->gerror_lock);
+            *r = vreg_reg32_extract(smmu->gerrorn, info);
+            spin_unlock(&smmu->gerror_lock);
+            break;
+
+        default:
+            printk(XENLOG_G_ERR
+                "%pd: vSMMUv3: unhandled read r%d offset %"PRIpaddr"\n",
+                v->domain, info->dabt.reg, (unsigned long)info->gpa & 0xffff);
+            return IO_HANDLED;
+        }
+    }
+    return IO_HANDLED;
+
+ read_impl_defined:
+    printk(XENLOG_G_DEBUG
+           "%pd: vSMMUv3: RAZ on implementation defined register offset 
%"PRIpaddr"\n",
+           v->domain, info->gpa & 0xffff);
+    *r = 0;
+    return IO_HANDLED;
+
+ read_reserved:
+    printk(XENLOG_G_DEBUG
+           "%pd: vSMMUv3: RAZ on reserved register offset %"PRIpaddr"\n",
+           v->domain, info->gpa & 0xffff);
+    *r = 0;
+    return IO_HANDLED;
+ bad_width:
+    gprintk(XENLOG_G_ERR,
+            "%pd: vSMMUv3: bad read width %d r%d offset %"PRIpaddr"\n",
+            v->domain, dabt.size, dabt.reg, info->gpa & 0xffff);
     return IO_HANDLED;
 }
 
@@ -44,6 +602,17 @@ static int vsmmuv3_init_single(struct domain *d, paddr_t 
addr, paddr_t size,
 
     smmu->d = d;
     smmu->features = features;
+    smmu->cmdq.ent_size = CMDQ_ENT_DWORDS * DWORDS_BYTES;
+    smmu->evtq.ent_size = EVTQ_ENT_DWORDS * DWORDS_BYTES;
+
+    spin_lock_init(&smmu->cmd_queue_lock);
+    spin_lock_init(&smmu->evt_queue_lock);
+    spin_lock_init(&smmu->gerror_lock);
+    spin_lock_init(&smmu->cr0_lock);
+    spin_lock_init(&smmu->cr1_lock);
+    spin_lock_init(&smmu->cr2_lock);
+    spin_lock_init(&smmu->strtab_cfg_lock);
+    spin_lock_init(&smmu->irq_cfg_lock);
 
     register_mmio_handler(d, &vsmmuv3_mmio_handler, addr, size, smmu);
 
-- 
2.43.0

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.