*/
#include "qemu/osdep.h"
-#include "hw/hw.h"
#include "hw/pci/msi.h"
#include "hw/pci/msix.h"
#include "hw/pci/pci.h"
#include "hw/xen/xen.h"
+#include "sysemu/xen.h"
+#include "migration/qemu-file-types.h"
+#include "migration/vmstate.h"
#include "qemu/range.h"
-
-#define MSIX_CAP_LENGTH 12
+#include "qapi/error.h"
+#include "trace.h"
/* MSI enable bit and maskall bit are in byte 1 in FLAGS register */
#define MSIX_CONTROL_OFFSET (PCI_MSIX_FLAGS + 1)
#define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8)
#define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8)
-MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
+static MSIMessage msix_prepare_message(PCIDevice *dev, unsigned vector)
{
uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE;
MSIMessage msg;
return msg;
}
+MSIMessage msix_get_message(PCIDevice *dev, unsigned vector)
+{
+ return dev->msix_prepare_message(dev, vector);
+}
+
/*
* Special API for POWER to configure the vectors through
* a side channel. Should never be used by devices.
}
}
+void msix_set_mask(PCIDevice *dev, int vector, bool mask)
+{
+ unsigned offset;
+ bool was_masked;
+
+ assert(vector < dev->msix_entries_nr);
+
+ offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL;
+
+ was_masked = msix_is_masked(dev, vector);
+
+ if (mask) {
+ dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT;
+ } else {
+ dev->msix_table[offset] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT;
+ }
+
+ msix_handle_mask_update(dev, vector, was_masked);
+}
+
+static bool msix_masked(PCIDevice *dev)
+{
+ return dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK;
+}
+
static void msix_update_function_masked(PCIDevice *dev)
{
- dev->msix_function_masked = !msix_enabled(dev) ||
- (dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] & MSIX_MASKALL_MASK);
+ dev->msix_function_masked = !msix_enabled(dev) || msix_masked(dev);
}
/* Handle MSI-X capability config write. */
return;
}
+ trace_msix_write_config(dev->name, msix_enabled(dev), msix_masked(dev));
+
was_masked = dev->msix_function_masked;
msix_update_function_masked(dev);
{
PCIDevice *dev = opaque;
+ assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
return pci_get_long(dev->msix_table + addr);
}
int vector = addr / PCI_MSIX_ENTRY_SIZE;
bool was_masked;
+ assert(addr + size <= dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
+
was_masked = msix_is_masked(dev, vector);
pci_set_long(dev->msix_table + addr, val);
msix_handle_mask_update(dev, vector, was_masked);
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
.max_access_size = 4,
},
};
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
+ .max_access_size = 8,
+ },
+ .impl = {
.max_access_size = 4,
},
};
}
}
-/* Initialize the MSI-X structures */
+/*
+ * Make PCI device @dev MSI-X capable
+ * @nentries is the max number of MSI-X vectors that the device support.
+ * @table_bar is the MemoryRegion that MSI-X table structure resides.
+ * @table_bar_nr is number of base address register corresponding to @table_bar.
+ * @table_offset indicates the offset that the MSI-X table structure starts with
+ * in @table_bar.
+ * @pba_bar is the MemoryRegion that the Pending Bit Array structure resides.
+ * @pba_bar_nr is number of base address register corresponding to @pba_bar.
+ * @pba_offset indicates the offset that the Pending Bit Array structure
+ * starts with in @pba_bar.
+ * Non-zero @cap_pos puts capability MSI-X at that offset in PCI config space.
+ * @errp is for returning errors.
+ *
+ * Return 0 on success; set @errp and return -errno on error:
+ * -ENOTSUP means lacking msi support for a msi-capable platform.
+ * -EINVAL means capability overlap, happens when @cap_pos is non-zero,
+ * also means a programming error, except device assignment, which can check
+ * if a real HW is broken.
+ */
int msix_init(struct PCIDevice *dev, unsigned short nentries,
MemoryRegion *table_bar, uint8_t table_bar_nr,
unsigned table_offset, MemoryRegion *pba_bar,
- uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos)
+ uint8_t pba_bar_nr, unsigned pba_offset, uint8_t cap_pos,
+ Error **errp)
{
int cap;
unsigned table_size, pba_size;
/* Nothing to do if MSI is not supported by interrupt controller */
if (!msi_nonbroken) {
+ error_setg(errp, "MSI-X is not supported by interrupt controller");
return -ENOTSUP;
}
if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) {
+ error_setg(errp, "The number of MSI-X vectors is invalid");
return -EINVAL;
}
table_offset + table_size > memory_region_size(table_bar) ||
pba_offset + pba_size > memory_region_size(pba_bar) ||
(table_offset | pba_offset) & PCI_MSIX_FLAGS_BIRMASK) {
+ error_setg(errp, "table & pba overlap, or they don't fit in BARs,"
+ " or don't align");
return -EINVAL;
}
- cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH);
+ cap = pci_add_capability(dev, PCI_CAP_ID_MSIX,
+ cap_pos, MSIX_CAP_LENGTH, errp);
if (cap < 0) {
return cap;
}
"msix-pba", pba_size);
memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio);
+ dev->msix_prepare_message = msix_prepare_message;
+
return 0;
}
int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries,
- uint8_t bar_nr)
+ uint8_t bar_nr, Error **errp)
{
int ret;
char *name;
uint32_t bar_size = 4096;
uint32_t bar_pba_offset = bar_size / 2;
- uint32_t bar_pba_size = (nentries / 8 + 1) * 8;
+ uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8;
/*
* Migration compatibility dictates that this remains a 4k
ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr,
0, &dev->msix_exclusive_bar,
bar_nr, bar_pba_offset,
- 0);
+ 0, errp);
if (ret) {
return ret;
}
g_free(dev->msix_entry_used);
dev->msix_entry_used = NULL;
dev->cap_present &= ~QEMU_PCI_CAP_MSIX;
+ dev->msix_prepare_message = NULL;
}
void msix_uninit_exclusive_bar(PCIDevice *dev)
}
qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
- qemu_put_buffer(f, dev->msix_pba, (n + 7) / 8);
+ qemu_put_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
}
/* Should be called after restoring the config space. */
msix_clear_all_vectors(dev);
qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE);
- qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8);
+ qemu_get_buffer(f, dev->msix_pba, DIV_ROUND_UP(n, 8));
msix_update_function_masked(dev);
for (vector = 0; vector < n; vector++) {
{
MSIMessage msg;
- if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector])
+ assert(vector < dev->msix_entries_nr);
+
+ if (!dev->msix_entry_used[vector]) {
return;
+ }
+
if (msix_is_masked(dev, vector)) {
msix_set_pending(dev, vector);
return;
}
msix_clear_all_vectors(dev);
dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &=
- ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
+ ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET];
memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE);
memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8);
msix_mask_all(dev, dev->msix_entries_nr);
* don't want to follow the spec suggestion can declare all vectors as used. */
/* Mark vector as used. */
-int msix_vector_use(PCIDevice *dev, unsigned vector)
+void msix_vector_use(PCIDevice *dev, unsigned vector)
{
- if (vector >= dev->msix_entries_nr)
- return -EINVAL;
+ assert(vector < dev->msix_entries_nr);
dev->msix_entry_used[vector]++;
- return 0;
}
/* Mark vector as unused. */
void msix_vector_unuse(PCIDevice *dev, unsigned vector)
{
- if (vector >= dev->msix_entries_nr || !dev->msix_entry_used[vector]) {
+ assert(vector < dev->msix_entries_nr);
+ if (!dev->msix_entry_used[vector]) {
return;
}
if (--dev->msix_entry_used[vector]) {
}
static int put_msix_state(QEMUFile *f, void *pv, size_t size,
- VMStateField *field, QJSON *vmdesc)
+ const VMStateField *field, JSONWriter *vmdesc)
{
msix_save(pv, f);
}
static int get_msix_state(QEMUFile *f, void *pv, size_t size,
- VMStateField *field)
+ const VMStateField *field)
{
msix_load(pv, f);
return 0;