xref: /qemu/tests/qtest/libqos/pci.c (revision 3df72d1c)
11cf4323eSThomas Huth /*
21cf4323eSThomas Huth  * libqos PCI bindings
31cf4323eSThomas Huth  *
41cf4323eSThomas Huth  * Copyright IBM, Corp. 2012-2013
51cf4323eSThomas Huth  *
61cf4323eSThomas Huth  * Authors:
71cf4323eSThomas Huth  *  Anthony Liguori   <aliguori@us.ibm.com>
81cf4323eSThomas Huth  *
91cf4323eSThomas Huth  * This work is licensed under the terms of the GNU GPL, version 2 or later.
101cf4323eSThomas Huth  * See the COPYING file in the top-level directory.
111cf4323eSThomas Huth  */
121cf4323eSThomas Huth 
131cf4323eSThomas Huth #include "qemu/osdep.h"
14a2ce7dbdSPaolo Bonzini #include "pci.h"
151cf4323eSThomas Huth 
16efe84f03SLaurent Vivier #include "hw/pci/pci.h"
17efe84f03SLaurent Vivier #include "hw/pci/pci_bridge.h"
181cf4323eSThomas Huth #include "hw/pci/pci_regs.h"
191cf4323eSThomas Huth #include "qemu/host-utils.h"
20a2ce7dbdSPaolo Bonzini #include "qgraph.h"
211cf4323eSThomas Huth 
qpci_device_foreach(QPCIBus * bus,int vendor_id,int device_id,void (* func)(QPCIDevice * dev,int devfn,void * data),void * data)221cf4323eSThomas Huth void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id,
231cf4323eSThomas Huth                          void (*func)(QPCIDevice *dev, int devfn, void *data),
241cf4323eSThomas Huth                          void *data)
251cf4323eSThomas Huth {
261cf4323eSThomas Huth     int slot;
271cf4323eSThomas Huth 
281cf4323eSThomas Huth     for (slot = 0; slot < 32; slot++) {
291cf4323eSThomas Huth         int fn;
301cf4323eSThomas Huth 
311cf4323eSThomas Huth         for (fn = 0; fn < 8; fn++) {
321cf4323eSThomas Huth             QPCIDevice *dev;
331cf4323eSThomas Huth 
341cf4323eSThomas Huth             dev = qpci_device_find(bus, QPCI_DEVFN(slot, fn));
351cf4323eSThomas Huth             if (!dev) {
361cf4323eSThomas Huth                 continue;
371cf4323eSThomas Huth             }
381cf4323eSThomas Huth 
391cf4323eSThomas Huth             if (vendor_id != -1 &&
401cf4323eSThomas Huth                 qpci_config_readw(dev, PCI_VENDOR_ID) != vendor_id) {
411cf4323eSThomas Huth                 g_free(dev);
421cf4323eSThomas Huth                 continue;
431cf4323eSThomas Huth             }
441cf4323eSThomas Huth 
451cf4323eSThomas Huth             if (device_id != -1 &&
461cf4323eSThomas Huth                 qpci_config_readw(dev, PCI_DEVICE_ID) != device_id) {
471cf4323eSThomas Huth                 g_free(dev);
481cf4323eSThomas Huth                 continue;
491cf4323eSThomas Huth             }
501cf4323eSThomas Huth 
511cf4323eSThomas Huth             func(dev, QPCI_DEVFN(slot, fn), data);
521cf4323eSThomas Huth         }
531cf4323eSThomas Huth     }
541cf4323eSThomas Huth }
551cf4323eSThomas Huth 
qpci_has_buggy_msi(QPCIDevice * dev)561cf4323eSThomas Huth bool qpci_has_buggy_msi(QPCIDevice *dev)
571cf4323eSThomas Huth {
581cf4323eSThomas Huth     return dev->bus->has_buggy_msi;
591cf4323eSThomas Huth }
601cf4323eSThomas Huth 
qpci_check_buggy_msi(QPCIDevice * dev)611cf4323eSThomas Huth bool qpci_check_buggy_msi(QPCIDevice *dev)
621cf4323eSThomas Huth {
631cf4323eSThomas Huth     if (qpci_has_buggy_msi(dev)) {
641cf4323eSThomas Huth         g_test_skip("Skipping due to incomplete support for MSI");
651cf4323eSThomas Huth         return true;
661cf4323eSThomas Huth     }
671cf4323eSThomas Huth     return false;
681cf4323eSThomas Huth }
691cf4323eSThomas Huth 
qpci_device_set(QPCIDevice * dev,QPCIBus * bus,int devfn)701cf4323eSThomas Huth static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn)
711cf4323eSThomas Huth {
721cf4323eSThomas Huth     g_assert(dev);
731cf4323eSThomas Huth 
741cf4323eSThomas Huth     dev->bus = bus;
751cf4323eSThomas Huth     dev->devfn = devfn;
761cf4323eSThomas Huth }
771cf4323eSThomas Huth 
qpci_device_find(QPCIBus * bus,int devfn)781cf4323eSThomas Huth QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn)
791cf4323eSThomas Huth {
801cf4323eSThomas Huth     QPCIDevice *dev;
811cf4323eSThomas Huth 
821cf4323eSThomas Huth     dev = g_malloc0(sizeof(*dev));
831cf4323eSThomas Huth     qpci_device_set(dev, bus, devfn);
841cf4323eSThomas Huth 
851cf4323eSThomas Huth     if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) {
861cf4323eSThomas Huth         g_free(dev);
871cf4323eSThomas Huth         return NULL;
881cf4323eSThomas Huth     }
891cf4323eSThomas Huth 
901cf4323eSThomas Huth     return dev;
911cf4323eSThomas Huth }
921cf4323eSThomas Huth 
qpci_device_init(QPCIDevice * dev,QPCIBus * bus,QPCIAddress * addr)931cf4323eSThomas Huth void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr)
941cf4323eSThomas Huth {
951cf4323eSThomas Huth     uint16_t vendor_id, device_id;
961cf4323eSThomas Huth 
971cf4323eSThomas Huth     qpci_device_set(dev, bus, addr->devfn);
981cf4323eSThomas Huth     vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID);
991cf4323eSThomas Huth     device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
1001cf4323eSThomas Huth     g_assert(!addr->vendor_id || vendor_id == addr->vendor_id);
1011cf4323eSThomas Huth     g_assert(!addr->device_id || device_id == addr->device_id);
1021cf4323eSThomas Huth }
1031cf4323eSThomas Huth 
qpci_find_resource_reserve_capability(QPCIDevice * dev)104efe84f03SLaurent Vivier static uint8_t qpci_find_resource_reserve_capability(QPCIDevice *dev)
105efe84f03SLaurent Vivier {
106efe84f03SLaurent Vivier     uint16_t device_id;
107efe84f03SLaurent Vivier     uint8_t cap = 0;
108efe84f03SLaurent Vivier 
109efe84f03SLaurent Vivier     if (qpci_config_readw(dev, PCI_VENDOR_ID) != PCI_VENDOR_ID_REDHAT) {
110efe84f03SLaurent Vivier         return 0;
111efe84f03SLaurent Vivier     }
112efe84f03SLaurent Vivier 
113efe84f03SLaurent Vivier     device_id = qpci_config_readw(dev, PCI_DEVICE_ID);
114efe84f03SLaurent Vivier 
115efe84f03SLaurent Vivier     if (device_id != PCI_DEVICE_ID_REDHAT_PCIE_RP &&
116efe84f03SLaurent Vivier         device_id != PCI_DEVICE_ID_REDHAT_BRIDGE) {
117efe84f03SLaurent Vivier         return 0;
118efe84f03SLaurent Vivier     }
119efe84f03SLaurent Vivier 
120efe84f03SLaurent Vivier     do {
121efe84f03SLaurent Vivier         cap = qpci_find_capability(dev, PCI_CAP_ID_VNDR, cap);
122efe84f03SLaurent Vivier     } while (cap &&
123efe84f03SLaurent Vivier              qpci_config_readb(dev, cap + REDHAT_PCI_CAP_TYPE_OFFSET) !=
124efe84f03SLaurent Vivier              REDHAT_PCI_CAP_RESOURCE_RESERVE);
125efe84f03SLaurent Vivier     if (cap) {
126efe84f03SLaurent Vivier         uint8_t cap_len = qpci_config_readb(dev, cap + PCI_CAP_FLAGS);
127efe84f03SLaurent Vivier         if (cap_len < REDHAT_PCI_CAP_RES_RESERVE_CAP_SIZE) {
128efe84f03SLaurent Vivier             return 0;
129efe84f03SLaurent Vivier         }
130efe84f03SLaurent Vivier     }
131efe84f03SLaurent Vivier     return cap;
132efe84f03SLaurent Vivier }
133efe84f03SLaurent Vivier 
qpci_secondary_buses_rec(QPCIBus * qbus,int bus,int * pci_bus)134efe84f03SLaurent Vivier static void qpci_secondary_buses_rec(QPCIBus *qbus, int bus, int *pci_bus)
135efe84f03SLaurent Vivier {
136efe84f03SLaurent Vivier     QPCIDevice *dev;
137efe84f03SLaurent Vivier     uint16_t class;
138efe84f03SLaurent Vivier     uint8_t pribus, secbus, subbus;
139efe84f03SLaurent Vivier     int index;
140efe84f03SLaurent Vivier 
141efe84f03SLaurent Vivier     for (index = 0; index < 32; index++) {
142efe84f03SLaurent Vivier         dev = qpci_device_find(qbus, QPCI_DEVFN(bus + index, 0));
143efe84f03SLaurent Vivier         if (dev == NULL) {
144efe84f03SLaurent Vivier             continue;
145efe84f03SLaurent Vivier         }
146efe84f03SLaurent Vivier         class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
147efe84f03SLaurent Vivier         if (class == PCI_CLASS_BRIDGE_PCI) {
148efe84f03SLaurent Vivier             qpci_config_writeb(dev, PCI_SECONDARY_BUS, 255);
149efe84f03SLaurent Vivier             qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 0);
150efe84f03SLaurent Vivier         }
151efe84f03SLaurent Vivier         g_free(dev);
152efe84f03SLaurent Vivier     }
153efe84f03SLaurent Vivier 
154efe84f03SLaurent Vivier     for (index = 0; index < 32; index++) {
155efe84f03SLaurent Vivier         dev = qpci_device_find(qbus, QPCI_DEVFN(bus + index, 0));
156efe84f03SLaurent Vivier         if (dev == NULL) {
157efe84f03SLaurent Vivier             continue;
158efe84f03SLaurent Vivier         }
159efe84f03SLaurent Vivier         class = qpci_config_readw(dev, PCI_CLASS_DEVICE);
160efe84f03SLaurent Vivier         if (class != PCI_CLASS_BRIDGE_PCI) {
161efe84f03SLaurent Vivier             g_free(dev);
162efe84f03SLaurent Vivier             continue;
163efe84f03SLaurent Vivier         }
164efe84f03SLaurent Vivier 
165efe84f03SLaurent Vivier         pribus = qpci_config_readb(dev, PCI_PRIMARY_BUS);
166efe84f03SLaurent Vivier         if (pribus != bus) {
167efe84f03SLaurent Vivier             qpci_config_writeb(dev, PCI_PRIMARY_BUS, bus);
168efe84f03SLaurent Vivier         }
169efe84f03SLaurent Vivier 
170efe84f03SLaurent Vivier         secbus = qpci_config_readb(dev, PCI_SECONDARY_BUS);
171efe84f03SLaurent Vivier         (*pci_bus)++;
172efe84f03SLaurent Vivier         if (*pci_bus != secbus) {
173efe84f03SLaurent Vivier             secbus = *pci_bus;
174efe84f03SLaurent Vivier             qpci_config_writeb(dev, PCI_SECONDARY_BUS, secbus);
175efe84f03SLaurent Vivier         }
176efe84f03SLaurent Vivier 
177efe84f03SLaurent Vivier         subbus = qpci_config_readb(dev, PCI_SUBORDINATE_BUS);
178efe84f03SLaurent Vivier         qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, 255);
179efe84f03SLaurent Vivier 
180efe84f03SLaurent Vivier         qpci_secondary_buses_rec(qbus, secbus << 5, pci_bus);
181efe84f03SLaurent Vivier 
182efe84f03SLaurent Vivier         if (subbus != *pci_bus) {
183efe84f03SLaurent Vivier             uint8_t res_bus = *pci_bus;
184efe84f03SLaurent Vivier             uint8_t cap = qpci_find_resource_reserve_capability(dev);
185efe84f03SLaurent Vivier 
186efe84f03SLaurent Vivier             if (cap) {
187efe84f03SLaurent Vivier                 uint32_t tmp_res_bus;
188efe84f03SLaurent Vivier 
189efe84f03SLaurent Vivier                 tmp_res_bus = qpci_config_readl(dev, cap +
190efe84f03SLaurent Vivier                                             REDHAT_PCI_CAP_RES_RESERVE_BUS_RES);
191efe84f03SLaurent Vivier                 if (tmp_res_bus != (uint32_t)-1) {
192efe84f03SLaurent Vivier                     res_bus = tmp_res_bus & 0xFF;
193efe84f03SLaurent Vivier                     if ((uint8_t)(res_bus + secbus) < secbus ||
194efe84f03SLaurent Vivier                         (uint8_t)(res_bus + secbus) < res_bus) {
195efe84f03SLaurent Vivier                         res_bus = 0;
196efe84f03SLaurent Vivier                     }
197efe84f03SLaurent Vivier                     if (secbus + res_bus > *pci_bus) {
198efe84f03SLaurent Vivier                         res_bus = secbus + res_bus;
199efe84f03SLaurent Vivier                     }
200efe84f03SLaurent Vivier                 }
201efe84f03SLaurent Vivier             }
202efe84f03SLaurent Vivier             subbus = res_bus;
203efe84f03SLaurent Vivier             *pci_bus = res_bus;
204efe84f03SLaurent Vivier         }
205efe84f03SLaurent Vivier 
206efe84f03SLaurent Vivier         qpci_config_writeb(dev, PCI_SUBORDINATE_BUS, subbus);
207efe84f03SLaurent Vivier         g_free(dev);
208efe84f03SLaurent Vivier     }
209efe84f03SLaurent Vivier }
210efe84f03SLaurent Vivier 
qpci_secondary_buses_init(QPCIBus * bus)211efe84f03SLaurent Vivier int qpci_secondary_buses_init(QPCIBus *bus)
212efe84f03SLaurent Vivier {
213efe84f03SLaurent Vivier     int last_bus = 0;
214efe84f03SLaurent Vivier 
215efe84f03SLaurent Vivier     qpci_secondary_buses_rec(bus, 0, &last_bus);
216efe84f03SLaurent Vivier 
217efe84f03SLaurent Vivier     return last_bus;
218efe84f03SLaurent Vivier }
219efe84f03SLaurent Vivier 
220efe84f03SLaurent Vivier 
qpci_device_enable(QPCIDevice * dev)2211cf4323eSThomas Huth void qpci_device_enable(QPCIDevice *dev)
2221cf4323eSThomas Huth {
2231cf4323eSThomas Huth     uint16_t cmd;
2241cf4323eSThomas Huth 
2251cf4323eSThomas Huth     /* FIXME -- does this need to be a bus callout? */
2261cf4323eSThomas Huth     cmd = qpci_config_readw(dev, PCI_COMMAND);
2271cf4323eSThomas Huth     cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
2281cf4323eSThomas Huth     qpci_config_writew(dev, PCI_COMMAND, cmd);
2291cf4323eSThomas Huth 
2301cf4323eSThomas Huth     /* Verify the bits are now set. */
2311cf4323eSThomas Huth     cmd = qpci_config_readw(dev, PCI_COMMAND);
2321cf4323eSThomas Huth     g_assert_cmphex(cmd & PCI_COMMAND_IO, ==, PCI_COMMAND_IO);
2331cf4323eSThomas Huth     g_assert_cmphex(cmd & PCI_COMMAND_MEMORY, ==, PCI_COMMAND_MEMORY);
2341cf4323eSThomas Huth     g_assert_cmphex(cmd & PCI_COMMAND_MASTER, ==, PCI_COMMAND_MASTER);
2351cf4323eSThomas Huth }
2361cf4323eSThomas Huth 
2371cf4323eSThomas Huth /**
2381cf4323eSThomas Huth  * qpci_find_capability:
2391cf4323eSThomas Huth  * @dev: the PCI device
2401cf4323eSThomas Huth  * @id: the PCI Capability ID (PCI_CAP_ID_*)
2411cf4323eSThomas Huth  * @start_addr: 0 to begin iteration or the last return value to continue
2421cf4323eSThomas Huth  *              iteration
2431cf4323eSThomas Huth  *
2441cf4323eSThomas Huth  * Iterate over the PCI Capabilities List.
2451cf4323eSThomas Huth  *
2461cf4323eSThomas Huth  * Returns: PCI Configuration Space offset of the capabililty structure or
2471cf4323eSThomas Huth  *          0 if no further matching capability is found
2481cf4323eSThomas Huth  */
qpci_find_capability(QPCIDevice * dev,uint8_t id,uint8_t start_addr)2491cf4323eSThomas Huth uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id, uint8_t start_addr)
2501cf4323eSThomas Huth {
2511cf4323eSThomas Huth     uint8_t cap;
2521cf4323eSThomas Huth     uint8_t addr;
2531cf4323eSThomas Huth 
2541cf4323eSThomas Huth     if (start_addr) {
2551cf4323eSThomas Huth         addr = qpci_config_readb(dev, start_addr + PCI_CAP_LIST_NEXT);
2561cf4323eSThomas Huth     } else {
2571cf4323eSThomas Huth         addr = qpci_config_readb(dev, PCI_CAPABILITY_LIST);
2581cf4323eSThomas Huth     }
2591cf4323eSThomas Huth 
2601cf4323eSThomas Huth     do {
2611cf4323eSThomas Huth         cap = qpci_config_readb(dev, addr);
2621cf4323eSThomas Huth         if (cap != id) {
2631cf4323eSThomas Huth             addr = qpci_config_readb(dev, addr + PCI_CAP_LIST_NEXT);
2641cf4323eSThomas Huth         }
2651cf4323eSThomas Huth     } while (cap != id && addr != 0);
2661cf4323eSThomas Huth 
2671cf4323eSThomas Huth     return addr;
2681cf4323eSThomas Huth }
2691cf4323eSThomas Huth 
qpci_msix_enable(QPCIDevice * dev)2701cf4323eSThomas Huth void qpci_msix_enable(QPCIDevice *dev)
2711cf4323eSThomas Huth {
2721cf4323eSThomas Huth     uint8_t addr;
2731cf4323eSThomas Huth     uint16_t val;
2741cf4323eSThomas Huth     uint32_t table;
2751cf4323eSThomas Huth     uint8_t bir_table;
2761cf4323eSThomas Huth     uint8_t bir_pba;
2771cf4323eSThomas Huth 
2781cf4323eSThomas Huth     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
2791cf4323eSThomas Huth     g_assert_cmphex(addr, !=, 0);
2801cf4323eSThomas Huth 
2811cf4323eSThomas Huth     val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
2821cf4323eSThomas Huth     qpci_config_writew(dev, addr + PCI_MSIX_FLAGS, val | PCI_MSIX_FLAGS_ENABLE);
2831cf4323eSThomas Huth 
2841cf4323eSThomas Huth     table = qpci_config_readl(dev, addr + PCI_MSIX_TABLE);
2851cf4323eSThomas Huth     bir_table = table & PCI_MSIX_FLAGS_BIRMASK;
2861cf4323eSThomas Huth     dev->msix_table_bar = qpci_iomap(dev, bir_table, NULL);
2871cf4323eSThomas Huth     dev->msix_table_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
2881cf4323eSThomas Huth 
2891cf4323eSThomas Huth     table = qpci_config_readl(dev, addr + PCI_MSIX_PBA);
2901cf4323eSThomas Huth     bir_pba = table & PCI_MSIX_FLAGS_BIRMASK;
2911cf4323eSThomas Huth     if (bir_pba != bir_table) {
2921cf4323eSThomas Huth         dev->msix_pba_bar = qpci_iomap(dev, bir_pba, NULL);
2931cf4323eSThomas Huth     } else {
2941cf4323eSThomas Huth         dev->msix_pba_bar = dev->msix_table_bar;
2951cf4323eSThomas Huth     }
2961cf4323eSThomas Huth     dev->msix_pba_off = table & ~PCI_MSIX_FLAGS_BIRMASK;
2971cf4323eSThomas Huth 
2981cf4323eSThomas Huth     dev->msix_enabled = true;
2991cf4323eSThomas Huth }
3001cf4323eSThomas Huth 
qpci_msix_disable(QPCIDevice * dev)3011cf4323eSThomas Huth void qpci_msix_disable(QPCIDevice *dev)
3021cf4323eSThomas Huth {
3031cf4323eSThomas Huth     uint8_t addr;
3041cf4323eSThomas Huth     uint16_t val;
3051cf4323eSThomas Huth 
3061cf4323eSThomas Huth     g_assert(dev->msix_enabled);
3071cf4323eSThomas Huth     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
3081cf4323eSThomas Huth     g_assert_cmphex(addr, !=, 0);
3091cf4323eSThomas Huth     val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
3101cf4323eSThomas Huth     qpci_config_writew(dev, addr + PCI_MSIX_FLAGS,
3111cf4323eSThomas Huth                                                 val & ~PCI_MSIX_FLAGS_ENABLE);
3121cf4323eSThomas Huth 
3131cf4323eSThomas Huth     if (dev->msix_pba_bar.addr != dev->msix_table_bar.addr) {
3141cf4323eSThomas Huth         qpci_iounmap(dev, dev->msix_pba_bar);
3151cf4323eSThomas Huth     }
3161cf4323eSThomas Huth     qpci_iounmap(dev, dev->msix_table_bar);
3171cf4323eSThomas Huth 
3181cf4323eSThomas Huth     dev->msix_enabled = 0;
3191cf4323eSThomas Huth     dev->msix_table_off = 0;
3201cf4323eSThomas Huth     dev->msix_pba_off = 0;
3211cf4323eSThomas Huth }
3221cf4323eSThomas Huth 
qpci_msix_pending(QPCIDevice * dev,uint16_t entry)3231cf4323eSThomas Huth bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry)
3241cf4323eSThomas Huth {
3251cf4323eSThomas Huth     uint32_t pba_entry;
3261cf4323eSThomas Huth     uint8_t bit_n = entry % 32;
3271cf4323eSThomas Huth     uint64_t  off = (entry / 32) * PCI_MSIX_ENTRY_SIZE / 4;
3281cf4323eSThomas Huth 
3291cf4323eSThomas Huth     g_assert(dev->msix_enabled);
3301cf4323eSThomas Huth     pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off);
3311cf4323eSThomas Huth     qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off,
3321cf4323eSThomas Huth                    pba_entry & ~(1 << bit_n));
3331cf4323eSThomas Huth     return (pba_entry & (1 << bit_n)) != 0;
3341cf4323eSThomas Huth }
3351cf4323eSThomas Huth 
qpci_msix_masked(QPCIDevice * dev,uint16_t entry)3361cf4323eSThomas Huth bool qpci_msix_masked(QPCIDevice *dev, uint16_t entry)
3371cf4323eSThomas Huth {
3381cf4323eSThomas Huth     uint8_t addr;
3391cf4323eSThomas Huth     uint16_t val;
3401cf4323eSThomas Huth     uint64_t vector_off = dev->msix_table_off + entry * PCI_MSIX_ENTRY_SIZE;
3411cf4323eSThomas Huth 
3421cf4323eSThomas Huth     g_assert(dev->msix_enabled);
3431cf4323eSThomas Huth     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
3441cf4323eSThomas Huth     g_assert_cmphex(addr, !=, 0);
3451cf4323eSThomas Huth     val = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
3461cf4323eSThomas Huth 
3471cf4323eSThomas Huth     if (val & PCI_MSIX_FLAGS_MASKALL) {
3481cf4323eSThomas Huth         return true;
3491cf4323eSThomas Huth     } else {
3501cf4323eSThomas Huth         return (qpci_io_readl(dev, dev->msix_table_bar,
3511cf4323eSThomas Huth                               vector_off + PCI_MSIX_ENTRY_VECTOR_CTRL)
3521cf4323eSThomas Huth                 & PCI_MSIX_ENTRY_CTRL_MASKBIT) != 0;
3531cf4323eSThomas Huth     }
3541cf4323eSThomas Huth }
3551cf4323eSThomas Huth 
qpci_msix_table_size(QPCIDevice * dev)3561cf4323eSThomas Huth uint16_t qpci_msix_table_size(QPCIDevice *dev)
3571cf4323eSThomas Huth {
3581cf4323eSThomas Huth     uint8_t addr;
3591cf4323eSThomas Huth     uint16_t control;
3601cf4323eSThomas Huth 
3611cf4323eSThomas Huth     addr = qpci_find_capability(dev, PCI_CAP_ID_MSIX, 0);
3621cf4323eSThomas Huth     g_assert_cmphex(addr, !=, 0);
3631cf4323eSThomas Huth 
3641cf4323eSThomas Huth     control = qpci_config_readw(dev, addr + PCI_MSIX_FLAGS);
3651cf4323eSThomas Huth     return (control & PCI_MSIX_FLAGS_QSIZE) + 1;
3661cf4323eSThomas Huth }
3671cf4323eSThomas Huth 
qpci_config_readb(QPCIDevice * dev,uint8_t offset)3681cf4323eSThomas Huth uint8_t qpci_config_readb(QPCIDevice *dev, uint8_t offset)
3691cf4323eSThomas Huth {
3701cf4323eSThomas Huth     return dev->bus->config_readb(dev->bus, dev->devfn, offset);
3711cf4323eSThomas Huth }
3721cf4323eSThomas Huth 
qpci_config_readw(QPCIDevice * dev,uint8_t offset)3731cf4323eSThomas Huth uint16_t qpci_config_readw(QPCIDevice *dev, uint8_t offset)
3741cf4323eSThomas Huth {
3751cf4323eSThomas Huth     return dev->bus->config_readw(dev->bus, dev->devfn, offset);
3761cf4323eSThomas Huth }
3771cf4323eSThomas Huth 
qpci_config_readl(QPCIDevice * dev,uint8_t offset)3781cf4323eSThomas Huth uint32_t qpci_config_readl(QPCIDevice *dev, uint8_t offset)
3791cf4323eSThomas Huth {
3801cf4323eSThomas Huth     return dev->bus->config_readl(dev->bus, dev->devfn, offset);
3811cf4323eSThomas Huth }
3821cf4323eSThomas Huth 
3831cf4323eSThomas Huth 
qpci_config_writeb(QPCIDevice * dev,uint8_t offset,uint8_t value)3841cf4323eSThomas Huth void qpci_config_writeb(QPCIDevice *dev, uint8_t offset, uint8_t value)
3851cf4323eSThomas Huth {
3861cf4323eSThomas Huth     dev->bus->config_writeb(dev->bus, dev->devfn, offset, value);
3871cf4323eSThomas Huth }
3881cf4323eSThomas Huth 
qpci_config_writew(QPCIDevice * dev,uint8_t offset,uint16_t value)3891cf4323eSThomas Huth void qpci_config_writew(QPCIDevice *dev, uint8_t offset, uint16_t value)
3901cf4323eSThomas Huth {
3911cf4323eSThomas Huth     dev->bus->config_writew(dev->bus, dev->devfn, offset, value);
3921cf4323eSThomas Huth }
3931cf4323eSThomas Huth 
qpci_config_writel(QPCIDevice * dev,uint8_t offset,uint32_t value)3941cf4323eSThomas Huth void qpci_config_writel(QPCIDevice *dev, uint8_t offset, uint32_t value)
3951cf4323eSThomas Huth {
3961cf4323eSThomas Huth     dev->bus->config_writel(dev->bus, dev->devfn, offset, value);
3971cf4323eSThomas Huth }
3981cf4323eSThomas Huth 
qpci_io_readb(QPCIDevice * dev,QPCIBar token,uint64_t off)3991cf4323eSThomas Huth uint8_t qpci_io_readb(QPCIDevice *dev, QPCIBar token, uint64_t off)
4001cf4323eSThomas Huth {
4013df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4023df72d1cSEric Auger 
4033df72d1cSEric Auger     if (token.is_io) {
4043df72d1cSEric Auger         return bus->pio_readb(bus, token.addr + off);
4051cf4323eSThomas Huth     } else {
4061cf4323eSThomas Huth         uint8_t val;
4073df72d1cSEric Auger 
4083df72d1cSEric Auger         bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
4091cf4323eSThomas Huth         return val;
4101cf4323eSThomas Huth     }
4111cf4323eSThomas Huth }
4121cf4323eSThomas Huth 
qpci_io_readw(QPCIDevice * dev,QPCIBar token,uint64_t off)4131cf4323eSThomas Huth uint16_t qpci_io_readw(QPCIDevice *dev, QPCIBar token, uint64_t off)
4141cf4323eSThomas Huth {
4153df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4163df72d1cSEric Auger 
4173df72d1cSEric Auger     if (token.is_io) {
4183df72d1cSEric Auger         return bus->pio_readw(bus, token.addr + off);
4191cf4323eSThomas Huth     } else {
4201cf4323eSThomas Huth         uint16_t val;
4213df72d1cSEric Auger 
4223df72d1cSEric Auger         bus->memread(bus, token.addr + off, &val, sizeof(val));
4231cf4323eSThomas Huth         return le16_to_cpu(val);
4241cf4323eSThomas Huth     }
4251cf4323eSThomas Huth }
4261cf4323eSThomas Huth 
qpci_io_readl(QPCIDevice * dev,QPCIBar token,uint64_t off)4271cf4323eSThomas Huth uint32_t qpci_io_readl(QPCIDevice *dev, QPCIBar token, uint64_t off)
4281cf4323eSThomas Huth {
4293df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4303df72d1cSEric Auger 
4313df72d1cSEric Auger     if (token.is_io) {
4323df72d1cSEric Auger         return bus->pio_readl(bus, token.addr + off);
4331cf4323eSThomas Huth     } else {
4341cf4323eSThomas Huth         uint32_t val;
4353df72d1cSEric Auger 
4363df72d1cSEric Auger         bus->memread(dev->bus, token.addr + off, &val, sizeof(val));
4371cf4323eSThomas Huth         return le32_to_cpu(val);
4381cf4323eSThomas Huth     }
4391cf4323eSThomas Huth }
4401cf4323eSThomas Huth 
qpci_io_readq(QPCIDevice * dev,QPCIBar token,uint64_t off)4411cf4323eSThomas Huth uint64_t qpci_io_readq(QPCIDevice *dev, QPCIBar token, uint64_t off)
4421cf4323eSThomas Huth {
4433df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4443df72d1cSEric Auger 
4453df72d1cSEric Auger     if (token.is_io) {
4463df72d1cSEric Auger         return bus->pio_readq(bus, token.addr + off);
4471cf4323eSThomas Huth     } else {
4481cf4323eSThomas Huth         uint64_t val;
4493df72d1cSEric Auger 
4503df72d1cSEric Auger         bus->memread(bus, token.addr + off, &val, sizeof(val));
4511cf4323eSThomas Huth         return le64_to_cpu(val);
4521cf4323eSThomas Huth     }
4531cf4323eSThomas Huth }
4541cf4323eSThomas Huth 
qpci_io_writeb(QPCIDevice * dev,QPCIBar token,uint64_t off,uint8_t value)4551cf4323eSThomas Huth void qpci_io_writeb(QPCIDevice *dev, QPCIBar token, uint64_t off,
4561cf4323eSThomas Huth                     uint8_t value)
4571cf4323eSThomas Huth {
4583df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4593df72d1cSEric Auger 
4603df72d1cSEric Auger     if (token.is_io) {
4613df72d1cSEric Auger         bus->pio_writeb(bus, token.addr + off, value);
4621cf4323eSThomas Huth     } else {
4633df72d1cSEric Auger         bus->memwrite(bus, token.addr + off, &value, sizeof(value));
4641cf4323eSThomas Huth     }
4651cf4323eSThomas Huth }
4661cf4323eSThomas Huth 
qpci_io_writew(QPCIDevice * dev,QPCIBar token,uint64_t off,uint16_t value)4671cf4323eSThomas Huth void qpci_io_writew(QPCIDevice *dev, QPCIBar token, uint64_t off,
4681cf4323eSThomas Huth                     uint16_t value)
4691cf4323eSThomas Huth {
4703df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4713df72d1cSEric Auger 
4723df72d1cSEric Auger     if (token.is_io) {
4733df72d1cSEric Auger         bus->pio_writew(bus, token.addr + off, value);
4741cf4323eSThomas Huth     } else {
4751cf4323eSThomas Huth         value = cpu_to_le16(value);
4763df72d1cSEric Auger         bus->memwrite(bus, token.addr + off, &value, sizeof(value));
4771cf4323eSThomas Huth     }
4781cf4323eSThomas Huth }
4791cf4323eSThomas Huth 
qpci_io_writel(QPCIDevice * dev,QPCIBar token,uint64_t off,uint32_t value)4801cf4323eSThomas Huth void qpci_io_writel(QPCIDevice *dev, QPCIBar token, uint64_t off,
4811cf4323eSThomas Huth                     uint32_t value)
4821cf4323eSThomas Huth {
4833df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4843df72d1cSEric Auger 
4853df72d1cSEric Auger     if (token.is_io) {
4863df72d1cSEric Auger         bus->pio_writel(bus, token.addr + off, value);
4871cf4323eSThomas Huth     } else {
4881cf4323eSThomas Huth         value = cpu_to_le32(value);
4893df72d1cSEric Auger         bus->memwrite(bus, token.addr + off, &value, sizeof(value));
4901cf4323eSThomas Huth     }
4911cf4323eSThomas Huth }
4921cf4323eSThomas Huth 
qpci_io_writeq(QPCIDevice * dev,QPCIBar token,uint64_t off,uint64_t value)4931cf4323eSThomas Huth void qpci_io_writeq(QPCIDevice *dev, QPCIBar token, uint64_t off,
4941cf4323eSThomas Huth                     uint64_t value)
4951cf4323eSThomas Huth {
4963df72d1cSEric Auger     QPCIBus *bus = dev->bus;
4973df72d1cSEric Auger 
4983df72d1cSEric Auger     if (token.is_io) {
4993df72d1cSEric Auger         bus->pio_writeq(bus, token.addr + off, value);
5001cf4323eSThomas Huth     } else {
5011cf4323eSThomas Huth         value = cpu_to_le64(value);
5023df72d1cSEric Auger         bus->memwrite(bus, token.addr + off, &value, sizeof(value));
5031cf4323eSThomas Huth     }
5041cf4323eSThomas Huth }
5051cf4323eSThomas Huth 
qpci_memread(QPCIDevice * dev,QPCIBar token,uint64_t off,void * buf,size_t len)5061cf4323eSThomas Huth void qpci_memread(QPCIDevice *dev, QPCIBar token, uint64_t off,
5071cf4323eSThomas Huth                   void *buf, size_t len)
5081cf4323eSThomas Huth {
5093df72d1cSEric Auger     g_assert(!token.is_io);
5101cf4323eSThomas Huth     dev->bus->memread(dev->bus, token.addr + off, buf, len);
5111cf4323eSThomas Huth }
5121cf4323eSThomas Huth 
qpci_memwrite(QPCIDevice * dev,QPCIBar token,uint64_t off,const void * buf,size_t len)5131cf4323eSThomas Huth void qpci_memwrite(QPCIDevice *dev, QPCIBar token, uint64_t off,
5141cf4323eSThomas Huth                    const void *buf, size_t len)
5151cf4323eSThomas Huth {
5163df72d1cSEric Auger     g_assert(!token.is_io);
5171cf4323eSThomas Huth     dev->bus->memwrite(dev->bus, token.addr + off, buf, len);
5181cf4323eSThomas Huth }
5191cf4323eSThomas Huth 
qpci_iomap(QPCIDevice * dev,int barno,uint64_t * sizeptr)5201cf4323eSThomas Huth QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr)
5211cf4323eSThomas Huth {
5221cf4323eSThomas Huth     QPCIBus *bus = dev->bus;
5231cf4323eSThomas Huth     static const int bar_reg_map[] = {
5241cf4323eSThomas Huth         PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_1, PCI_BASE_ADDRESS_2,
5251cf4323eSThomas Huth         PCI_BASE_ADDRESS_3, PCI_BASE_ADDRESS_4, PCI_BASE_ADDRESS_5,
5261cf4323eSThomas Huth     };
5271cf4323eSThomas Huth     QPCIBar bar;
5281cf4323eSThomas Huth     int bar_reg;
5291cf4323eSThomas Huth     uint32_t addr, size;
5301cf4323eSThomas Huth     uint32_t io_type;
5311cf4323eSThomas Huth     uint64_t loc;
5321cf4323eSThomas Huth 
5331cf4323eSThomas Huth     g_assert(barno >= 0 && barno <= 5);
5341cf4323eSThomas Huth     bar_reg = bar_reg_map[barno];
5351cf4323eSThomas Huth 
5361cf4323eSThomas Huth     qpci_config_writel(dev, bar_reg, 0xFFFFFFFF);
5371cf4323eSThomas Huth     addr = qpci_config_readl(dev, bar_reg);
5381cf4323eSThomas Huth 
5391cf4323eSThomas Huth     io_type = addr & PCI_BASE_ADDRESS_SPACE;
5401cf4323eSThomas Huth     if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
5411cf4323eSThomas Huth         addr &= PCI_BASE_ADDRESS_IO_MASK;
5421cf4323eSThomas Huth     } else {
5431cf4323eSThomas Huth         addr &= PCI_BASE_ADDRESS_MEM_MASK;
5441cf4323eSThomas Huth     }
5451cf4323eSThomas Huth 
5461cf4323eSThomas Huth     g_assert(addr); /* Must have *some* size bits */
5471cf4323eSThomas Huth 
5481cf4323eSThomas Huth     size = 1U << ctz32(addr);
5491cf4323eSThomas Huth     if (sizeptr) {
5501cf4323eSThomas Huth         *sizeptr = size;
5511cf4323eSThomas Huth     }
5521cf4323eSThomas Huth 
5531cf4323eSThomas Huth     if (io_type == PCI_BASE_ADDRESS_SPACE_IO) {
5541cf4323eSThomas Huth         loc = QEMU_ALIGN_UP(bus->pio_alloc_ptr, size);
5551cf4323eSThomas Huth 
5561cf4323eSThomas Huth         g_assert(loc >= bus->pio_alloc_ptr);
5573df72d1cSEric Auger         g_assert(loc + size <= bus->pio_limit);
5581cf4323eSThomas Huth 
5591cf4323eSThomas Huth         bus->pio_alloc_ptr = loc + size;
5603df72d1cSEric Auger         bar.is_io = true;
5611cf4323eSThomas Huth 
5621cf4323eSThomas Huth         qpci_config_writel(dev, bar_reg, loc | PCI_BASE_ADDRESS_SPACE_IO);
5631cf4323eSThomas Huth     } else {
5641cf4323eSThomas Huth         loc = QEMU_ALIGN_UP(bus->mmio_alloc_ptr, size);
5651cf4323eSThomas Huth 
5661cf4323eSThomas Huth         /* Check for space */
5671cf4323eSThomas Huth         g_assert(loc >= bus->mmio_alloc_ptr);
5681cf4323eSThomas Huth         g_assert(loc + size <= bus->mmio_limit);
5691cf4323eSThomas Huth 
5701cf4323eSThomas Huth         bus->mmio_alloc_ptr = loc + size;
5713df72d1cSEric Auger         bar.is_io = false;
5721cf4323eSThomas Huth 
5731cf4323eSThomas Huth         qpci_config_writel(dev, bar_reg, loc);
5741cf4323eSThomas Huth     }
5751cf4323eSThomas Huth 
5761cf4323eSThomas Huth     bar.addr = loc;
5771cf4323eSThomas Huth     return bar;
5781cf4323eSThomas Huth }
5791cf4323eSThomas Huth 
qpci_iounmap(QPCIDevice * dev,QPCIBar bar)5801cf4323eSThomas Huth void qpci_iounmap(QPCIDevice *dev, QPCIBar bar)
5811cf4323eSThomas Huth {
5821cf4323eSThomas Huth     /* FIXME */
5831cf4323eSThomas Huth }
5841cf4323eSThomas Huth 
qpci_legacy_iomap(QPCIDevice * dev,uint16_t addr)5851cf4323eSThomas Huth QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr)
5861cf4323eSThomas Huth {
5873df72d1cSEric Auger     QPCIBar bar = { .addr = addr, .is_io = true };
5881cf4323eSThomas Huth     return bar;
5891cf4323eSThomas Huth }
5901cf4323eSThomas Huth 
add_qpci_address(QOSGraphEdgeOptions * opts,QPCIAddress * addr)5911cf4323eSThomas Huth void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr)
5921cf4323eSThomas Huth {
5931cf4323eSThomas Huth     g_assert(addr);
5941cf4323eSThomas Huth     g_assert(opts);
5951cf4323eSThomas Huth 
5961cf4323eSThomas Huth     opts->arg = addr;
5971cf4323eSThomas Huth     opts->size_arg = sizeof(QPCIAddress);
5981cf4323eSThomas Huth }
599