aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/xen/xen_pt.h4
-rw-r--r--hw/xen/xen_pt_config_init.c2
-rw-r--r--hw/xen/xen_pt_msi.c74
3 files changed, 32 insertions, 48 deletions
diff --git a/hw/xen/xen_pt.h b/hw/xen/xen_pt.h
index c545280085..4f922f46a0 100644
--- a/hw/xen/xen_pt.h
+++ b/hw/xen/xen_pt.h
@@ -187,13 +187,13 @@ typedef struct XenPTMSIXEntry {
int pirq;
uint64_t addr;
uint32_t data;
- uint32_t vector_ctrl;
+ uint32_t latch[4];
bool updated; /* indicate whether MSI ADDR or DATA is updated */
- bool warned; /* avoid issuing (bogus) warning more than once */
} XenPTMSIXEntry;
typedef struct XenPTMSIX {
uint32_t ctrl_offset;
bool enabled;
+ bool maskall;
int total_entries;
int bar_index;
uint64_t table_base;
diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c
index 3d8686d550..4fd7206071 100644
--- a/hw/xen/xen_pt_config_init.c
+++ b/hw/xen/xen_pt_config_init.c
@@ -1499,6 +1499,8 @@ static int xen_pt_msixctrl_reg_write(XenPCIPassthroughState *s,
xen_pt_msix_disable(s);
}
+ s->msix->maskall = *val & PCI_MSIX_FLAGS_MASKALL;
+
debug_msix_enabled_old = s->msix->enabled;
s->msix->enabled = !!(*val & PCI_MSIX_FLAGS_ENABLE);
if (s->msix->enabled != debug_msix_enabled_old) {
diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c
index 82de2bced3..e10cd2a79f 100644
--- a/hw/xen/xen_pt_msi.c
+++ b/hw/xen/xen_pt_msi.c
@@ -25,6 +25,7 @@
#define XEN_PT_GFLAGSSHIFT_DELIV_MODE 12
#define XEN_PT_GFLAGSSHIFT_TRG_MODE 15
+#define latch(fld) latch[PCI_MSIX_ENTRY_##fld / sizeof(uint32_t)]
/*
* Helpers
@@ -314,7 +315,8 @@ static int msix_set_enable(XenPCIPassthroughState *s, bool enabled)
enabled);
}
-static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr)
+static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr,
+ uint32_t vec_ctrl)
{
XenPTMSIXEntry *entry = NULL;
int pirq;
@@ -332,6 +334,19 @@ static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr)
pirq = entry->pirq;
+ /*
+ * Update the entry addr and data to the latest values only when the
+ * entry is masked or they are all masked, as required by the spec.
+ * Addr and data changes while the MSI-X entry is unmasked get deferred
+ * until the next masked -> unmasked transition.
+ */
+ if (pirq == XEN_PT_UNASSIGNED_PIRQ || s->msix->maskall ||
+ (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
+ entry->addr = entry->latch(LOWER_ADDR) |
+ ((uint64_t)entry->latch(UPPER_ADDR) << 32);
+ entry->data = entry->latch(DATA);
+ }
+
rc = msi_msix_setup(s, entry->addr, entry->data, &pirq, true, entry_nr,
entry->pirq == XEN_PT_UNASSIGNED_PIRQ);
if (rc) {
@@ -357,7 +372,7 @@ int xen_pt_msix_update(XenPCIPassthroughState *s)
int i;
for (i = 0; i < msix->total_entries; i++) {
- xen_pt_msix_update_one(s, i);
+ xen_pt_msix_update_one(s, i, msix->msix_entry[i].latch(VECTOR_CTRL));
}
return 0;
@@ -406,35 +421,15 @@ int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index)
static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset)
{
- switch (offset) {
- case PCI_MSIX_ENTRY_LOWER_ADDR:
- return e->addr & UINT32_MAX;
- case PCI_MSIX_ENTRY_UPPER_ADDR:
- return e->addr >> 32;
- case PCI_MSIX_ENTRY_DATA:
- return e->data;
- case PCI_MSIX_ENTRY_VECTOR_CTRL:
- return e->vector_ctrl;
- default:
- return 0;
- }
+ return !(offset % sizeof(*e->latch))
+ ? e->latch[offset / sizeof(*e->latch)] : 0;
}
static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val)
{
- switch (offset) {
- case PCI_MSIX_ENTRY_LOWER_ADDR:
- e->addr = (e->addr & ((uint64_t)UINT32_MAX << 32)) | val;
- break;
- case PCI_MSIX_ENTRY_UPPER_ADDR:
- e->addr = (uint64_t)val << 32 | (e->addr & UINT32_MAX);
- break;
- case PCI_MSIX_ENTRY_DATA:
- e->data = val;
- break;
- case PCI_MSIX_ENTRY_VECTOR_CTRL:
- e->vector_ctrl = val;
- break;
+ if (!(offset % sizeof(*e->latch)))
+ {
+ e->latch[offset / sizeof(*e->latch)] = val;
}
}
@@ -454,39 +449,26 @@ static void pci_msix_write(void *opaque, hwaddr addr,
offset = addr % PCI_MSIX_ENTRY_SIZE;
if (offset != PCI_MSIX_ENTRY_VECTOR_CTRL) {
- const volatile uint32_t *vec_ctrl;
-
if (get_entry_value(entry, offset) == val
&& entry->pirq != XEN_PT_UNASSIGNED_PIRQ) {
return;
}
+ entry->updated = true;
+ } else if (msix->enabled && entry->updated &&
+ !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
+ const volatile uint32_t *vec_ctrl;
+
/*
* If Xen intercepts the mask bit access, entry->vec_ctrl may not be
* up-to-date. Read from hardware directly.
*/
vec_ctrl = s->msix->phys_iomem_base + entry_nr * PCI_MSIX_ENTRY_SIZE
+ PCI_MSIX_ENTRY_VECTOR_CTRL;
-
- if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
- if (!entry->warned) {
- entry->warned = true;
- XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
- " already enabled.\n", entry_nr);
- }
- return;
- }
-
- entry->updated = true;
+ xen_pt_msix_update_one(s, entry_nr, *vec_ctrl);
}
set_entry_value(entry, offset, val);
-
- if (offset == PCI_MSIX_ENTRY_VECTOR_CTRL) {
- if (msix->enabled && !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
- xen_pt_msix_update_one(s, entry_nr);
- }
- }
}
static uint64_t pci_msix_read(void *opaque, hwaddr addr,