From 393a98924eb00df76231384b86652e1d5f964d67 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 4 Jun 2012 16:56:01 +0200 Subject: msix: drop unused msix_bar_size, require valid bar_size No user in sight for msix_bar_size. bar_size for all users is aligned, let's simply require this instead of trying to fix up invalid input. Signed-off-by: Jan Kiszka Signed-off-by: Michael S. Tsirkin --- hw/msix.c | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) (limited to 'hw/msix.c') diff --git a/hw/msix.c b/hw/msix.c index ded3c55b92..b64f109326 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -55,24 +55,17 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries, { int config_offset; uint8_t *config; - uint32_t new_size; if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) return -EINVAL; if (bar_size > 0x80000000) return -ENOSPC; - /* Add space for MSI-X structures */ - if (!bar_size) { - new_size = MSIX_PAGE_SIZE; - } else if (bar_size < MSIX_PAGE_SIZE) { - bar_size = MSIX_PAGE_SIZE; - new_size = MSIX_PAGE_SIZE * 2; - } else { - new_size = bar_size * 2; + /* Require aligned offset for MSI-X structures */ + if (bar_size & ~(MSIX_PAGE_SIZE - 1)) { + return -EINVAL; } - pdev->msix_bar_size = new_size; config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX, 0, MSIX_CAP_LENGTH); if (config_offset < 0) @@ -382,13 +375,6 @@ int msix_enabled(PCIDevice *dev) MSIX_ENABLE_MASK); } -/* Size of bar where MSI-X table resides, or 0 if MSI-X not supported. */ -uint32_t msix_bar_size(PCIDevice *dev) -{ - return (dev->cap_present & QEMU_PCI_CAP_MSIX) ? - dev->msix_bar_size : 0; -} - /* Send an MSI-X message */ void msix_notify(PCIDevice *dev, unsigned vector) { -- cgit v1.2.3-55-g7522 From 53f949254ad2435bfd45cb0dee96f246a0bdd7e3 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:15:51 -0600 Subject: msix: Add simple BAR allocation MSIX setup functions msi_init() takes over a BAR without really specifying or allowing specification of how it does so. Instead, let's split it into two interfaces, one fully specified, and one trivially easy. This implements the latter. msix_init_exclusive_bar() takes over allocating and filling a PCI BAR _exclusively_ for the use of MSIX. When used, the matching msi_uninit_exclusive_bar() should be used to tear it down. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/msix.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ hw/msix.h | 3 +++ hw/pci.h | 2 ++ 3 files changed, 52 insertions(+) (limited to 'hw/msix.c') diff --git a/hw/msix.c b/hw/msix.c index b64f109326..bafea94084 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -299,6 +299,45 @@ err_config: return ret; } +int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, + uint8_t bar_nr) +{ + int ret; + char *name; + + /* + * Migration compatibility dictates that this remains a 4k + * BAR with the vector table in the lower half and PBA in + * the upper half. Do not use these elsewhere! + */ +#define MSIX_EXCLUSIVE_BAR_SIZE 4096 +#define MSIX_EXCLUSIVE_BAR_PBA_OFFSET (MSIX_EXCLUSIVE_BAR_SIZE / 2) + + if (nentries * PCI_MSIX_ENTRY_SIZE > MSIX_EXCLUSIVE_BAR_PBA_OFFSET) { + return -EINVAL; + } + + if (asprintf(&name, "%s-msix", dev->name) == -1) { + return -ENOMEM; + } + + memory_region_init(&dev->msix_exclusive_bar, name, MSIX_EXCLUSIVE_BAR_SIZE); + + free(name); + + ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr, + MSIX_EXCLUSIVE_BAR_SIZE); + if (ret) { + memory_region_destroy(&dev->msix_exclusive_bar); + return ret; + } + + pci_register_bar(dev, bar_nr, PCI_BASE_ADDRESS_SPACE_MEMORY, + &dev->msix_exclusive_bar); + + return 0; +} + static void msix_free_irq_entries(PCIDevice *dev) { int vector; @@ -329,6 +368,14 @@ int msix_uninit(PCIDevice *dev, MemoryRegion *bar) return 0; } +void msix_uninit_exclusive_bar(PCIDevice *dev) +{ + if (msix_present(dev)) { + msix_uninit(dev, &dev->msix_exclusive_bar); + memory_region_destroy(&dev->msix_exclusive_bar); + } +} + void msix_save(PCIDevice *dev, QEMUFile *f) { unsigned n = dev->msix_entries_nr; diff --git a/hw/msix.h b/hw/msix.h index 4a17f94540..f681bb0855 100644 --- a/hw/msix.h +++ b/hw/msix.h @@ -7,10 +7,13 @@ int msix_init(PCIDevice *pdev, unsigned short nentries, MemoryRegion *bar, unsigned bar_nr, unsigned bar_size); +int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, + uint8_t bar_nr); void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len); int msix_uninit(PCIDevice *d, MemoryRegion *bar); +void msix_uninit_exclusive_bar(PCIDevice *dev); unsigned int msix_nr_vectors_allocated(const PCIDevice *dev); diff --git a/hw/pci.h b/hw/pci.h index 3d534e77ae..7344891706 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -222,6 +222,8 @@ struct PCIDevice { /* Space to store MSIX table */ uint8_t *msix_table_page; + /* MemoryRegion container for msix exclusive BAR setup */ + MemoryRegion msix_exclusive_bar; /* MMIO index used to map MSIX table and pending bit entries. */ MemoryRegion msix_mmio; /* Reference-count for entries actually in use by driver. */ -- cgit v1.2.3-55-g7522 From eebcb0a76a7e8b093740c9bd3db00f5b38e79a33 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:16:19 -0600 Subject: msix: Move msix_mmio_read What's this doing so far from msix_mmio_ops? Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/msix.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'hw/msix.c') diff --git a/hw/msix.c b/hw/msix.c index bafea94084..50885acb91 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -86,16 +86,6 @@ static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries, return 0; } -static uint64_t msix_mmio_read(void *opaque, target_phys_addr_t addr, - unsigned size) -{ - PCIDevice *dev = opaque; - unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; - void *page = dev->msix_table_page; - - return pci_get_long(page + offset); -} - static uint8_t msix_pending_mask(int vector) { return 1 << (vector % 8); @@ -203,6 +193,16 @@ void msix_write_config(PCIDevice *dev, uint32_t addr, } } +static uint64_t msix_mmio_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + PCIDevice *dev = opaque; + unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; + void *page = dev->msix_table_page; + + return pci_get_long(page + offset); +} + static void msix_mmio_write(void *opaque, target_phys_addr_t addr, uint64_t val, unsigned size) { -- cgit v1.2.3-55-g7522 From 2cf62ad74261a9bd90e5b720e726f0404640b16a Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:16:28 -0600 Subject: msix: Note endian TODO item MSIX, like PCI, is little endian. Specifying native is wrong here, but we need to check the rest of the file to determine if it's as simple as flipping this macro. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/msix.c | 1 + 1 file changed, 1 insertion(+) (limited to 'hw/msix.c') diff --git a/hw/msix.c b/hw/msix.c index 50885acb91..87d316a580 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -224,6 +224,7 @@ static void msix_mmio_write(void *opaque, target_phys_addr_t addr, static const MemoryRegionOps msix_mmio_ops = { .read = msix_mmio_read, .write = msix_mmio_write, + /* TODO: MSIX should be LITTLE_ENDIAN. */ .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 4, -- cgit v1.2.3-55-g7522 From d35e428c8400f9ddc07e5a15ff19622c869b9ba0 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:16:37 -0600 Subject: msix: Split PBA into it's own MemoryRegion These don't have to be contiguous. Size them to only what they need and use separate MemoryRegions for the vector table and PBA. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/msix.c | 106 +++++++++++++++++++++++++++++++++++++++----------------------- hw/pci.h | 10 +++--- 2 files changed, 73 insertions(+), 43 deletions(-) (limited to 'hw/msix.c') diff --git a/hw/msix.c b/hw/msix.c index 87d316a580..33121398e8 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -37,7 +37,7 @@ static MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) { - uint8_t *table_entry = dev->msix_table_page + vector * PCI_MSIX_ENTRY_SIZE; + uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; MSIMessage msg; msg.address = pci_get_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR); @@ -93,7 +93,7 @@ static uint8_t msix_pending_mask(int vector) static uint8_t *msix_pending_byte(PCIDevice *dev, int vector) { - return dev->msix_table_page + MSIX_PAGE_PENDING + vector / 8; + return dev->msix_pba + vector / 8; } static int msix_is_pending(PCIDevice *dev, int vector) @@ -114,7 +114,7 @@ static void msix_clr_pending(PCIDevice *dev, int vector) static bool msix_vector_masked(PCIDevice *dev, int vector, bool fmask) { unsigned offset = vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; - return fmask || dev->msix_table_page[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT; + return fmask || dev->msix_table[offset] & PCI_MSIX_ENTRY_CTRL_MASKBIT; } static bool msix_is_masked(PCIDevice *dev, int vector) @@ -193,37 +193,47 @@ void msix_write_config(PCIDevice *dev, uint32_t addr, } } -static uint64_t msix_mmio_read(void *opaque, target_phys_addr_t addr, - unsigned size) +static uint64_t msix_table_mmio_read(void *opaque, target_phys_addr_t addr, + unsigned size) { PCIDevice *dev = opaque; - unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; - void *page = dev->msix_table_page; - return pci_get_long(page + offset); + return pci_get_long(dev->msix_table + addr); } -static void msix_mmio_write(void *opaque, target_phys_addr_t addr, - uint64_t val, unsigned size) +static void msix_table_mmio_write(void *opaque, target_phys_addr_t addr, + uint64_t val, unsigned size) { PCIDevice *dev = opaque; - unsigned int offset = addr & (MSIX_PAGE_SIZE - 1) & ~0x3; - int vector = offset / PCI_MSIX_ENTRY_SIZE; + int vector = addr / PCI_MSIX_ENTRY_SIZE; bool was_masked; - /* MSI-X page includes a read-only PBA and a writeable Vector Control. */ - if (vector >= dev->msix_entries_nr) { - return; - } - was_masked = msix_is_masked(dev, vector); - pci_set_long(dev->msix_table_page + offset, val); + pci_set_long(dev->msix_table + addr, val); msix_handle_mask_update(dev, vector, was_masked); } -static const MemoryRegionOps msix_mmio_ops = { - .read = msix_mmio_read, - .write = msix_mmio_write, +static const MemoryRegionOps msix_table_mmio_ops = { + .read = msix_table_mmio_read, + .write = msix_table_mmio_write, + /* TODO: MSIX should be LITTLE_ENDIAN. */ + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + +static uint64_t msix_pba_mmio_read(void *opaque, target_phys_addr_t addr, + unsigned size) +{ + PCIDevice *dev = opaque; + + return pci_get_long(dev->msix_pba + addr); +} + +static const MemoryRegionOps msix_pba_mmio_ops = { + .read = msix_pba_mmio_read, /* TODO: MSIX should be LITTLE_ENDIAN. */ .endianness = DEVICE_NATIVE_ENDIAN, .valid = { @@ -236,11 +246,14 @@ static void msix_mmio_setup(PCIDevice *d, MemoryRegion *bar) { uint8_t *config = d->config + d->msix_cap; uint32_t table = pci_get_long(config + PCI_MSIX_TABLE); - uint32_t offset = table & ~(MSIX_PAGE_SIZE - 1); + uint32_t table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; + uint32_t pba = pci_get_long(config + PCI_MSIX_PBA); + uint32_t pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; /* TODO: for assigned devices, we'll want to make it possible to map * pending bits separately in case they are in a separate bar. */ - memory_region_add_subregion(bar, offset, &d->msix_mmio); + memory_region_add_subregion(bar, table_offset, &d->msix_table_mmio); + memory_region_add_subregion(bar, pba_offset, &d->msix_pba_mmio); } static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) @@ -252,7 +265,7 @@ static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) vector * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; bool was_masked = msix_is_masked(dev, vector); - dev->msix_table_page[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT; + dev->msix_table[offset] |= PCI_MSIX_ENTRY_CTRL_MASKBIT; msix_handle_mask_update(dev, vector, was_masked); } } @@ -264,6 +277,7 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, unsigned bar_nr, unsigned bar_size) { int ret; + unsigned table_size, pba_size; /* Nothing to do if MSI is not supported by interrupt controller */ if (!msi_supported) { @@ -272,14 +286,20 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, if (nentries > MSIX_MAX_ENTRIES) return -EINVAL; + table_size = nentries * PCI_MSIX_ENTRY_SIZE; + pba_size = QEMU_ALIGN_UP(nentries, 64) / 8; + dev->msix_entry_used = g_malloc0(MSIX_MAX_ENTRIES * sizeof *dev->msix_entry_used); - dev->msix_table_page = g_malloc0(MSIX_PAGE_SIZE); + dev->msix_table = g_malloc0(table_size); + dev->msix_pba = g_malloc0(pba_size); msix_mask_all(dev, nentries); - memory_region_init_io(&dev->msix_mmio, &msix_mmio_ops, dev, - "msix", MSIX_PAGE_SIZE); + memory_region_init_io(&dev->msix_table_mmio, &msix_table_mmio_ops, dev, + "msix-table", table_size); + memory_region_init_io(&dev->msix_pba_mmio, &msix_pba_mmio_ops, dev, + "msix-pba", pba_size); dev->msix_entries_nr = nentries; ret = msix_add_config(dev, nentries, bar_nr, bar_size); @@ -292,9 +312,12 @@ int msix_init(struct PCIDevice *dev, unsigned short nentries, err_config: dev->msix_entries_nr = 0; - memory_region_destroy(&dev->msix_mmio); - g_free(dev->msix_table_page); - dev->msix_table_page = NULL; + memory_region_destroy(&dev->msix_pba_mmio); + g_free(dev->msix_pba); + dev->msix_pba = NULL; + memory_region_destroy(&dev->msix_table_mmio); + g_free(dev->msix_table); + dev->msix_table = NULL; g_free(dev->msix_entry_used); dev->msix_entry_used = NULL; return ret; @@ -359,10 +382,14 @@ int msix_uninit(PCIDevice *dev, MemoryRegion *bar) dev->msix_cap = 0; msix_free_irq_entries(dev); dev->msix_entries_nr = 0; - memory_region_del_subregion(bar, &dev->msix_mmio); - memory_region_destroy(&dev->msix_mmio); - g_free(dev->msix_table_page); - dev->msix_table_page = NULL; + memory_region_del_subregion(bar, &dev->msix_pba_mmio); + memory_region_destroy(&dev->msix_pba_mmio); + g_free(dev->msix_pba); + dev->msix_pba = NULL; + memory_region_del_subregion(bar, &dev->msix_table_mmio); + memory_region_destroy(&dev->msix_table_mmio); + g_free(dev->msix_table); + dev->msix_table = NULL; g_free(dev->msix_entry_used); dev->msix_entry_used = NULL; dev->cap_present &= ~QEMU_PCI_CAP_MSIX; @@ -385,8 +412,8 @@ void msix_save(PCIDevice *dev, QEMUFile *f) return; } - qemu_put_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE); - qemu_put_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8); + qemu_put_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); + qemu_put_buffer(f, dev->msix_pba, (n + 7) / 8); } /* Should be called after restoring the config space. */ @@ -400,8 +427,8 @@ void msix_load(PCIDevice *dev, QEMUFile *f) } msix_free_irq_entries(dev); - qemu_get_buffer(f, dev->msix_table_page, n * PCI_MSIX_ENTRY_SIZE); - qemu_get_buffer(f, dev->msix_table_page + MSIX_PAGE_PENDING, (n + 7) / 8); + qemu_get_buffer(f, dev->msix_table, n * PCI_MSIX_ENTRY_SIZE); + qemu_get_buffer(f, dev->msix_pba, (n + 7) / 8); msix_update_function_masked(dev); for (vector = 0; vector < n; vector++) { @@ -448,7 +475,8 @@ void msix_reset(PCIDevice *dev) msix_free_irq_entries(dev); dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &= ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; - memset(dev->msix_table_page, 0, MSIX_PAGE_SIZE); + 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); } diff --git a/hw/pci.h b/hw/pci.h index 7344891706..44ae8715b0 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -220,12 +220,14 @@ struct PCIDevice { /* MSI-X entries */ int msix_entries_nr; - /* Space to store MSIX table */ - uint8_t *msix_table_page; + /* Space to store MSIX table & pending bit array */ + uint8_t *msix_table; + uint8_t *msix_pba; /* MemoryRegion container for msix exclusive BAR setup */ MemoryRegion msix_exclusive_bar; - /* MMIO index used to map MSIX table and pending bit entries. */ - MemoryRegion msix_mmio; + /* Memory Regions for MSIX table and pending bit entries. */ + MemoryRegion msix_table_mmio; + MemoryRegion msix_pba_mmio; /* Reference-count for entries actually in use by driver. */ unsigned *msix_entry_used; /* MSIX function mask set or MSIX disabled */ -- cgit v1.2.3-55-g7522 From 5a2c20298196e1eea212ca0fb6d0f68869a1b86d Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:16:47 -0600 Subject: msix: Allow full specification of MSIX layout Finally, complete the fully specified interface. msix_add_config() gets folded into msix_init() because we now have quite a few parameters to pass and rolling it in let's us error earlier, avoiding the ugly unwind exit path. msix_mmio_setup() also gets rolled in, just because it's redundant to rediscover offsets when we already have them for such a tiny function. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/msix.c | 145 ++++++++++++++++++++++---------------------------------------- hw/msix.h | 10 +++-- 2 files changed, 56 insertions(+), 99 deletions(-) (limited to 'hw/msix.c') diff --git a/hw/msix.c b/hw/msix.c index 33121398e8..15f8d7dfa7 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -27,14 +27,6 @@ #define MSIX_ENABLE_MASK (PCI_MSIX_FLAGS_ENABLE >> 8) #define MSIX_MASKALL_MASK (PCI_MSIX_FLAGS_MASKALL >> 8) -/* How much space does an MSIX table need. */ -/* The spec requires giving the table structure - * a 4K aligned region all by itself. */ -#define MSIX_PAGE_SIZE 0x1000 -/* Reserve second half of the page for pending bits */ -#define MSIX_PAGE_PENDING (MSIX_PAGE_SIZE / 2) -#define MSIX_MAX_ENTRIES 32 - static MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) { uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; @@ -45,47 +37,6 @@ static MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) return msg; } -/* Add MSI-X capability to the config space for the device. */ -/* Given a bar and its size, add MSI-X table on top of it - * and fill MSI-X capability in the config space. - * Original bar size must be a power of 2 or 0. - * New bar size is returned. */ -static int msix_add_config(struct PCIDevice *pdev, unsigned short nentries, - unsigned bar_nr, unsigned bar_size) -{ - int config_offset; - uint8_t *config; - - if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) - return -EINVAL; - if (bar_size > 0x80000000) - return -ENOSPC; - - /* Require aligned offset for MSI-X structures */ - if (bar_size & ~(MSIX_PAGE_SIZE - 1)) { - return -EINVAL; - } - - config_offset = pci_add_capability(pdev, PCI_CAP_ID_MSIX, - 0, MSIX_CAP_LENGTH); - if (config_offset < 0) - return config_offset; - config = pdev->config + config_offset; - - pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1); - /* Table on top of BAR */ - pci_set_long(config + PCI_MSIX_TABLE, bar_size | bar_nr); - /* Pending bits on top of that */ - pci_set_long(config + PCI_MSIX_PBA, (bar_size + MSIX_PAGE_PENDING) | - bar_nr); - pdev->msix_cap = config_offset; - /* Make flags bit writable. */ - pdev->wmask[config_offset + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | - MSIX_MASKALL_MASK; - pdev->msix_function_masked = true; - return 0; -} - static uint8_t msix_pending_mask(int vector) { return 1 << (vector % 8); @@ -242,20 +193,6 @@ static const MemoryRegionOps msix_pba_mmio_ops = { }, }; -static void msix_mmio_setup(PCIDevice *d, MemoryRegion *bar) -{ - uint8_t *config = d->config + d->msix_cap; - uint32_t table = pci_get_long(config + PCI_MSIX_TABLE); - uint32_t table_offset = table & ~PCI_MSIX_FLAGS_BIRMASK; - uint32_t pba = pci_get_long(config + PCI_MSIX_PBA); - uint32_t pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; - /* TODO: for assigned devices, we'll want to make it possible to map - * pending bits separately in case they are in a separate bar. */ - - memory_region_add_subregion(bar, table_offset, &d->msix_table_mmio); - memory_region_add_subregion(bar, pba_offset, &d->msix_pba_mmio); -} - static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) { int vector; @@ -270,57 +207,71 @@ static void msix_mask_all(struct PCIDevice *dev, unsigned nentries) } } -/* Initialize the MSI-X structures. Note: if MSI-X is supported, BAR size is - * modified, it should be retrieved with msix_bar_size. */ +/* Initialize the MSI-X structures */ int msix_init(struct PCIDevice *dev, unsigned short nentries, - MemoryRegion *bar, - unsigned bar_nr, unsigned bar_size) + 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) { - int ret; + int cap; unsigned table_size, pba_size; + uint8_t *config; /* Nothing to do if MSI is not supported by interrupt controller */ if (!msi_supported) { return -ENOTSUP; } - if (nentries > MSIX_MAX_ENTRIES) + + if (nentries < 1 || nentries > PCI_MSIX_FLAGS_QSIZE + 1) { return -EINVAL; + } table_size = nentries * PCI_MSIX_ENTRY_SIZE; pba_size = QEMU_ALIGN_UP(nentries, 64) / 8; - dev->msix_entry_used = g_malloc0(MSIX_MAX_ENTRIES * - sizeof *dev->msix_entry_used); + /* Sanity test: table & pba don't overlap, fit within BARs, min aligned */ + if ((table_bar_nr == pba_bar_nr && + ranges_overlap(table_offset, table_size, pba_offset, pba_size)) || + 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) { + return -EINVAL; + } + + cap = pci_add_capability(dev, PCI_CAP_ID_MSIX, cap_pos, MSIX_CAP_LENGTH); + if (cap < 0) { + return cap; + } + + dev->msix_cap = cap; + dev->cap_present |= QEMU_PCI_CAP_MSIX; + config = dev->config + cap; + + pci_set_word(config + PCI_MSIX_FLAGS, nentries - 1); + dev->msix_entries_nr = nentries; + dev->msix_function_masked = true; + + pci_set_long(config + PCI_MSIX_TABLE, table_offset | table_bar_nr); + pci_set_long(config + PCI_MSIX_PBA, pba_offset | pba_bar_nr); + + /* Make flags bit writable. */ + dev->wmask[cap + MSIX_CONTROL_OFFSET] |= MSIX_ENABLE_MASK | + MSIX_MASKALL_MASK; dev->msix_table = g_malloc0(table_size); dev->msix_pba = g_malloc0(pba_size); + dev->msix_entry_used = g_malloc0(nentries * sizeof *dev->msix_entry_used); + msix_mask_all(dev, nentries); memory_region_init_io(&dev->msix_table_mmio, &msix_table_mmio_ops, dev, "msix-table", table_size); + memory_region_add_subregion(table_bar, table_offset, &dev->msix_table_mmio); memory_region_init_io(&dev->msix_pba_mmio, &msix_pba_mmio_ops, dev, "msix-pba", pba_size); + memory_region_add_subregion(pba_bar, pba_offset, &dev->msix_pba_mmio); - dev->msix_entries_nr = nentries; - ret = msix_add_config(dev, nentries, bar_nr, bar_size); - if (ret) - goto err_config; - - dev->cap_present |= QEMU_PCI_CAP_MSIX; - msix_mmio_setup(dev, bar); return 0; - -err_config: - dev->msix_entries_nr = 0; - memory_region_destroy(&dev->msix_pba_mmio); - g_free(dev->msix_pba); - dev->msix_pba = NULL; - memory_region_destroy(&dev->msix_table_mmio); - g_free(dev->msix_table); - dev->msix_table = NULL; - g_free(dev->msix_entry_used); - dev->msix_entry_used = NULL; - return ret; } int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, @@ -335,7 +286,9 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, * the upper half. Do not use these elsewhere! */ #define MSIX_EXCLUSIVE_BAR_SIZE 4096 +#define MSIX_EXCLUSIVE_BAR_TABLE_OFFSET 0 #define MSIX_EXCLUSIVE_BAR_PBA_OFFSET (MSIX_EXCLUSIVE_BAR_SIZE / 2) +#define MSIX_EXCLUSIVE_CAP_OFFSET 0 if (nentries * PCI_MSIX_ENTRY_SIZE > MSIX_EXCLUSIVE_BAR_PBA_OFFSET) { return -EINVAL; @@ -350,7 +303,9 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, free(name); ret = msix_init(dev, nentries, &dev->msix_exclusive_bar, bar_nr, - MSIX_EXCLUSIVE_BAR_SIZE); + MSIX_EXCLUSIVE_BAR_TABLE_OFFSET, &dev->msix_exclusive_bar, + bar_nr, MSIX_EXCLUSIVE_BAR_PBA_OFFSET, + MSIX_EXCLUSIVE_CAP_OFFSET); if (ret) { memory_region_destroy(&dev->msix_exclusive_bar); return ret; @@ -373,7 +328,7 @@ static void msix_free_irq_entries(PCIDevice *dev) } /* Clean up resources for the device. */ -int msix_uninit(PCIDevice *dev, MemoryRegion *bar) +int msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) { if (!msix_present(dev)) { return 0; @@ -382,11 +337,11 @@ int msix_uninit(PCIDevice *dev, MemoryRegion *bar) dev->msix_cap = 0; msix_free_irq_entries(dev); dev->msix_entries_nr = 0; - memory_region_del_subregion(bar, &dev->msix_pba_mmio); + memory_region_del_subregion(pba_bar, &dev->msix_pba_mmio); memory_region_destroy(&dev->msix_pba_mmio); g_free(dev->msix_pba); dev->msix_pba = NULL; - memory_region_del_subregion(bar, &dev->msix_table_mmio); + memory_region_del_subregion(table_bar, &dev->msix_table_mmio); memory_region_destroy(&dev->msix_table_mmio); g_free(dev->msix_table); dev->msix_table = NULL; @@ -399,7 +354,7 @@ int msix_uninit(PCIDevice *dev, MemoryRegion *bar) void msix_uninit_exclusive_bar(PCIDevice *dev) { if (msix_present(dev)) { - msix_uninit(dev, &dev->msix_exclusive_bar); + msix_uninit(dev, &dev->msix_exclusive_bar, &dev->msix_exclusive_bar); memory_region_destroy(&dev->msix_exclusive_bar); } } diff --git a/hw/msix.h b/hw/msix.h index f681bb0855..f637797b8f 100644 --- a/hw/msix.h +++ b/hw/msix.h @@ -4,15 +4,17 @@ #include "qemu-common.h" #include "pci.h" -int msix_init(PCIDevice *pdev, unsigned short nentries, - MemoryRegion *bar, - unsigned bar_nr, unsigned bar_size); +int msix_init(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); int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, uint8_t bar_nr); void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len); -int msix_uninit(PCIDevice *d, MemoryRegion *bar); +int msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, + MemoryRegion *pba_bar); void msix_uninit_exclusive_bar(PCIDevice *dev); unsigned int msix_nr_vectors_allocated(const PCIDevice *dev); -- cgit v1.2.3-55-g7522 From 572992eefa74bfb92c24a28bd268de91a9311b0f Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:16:57 -0600 Subject: msix: Switch msix_uninit to return void It can't fail. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/msix.c | 6 +++--- hw/msix.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'hw/msix.c') diff --git a/hw/msix.c b/hw/msix.c index 15f8d7dfa7..fd9ea95da1 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -328,10 +328,10 @@ static void msix_free_irq_entries(PCIDevice *dev) } /* Clean up resources for the device. */ -int msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) +void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) { if (!msix_present(dev)) { - return 0; + return; } pci_del_capability(dev, PCI_CAP_ID_MSIX, MSIX_CAP_LENGTH); dev->msix_cap = 0; @@ -348,7 +348,7 @@ int msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, MemoryRegion *pba_bar) g_free(dev->msix_entry_used); dev->msix_entry_used = NULL; dev->cap_present &= ~QEMU_PCI_CAP_MSIX; - return 0; + return; } void msix_uninit_exclusive_bar(PCIDevice *dev) diff --git a/hw/msix.h b/hw/msix.h index f637797b8f..1786e2766b 100644 --- a/hw/msix.h +++ b/hw/msix.h @@ -13,8 +13,8 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len); -int msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, - MemoryRegion *pba_bar); +void msix_uninit(PCIDevice *dev, MemoryRegion *table_bar, + MemoryRegion *pba_bar); void msix_uninit_exclusive_bar(PCIDevice *dev); unsigned int msix_nr_vectors_allocated(const PCIDevice *dev); -- cgit v1.2.3-55-g7522 From 932d4a42afa28829fadf3cbfbb0507cc09aafd8b Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 19 Jul 2012 10:35:07 +1000 Subject: msi/msix: added API to set MSI message address and data Added (msi|msix)_set_message() function for whoever might want to use them. Currently msi_notify()/msix_notify() write to these vectors to signal the guest about an interrupt so the correct values have to written there by the guest or QEMU. For example, POWER guest never initializes MSI/MSIX vectors, instead it uses RTAS hypercalls. So in order to support MSIX for virtio-pci on POWER we have to initialize MSI/MSIX message from QEMU. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Michael S. Tsirkin --- hw/msi.c | 17 +++++++++++++++++ hw/msi.h | 1 + hw/msix.c | 13 +++++++++++++ hw/msix.h | 1 + 4 files changed, 32 insertions(+) (limited to 'hw/msix.c') diff --git a/hw/msi.c b/hw/msi.c index 52332041e7..e2273a09ae 100644 --- a/hw/msi.c +++ b/hw/msi.c @@ -105,6 +105,23 @@ static inline uint8_t msi_pending_off(const PCIDevice* dev, bool msi64bit) return dev->msi_cap + (msi64bit ? PCI_MSI_PENDING_64 : PCI_MSI_PENDING_32); } +/* + * Special API for POWER to configure the vectors through + * a side channel. Should never be used by devices. + */ +void msi_set_message(PCIDevice *dev, MSIMessage msg) +{ + uint16_t flags = pci_get_word(dev->config + msi_flags_off(dev)); + bool msi64bit = flags & PCI_MSI_FLAGS_64BIT; + + if (msi64bit) { + pci_set_quad(dev->config + msi_address_lo_off(dev), msg.address); + } else { + pci_set_long(dev->config + msi_address_lo_off(dev), msg.address); + } + pci_set_word(dev->config + msi_data_off(dev, msi64bit), msg.data); +} + bool msi_enabled(const PCIDevice *dev) { return msi_present(dev) && diff --git a/hw/msi.h b/hw/msi.h index 75747abc25..6ec1f99f80 100644 --- a/hw/msi.h +++ b/hw/msi.h @@ -31,6 +31,7 @@ struct MSIMessage { extern bool msi_supported; +void msi_set_message(PCIDevice *dev, MSIMessage msg); bool msi_enabled(const PCIDevice *dev); int msi_init(struct PCIDevice *dev, uint8_t offset, unsigned int nr_vectors, bool msi64bit, bool msi_per_vector_mask); diff --git a/hw/msix.c b/hw/msix.c index fd9ea95da1..800fc32f0b 100644 --- a/hw/msix.c +++ b/hw/msix.c @@ -37,6 +37,19 @@ static MSIMessage msix_get_message(PCIDevice *dev, unsigned vector) return msg; } +/* + * Special API for POWER to configure the vectors through + * a side channel. Should never be used by devices. + */ +void msix_set_message(PCIDevice *dev, int vector, struct MSIMessage msg) +{ + uint8_t *table_entry = dev->msix_table + vector * PCI_MSIX_ENTRY_SIZE; + + pci_set_quad(table_entry + PCI_MSIX_ENTRY_LOWER_ADDR, msg.address); + pci_set_long(table_entry + PCI_MSIX_ENTRY_DATA, msg.data); + table_entry[PCI_MSIX_ENTRY_VECTOR_CTRL] &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; +} + static uint8_t msix_pending_mask(int vector) { return 1 << (vector % 8); diff --git a/hw/msix.h b/hw/msix.h index 1786e2766b..15211cb592 100644 --- a/hw/msix.h +++ b/hw/msix.h @@ -4,6 +4,7 @@ #include "qemu-common.h" #include "pci.h" +void msix_set_message(PCIDevice *dev, int vector, MSIMessage msg); int msix_init(PCIDevice *dev, unsigned short nentries, MemoryRegion *table_bar, uint8_t table_bar_nr, unsigned table_offset, MemoryRegion *pba_bar, -- cgit v1.2.3-55-g7522