|
@@ -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,
|