@@ -45,6 +45,13 @@ struct vvtd {
uint64_t base_addr;
/* Point back to the owner domain */
struct domain *domain;
+ /* Is in Extended Interrupt Mode */
+ bool eim;
+ /* Interrupt remapping table base gfn */
+ uint64_t irt;
+ /* Max remapping entries in IRT */
+ int irt_max_entry;
+
struct hvm_hw_vvtd_regs *regs;
struct page_info *regs_page;
};
@@ -81,6 +88,11 @@ static inline struct vvtd *vcpu_vvtd(struct vcpu *v)
return domain_vvtd(v->domain);
}
+static inline void __vvtd_set_bit(struct vvtd *vvtd, uint32_t reg, int nr)
+{
+ return __set_bit(nr, (uint32_t *)&vvtd->regs->data[reg]);
+}
+
static inline void vvtd_set_reg(struct vvtd *vtd, uint32_t reg,
uint32_t value)
{
@@ -107,6 +119,41 @@ static inline uint8_t vvtd_get_reg_byte(struct vvtd *vtd, uint32_t reg)
vvtd_set_reg(vvtd, (reg) + 4, (uint32_t)((val) >> 32)); \
} while(0)
+static int vvtd_handle_gcmd_sirtp(struct vvtd *vvtd, unsigned long val)
+{
+ uint64_t irta;
+
+ if ( !(val & DMA_GCMD_SIRTP) )
+ return X86EMUL_OKAY;
+
+ vvtd_get_reg_quad(vvtd, DMAR_IRTA_REG, irta);
+ vvtd->irt = DMA_IRTA_ADDR(irta) >> PAGE_SHIFT;
+ vvtd->irt_max_entry = DMA_IRTA_SIZE(irta);
+ vvtd->eim = !!(irta & IRTA_EIME);
+ VVTD_DEBUG(VVTD_DBG_RW, "Update IR info (addr=%lx eim=%d size=%d).",
+ vvtd->irt, vvtd->eim, vvtd->irt_max_entry);
+ __vvtd_set_bit(vvtd, DMAR_GSTS_REG, DMA_GSTS_SIRTPS_BIT);
+
+ return X86EMUL_OKAY;
+}
+
+static int vvtd_write_gcmd(struct vvtd *vvtd, unsigned long val)
+{
+ uint32_t orig = vvtd_get_reg(vvtd, DMAR_GSTS_REG);
+ uint32_t changed = orig ^ val;
+
+ if ( !changed )
+ return X86EMUL_OKAY;
+ if ( (changed & (changed - 1)) )
+ VVTD_DEBUG(VVTD_DBG_RW, "Guest attempts to update multiple fields "
+ "of GCMD_REG in one write transation.");
+
+ if ( changed & DMA_GCMD_SIRTP )
+ vvtd_handle_gcmd_sirtp(vvtd, val);
+
+ return X86EMUL_OKAY;
+}
+
static int vvtd_range(struct vcpu *v, unsigned long addr)
{
struct vvtd *vvtd = vcpu_vvtd(v);
@@ -183,6 +230,26 @@ static int vvtd_write(struct vcpu *v, unsigned long addr,
ret = X86EMUL_UNHANDLEABLE;
switch ( offset_aligned )
{
+ case DMAR_GCMD_REG:
+ if ( len == 8 )
+ goto error;
+ ret = vvtd_write_gcmd(vvtd, val_lo);
+ break;
+
+ case DMAR_IRTA_REG:
+ if ( len == 8 )
+ vvtd_set_reg_quad(vvtd, DMAR_IRTA_REG, val);
+ else
+ vvtd_set_reg(vvtd, DMAR_IRTA_REG, val_lo);
+ break;
+
+ case DMAR_IRTA_REG_HI:
+ if ( len == 8 )
+ goto error;
+ vvtd_set_reg(vvtd, DMAR_IRTA_REG_HI, val_lo);
+ ret = X86EMUL_OKAY;
+ break;
+
case DMAR_IEDATA_REG:
case DMAR_IEADDR_REG:
case DMAR_IEUADDR_REG:
@@ -266,6 +333,9 @@ static struct vvtd *__vvtd_create(struct domain *d,
vvtd->base_addr = base_addr;
vvtd->domain = d;
vvtd->status = 0;
+ vvtd->eim = 0;
+ vvtd->irt = 0;
+ vvtd->irt_max_entry = 0;
register_mmio_handler(d, &vvtd_mmio_ops);
return vvtd;
@@ -195,9 +195,16 @@
#define DMA_GSTS_WBFS (((u64)1) << 27)
#define DMA_GSTS_QIES (((u64)1) <<26)
#define DMA_GSTS_IRES (((u64)1) <<25)
-#define DMA_GSTS_SIRTPS (((u64)1) << 24)
+#define DMA_GSTS_SIRTPS_BIT 24
+#define DMA_GSTS_SIRTPS (((u64)1) << DMA_GSTS_SIRTPS_BIT)
#define DMA_GSTS_CFIS (((u64)1) <<23)
+/* IRTA_REG */
+#define DMA_IRTA_ADDR(val) (val & ~0xfffULL)
+#define DMA_IRTA_EIME(val) (!!(val & (1 << 11)))
+#define DMA_IRTA_S(val) (val & 0xf)
+#define DMA_IRTA_SIZE(val) (1UL << (DMA_IRTA_S(val) + 1))
+
/* PMEN_REG */
#define DMA_PMEN_EPM (((u32)1) << 31)
#define DMA_PMEN_PRS (((u32)1) << 0)