|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [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(®32, 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(®32, 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(®32, 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(®32, 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(®32, 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(®, 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(®32, 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(®, 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(®32, 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(®32, 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(®, 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(®32, 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(®, 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(®, 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(®32, 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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |