When a MSI interrupt is bound to a guest using
xc_domain_update_msi_irq (XEN_DOMCTL_bind_pt_irq) the interrupt is
left masked by default.
This causes problems with guests that first configure interrupts and
clean the per-entry MSIX table mask bit and afterwards enable MSIX
globally. In such scenario the Xen internal msixtbl handlers would not
detect the unmasking of MSIX entries because vectors are not yet
registered since MSIX is not enabled, and vectors would be left
masked.
Introduce a new flag in the gflags field to signal Xen whether a MSI
interrupt should be unmasked after being bound.
This also requires to track the mask register for MSI interrupts, so
QEMU can also notify to Xen whether the MSI interrupt should be bound
masked or unmasked
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
Reported-by: Andreas Kinzler <hfp@posteo.de>
Reviewed-by: Stefano Stabellini <sstabellini@kernel.org>
Signed-off-by: Stefano Stabellini <sstabellini@kernel.org>
[Backported from commit
a8036336609d2e184fc3543a4c439c0ba7d7f3a2
https://git.qemu.org/?p=qemu.git;a=commit;h=
a8036336609d2e184fc3543a4c439c0ba7d7f3a2
https://xenbits.xen.org/gitweb/?p=qemu-xen.git;a=commit;h=
a8036336609d2e184fc3543a4c439c0ba7d7f3a2]
Signed-off-by: Andra Paraschiv <andraprs@amazon.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
static int pt_long_reg_write(struct pt_dev *ptdev,
struct pt_reg_tbl *cfg_entry,
uint32_t *value, uint32_t dev_value, uint32_t valid_mask);
+static int pt_mask_reg_write(struct pt_dev *ptdev,
+ struct pt_reg_tbl *cfg_entry,
+ uint32_t *value, uint32_t dev_value, uint32_t valid_mask);
static int pt_cmd_reg_write(struct pt_dev *ptdev,
struct pt_reg_tbl *cfg_entry,
uint16_t *value, uint16_t dev_value, uint16_t valid_mask);
.emu_mask = 0xFFFFFFFF,
.init = pt_mask_reg_init,
.u.dw.read = pt_long_reg_read,
- .u.dw.write = pt_long_reg_write,
+ .u.dw.write = pt_mask_reg_write,
},
/* Mask reg (if PCI_MSI_FLAGS_MASK_BIT set, for 64-bit devices) */
{
.emu_mask = 0xFFFFFFFF,
.init = pt_mask_reg_init,
.u.dw.read = pt_long_reg_read,
- .u.dw.write = pt_long_reg_write,
+ .u.dw.write = pt_mask_reg_write,
},
/* Pending reg (if PCI_MSI_FLAGS_MASK_BIT set, for 32-bit devices) */
{
return 0;
}
+/* write guest mask bits */
+static int pt_mask_reg_write(struct pt_dev *ptdev,
+ struct pt_reg_tbl *cfg_entry,
+ uint32_t *value, uint32_t dev_value, uint32_t valid_mask)
+{
+ int rc;
+
+ rc = pt_long_reg_write(ptdev, cfg_entry, value, dev_value, valid_mask);
+ if (rc)
+ return rc;
+
+ ptdev->msi->mask = *value;
+
+ return 0;
+}
+
/* write Command register */
static int pt_cmd_reg_write(struct pt_dev *ptdev,
struct pt_reg_tbl *cfg_entry,
struct pt_msi_info {
uint32_t flags;
uint32_t ctrl_offset; /* saved control offset */
+ uint32_t mask; /* guest mask bits */
int pirq; /* guest pirq corresponding */
uint32_t addr_lo; /* guest message address */
uint32_t addr_hi; /* guest message upper address */
addr = (uint64_t)d->msi->addr_hi << 32 | d->msi->addr_lo;
gflags = __get_msi_gflags(d->msi->data, addr);
+ /* Current MSI emulation in QEMU only supports 1 vector */
+ gflags |= (d->msi->mask & 1) ? 0 : (1u << GLFAGS_SHIFT_UNMASKED);
+
PT_LOG("Update msi with pirq %x gvec %x gflags %x\n",
d->msi->pirq, gvec, gflags);
}
}
-static int pt_msix_update_one(struct pt_dev *dev, int entry_nr)
+static int pt_msix_update_one(struct pt_dev *dev, int entry_nr,
+ uint32_t vec_ctrl)
{
struct msix_entry_info *entry = &dev->msix->msix_entry[entry_nr];
int pirq = entry->pirq;
entry->pirq = pirq;
}
+ gflags |= (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT) ? 0 :
+ (1u << GLFAGS_SHIFT_UNMASKED);
+
PT_LOG("Update msix entry %x with pirq %x gvec %x\n",
entry_nr, pirq, gvec);
for ( i = 0; i < msix->total_entries; i++ )
{
- pt_msix_update_one(dev, i);
+ pt_msix_update_one(dev, i, msix->msix_entry[i].io_mem[3]);
}
return 0;
if ( offset == 3 )
{
- if ( msix->enabled && !(val & 0x1) )
- pt_msix_update_one(dev, entry_nr);
+ if ( msix->enabled && !(val & 0x1) ) {
+ const volatile uint32_t *vec_ctrl;
+
+ /*
+ * If Xen intercepts the mask bit access, io_mem[3] may not be
+ * up-to-date. Read from hardware directly.
+ */
+ vec_ctrl = dev->msix->phys_iomem_base +
+ PCI_MSIX_ENTRY_SIZE * entry_nr + PCI_MSIX_ENTRY_VECTOR_CTRL;
+
+ pt_msix_update_one(dev, entry_nr, *vec_ctrl);
+ }
}
}
#define PCI_MSIX_PBA 8
#define PCI_MSIX_BIR 0x7
+/* MSI-X Table entry format */
+#define PCI_MSIX_ENTRY_SIZE 16
+#define PCI_MSIX_ENTRY_LOWER_ADDR 0
+#define PCI_MSIX_ENTRY_UPPER_ADDR 4
+#define PCI_MSIX_ENTRY_DATA 8
+#define PCI_MSIX_ENTRY_VECTOR_CTRL 12
+#define PCI_MSIX_ENTRY_CTRL_MASKBIT 1
+
#define MSI_FLAG_UNINIT 0x1000
#define PT_MSI_MAPPED 0x2000
#define GFLAGS_SHIFT_DM 9
#define GLFAGS_SHIFT_DELIV_MODE 12
#define GLFAGS_SHIFT_TRG_MODE 15
+#define GLFAGS_SHIFT_UNMASKED 16
void
msi_set_enable(struct pt_dev *dev, int en);