xref: /qemu/hw/xen/xen_pt_msi.c (revision 22fc860b)
180b4ecc8SPaolo Bonzini /*
280b4ecc8SPaolo Bonzini  * Copyright (c) 2007, Intel Corporation.
380b4ecc8SPaolo Bonzini  *
480b4ecc8SPaolo Bonzini  * This work is licensed under the terms of the GNU GPL, version 2.  See
580b4ecc8SPaolo Bonzini  * the COPYING file in the top-level directory.
680b4ecc8SPaolo Bonzini  *
780b4ecc8SPaolo Bonzini  * Jiang Yunhong <yunhong.jiang@intel.com>
880b4ecc8SPaolo Bonzini  *
980b4ecc8SPaolo Bonzini  * This file implements direct PCI assignment to a HVM guest
1080b4ecc8SPaolo Bonzini  */
1180b4ecc8SPaolo Bonzini 
1280b4ecc8SPaolo Bonzini #include <sys/mman.h>
1380b4ecc8SPaolo Bonzini 
1480b4ecc8SPaolo Bonzini #include "hw/xen/xen_backend.h"
1547b43a1fSPaolo Bonzini #include "xen_pt.h"
1680b4ecc8SPaolo Bonzini #include "hw/i386/apic-msidef.h"
1780b4ecc8SPaolo Bonzini 
1880b4ecc8SPaolo Bonzini 
1980b4ecc8SPaolo Bonzini #define XEN_PT_AUTO_ASSIGN -1
2080b4ecc8SPaolo Bonzini 
2180b4ecc8SPaolo Bonzini /* shift count for gflags */
2280b4ecc8SPaolo Bonzini #define XEN_PT_GFLAGS_SHIFT_DEST_ID        0
2380b4ecc8SPaolo Bonzini #define XEN_PT_GFLAGS_SHIFT_RH             8
2480b4ecc8SPaolo Bonzini #define XEN_PT_GFLAGS_SHIFT_DM             9
2580b4ecc8SPaolo Bonzini #define XEN_PT_GFLAGSSHIFT_DELIV_MODE     12
2680b4ecc8SPaolo Bonzini #define XEN_PT_GFLAGSSHIFT_TRG_MODE       15
2780b4ecc8SPaolo Bonzini 
2880b4ecc8SPaolo Bonzini 
2980b4ecc8SPaolo Bonzini /*
3080b4ecc8SPaolo Bonzini  * Helpers
3180b4ecc8SPaolo Bonzini  */
3280b4ecc8SPaolo Bonzini 
3380b4ecc8SPaolo Bonzini static inline uint8_t msi_vector(uint32_t data)
3480b4ecc8SPaolo Bonzini {
3580b4ecc8SPaolo Bonzini     return (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
3680b4ecc8SPaolo Bonzini }
3780b4ecc8SPaolo Bonzini 
3880b4ecc8SPaolo Bonzini static inline uint8_t msi_dest_id(uint32_t addr)
3980b4ecc8SPaolo Bonzini {
4080b4ecc8SPaolo Bonzini     return (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
4180b4ecc8SPaolo Bonzini }
4280b4ecc8SPaolo Bonzini 
4380b4ecc8SPaolo Bonzini static inline uint32_t msi_ext_dest_id(uint32_t addr_hi)
4480b4ecc8SPaolo Bonzini {
4580b4ecc8SPaolo Bonzini     return addr_hi & 0xffffff00;
4680b4ecc8SPaolo Bonzini }
4780b4ecc8SPaolo Bonzini 
4880b4ecc8SPaolo Bonzini static uint32_t msi_gflags(uint32_t data, uint64_t addr)
4980b4ecc8SPaolo Bonzini {
5080b4ecc8SPaolo Bonzini     uint32_t result = 0;
5180b4ecc8SPaolo Bonzini     int rh, dm, dest_id, deliv_mode, trig_mode;
5280b4ecc8SPaolo Bonzini 
5380b4ecc8SPaolo Bonzini     rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1;
5480b4ecc8SPaolo Bonzini     dm = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1;
5580b4ecc8SPaolo Bonzini     dest_id = msi_dest_id(addr);
5680b4ecc8SPaolo Bonzini     deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
5780b4ecc8SPaolo Bonzini     trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
5880b4ecc8SPaolo Bonzini 
5980b4ecc8SPaolo Bonzini     result = dest_id | (rh << XEN_PT_GFLAGS_SHIFT_RH)
6080b4ecc8SPaolo Bonzini         | (dm << XEN_PT_GFLAGS_SHIFT_DM)
6180b4ecc8SPaolo Bonzini         | (deliv_mode << XEN_PT_GFLAGSSHIFT_DELIV_MODE)
6280b4ecc8SPaolo Bonzini         | (trig_mode << XEN_PT_GFLAGSSHIFT_TRG_MODE);
6380b4ecc8SPaolo Bonzini 
6480b4ecc8SPaolo Bonzini     return result;
6580b4ecc8SPaolo Bonzini }
6680b4ecc8SPaolo Bonzini 
6780b4ecc8SPaolo Bonzini static inline uint64_t msi_addr64(XenPTMSI *msi)
6880b4ecc8SPaolo Bonzini {
6980b4ecc8SPaolo Bonzini     return (uint64_t)msi->addr_hi << 32 | msi->addr_lo;
7080b4ecc8SPaolo Bonzini }
7180b4ecc8SPaolo Bonzini 
7280b4ecc8SPaolo Bonzini static int msi_msix_enable(XenPCIPassthroughState *s,
7380b4ecc8SPaolo Bonzini                            uint32_t address,
7480b4ecc8SPaolo Bonzini                            uint16_t flag,
7580b4ecc8SPaolo Bonzini                            bool enable)
7680b4ecc8SPaolo Bonzini {
7780b4ecc8SPaolo Bonzini     uint16_t val = 0;
7880b4ecc8SPaolo Bonzini 
7980b4ecc8SPaolo Bonzini     if (!address) {
8080b4ecc8SPaolo Bonzini         return -1;
8180b4ecc8SPaolo Bonzini     }
8280b4ecc8SPaolo Bonzini 
8380b4ecc8SPaolo Bonzini     xen_host_pci_get_word(&s->real_device, address, &val);
8480b4ecc8SPaolo Bonzini     if (enable) {
8580b4ecc8SPaolo Bonzini         val |= flag;
8680b4ecc8SPaolo Bonzini     } else {
8780b4ecc8SPaolo Bonzini         val &= ~flag;
8880b4ecc8SPaolo Bonzini     }
8980b4ecc8SPaolo Bonzini     xen_host_pci_set_word(&s->real_device, address, val);
9080b4ecc8SPaolo Bonzini     return 0;
9180b4ecc8SPaolo Bonzini }
9280b4ecc8SPaolo Bonzini 
9380b4ecc8SPaolo Bonzini static int msi_msix_setup(XenPCIPassthroughState *s,
9480b4ecc8SPaolo Bonzini                           uint64_t addr,
9580b4ecc8SPaolo Bonzini                           uint32_t data,
9680b4ecc8SPaolo Bonzini                           int *ppirq,
9780b4ecc8SPaolo Bonzini                           bool is_msix,
9880b4ecc8SPaolo Bonzini                           int msix_entry,
9980b4ecc8SPaolo Bonzini                           bool is_not_mapped)
10080b4ecc8SPaolo Bonzini {
10180b4ecc8SPaolo Bonzini     uint8_t gvec = msi_vector(data);
10280b4ecc8SPaolo Bonzini     int rc = 0;
10380b4ecc8SPaolo Bonzini 
10480b4ecc8SPaolo Bonzini     assert((!is_msix && msix_entry == 0) || is_msix);
10580b4ecc8SPaolo Bonzini 
10680b4ecc8SPaolo Bonzini     if (gvec == 0) {
10780b4ecc8SPaolo Bonzini         /* if gvec is 0, the guest is asking for a particular pirq that
10880b4ecc8SPaolo Bonzini          * is passed as dest_id */
10980b4ecc8SPaolo Bonzini         *ppirq = msi_ext_dest_id(addr >> 32) | msi_dest_id(addr);
11080b4ecc8SPaolo Bonzini         if (!*ppirq) {
11180b4ecc8SPaolo Bonzini             /* this probably identifies an misconfiguration of the guest,
11280b4ecc8SPaolo Bonzini              * try the emulated path */
11380b4ecc8SPaolo Bonzini             *ppirq = XEN_PT_UNASSIGNED_PIRQ;
11480b4ecc8SPaolo Bonzini         } else {
11580b4ecc8SPaolo Bonzini             XEN_PT_LOG(&s->dev, "requested pirq %d for MSI%s"
11680b4ecc8SPaolo Bonzini                        " (vec: %#x, entry: %#x)\n",
11780b4ecc8SPaolo Bonzini                        *ppirq, is_msix ? "-X" : "", gvec, msix_entry);
11880b4ecc8SPaolo Bonzini         }
11980b4ecc8SPaolo Bonzini     }
12080b4ecc8SPaolo Bonzini 
12180b4ecc8SPaolo Bonzini     if (is_not_mapped) {
12280b4ecc8SPaolo Bonzini         uint64_t table_base = 0;
12380b4ecc8SPaolo Bonzini 
12480b4ecc8SPaolo Bonzini         if (is_msix) {
12580b4ecc8SPaolo Bonzini             table_base = s->msix->table_base;
12680b4ecc8SPaolo Bonzini         }
12780b4ecc8SPaolo Bonzini 
12880b4ecc8SPaolo Bonzini         rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, XEN_PT_AUTO_ASSIGN,
12980b4ecc8SPaolo Bonzini                                      ppirq, PCI_DEVFN(s->real_device.dev,
13080b4ecc8SPaolo Bonzini                                                       s->real_device.func),
13180b4ecc8SPaolo Bonzini                                      s->real_device.bus,
13280b4ecc8SPaolo Bonzini                                      msix_entry, table_base);
13380b4ecc8SPaolo Bonzini         if (rc) {
13480b4ecc8SPaolo Bonzini             XEN_PT_ERR(&s->dev,
13580b4ecc8SPaolo Bonzini                        "Mapping of MSI%s (rc: %i, vec: %#x, entry %#x)\n",
13680b4ecc8SPaolo Bonzini                        is_msix ? "-X" : "", rc, gvec, msix_entry);
13780b4ecc8SPaolo Bonzini             return rc;
13880b4ecc8SPaolo Bonzini         }
13980b4ecc8SPaolo Bonzini     }
14080b4ecc8SPaolo Bonzini 
14180b4ecc8SPaolo Bonzini     return 0;
14280b4ecc8SPaolo Bonzini }
14380b4ecc8SPaolo Bonzini static int msi_msix_update(XenPCIPassthroughState *s,
14480b4ecc8SPaolo Bonzini                            uint64_t addr,
14580b4ecc8SPaolo Bonzini                            uint32_t data,
14680b4ecc8SPaolo Bonzini                            int pirq,
14780b4ecc8SPaolo Bonzini                            bool is_msix,
14880b4ecc8SPaolo Bonzini                            int msix_entry,
14980b4ecc8SPaolo Bonzini                            int *old_pirq)
15080b4ecc8SPaolo Bonzini {
15180b4ecc8SPaolo Bonzini     PCIDevice *d = &s->dev;
15280b4ecc8SPaolo Bonzini     uint8_t gvec = msi_vector(data);
15380b4ecc8SPaolo Bonzini     uint32_t gflags = msi_gflags(data, addr);
15480b4ecc8SPaolo Bonzini     int rc = 0;
15580b4ecc8SPaolo Bonzini     uint64_t table_addr = 0;
15680b4ecc8SPaolo Bonzini 
15780b4ecc8SPaolo Bonzini     XEN_PT_LOG(d, "Updating MSI%s with pirq %d gvec %#x gflags %#x"
15880b4ecc8SPaolo Bonzini                " (entry: %#x)\n",
15980b4ecc8SPaolo Bonzini                is_msix ? "-X" : "", pirq, gvec, gflags, msix_entry);
16080b4ecc8SPaolo Bonzini 
16180b4ecc8SPaolo Bonzini     if (is_msix) {
16280b4ecc8SPaolo Bonzini         table_addr = s->msix->mmio_base_addr;
16380b4ecc8SPaolo Bonzini     }
16480b4ecc8SPaolo Bonzini 
16580b4ecc8SPaolo Bonzini     rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec,
16680b4ecc8SPaolo Bonzini                                   pirq, gflags, table_addr);
16780b4ecc8SPaolo Bonzini 
16880b4ecc8SPaolo Bonzini     if (rc) {
16980b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Updating of MSI%s failed. (rc: %d)\n",
17080b4ecc8SPaolo Bonzini                    is_msix ? "-X" : "", rc);
17180b4ecc8SPaolo Bonzini 
17280b4ecc8SPaolo Bonzini         if (xc_physdev_unmap_pirq(xen_xc, xen_domid, *old_pirq)) {
17380b4ecc8SPaolo Bonzini             XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed.\n",
17480b4ecc8SPaolo Bonzini                        is_msix ? "-X" : "", *old_pirq);
17580b4ecc8SPaolo Bonzini         }
17680b4ecc8SPaolo Bonzini         *old_pirq = XEN_PT_UNASSIGNED_PIRQ;
17780b4ecc8SPaolo Bonzini     }
17880b4ecc8SPaolo Bonzini     return rc;
17980b4ecc8SPaolo Bonzini }
18080b4ecc8SPaolo Bonzini 
18180b4ecc8SPaolo Bonzini static int msi_msix_disable(XenPCIPassthroughState *s,
18280b4ecc8SPaolo Bonzini                             uint64_t addr,
18380b4ecc8SPaolo Bonzini                             uint32_t data,
18480b4ecc8SPaolo Bonzini                             int pirq,
18580b4ecc8SPaolo Bonzini                             bool is_msix,
18680b4ecc8SPaolo Bonzini                             bool is_binded)
18780b4ecc8SPaolo Bonzini {
18880b4ecc8SPaolo Bonzini     PCIDevice *d = &s->dev;
18980b4ecc8SPaolo Bonzini     uint8_t gvec = msi_vector(data);
19080b4ecc8SPaolo Bonzini     uint32_t gflags = msi_gflags(data, addr);
19180b4ecc8SPaolo Bonzini     int rc = 0;
19280b4ecc8SPaolo Bonzini 
19380b4ecc8SPaolo Bonzini     if (pirq == XEN_PT_UNASSIGNED_PIRQ) {
19480b4ecc8SPaolo Bonzini         return 0;
19580b4ecc8SPaolo Bonzini     }
19680b4ecc8SPaolo Bonzini 
19780b4ecc8SPaolo Bonzini     if (is_binded) {
19880b4ecc8SPaolo Bonzini         XEN_PT_LOG(d, "Unbind MSI%s with pirq %d, gvec %#x\n",
19980b4ecc8SPaolo Bonzini                    is_msix ? "-X" : "", pirq, gvec);
20080b4ecc8SPaolo Bonzini         rc = xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags);
20180b4ecc8SPaolo Bonzini         if (rc) {
20280b4ecc8SPaolo Bonzini             XEN_PT_ERR(d, "Unbinding of MSI%s failed. (pirq: %d, gvec: %#x)\n",
20380b4ecc8SPaolo Bonzini                        is_msix ? "-X" : "", pirq, gvec);
20480b4ecc8SPaolo Bonzini             return rc;
20580b4ecc8SPaolo Bonzini         }
20680b4ecc8SPaolo Bonzini     }
20780b4ecc8SPaolo Bonzini 
20880b4ecc8SPaolo Bonzini     XEN_PT_LOG(d, "Unmap MSI%s pirq %d\n", is_msix ? "-X" : "", pirq);
20980b4ecc8SPaolo Bonzini     rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, pirq);
21080b4ecc8SPaolo Bonzini     if (rc) {
21180b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (rc: %i)\n",
21280b4ecc8SPaolo Bonzini                    is_msix ? "-X" : "", pirq, rc);
21380b4ecc8SPaolo Bonzini         return rc;
21480b4ecc8SPaolo Bonzini     }
21580b4ecc8SPaolo Bonzini 
21680b4ecc8SPaolo Bonzini     return 0;
21780b4ecc8SPaolo Bonzini }
21880b4ecc8SPaolo Bonzini 
21980b4ecc8SPaolo Bonzini /*
22080b4ecc8SPaolo Bonzini  * MSI virtualization functions
22180b4ecc8SPaolo Bonzini  */
22280b4ecc8SPaolo Bonzini 
22380b4ecc8SPaolo Bonzini int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable)
22480b4ecc8SPaolo Bonzini {
22580b4ecc8SPaolo Bonzini     XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling");
22680b4ecc8SPaolo Bonzini 
22780b4ecc8SPaolo Bonzini     if (!s->msi) {
22880b4ecc8SPaolo Bonzini         return -1;
22980b4ecc8SPaolo Bonzini     }
23080b4ecc8SPaolo Bonzini 
23180b4ecc8SPaolo Bonzini     return msi_msix_enable(s, s->msi->ctrl_offset, PCI_MSI_FLAGS_ENABLE,
23280b4ecc8SPaolo Bonzini                            enable);
23380b4ecc8SPaolo Bonzini }
23480b4ecc8SPaolo Bonzini 
23580b4ecc8SPaolo Bonzini /* setup physical msi, but don't enable it */
23680b4ecc8SPaolo Bonzini int xen_pt_msi_setup(XenPCIPassthroughState *s)
23780b4ecc8SPaolo Bonzini {
23880b4ecc8SPaolo Bonzini     int pirq = XEN_PT_UNASSIGNED_PIRQ;
23980b4ecc8SPaolo Bonzini     int rc = 0;
24080b4ecc8SPaolo Bonzini     XenPTMSI *msi = s->msi;
24180b4ecc8SPaolo Bonzini 
24280b4ecc8SPaolo Bonzini     if (msi->initialized) {
24380b4ecc8SPaolo Bonzini         XEN_PT_ERR(&s->dev,
24480b4ecc8SPaolo Bonzini                    "Setup physical MSI when it has been properly initialized.\n");
24580b4ecc8SPaolo Bonzini         return -1;
24680b4ecc8SPaolo Bonzini     }
24780b4ecc8SPaolo Bonzini 
24880b4ecc8SPaolo Bonzini     rc = msi_msix_setup(s, msi_addr64(msi), msi->data, &pirq, false, 0, true);
24980b4ecc8SPaolo Bonzini     if (rc) {
25080b4ecc8SPaolo Bonzini         return rc;
25180b4ecc8SPaolo Bonzini     }
25280b4ecc8SPaolo Bonzini 
25380b4ecc8SPaolo Bonzini     if (pirq < 0) {
25480b4ecc8SPaolo Bonzini         XEN_PT_ERR(&s->dev, "Invalid pirq number: %d.\n", pirq);
25580b4ecc8SPaolo Bonzini         return -1;
25680b4ecc8SPaolo Bonzini     }
25780b4ecc8SPaolo Bonzini 
25880b4ecc8SPaolo Bonzini     msi->pirq = pirq;
25980b4ecc8SPaolo Bonzini     XEN_PT_LOG(&s->dev, "MSI mapped with pirq %d.\n", pirq);
26080b4ecc8SPaolo Bonzini 
26180b4ecc8SPaolo Bonzini     return 0;
26280b4ecc8SPaolo Bonzini }
26380b4ecc8SPaolo Bonzini 
26480b4ecc8SPaolo Bonzini int xen_pt_msi_update(XenPCIPassthroughState *s)
26580b4ecc8SPaolo Bonzini {
26680b4ecc8SPaolo Bonzini     XenPTMSI *msi = s->msi;
26780b4ecc8SPaolo Bonzini     return msi_msix_update(s, msi_addr64(msi), msi->data, msi->pirq,
26880b4ecc8SPaolo Bonzini                            false, 0, &msi->pirq);
26980b4ecc8SPaolo Bonzini }
27080b4ecc8SPaolo Bonzini 
27180b4ecc8SPaolo Bonzini void xen_pt_msi_disable(XenPCIPassthroughState *s)
27280b4ecc8SPaolo Bonzini {
27380b4ecc8SPaolo Bonzini     XenPTMSI *msi = s->msi;
27480b4ecc8SPaolo Bonzini 
27580b4ecc8SPaolo Bonzini     if (!msi) {
27680b4ecc8SPaolo Bonzini         return;
27780b4ecc8SPaolo Bonzini     }
27880b4ecc8SPaolo Bonzini 
27980b4ecc8SPaolo Bonzini     xen_pt_msi_set_enable(s, false);
28080b4ecc8SPaolo Bonzini 
28180b4ecc8SPaolo Bonzini     msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false,
28280b4ecc8SPaolo Bonzini                      msi->initialized);
28380b4ecc8SPaolo Bonzini 
28480b4ecc8SPaolo Bonzini     /* clear msi info */
28580b4ecc8SPaolo Bonzini     msi->flags = 0;
28680b4ecc8SPaolo Bonzini     msi->mapped = false;
28780b4ecc8SPaolo Bonzini     msi->pirq = XEN_PT_UNASSIGNED_PIRQ;
28880b4ecc8SPaolo Bonzini }
28980b4ecc8SPaolo Bonzini 
29080b4ecc8SPaolo Bonzini /*
29180b4ecc8SPaolo Bonzini  * MSI-X virtualization functions
29280b4ecc8SPaolo Bonzini  */
29380b4ecc8SPaolo Bonzini 
29480b4ecc8SPaolo Bonzini static int msix_set_enable(XenPCIPassthroughState *s, bool enabled)
29580b4ecc8SPaolo Bonzini {
29680b4ecc8SPaolo Bonzini     XEN_PT_LOG(&s->dev, "%s MSI-X.\n", enabled ? "enabling" : "disabling");
29780b4ecc8SPaolo Bonzini 
29880b4ecc8SPaolo Bonzini     if (!s->msix) {
29980b4ecc8SPaolo Bonzini         return -1;
30080b4ecc8SPaolo Bonzini     }
30180b4ecc8SPaolo Bonzini 
30280b4ecc8SPaolo Bonzini     return msi_msix_enable(s, s->msix->ctrl_offset, PCI_MSIX_FLAGS_ENABLE,
30380b4ecc8SPaolo Bonzini                            enabled);
30480b4ecc8SPaolo Bonzini }
30580b4ecc8SPaolo Bonzini 
30680b4ecc8SPaolo Bonzini static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr)
30780b4ecc8SPaolo Bonzini {
30880b4ecc8SPaolo Bonzini     XenPTMSIXEntry *entry = NULL;
30980b4ecc8SPaolo Bonzini     int pirq;
31080b4ecc8SPaolo Bonzini     int rc;
31180b4ecc8SPaolo Bonzini 
31280b4ecc8SPaolo Bonzini     if (entry_nr < 0 || entry_nr >= s->msix->total_entries) {
31380b4ecc8SPaolo Bonzini         return -EINVAL;
31480b4ecc8SPaolo Bonzini     }
31580b4ecc8SPaolo Bonzini 
31680b4ecc8SPaolo Bonzini     entry = &s->msix->msix_entry[entry_nr];
31780b4ecc8SPaolo Bonzini 
31880b4ecc8SPaolo Bonzini     if (!entry->updated) {
31980b4ecc8SPaolo Bonzini         return 0;
32080b4ecc8SPaolo Bonzini     }
32180b4ecc8SPaolo Bonzini 
32280b4ecc8SPaolo Bonzini     pirq = entry->pirq;
32380b4ecc8SPaolo Bonzini 
32480b4ecc8SPaolo Bonzini     rc = msi_msix_setup(s, entry->addr, entry->data, &pirq, true, entry_nr,
32580b4ecc8SPaolo Bonzini                         entry->pirq == XEN_PT_UNASSIGNED_PIRQ);
32680b4ecc8SPaolo Bonzini     if (rc) {
32780b4ecc8SPaolo Bonzini         return rc;
32880b4ecc8SPaolo Bonzini     }
32980b4ecc8SPaolo Bonzini     if (entry->pirq == XEN_PT_UNASSIGNED_PIRQ) {
33080b4ecc8SPaolo Bonzini         entry->pirq = pirq;
33180b4ecc8SPaolo Bonzini     }
33280b4ecc8SPaolo Bonzini 
33380b4ecc8SPaolo Bonzini     rc = msi_msix_update(s, entry->addr, entry->data, pirq, true,
33480b4ecc8SPaolo Bonzini                          entry_nr, &entry->pirq);
33580b4ecc8SPaolo Bonzini 
33680b4ecc8SPaolo Bonzini     if (!rc) {
33780b4ecc8SPaolo Bonzini         entry->updated = false;
33880b4ecc8SPaolo Bonzini     }
33980b4ecc8SPaolo Bonzini 
34080b4ecc8SPaolo Bonzini     return rc;
34180b4ecc8SPaolo Bonzini }
34280b4ecc8SPaolo Bonzini 
34380b4ecc8SPaolo Bonzini int xen_pt_msix_update(XenPCIPassthroughState *s)
34480b4ecc8SPaolo Bonzini {
34580b4ecc8SPaolo Bonzini     XenPTMSIX *msix = s->msix;
34680b4ecc8SPaolo Bonzini     int i;
34780b4ecc8SPaolo Bonzini 
34880b4ecc8SPaolo Bonzini     for (i = 0; i < msix->total_entries; i++) {
34980b4ecc8SPaolo Bonzini         xen_pt_msix_update_one(s, i);
35080b4ecc8SPaolo Bonzini     }
35180b4ecc8SPaolo Bonzini 
35280b4ecc8SPaolo Bonzini     return 0;
35380b4ecc8SPaolo Bonzini }
35480b4ecc8SPaolo Bonzini 
35580b4ecc8SPaolo Bonzini void xen_pt_msix_disable(XenPCIPassthroughState *s)
35680b4ecc8SPaolo Bonzini {
35780b4ecc8SPaolo Bonzini     int i = 0;
35880b4ecc8SPaolo Bonzini 
35980b4ecc8SPaolo Bonzini     msix_set_enable(s, false);
36080b4ecc8SPaolo Bonzini 
36180b4ecc8SPaolo Bonzini     for (i = 0; i < s->msix->total_entries; i++) {
36280b4ecc8SPaolo Bonzini         XenPTMSIXEntry *entry = &s->msix->msix_entry[i];
36380b4ecc8SPaolo Bonzini 
36480b4ecc8SPaolo Bonzini         msi_msix_disable(s, entry->addr, entry->data, entry->pirq, true, true);
36580b4ecc8SPaolo Bonzini 
36680b4ecc8SPaolo Bonzini         /* clear MSI-X info */
36780b4ecc8SPaolo Bonzini         entry->pirq = XEN_PT_UNASSIGNED_PIRQ;
36880b4ecc8SPaolo Bonzini         entry->updated = false;
36980b4ecc8SPaolo Bonzini     }
37080b4ecc8SPaolo Bonzini }
37180b4ecc8SPaolo Bonzini 
37280b4ecc8SPaolo Bonzini int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index)
37380b4ecc8SPaolo Bonzini {
37480b4ecc8SPaolo Bonzini     XenPTMSIXEntry *entry;
37580b4ecc8SPaolo Bonzini     int i, ret;
37680b4ecc8SPaolo Bonzini 
37780b4ecc8SPaolo Bonzini     if (!(s->msix && s->msix->bar_index == bar_index)) {
37880b4ecc8SPaolo Bonzini         return 0;
37980b4ecc8SPaolo Bonzini     }
38080b4ecc8SPaolo Bonzini 
38180b4ecc8SPaolo Bonzini     for (i = 0; i < s->msix->total_entries; i++) {
38280b4ecc8SPaolo Bonzini         entry = &s->msix->msix_entry[i];
38380b4ecc8SPaolo Bonzini         if (entry->pirq != XEN_PT_UNASSIGNED_PIRQ) {
38480b4ecc8SPaolo Bonzini             ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq,
38580b4ecc8SPaolo Bonzini                                           PT_IRQ_TYPE_MSI, 0, 0, 0, 0);
38680b4ecc8SPaolo Bonzini             if (ret) {
38780b4ecc8SPaolo Bonzini                 XEN_PT_ERR(&s->dev, "unbind MSI-X entry %d failed\n",
38880b4ecc8SPaolo Bonzini                            entry->pirq);
38980b4ecc8SPaolo Bonzini             }
39080b4ecc8SPaolo Bonzini             entry->updated = true;
39180b4ecc8SPaolo Bonzini         }
39280b4ecc8SPaolo Bonzini     }
39380b4ecc8SPaolo Bonzini     return xen_pt_msix_update(s);
39480b4ecc8SPaolo Bonzini }
39580b4ecc8SPaolo Bonzini 
39680b4ecc8SPaolo Bonzini static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset)
39780b4ecc8SPaolo Bonzini {
39880b4ecc8SPaolo Bonzini     switch (offset) {
39980b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_LOWER_ADDR:
40080b4ecc8SPaolo Bonzini         return e->addr & UINT32_MAX;
40180b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_UPPER_ADDR:
40280b4ecc8SPaolo Bonzini         return e->addr >> 32;
40380b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_DATA:
40480b4ecc8SPaolo Bonzini         return e->data;
40580b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_VECTOR_CTRL:
40680b4ecc8SPaolo Bonzini         return e->vector_ctrl;
40780b4ecc8SPaolo Bonzini     default:
40880b4ecc8SPaolo Bonzini         return 0;
40980b4ecc8SPaolo Bonzini     }
41080b4ecc8SPaolo Bonzini }
41180b4ecc8SPaolo Bonzini 
41280b4ecc8SPaolo Bonzini static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val)
41380b4ecc8SPaolo Bonzini {
41480b4ecc8SPaolo Bonzini     switch (offset) {
41580b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_LOWER_ADDR:
41680b4ecc8SPaolo Bonzini         e->addr = (e->addr & ((uint64_t)UINT32_MAX << 32)) | val;
41780b4ecc8SPaolo Bonzini         break;
41880b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_UPPER_ADDR:
41980b4ecc8SPaolo Bonzini         e->addr = (uint64_t)val << 32 | (e->addr & UINT32_MAX);
42080b4ecc8SPaolo Bonzini         break;
42180b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_DATA:
42280b4ecc8SPaolo Bonzini         e->data = val;
42380b4ecc8SPaolo Bonzini         break;
42480b4ecc8SPaolo Bonzini     case PCI_MSIX_ENTRY_VECTOR_CTRL:
42580b4ecc8SPaolo Bonzini         e->vector_ctrl = val;
42680b4ecc8SPaolo Bonzini         break;
42780b4ecc8SPaolo Bonzini     }
42880b4ecc8SPaolo Bonzini }
42980b4ecc8SPaolo Bonzini 
43080b4ecc8SPaolo Bonzini static void pci_msix_write(void *opaque, hwaddr addr,
43180b4ecc8SPaolo Bonzini                            uint64_t val, unsigned size)
43280b4ecc8SPaolo Bonzini {
43380b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s = opaque;
43480b4ecc8SPaolo Bonzini     XenPTMSIX *msix = s->msix;
43580b4ecc8SPaolo Bonzini     XenPTMSIXEntry *entry;
43680b4ecc8SPaolo Bonzini     int entry_nr, offset;
43780b4ecc8SPaolo Bonzini 
43880b4ecc8SPaolo Bonzini     entry_nr = addr / PCI_MSIX_ENTRY_SIZE;
43980b4ecc8SPaolo Bonzini     if (entry_nr < 0 || entry_nr >= msix->total_entries) {
44080b4ecc8SPaolo Bonzini         XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr);
44180b4ecc8SPaolo Bonzini         return;
44280b4ecc8SPaolo Bonzini     }
44380b4ecc8SPaolo Bonzini     entry = &msix->msix_entry[entry_nr];
44480b4ecc8SPaolo Bonzini     offset = addr % PCI_MSIX_ENTRY_SIZE;
44580b4ecc8SPaolo Bonzini 
44680b4ecc8SPaolo Bonzini     if (offset != PCI_MSIX_ENTRY_VECTOR_CTRL) {
44780b4ecc8SPaolo Bonzini         const volatile uint32_t *vec_ctrl;
44880b4ecc8SPaolo Bonzini 
44980b4ecc8SPaolo Bonzini         if (get_entry_value(entry, offset) == val) {
45080b4ecc8SPaolo Bonzini             return;
45180b4ecc8SPaolo Bonzini         }
45280b4ecc8SPaolo Bonzini 
45380b4ecc8SPaolo Bonzini         /*
45480b4ecc8SPaolo Bonzini          * If Xen intercepts the mask bit access, entry->vec_ctrl may not be
45580b4ecc8SPaolo Bonzini          * up-to-date. Read from hardware directly.
45680b4ecc8SPaolo Bonzini          */
45780b4ecc8SPaolo Bonzini         vec_ctrl = s->msix->phys_iomem_base + entry_nr * PCI_MSIX_ENTRY_SIZE
45880b4ecc8SPaolo Bonzini             + PCI_MSIX_ENTRY_VECTOR_CTRL;
45980b4ecc8SPaolo Bonzini 
46080b4ecc8SPaolo Bonzini         if (msix->enabled && !(*vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
46180b4ecc8SPaolo Bonzini             XEN_PT_ERR(&s->dev, "Can't update msix entry %d since MSI-X is"
46280b4ecc8SPaolo Bonzini                        " already enabled.\n", entry_nr);
46380b4ecc8SPaolo Bonzini             return;
46480b4ecc8SPaolo Bonzini         }
46580b4ecc8SPaolo Bonzini 
46680b4ecc8SPaolo Bonzini         entry->updated = true;
46780b4ecc8SPaolo Bonzini     }
46880b4ecc8SPaolo Bonzini 
46980b4ecc8SPaolo Bonzini     set_entry_value(entry, offset, val);
47080b4ecc8SPaolo Bonzini 
47180b4ecc8SPaolo Bonzini     if (offset == PCI_MSIX_ENTRY_VECTOR_CTRL) {
47280b4ecc8SPaolo Bonzini         if (msix->enabled && !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) {
47380b4ecc8SPaolo Bonzini             xen_pt_msix_update_one(s, entry_nr);
47480b4ecc8SPaolo Bonzini         }
47580b4ecc8SPaolo Bonzini     }
47680b4ecc8SPaolo Bonzini }
47780b4ecc8SPaolo Bonzini 
47880b4ecc8SPaolo Bonzini static uint64_t pci_msix_read(void *opaque, hwaddr addr,
47980b4ecc8SPaolo Bonzini                               unsigned size)
48080b4ecc8SPaolo Bonzini {
48180b4ecc8SPaolo Bonzini     XenPCIPassthroughState *s = opaque;
48280b4ecc8SPaolo Bonzini     XenPTMSIX *msix = s->msix;
48380b4ecc8SPaolo Bonzini     int entry_nr, offset;
48480b4ecc8SPaolo Bonzini 
48580b4ecc8SPaolo Bonzini     entry_nr = addr / PCI_MSIX_ENTRY_SIZE;
48680b4ecc8SPaolo Bonzini     if (entry_nr < 0) {
48780b4ecc8SPaolo Bonzini         XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr);
48880b4ecc8SPaolo Bonzini         return 0;
48980b4ecc8SPaolo Bonzini     }
49080b4ecc8SPaolo Bonzini 
49180b4ecc8SPaolo Bonzini     offset = addr % PCI_MSIX_ENTRY_SIZE;
49280b4ecc8SPaolo Bonzini 
49380b4ecc8SPaolo Bonzini     if (addr < msix->total_entries * PCI_MSIX_ENTRY_SIZE) {
49480b4ecc8SPaolo Bonzini         return get_entry_value(&msix->msix_entry[entry_nr], offset);
49580b4ecc8SPaolo Bonzini     } else {
49680b4ecc8SPaolo Bonzini         /* Pending Bit Array (PBA) */
49780b4ecc8SPaolo Bonzini         return *(uint32_t *)(msix->phys_iomem_base + addr);
49880b4ecc8SPaolo Bonzini     }
49980b4ecc8SPaolo Bonzini }
50080b4ecc8SPaolo Bonzini 
50180b4ecc8SPaolo Bonzini static const MemoryRegionOps pci_msix_ops = {
50280b4ecc8SPaolo Bonzini     .read = pci_msix_read,
50380b4ecc8SPaolo Bonzini     .write = pci_msix_write,
50480b4ecc8SPaolo Bonzini     .endianness = DEVICE_NATIVE_ENDIAN,
50580b4ecc8SPaolo Bonzini     .valid = {
50680b4ecc8SPaolo Bonzini         .min_access_size = 4,
50780b4ecc8SPaolo Bonzini         .max_access_size = 4,
50880b4ecc8SPaolo Bonzini         .unaligned = false,
50980b4ecc8SPaolo Bonzini     },
51080b4ecc8SPaolo Bonzini };
51180b4ecc8SPaolo Bonzini 
51280b4ecc8SPaolo Bonzini int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base)
51380b4ecc8SPaolo Bonzini {
51480b4ecc8SPaolo Bonzini     uint8_t id = 0;
51580b4ecc8SPaolo Bonzini     uint16_t control = 0;
51680b4ecc8SPaolo Bonzini     uint32_t table_off = 0;
51780b4ecc8SPaolo Bonzini     int i, total_entries, bar_index;
51880b4ecc8SPaolo Bonzini     XenHostPCIDevice *hd = &s->real_device;
51980b4ecc8SPaolo Bonzini     PCIDevice *d = &s->dev;
52080b4ecc8SPaolo Bonzini     int fd = -1;
52180b4ecc8SPaolo Bonzini     XenPTMSIX *msix = NULL;
52280b4ecc8SPaolo Bonzini     int rc = 0;
52380b4ecc8SPaolo Bonzini 
52480b4ecc8SPaolo Bonzini     rc = xen_host_pci_get_byte(hd, base + PCI_CAP_LIST_ID, &id);
52580b4ecc8SPaolo Bonzini     if (rc) {
52680b4ecc8SPaolo Bonzini         return rc;
52780b4ecc8SPaolo Bonzini     }
52880b4ecc8SPaolo Bonzini 
52980b4ecc8SPaolo Bonzini     if (id != PCI_CAP_ID_MSIX) {
53080b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Invalid id %#x base %#x\n", id, base);
53180b4ecc8SPaolo Bonzini         return -1;
53280b4ecc8SPaolo Bonzini     }
53380b4ecc8SPaolo Bonzini 
53480b4ecc8SPaolo Bonzini     xen_host_pci_get_word(hd, base + PCI_MSIX_FLAGS, &control);
53580b4ecc8SPaolo Bonzini     total_entries = control & PCI_MSIX_FLAGS_QSIZE;
53680b4ecc8SPaolo Bonzini     total_entries += 1;
53780b4ecc8SPaolo Bonzini 
53880b4ecc8SPaolo Bonzini     s->msix = g_malloc0(sizeof (XenPTMSIX)
53980b4ecc8SPaolo Bonzini                         + total_entries * sizeof (XenPTMSIXEntry));
54080b4ecc8SPaolo Bonzini     msix = s->msix;
54180b4ecc8SPaolo Bonzini 
54280b4ecc8SPaolo Bonzini     msix->total_entries = total_entries;
54380b4ecc8SPaolo Bonzini     for (i = 0; i < total_entries; i++) {
54480b4ecc8SPaolo Bonzini         msix->msix_entry[i].pirq = XEN_PT_UNASSIGNED_PIRQ;
54580b4ecc8SPaolo Bonzini     }
54680b4ecc8SPaolo Bonzini 
547*22fc860bSPaolo Bonzini     memory_region_init_io(&msix->mmio, OBJECT(s), &pci_msix_ops,
548*22fc860bSPaolo Bonzini                           s, "xen-pci-pt-msix",
54980b4ecc8SPaolo Bonzini                           (total_entries * PCI_MSIX_ENTRY_SIZE
55080b4ecc8SPaolo Bonzini                            + XC_PAGE_SIZE - 1)
55180b4ecc8SPaolo Bonzini                           & XC_PAGE_MASK);
55280b4ecc8SPaolo Bonzini 
55380b4ecc8SPaolo Bonzini     xen_host_pci_get_long(hd, base + PCI_MSIX_TABLE, &table_off);
55480b4ecc8SPaolo Bonzini     bar_index = msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK;
55580b4ecc8SPaolo Bonzini     table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK;
55680b4ecc8SPaolo Bonzini     msix->table_base = s->real_device.io_regions[bar_index].base_addr;
55780b4ecc8SPaolo Bonzini     XEN_PT_LOG(d, "get MSI-X table BAR base 0x%"PRIx64"\n", msix->table_base);
55880b4ecc8SPaolo Bonzini 
55980b4ecc8SPaolo Bonzini     fd = open("/dev/mem", O_RDWR);
56080b4ecc8SPaolo Bonzini     if (fd == -1) {
56180b4ecc8SPaolo Bonzini         rc = -errno;
56280b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Can't open /dev/mem: %s\n", strerror(errno));
56380b4ecc8SPaolo Bonzini         goto error_out;
56480b4ecc8SPaolo Bonzini     }
56580b4ecc8SPaolo Bonzini     XEN_PT_LOG(d, "table_off = %#x, total_entries = %d\n",
56680b4ecc8SPaolo Bonzini                table_off, total_entries);
56780b4ecc8SPaolo Bonzini     msix->table_offset_adjust = table_off & 0x0fff;
56880b4ecc8SPaolo Bonzini     msix->phys_iomem_base =
56980b4ecc8SPaolo Bonzini         mmap(NULL,
57080b4ecc8SPaolo Bonzini              total_entries * PCI_MSIX_ENTRY_SIZE + msix->table_offset_adjust,
57180b4ecc8SPaolo Bonzini              PROT_READ,
57280b4ecc8SPaolo Bonzini              MAP_SHARED | MAP_LOCKED,
57380b4ecc8SPaolo Bonzini              fd,
57480b4ecc8SPaolo Bonzini              msix->table_base + table_off - msix->table_offset_adjust);
57580b4ecc8SPaolo Bonzini     close(fd);
57680b4ecc8SPaolo Bonzini     if (msix->phys_iomem_base == MAP_FAILED) {
57780b4ecc8SPaolo Bonzini         rc = -errno;
57880b4ecc8SPaolo Bonzini         XEN_PT_ERR(d, "Can't map physical MSI-X table: %s\n", strerror(errno));
57980b4ecc8SPaolo Bonzini         goto error_out;
58080b4ecc8SPaolo Bonzini     }
58180b4ecc8SPaolo Bonzini     msix->phys_iomem_base = (char *)msix->phys_iomem_base
58280b4ecc8SPaolo Bonzini         + msix->table_offset_adjust;
58380b4ecc8SPaolo Bonzini 
58480b4ecc8SPaolo Bonzini     XEN_PT_LOG(d, "mapping physical MSI-X table to %p\n",
58580b4ecc8SPaolo Bonzini                msix->phys_iomem_base);
58680b4ecc8SPaolo Bonzini 
58780b4ecc8SPaolo Bonzini     memory_region_add_subregion_overlap(&s->bar[bar_index], table_off,
58880b4ecc8SPaolo Bonzini                                         &msix->mmio,
58980b4ecc8SPaolo Bonzini                                         2); /* Priority: pci default + 1 */
59080b4ecc8SPaolo Bonzini 
59180b4ecc8SPaolo Bonzini     return 0;
59280b4ecc8SPaolo Bonzini 
59380b4ecc8SPaolo Bonzini error_out:
59480b4ecc8SPaolo Bonzini     memory_region_destroy(&msix->mmio);
59580b4ecc8SPaolo Bonzini     g_free(s->msix);
59680b4ecc8SPaolo Bonzini     s->msix = NULL;
59780b4ecc8SPaolo Bonzini     return rc;
59880b4ecc8SPaolo Bonzini }
59980b4ecc8SPaolo Bonzini 
60080b4ecc8SPaolo Bonzini void xen_pt_msix_delete(XenPCIPassthroughState *s)
60180b4ecc8SPaolo Bonzini {
60280b4ecc8SPaolo Bonzini     XenPTMSIX *msix = s->msix;
60380b4ecc8SPaolo Bonzini 
60480b4ecc8SPaolo Bonzini     if (!msix) {
60580b4ecc8SPaolo Bonzini         return;
60680b4ecc8SPaolo Bonzini     }
60780b4ecc8SPaolo Bonzini 
60880b4ecc8SPaolo Bonzini     /* unmap the MSI-X memory mapped register area */
60980b4ecc8SPaolo Bonzini     if (msix->phys_iomem_base) {
61080b4ecc8SPaolo Bonzini         XEN_PT_LOG(&s->dev, "unmapping physical MSI-X table from %p\n",
61180b4ecc8SPaolo Bonzini                    msix->phys_iomem_base);
61280b4ecc8SPaolo Bonzini         munmap(msix->phys_iomem_base, msix->total_entries * PCI_MSIX_ENTRY_SIZE
61380b4ecc8SPaolo Bonzini                + msix->table_offset_adjust);
61480b4ecc8SPaolo Bonzini     }
61580b4ecc8SPaolo Bonzini 
61680b4ecc8SPaolo Bonzini     memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio);
61780b4ecc8SPaolo Bonzini     memory_region_destroy(&msix->mmio);
61880b4ecc8SPaolo Bonzini 
61980b4ecc8SPaolo Bonzini     g_free(s->msix);
62080b4ecc8SPaolo Bonzini     s->msix = NULL;
62180b4ecc8SPaolo Bonzini }
622