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 +++----------------- hw/msix.h | 2 -- hw/pci.h | 2 -- 3 files changed, 3 insertions(+), 21 deletions(-) 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) { diff --git a/hw/msix.h b/hw/msix.h index 50aee8221a..e5a488df0c 100644 --- a/hw/msix.h +++ b/hw/msix.h @@ -21,8 +21,6 @@ void msix_load(PCIDevice *dev, QEMUFile *f); int msix_enabled(PCIDevice *dev); int msix_present(PCIDevice *dev); -uint32_t msix_bar_size(PCIDevice *dev); - int msix_vector_use(PCIDevice *dev, unsigned vector); void msix_vector_unuse(PCIDevice *dev, unsigned vector); void msix_unuse_all_vectors(PCIDevice *dev); diff --git a/hw/pci.h b/hw/pci.h index c3cacce046..3d534e77ae 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -226,8 +226,6 @@ struct PCIDevice { MemoryRegion msix_mmio; /* Reference-count for entries actually in use by driver. */ unsigned *msix_entry_used; - /* Region including the MSI-X table */ - uint32_t msix_bar_size; /* MSIX function mask set or MSIX disabled */ bool msix_function_masked; /* Version id needed for VMState */ -- cgit v1.2.1 From 118f2c2b48b9758a3a2967868b337eb388d1531b Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 13 Jun 2012 22:52:06 -0600 Subject: msix: fix PCIDevice naming inconsistency msix.h calls the PCIDevice * parameter "dev" almost everywhere except the msix_write_config declaration. Fix the inconsistency. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/msix.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/msix.h b/hw/msix.h index e5a488df0c..4a17f94540 100644 --- a/hw/msix.h +++ b/hw/msix.h @@ -8,8 +8,7 @@ int msix_init(PCIDevice *pdev, unsigned short nentries, MemoryRegion *bar, unsigned bar_nr, unsigned bar_size); -void msix_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len); +void msix_write_config(PCIDevice *dev, uint32_t address, uint32_t val, int len); int msix_uninit(PCIDevice *d, MemoryRegion *bar); -- cgit v1.2.1 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(+) 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.1 From 1116b5392129a9961335c2b64e63003736e3c2df Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:16:01 -0600 Subject: ivshmem: Convert to msix_init_exclusive_bar() interface Trivial conversion, failed to have an uninit before and after. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/ivshmem.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/ivshmem.c b/hw/ivshmem.c index 05559b639c..8b49eee8e9 100644 --- a/hw/ivshmem.c +++ b/hw/ivshmem.c @@ -70,7 +70,6 @@ typedef struct IVShmemState { */ MemoryRegion bar; MemoryRegion ivshmem; - MemoryRegion msix_bar; uint64_t ivshmem_size; /* size of shared memory region */ int shm_fd; /* shared memory file descriptor */ @@ -563,16 +562,13 @@ static uint64_t ivshmem_get_size(IVShmemState * s) { static void ivshmem_setup_msi(IVShmemState * s) { - memory_region_init(&s->msix_bar, "ivshmem-msix", 4096); - if (!msix_init(&s->dev, s->vectors, &s->msix_bar, 1, 0)) { - pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, - &s->msix_bar); - IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); - } else { + if (msix_init_exclusive_bar(&s->dev, s->vectors, 1)) { IVSHMEM_DPRINTF("msix initialization failed\n"); exit(1); } + IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); + /* allocate QEMU char devices for receiving interrupts */ s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry)); -- cgit v1.2.1 From b2357c484d3b3e2afd305b71fb5bc0b0b8d61ca8 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Thu, 14 Jun 2012 12:16:10 -0600 Subject: virtio: Convert to msix_init_exclusive_bar() interface Simple conversion. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/virtio-pci.c | 15 +++++---------- hw/virtio-pci.h | 1 - 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 9342eed070..3dca37ff0c 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -782,13 +782,10 @@ void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev) pci_set_word(config + PCI_SUBSYSTEM_ID, vdev->device_id); config[PCI_INTERRUPT_PIN] = 1; - memory_region_init(&proxy->msix_bar, "virtio-msix", 4096); - if (vdev->nvectors && !msix_init(&proxy->pci_dev, vdev->nvectors, - &proxy->msix_bar, 1, 0)) { - pci_register_bar(&proxy->pci_dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, - &proxy->msix_bar); - } else + if (vdev->nvectors && + msix_init_exclusive_bar(&proxy->pci_dev, vdev->nvectors, 1)) { vdev->nvectors = 0; + } proxy->pci_dev.config_write = virtio_write_config; @@ -834,12 +831,10 @@ static int virtio_blk_init_pci(PCIDevice *pci_dev) static int virtio_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - int r; memory_region_destroy(&proxy->bar); - r = msix_uninit(pci_dev, &proxy->msix_bar); - memory_region_destroy(&proxy->msix_bar); - return r; + msix_uninit_exclusive_bar(pci_dev); + return 0; } static int virtio_blk_exit_pci(PCIDevice *pci_dev) diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h index 91b791ba9d..ac9d522f37 100644 --- a/hw/virtio-pci.h +++ b/hw/virtio-pci.h @@ -34,7 +34,6 @@ typedef struct { PCIDevice pci_dev; VirtIODevice *vdev; MemoryRegion bar; - MemoryRegion msix_bar; uint32_t flags; uint32_t class_code; uint32_t nvectors; -- cgit v1.2.1 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(-) 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.1 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(+) 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.1 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(-) 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.1 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(-) 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.1 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(-) 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.1 From f90c2bcdbc69e41e575f868b984c3e2de8f51bac Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 3 Jul 2012 22:39:27 -0600 Subject: pci: convert PCIUnregisterFunc to void Not a single driver has any possibility of failure on their exit function, let's keep it that way. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/ac97.c | 3 +-- hw/e1000.c | 3 +-- hw/eepro100.c | 3 +-- hw/es1370.c | 3 +-- hw/ide/cmd646.c | 4 +--- hw/ide/ich.c | 4 +--- hw/ide/piix.c | 4 +--- hw/ide/via.c | 4 +--- hw/intel-hda.c | 3 +-- hw/ioh3420.c | 8 +++----- hw/ivshmem.c | 4 +--- hw/lsi53c895a.c | 4 +--- hw/ne2000.c | 3 +-- hw/pci.c | 8 +++----- hw/pci.h | 2 +- hw/pci_bridge.c | 3 +-- hw/pci_bridge.h | 2 +- hw/pci_bridge_dev.c | 13 +++++-------- hw/pcnet-pci.c | 3 +-- hw/rtl8139.c | 3 +-- hw/usb/hcd-uhci.c | 3 +-- hw/virtio-pci.c | 23 +++++++++++------------ hw/wdt_i6300esb.c | 4 +--- hw/xio3130_downstream.c | 8 +++----- hw/xio3130_upstream.c | 8 +++----- 25 files changed, 47 insertions(+), 83 deletions(-) diff --git a/hw/ac97.c b/hw/ac97.c index e791b9d3e6..0f561fa5c1 100644 --- a/hw/ac97.c +++ b/hw/ac97.c @@ -1319,13 +1319,12 @@ static int ac97_initfn (PCIDevice *dev) return 0; } -static int ac97_exitfn (PCIDevice *dev) +static void ac97_exitfn (PCIDevice *dev) { AC97LinkState *s = DO_UPCAST (AC97LinkState, dev, dev); memory_region_destroy (&s->io_nam); memory_region_destroy (&s->io_nabm); - return 0; } int ac97_init (PCIBus *bus) diff --git a/hw/e1000.c b/hw/e1000.c index 4573f1301e..6c5bc44e8c 100644 --- a/hw/e1000.c +++ b/hw/e1000.c @@ -1192,7 +1192,7 @@ e1000_cleanup(VLANClientState *nc) s->nic = NULL; } -static int +static void pci_e1000_uninit(PCIDevice *dev) { E1000State *d = DO_UPCAST(E1000State, dev, dev); @@ -1202,7 +1202,6 @@ pci_e1000_uninit(PCIDevice *dev) memory_region_destroy(&d->mmio); memory_region_destroy(&d->io); qemu_del_vlan_client(&d->nic->nc); - return 0; } static NetClientInfo net_e1000_info = { diff --git a/hw/eepro100.c b/hw/eepro100.c index 6279ae36ec..9745ad592e 100644 --- a/hw/eepro100.c +++ b/hw/eepro100.c @@ -1831,7 +1831,7 @@ static void nic_cleanup(VLANClientState *nc) s->nic = NULL; } -static int pci_nic_uninit(PCIDevice *pci_dev) +static void pci_nic_uninit(PCIDevice *pci_dev) { EEPRO100State *s = DO_UPCAST(EEPRO100State, dev, pci_dev); @@ -1841,7 +1841,6 @@ static int pci_nic_uninit(PCIDevice *pci_dev) vmstate_unregister(&pci_dev->qdev, s->vmstate, s); eeprom93xx_free(&pci_dev->qdev, s->eeprom); qemu_del_vlan_client(&s->nic->nc); - return 0; } static NetClientInfo net_eepro100_info = { diff --git a/hw/es1370.c b/hw/es1370.c index 573f747362..e34234c350 100644 --- a/hw/es1370.c +++ b/hw/es1370.c @@ -1018,12 +1018,11 @@ static int es1370_initfn (PCIDevice *dev) return 0; } -static int es1370_exitfn (PCIDevice *dev) +static void es1370_exitfn (PCIDevice *dev) { ES1370State *s = DO_UPCAST (ES1370State, dev, dev); memory_region_destroy (&s->io); - return 0; } int es1370_init (PCIBus *bus) diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index bf8ece4708..4ff3624aa9 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -295,7 +295,7 @@ static int pci_cmd646_ide_initfn(PCIDevice *dev) return 0; } -static int pci_cmd646_ide_exitfn(PCIDevice *dev) +static void pci_cmd646_ide_exitfn(PCIDevice *dev) { PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); unsigned i; @@ -309,8 +309,6 @@ static int pci_cmd646_ide_exitfn(PCIDevice *dev) memory_region_destroy(&d->cmd646_bar[i].data); } memory_region_destroy(&d->bmdma_bar); - - return 0; } void pci_cmd646_ide_init(PCIBus *bus, DriveInfo **hd_table, diff --git a/hw/ide/ich.c b/hw/ide/ich.c index e3eaaea882..c3a425b22f 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -132,15 +132,13 @@ static int pci_ich9_ahci_init(PCIDevice *dev) return 0; } -static int pci_ich9_uninit(PCIDevice *dev) +static void pci_ich9_uninit(PCIDevice *dev) { struct AHCIPCIState *d; d = DO_UPCAST(struct AHCIPCIState, card, dev); msi_uninit(dev); ahci_uninit(&d->ahci); - - return 0; } static void ich_ahci_class_init(ObjectClass *klass, void *data) diff --git a/hw/ide/piix.c b/hw/ide/piix.c index bcaa400e2d..455c1b2698 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -199,7 +199,7 @@ PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) return dev; } -static int pci_piix_ide_exitfn(PCIDevice *dev) +static void pci_piix_ide_exitfn(PCIDevice *dev) { PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); unsigned i; @@ -211,8 +211,6 @@ static int pci_piix_ide_exitfn(PCIDevice *dev) memory_region_destroy(&d->bmdma[i].addr_ioport); } memory_region_destroy(&d->bmdma_bar); - - return 0; } /* hd_table must contain 4 block drivers */ diff --git a/hw/ide/via.c b/hw/ide/via.c index eec5136019..3e25085d42 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -189,7 +189,7 @@ static int vt82c686b_ide_initfn(PCIDevice *dev) return 0; } -static int vt82c686b_ide_exitfn(PCIDevice *dev) +static void vt82c686b_ide_exitfn(PCIDevice *dev) { PCIIDEState *d = DO_UPCAST(PCIIDEState, dev, dev); unsigned i; @@ -201,8 +201,6 @@ static int vt82c686b_ide_exitfn(PCIDevice *dev) memory_region_destroy(&d->bmdma[i].addr_ioport); } memory_region_destroy(&d->bmdma_bar); - - return 0; } void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) diff --git a/hw/intel-hda.c b/hw/intel-hda.c index 8f3b70bd14..04bed5e894 100644 --- a/hw/intel-hda.c +++ b/hw/intel-hda.c @@ -1144,13 +1144,12 @@ static int intel_hda_init(PCIDevice *pci) return 0; } -static int intel_hda_exit(PCIDevice *pci) +static void intel_hda_exit(PCIDevice *pci) { IntelHDAState *d = DO_UPCAST(IntelHDAState, pci, pci); msi_uninit(&d->pci); memory_region_destroy(&d->mmio); - return 0; } static int intel_hda_post_load(void *opaque, int version) diff --git a/hw/ioh3420.c b/hw/ioh3420.c index 0a2601cac4..94a537c9b3 100644 --- a/hw/ioh3420.c +++ b/hw/ioh3420.c @@ -96,7 +96,6 @@ static int ioh3420_initfn(PCIDevice *d) PCIEPort *p = DO_UPCAST(PCIEPort, br, br); PCIESlot *s = DO_UPCAST(PCIESlot, port, p); int rc; - int tmp; rc = pci_bridge_initfn(d); if (rc < 0) { @@ -144,12 +143,11 @@ err_pcie_cap: err_msi: msi_uninit(d); err_bridge: - tmp = pci_bridge_exitfn(d); - assert(!tmp); + pci_bridge_exitfn(d); return rc; } -static int ioh3420_exitfn(PCIDevice *d) +static void ioh3420_exitfn(PCIDevice *d) { PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); PCIEPort *p = DO_UPCAST(PCIEPort, br, br); @@ -159,7 +157,7 @@ static int ioh3420_exitfn(PCIDevice *d) pcie_chassis_del_slot(s); pcie_cap_exit(d); msi_uninit(d); - return pci_bridge_exitfn(d); + pci_bridge_exitfn(d); } PCIESlot *ioh3420_init(PCIBus *bus, int devfn, bool multifunction, diff --git a/hw/ivshmem.c b/hw/ivshmem.c index 8b49eee8e9..7d4123cc38 100644 --- a/hw/ivshmem.c +++ b/hw/ivshmem.c @@ -760,7 +760,7 @@ static int pci_ivshmem_init(PCIDevice *dev) return 0; } -static int pci_ivshmem_uninit(PCIDevice *dev) +static void pci_ivshmem_uninit(PCIDevice *dev) { IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); @@ -775,8 +775,6 @@ static int pci_ivshmem_uninit(PCIDevice *dev) memory_region_destroy(&s->ivshmem); memory_region_destroy(&s->bar); unregister_savevm(&dev->qdev, "ivshmem", s); - - return 0; } static Property ivshmem_properties[] = { diff --git a/hw/lsi53c895a.c b/hw/lsi53c895a.c index f022a02447..9205f659fe 100644 --- a/hw/lsi53c895a.c +++ b/hw/lsi53c895a.c @@ -2070,15 +2070,13 @@ static const VMStateDescription vmstate_lsi_scsi = { } }; -static int lsi_scsi_uninit(PCIDevice *d) +static void lsi_scsi_uninit(PCIDevice *d) { LSIState *s = DO_UPCAST(LSIState, dev, d); memory_region_destroy(&s->mmio_io); memory_region_destroy(&s->ram_io); memory_region_destroy(&s->io_io); - - return 0; } static const struct SCSIBusInfo lsi_scsi_info = { diff --git a/hw/ne2000.c b/hw/ne2000.c index d02e60c4a6..afadbdbac1 100644 --- a/hw/ne2000.c +++ b/hw/ne2000.c @@ -744,14 +744,13 @@ static int pci_ne2000_init(PCIDevice *pci_dev) return 0; } -static int pci_ne2000_exit(PCIDevice *pci_dev) +static void pci_ne2000_exit(PCIDevice *pci_dev) { PCINE2000State *d = DO_UPCAST(PCINE2000State, dev, pci_dev); NE2000State *s = &d->ne2000; memory_region_destroy(&s->io); qemu_del_vlan_client(&s->nic->nc); - return 0; } static Property ne2000_properties[] = { diff --git a/hw/pci.c b/hw/pci.c index d5c664c9cd..f783362ae3 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -837,12 +837,10 @@ static int pci_unregister_device(DeviceState *dev) { PCIDevice *pci_dev = PCI_DEVICE(dev); PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); - int ret = 0; - if (pc->exit) - ret = pc->exit(pci_dev); - if (ret) - return ret; + if (pc->exit) { + pc->exit(pci_dev); + } pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); diff --git a/hw/pci.h b/hw/pci.h index 44ae8715b0..6983b2f57a 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -85,7 +85,7 @@ typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, uint32_t address, int len); typedef void PCIMapIORegionFunc(PCIDevice *pci_dev, int region_num, pcibus_t addr, pcibus_t size, int type); -typedef int PCIUnregisterFunc(PCIDevice *pci_dev); +typedef void PCIUnregisterFunc(PCIDevice *pci_dev); typedef struct PCIIORegion { pcibus_t addr; /* current PCI mapping address. -1 means not mapped */ diff --git a/hw/pci_bridge.c b/hw/pci_bridge.c index e0832b4a67..0125fd95fc 100644 --- a/hw/pci_bridge.c +++ b/hw/pci_bridge.c @@ -333,7 +333,7 @@ int pci_bridge_initfn(PCIDevice *dev) } /* default qdev clean up function for PCI-to-PCI bridge */ -int pci_bridge_exitfn(PCIDevice *pci_dev) +void pci_bridge_exitfn(PCIDevice *pci_dev) { PCIBridge *s = DO_UPCAST(PCIBridge, dev, pci_dev); assert(QLIST_EMPTY(&s->sec_bus.child)); @@ -342,7 +342,6 @@ int pci_bridge_exitfn(PCIDevice *pci_dev) memory_region_destroy(&s->address_space_mem); memory_region_destroy(&s->address_space_io); /* qbus_free() is called automatically by qdev_free() */ - return 0; } /* diff --git a/hw/pci_bridge.h b/hw/pci_bridge.h index 84411a69dc..a00accc172 100644 --- a/hw/pci_bridge.h +++ b/hw/pci_bridge.h @@ -44,7 +44,7 @@ void pci_bridge_reset_reg(PCIDevice *dev); void pci_bridge_reset(DeviceState *qdev); int pci_bridge_initfn(PCIDevice *pci_dev); -int pci_bridge_exitfn(PCIDevice *pci_dev); +void pci_bridge_exitfn(PCIDevice *pci_dev); /* diff --git a/hw/pci_bridge_dev.c b/hw/pci_bridge_dev.c index 1cc1d2049c..f7063961a0 100644 --- a/hw/pci_bridge_dev.c +++ b/hw/pci_bridge_dev.c @@ -52,7 +52,8 @@ static int pci_bridge_dev_initfn(PCIDevice *dev) { PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br); - int err, ret; + int err; + pci_bridge_map_irq(br, NULL, pci_bridge_dev_map_irq_fn); err = pci_bridge_initfn(dev); if (err) { @@ -86,26 +87,22 @@ slotid_error: shpc_cleanup(dev, &bridge_dev->bar); shpc_error: memory_region_destroy(&bridge_dev->bar); - ret = pci_bridge_exitfn(dev); - assert(!ret); + pci_bridge_exitfn(dev); bridge_error: return err; } -static int pci_bridge_dev_exitfn(PCIDevice *dev) +static void pci_bridge_dev_exitfn(PCIDevice *dev) { PCIBridge *br = DO_UPCAST(PCIBridge, dev, dev); PCIBridgeDev *bridge_dev = DO_UPCAST(PCIBridgeDev, bridge, br); - int ret; if (msi_present(dev)) { msi_uninit(dev); } slotid_cap_cleanup(dev); shpc_cleanup(dev, &bridge_dev->bar); memory_region_destroy(&bridge_dev->bar); - ret = pci_bridge_exitfn(dev); - assert(!ret); - return 0; + pci_bridge_exitfn(dev); } static void pci_bridge_dev_write_config(PCIDevice *d, diff --git a/hw/pcnet-pci.c b/hw/pcnet-pci.c index 34d73aaea1..5439db3494 100644 --- a/hw/pcnet-pci.c +++ b/hw/pcnet-pci.c @@ -271,7 +271,7 @@ static void pci_pcnet_cleanup(VLANClientState *nc) pcnet_common_cleanup(d); } -static int pci_pcnet_uninit(PCIDevice *dev) +static void pci_pcnet_uninit(PCIDevice *dev) { PCIPCNetState *d = DO_UPCAST(PCIPCNetState, pci_dev, dev); @@ -280,7 +280,6 @@ static int pci_pcnet_uninit(PCIDevice *dev) qemu_del_timer(d->state.poll_timer); qemu_free_timer(d->state.poll_timer); qemu_del_vlan_client(&d->state.nic->nc); - return 0; } static NetClientInfo net_pci_pcnet_info = { diff --git a/hw/rtl8139.c b/hw/rtl8139.c index 060404c137..a7a3f27084 100644 --- a/hw/rtl8139.c +++ b/hw/rtl8139.c @@ -3427,7 +3427,7 @@ static void rtl8139_cleanup(VLANClientState *nc) s->nic = NULL; } -static int pci_rtl8139_uninit(PCIDevice *dev) +static void pci_rtl8139_uninit(PCIDevice *dev) { RTL8139State *s = DO_UPCAST(RTL8139State, dev, dev); @@ -3440,7 +3440,6 @@ static int pci_rtl8139_uninit(PCIDevice *dev) qemu_del_timer(s->timer); qemu_free_timer(s->timer); qemu_del_vlan_client(&s->nic->nc); - return 0; } static NetClientInfo net_rtl8139_info = { diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 9e211a0bb4..04aabd9aac 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1233,12 +1233,11 @@ static int usb_uhci_vt82c686b_initfn(PCIDevice *dev) return usb_uhci_common_initfn(dev); } -static int usb_uhci_exit(PCIDevice *dev) +static void usb_uhci_exit(PCIDevice *dev) { UHCIState *s = DO_UPCAST(UHCIState, dev, dev); memory_region_destroy(&s->io_bar); - return 0; } static Property uhci_properties[] = { diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 3dca37ff0c..6ed21b75c8 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -828,22 +828,21 @@ static int virtio_blk_init_pci(PCIDevice *pci_dev) return 0; } -static int virtio_exit_pci(PCIDevice *pci_dev) +static void virtio_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); memory_region_destroy(&proxy->bar); msix_uninit_exclusive_bar(pci_dev); - return 0; } -static int virtio_blk_exit_pci(PCIDevice *pci_dev) +static void virtio_blk_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); virtio_pci_stop_ioeventfd(proxy); virtio_blk_exit(proxy->vdev); - return virtio_exit_pci(pci_dev); + virtio_exit_pci(pci_dev); } static int virtio_serial_init_pci(PCIDevice *pci_dev) @@ -868,13 +867,13 @@ static int virtio_serial_init_pci(PCIDevice *pci_dev) return 0; } -static int virtio_serial_exit_pci(PCIDevice *pci_dev) +static void virtio_serial_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); virtio_pci_stop_ioeventfd(proxy); virtio_serial_exit(proxy->vdev); - return virtio_exit_pci(pci_dev); + virtio_exit_pci(pci_dev); } static int virtio_net_init_pci(PCIDevice *pci_dev) @@ -892,13 +891,13 @@ static int virtio_net_init_pci(PCIDevice *pci_dev) return 0; } -static int virtio_net_exit_pci(PCIDevice *pci_dev) +static void virtio_net_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); virtio_pci_stop_ioeventfd(proxy); virtio_net_exit(proxy->vdev); - return virtio_exit_pci(pci_dev); + virtio_exit_pci(pci_dev); } static int virtio_balloon_init_pci(PCIDevice *pci_dev) @@ -919,13 +918,13 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev) return 0; } -static int virtio_balloon_exit_pci(PCIDevice *pci_dev) +static void virtio_balloon_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); virtio_pci_stop_ioeventfd(proxy); virtio_balloon_exit(proxy->vdev); - return virtio_exit_pci(pci_dev); + virtio_exit_pci(pci_dev); } static Property virtio_blk_properties[] = { @@ -1074,12 +1073,12 @@ static int virtio_scsi_init_pci(PCIDevice *pci_dev) return 0; } -static int virtio_scsi_exit_pci(PCIDevice *pci_dev) +static void virtio_scsi_exit_pci(PCIDevice *pci_dev) { VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); virtio_scsi_exit(proxy->vdev); - return virtio_exit_pci(pci_dev); + virtio_exit_pci(pci_dev); } static Property virtio_scsi_properties[] = { diff --git a/hw/wdt_i6300esb.c b/hw/wdt_i6300esb.c index 15c69db932..4a83474906 100644 --- a/hw/wdt_i6300esb.c +++ b/hw/wdt_i6300esb.c @@ -411,13 +411,11 @@ static int i6300esb_init(PCIDevice *dev) return 0; } -static int i6300esb_exit(PCIDevice *dev) +static void i6300esb_exit(PCIDevice *dev) { I6300State *d = DO_UPCAST(I6300State, dev, dev); memory_region_destroy(&d->io_mem); - - return 0; } static WatchdogTimerModel model = { diff --git a/hw/xio3130_downstream.c b/hw/xio3130_downstream.c index 56d1b353d0..0d8a5e7020 100644 --- a/hw/xio3130_downstream.c +++ b/hw/xio3130_downstream.c @@ -60,7 +60,6 @@ static int xio3130_downstream_initfn(PCIDevice *d) PCIEPort *p = DO_UPCAST(PCIEPort, br, br); PCIESlot *s = DO_UPCAST(PCIESlot, port, p); int rc; - int tmp; rc = pci_bridge_initfn(d); if (rc < 0) { @@ -108,12 +107,11 @@ err_pcie_cap: err_msi: msi_uninit(d); err_bridge: - tmp = pci_bridge_exitfn(d); - assert(!tmp); + pci_bridge_exitfn(d); return rc; } -static int xio3130_downstream_exitfn(PCIDevice *d) +static void xio3130_downstream_exitfn(PCIDevice *d) { PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); PCIEPort *p = DO_UPCAST(PCIEPort, br, br); @@ -123,7 +121,7 @@ static int xio3130_downstream_exitfn(PCIDevice *d) pcie_chassis_del_slot(s); pcie_cap_exit(d); msi_uninit(d); - return pci_bridge_exitfn(d); + pci_bridge_exitfn(d); } PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, diff --git a/hw/xio3130_upstream.c b/hw/xio3130_upstream.c index 79725813a2..d46b86c74d 100644 --- a/hw/xio3130_upstream.c +++ b/hw/xio3130_upstream.c @@ -56,7 +56,6 @@ static int xio3130_upstream_initfn(PCIDevice *d) PCIBridge* br = DO_UPCAST(PCIBridge, dev, d); PCIEPort *p = DO_UPCAST(PCIEPort, br, br); int rc; - int tmp; rc = pci_bridge_initfn(d); if (rc < 0) { @@ -95,17 +94,16 @@ err: err_msi: msi_uninit(d); err_bridge: - tmp = pci_bridge_exitfn(d); - assert(!tmp); + pci_bridge_exitfn(d); return rc; } -static int xio3130_upstream_exitfn(PCIDevice *d) +static void xio3130_upstream_exitfn(PCIDevice *d) { pcie_aer_exit(d); pcie_cap_exit(d); msi_uninit(d); - return pci_bridge_exitfn(d); + pci_bridge_exitfn(d); } PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, -- cgit v1.2.1 From 7cf1b0fd95657e722b2ad290649eee88d1365786 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Tue, 3 Jul 2012 22:39:34 -0600 Subject: pci: Unregister BARs before device exit BARs are registered in init functions from memory regions created by the drivers. Exit functions destroy those memory regions. By unregistering the io regions after exit(), we're calling memory_region_del_subregion on freed memory. Don't do that. The option rom comes along for the ride because it's more symmetric to how it's created. Signed-off-by: Alex Williamson Signed-off-by: Michael S. Tsirkin --- hw/pci.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/pci.c b/hw/pci.c index f783362ae3..ef7607e177 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -838,12 +838,13 @@ static int pci_unregister_device(DeviceState *dev) PCIDevice *pci_dev = PCI_DEVICE(dev); PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(pci_dev); + pci_unregister_io_regions(pci_dev); + pci_del_option_rom(pci_dev); + if (pc->exit) { pc->exit(pci_dev); } - pci_unregister_io_regions(pci_dev); - pci_del_option_rom(pci_dev); do_pci_unregister_device(pci_dev); return 0; } -- cgit v1.2.1 From 3afa9bb488ea981d39255a25aaeb85eeafda41cb Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Thu, 19 Jul 2012 17:11:47 +0300 Subject: pci: Add pci_device_route_intx_to_irq Device assigned on KVM needs to know the mode (enabled/inverted/disabled) and the IRQ number that a given device triggers in the attached interrupt controller. Add a PCI IRQ path discovery function that walks from a given device to the host bridge, and gets this information. For this purpose, a host bridge callback function is introduced: route_intx_to_irq. It is so far only implemented by the PIIX3, other host bridges can be added later on as required. Will be used for KVM PCI device assignment and VFIO. Based on patch by Jan Kiszka, with minor tweaks. Signed-off-by: Jan Kiszka Signed-off-by: Michael S. Tsirkin --- hw/pci.c | 20 ++++++++++++++++++++ hw/pci.h | 12 ++++++++++++ hw/pci_internals.h | 1 + hw/piix_pci.c | 18 ++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/hw/pci.c b/hw/pci.c index ef7607e177..e80599f058 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -1066,6 +1066,26 @@ static void pci_set_irq(void *opaque, int irq_num, int level) pci_change_irq_level(pci_dev, irq_num, change); } +/* Special hooks used by device assignment */ +void pci_bus_set_route_irq_fn(PCIBus *bus, pci_route_irq_fn route_intx_to_irq) +{ + assert(!bus->parent_dev); + bus->route_intx_to_irq = route_intx_to_irq; +} + +PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin) +{ + PCIBus *bus; + + do { + bus = dev->bus; + pin = bus->map_irq(dev, pin); + dev = bus->parent_dev; + } while (dev); + assert(bus->route_intx_to_irq); + return bus->route_intx_to_irq(bus->irq_opaque, pin); +} + /***********************************************************/ /* monitor info on PCI */ diff --git a/hw/pci.h b/hw/pci.h index 6983b2f57a..7f7f88c6b9 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -141,6 +141,15 @@ enum { #define PCI_DEVICE_GET_CLASS(obj) \ OBJECT_GET_CLASS(PCIDeviceClass, (obj), TYPE_PCI_DEVICE) +typedef struct PCIINTxRoute { + enum { + PCI_INTX_ENABLED, + PCI_INTX_INVERTED, + PCI_INTX_DISABLED, + } mode; + int irq; +} PCIINTxRoute; + typedef struct PCIDeviceClass { DeviceClass parent_class; @@ -278,6 +287,7 @@ MemoryRegion *pci_address_space_io(PCIDevice *dev); typedef void (*pci_set_irq_fn)(void *opaque, int irq_num, int level); typedef int (*pci_map_irq_fn)(PCIDevice *pci_dev, int irq_num); +typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); typedef enum { PCI_HOTPLUG_DISABLED, @@ -306,6 +316,8 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min, int nirq); +void pci_bus_set_route_irq_fn(PCIBus *, pci_route_irq_fn); +PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin); void pci_device_reset(PCIDevice *dev); void pci_bus_reset(PCIBus *bus); diff --git a/hw/pci_internals.h b/hw/pci_internals.h index 96690b72d3..d704704833 100644 --- a/hw/pci_internals.h +++ b/hw/pci_internals.h @@ -19,6 +19,7 @@ struct PCIBus { uint8_t devfn_min; pci_set_irq_fn set_irq; pci_map_irq_fn map_irq; + pci_route_irq_fn route_intx_to_irq; pci_hotplug_fn hotplug; DeviceState *hotplug_qdev; void *irq_opaque; diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 09e84f59b6..8ece07c182 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -89,6 +89,7 @@ struct PCII440FXState { #define I440FX_SMRAM 0x72 static void piix3_set_irq(void *opaque, int pirq, int level); +static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pci_intx); static void piix3_write_config_xen(PCIDevice *dev, uint32_t address, uint32_t val, int len); @@ -315,6 +316,7 @@ static PCIBus *i440fx_common_init(const char *device_name, pci_create_simple_multifunction(b, -1, true, "PIIX3")); pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, PIIX_NUM_PIRQS); + pci_bus_set_route_irq_fn(b, piix3_route_intx_pin_to_irq); } piix3->pic = pic; *isa_bus = DO_UPCAST(ISABus, qbus, @@ -386,6 +388,22 @@ static void piix3_set_irq(void *opaque, int pirq, int level) piix3_set_irq_level(piix3, pirq, level); } +static PCIINTxRoute piix3_route_intx_pin_to_irq(void *opaque, int pin) +{ + PIIX3State *piix3 = opaque; + int irq = piix3->dev.config[PIIX_PIRQC + pin]; + PCIINTxRoute route; + + if (irq < PIIX_NUM_PIC_IRQS) { + route.mode = PCI_INTX_ENABLED; + route.irq = irq; + } else { + route.mode = PCI_INTX_DISABLED; + route.irq = -1; + } + return route; +} + /* irq routing is changed. so rebuild bitmap */ static void piix3_update_irq_levels(PIIX3State *piix3) { -- cgit v1.2.1 From 0ae1625177aba9ac70beb1556615530ddb18086d Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 2 Jul 2012 14:38:47 +0200 Subject: pci: Add INTx routing notifier This per-device notifier shall be triggered by any interrupt router along the path of a device's legacy interrupt signal on routing changes. For simplicity reasons and as this is a slow path anyway, no further details on the routing changes are provided. Instead, the callback is expected to use pci_device_route_intx_to_irq to check the effect of the change. Will be used by KVM PCI device assignment and VFIO. Signed-off-by: Jan Kiszka Signed-off-by: Michael S. Tsirkin --- hw/pci.c | 23 +++++++++++++++++++++++ hw/pci.h | 7 +++++++ hw/piix_pci.c | 2 ++ 3 files changed, 32 insertions(+) diff --git a/hw/pci.c b/hw/pci.c index e80599f058..94601f28f9 100644 --- a/hw/pci.c +++ b/hw/pci.c @@ -1086,6 +1086,29 @@ PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin) return bus->route_intx_to_irq(bus->irq_opaque, pin); } +void pci_bus_fire_intx_routing_notifier(PCIBus *bus) +{ + PCIDevice *dev; + PCIBus *sec; + int i; + + for (i = 0; i < ARRAY_SIZE(bus->devices); ++i) { + dev = bus->devices[i]; + if (dev && dev->intx_routing_notifier) { + dev->intx_routing_notifier(dev); + } + QLIST_FOREACH(sec, &bus->child, sibling) { + pci_bus_fire_intx_routing_notifier(sec); + } + } +} + +void pci_device_set_intx_routing_notifier(PCIDevice *dev, + PCIINTxRoutingNotifier notifier) +{ + dev->intx_routing_notifier = notifier; +} + /***********************************************************/ /* monitor info on PCI */ diff --git a/hw/pci.h b/hw/pci.h index 7f7f88c6b9..e96e8a7e54 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -182,6 +182,7 @@ typedef struct PCIDeviceClass { const char *romfile; } PCIDeviceClass; +typedef void (*PCIINTxRoutingNotifier)(PCIDevice *dev); typedef int (*MSIVectorUseNotifier)(PCIDevice *dev, unsigned int vector, MSIMessage msg); typedef void (*MSIVectorReleaseNotifier)(PCIDevice *dev, unsigned int vector); @@ -259,6 +260,9 @@ struct PCIDevice { MemoryRegion rom; uint32_t rom_bar; + /* INTx routing notifier */ + PCIINTxRoutingNotifier intx_routing_notifier; + /* MSI-X notifiers */ MSIVectorUseNotifier msix_vector_use_notifier; MSIVectorReleaseNotifier msix_vector_release_notifier; @@ -318,6 +322,9 @@ PCIBus *pci_register_bus(DeviceState *parent, const char *name, uint8_t devfn_min, int nirq); void pci_bus_set_route_irq_fn(PCIBus *, pci_route_irq_fn); PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin); +void pci_bus_fire_intx_routing_notifier(PCIBus *bus); +void pci_device_set_intx_routing_notifier(PCIDevice *dev, + PCIINTxRoutingNotifier notifier); void pci_device_reset(PCIDevice *dev); void pci_bus_reset(PCIBus *bus); diff --git a/hw/piix_pci.c b/hw/piix_pci.c index 8ece07c182..c497a014af 100644 --- a/hw/piix_pci.c +++ b/hw/piix_pci.c @@ -423,6 +423,8 @@ static void piix3_write_config(PCIDevice *dev, if (ranges_overlap(address, len, PIIX_PIRQC, 4)) { PIIX3State *piix3 = DO_UPCAST(PIIX3State, dev, dev); int pic_irq; + + pci_bus_fire_intx_routing_notifier(piix3->dev.bus); piix3_update_irq_levels(piix3); for (pic_irq = 0; pic_irq < PIIX_NUM_PIC_IRQS; pic_irq++) { piix3_set_irq_pic(piix3, pic_irq); -- cgit v1.2.1 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(+) 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.1