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 1221cbfe5fSPeter Maydell #include "qemu/osdep.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 27a8036336SRoger Pau Monne #define XEN_PT_GFLAGSSHIFT_UNMASKED 16 2880b4ecc8SPaolo Bonzini 29f0ada360SJan Beulich #define latch(fld) latch[PCI_MSIX_ENTRY_##fld / sizeof(uint32_t)] 3080b4ecc8SPaolo Bonzini 3180b4ecc8SPaolo Bonzini /* 3280b4ecc8SPaolo Bonzini * Helpers 3380b4ecc8SPaolo Bonzini */ 3480b4ecc8SPaolo Bonzini 3580b4ecc8SPaolo Bonzini static inline uint8_t msi_vector(uint32_t data) 3680b4ecc8SPaolo Bonzini { 3780b4ecc8SPaolo Bonzini return (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT; 3880b4ecc8SPaolo Bonzini } 3980b4ecc8SPaolo Bonzini 4080b4ecc8SPaolo Bonzini static inline uint8_t msi_dest_id(uint32_t addr) 4180b4ecc8SPaolo Bonzini { 4280b4ecc8SPaolo Bonzini return (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT; 4380b4ecc8SPaolo Bonzini } 4480b4ecc8SPaolo Bonzini 4580b4ecc8SPaolo Bonzini static inline uint32_t msi_ext_dest_id(uint32_t addr_hi) 4680b4ecc8SPaolo Bonzini { 4780b4ecc8SPaolo Bonzini return addr_hi & 0xffffff00; 4880b4ecc8SPaolo Bonzini } 4980b4ecc8SPaolo Bonzini 5080b4ecc8SPaolo Bonzini static uint32_t msi_gflags(uint32_t data, uint64_t addr) 5180b4ecc8SPaolo Bonzini { 5280b4ecc8SPaolo Bonzini uint32_t result = 0; 5380b4ecc8SPaolo Bonzini int rh, dm, dest_id, deliv_mode, trig_mode; 5480b4ecc8SPaolo Bonzini 5580b4ecc8SPaolo Bonzini rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1; 5680b4ecc8SPaolo Bonzini dm = (addr >> MSI_ADDR_DEST_MODE_SHIFT) & 0x1; 5780b4ecc8SPaolo Bonzini dest_id = msi_dest_id(addr); 5880b4ecc8SPaolo Bonzini deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7; 5980b4ecc8SPaolo Bonzini trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1; 6080b4ecc8SPaolo Bonzini 6180b4ecc8SPaolo Bonzini result = dest_id | (rh << XEN_PT_GFLAGS_SHIFT_RH) 6280b4ecc8SPaolo Bonzini | (dm << XEN_PT_GFLAGS_SHIFT_DM) 6380b4ecc8SPaolo Bonzini | (deliv_mode << XEN_PT_GFLAGSSHIFT_DELIV_MODE) 6480b4ecc8SPaolo Bonzini | (trig_mode << XEN_PT_GFLAGSSHIFT_TRG_MODE); 6580b4ecc8SPaolo Bonzini 6680b4ecc8SPaolo Bonzini return result; 6780b4ecc8SPaolo Bonzini } 6880b4ecc8SPaolo Bonzini 6980b4ecc8SPaolo Bonzini static inline uint64_t msi_addr64(XenPTMSI *msi) 7080b4ecc8SPaolo Bonzini { 7180b4ecc8SPaolo Bonzini return (uint64_t)msi->addr_hi << 32 | msi->addr_lo; 7280b4ecc8SPaolo Bonzini } 7380b4ecc8SPaolo Bonzini 7480b4ecc8SPaolo Bonzini static int msi_msix_enable(XenPCIPassthroughState *s, 7580b4ecc8SPaolo Bonzini uint32_t address, 7680b4ecc8SPaolo Bonzini uint16_t flag, 7780b4ecc8SPaolo Bonzini bool enable) 7880b4ecc8SPaolo Bonzini { 7980b4ecc8SPaolo Bonzini uint16_t val = 0; 80fe2da64cSKonrad Rzeszutek Wilk int rc; 8180b4ecc8SPaolo Bonzini 8280b4ecc8SPaolo Bonzini if (!address) { 8380b4ecc8SPaolo Bonzini return -1; 8480b4ecc8SPaolo Bonzini } 8580b4ecc8SPaolo Bonzini 86fe2da64cSKonrad Rzeszutek Wilk rc = xen_host_pci_get_word(&s->real_device, address, &val); 87fe2da64cSKonrad Rzeszutek Wilk if (rc) { 88fe2da64cSKonrad Rzeszutek Wilk XEN_PT_ERR(&s->dev, "Failed to read MSI/MSI-X register (0x%x), rc:%d\n", 89fe2da64cSKonrad Rzeszutek Wilk address, rc); 90fe2da64cSKonrad Rzeszutek Wilk return rc; 91fe2da64cSKonrad Rzeszutek Wilk } 9280b4ecc8SPaolo Bonzini if (enable) { 9380b4ecc8SPaolo Bonzini val |= flag; 9480b4ecc8SPaolo Bonzini } else { 9580b4ecc8SPaolo Bonzini val &= ~flag; 9680b4ecc8SPaolo Bonzini } 97fe2da64cSKonrad Rzeszutek Wilk rc = xen_host_pci_set_word(&s->real_device, address, val); 98fe2da64cSKonrad Rzeszutek Wilk if (rc) { 99fe2da64cSKonrad Rzeszutek Wilk XEN_PT_ERR(&s->dev, "Failed to write MSI/MSI-X register (0x%x), rc:%d\n", 100fe2da64cSKonrad Rzeszutek Wilk address, rc); 101fe2da64cSKonrad Rzeszutek Wilk } 102fe2da64cSKonrad Rzeszutek Wilk return rc; 10380b4ecc8SPaolo Bonzini } 10480b4ecc8SPaolo Bonzini 10580b4ecc8SPaolo Bonzini static int msi_msix_setup(XenPCIPassthroughState *s, 10680b4ecc8SPaolo Bonzini uint64_t addr, 10780b4ecc8SPaolo Bonzini uint32_t data, 10880b4ecc8SPaolo Bonzini int *ppirq, 10980b4ecc8SPaolo Bonzini bool is_msix, 11080b4ecc8SPaolo Bonzini int msix_entry, 11180b4ecc8SPaolo Bonzini bool is_not_mapped) 11280b4ecc8SPaolo Bonzini { 11380b4ecc8SPaolo Bonzini uint8_t gvec = msi_vector(data); 11480b4ecc8SPaolo Bonzini int rc = 0; 11580b4ecc8SPaolo Bonzini 11680b4ecc8SPaolo Bonzini assert((!is_msix && msix_entry == 0) || is_msix); 11780b4ecc8SPaolo Bonzini 118428c3eceSStefano Stabellini if (xen_is_pirq_msi(data)) { 11980b4ecc8SPaolo Bonzini *ppirq = msi_ext_dest_id(addr >> 32) | msi_dest_id(addr); 12080b4ecc8SPaolo Bonzini if (!*ppirq) { 12180b4ecc8SPaolo Bonzini /* this probably identifies an misconfiguration of the guest, 12280b4ecc8SPaolo Bonzini * try the emulated path */ 12380b4ecc8SPaolo Bonzini *ppirq = XEN_PT_UNASSIGNED_PIRQ; 12480b4ecc8SPaolo Bonzini } else { 12580b4ecc8SPaolo Bonzini XEN_PT_LOG(&s->dev, "requested pirq %d for MSI%s" 12680b4ecc8SPaolo Bonzini " (vec: %#x, entry: %#x)\n", 12780b4ecc8SPaolo Bonzini *ppirq, is_msix ? "-X" : "", gvec, msix_entry); 12880b4ecc8SPaolo Bonzini } 12980b4ecc8SPaolo Bonzini } 13080b4ecc8SPaolo Bonzini 13180b4ecc8SPaolo Bonzini if (is_not_mapped) { 13280b4ecc8SPaolo Bonzini uint64_t table_base = 0; 13380b4ecc8SPaolo Bonzini 13480b4ecc8SPaolo Bonzini if (is_msix) { 13580b4ecc8SPaolo Bonzini table_base = s->msix->table_base; 13680b4ecc8SPaolo Bonzini } 13780b4ecc8SPaolo Bonzini 13880b4ecc8SPaolo Bonzini rc = xc_physdev_map_pirq_msi(xen_xc, xen_domid, XEN_PT_AUTO_ASSIGN, 13980b4ecc8SPaolo Bonzini ppirq, PCI_DEVFN(s->real_device.dev, 14080b4ecc8SPaolo Bonzini s->real_device.func), 14180b4ecc8SPaolo Bonzini s->real_device.bus, 14280b4ecc8SPaolo Bonzini msix_entry, table_base); 14380b4ecc8SPaolo Bonzini if (rc) { 14480b4ecc8SPaolo Bonzini XEN_PT_ERR(&s->dev, 1453782f60dSJan Beulich "Mapping of MSI%s (err: %i, vec: %#x, entry %#x)\n", 1463782f60dSJan Beulich is_msix ? "-X" : "", errno, gvec, msix_entry); 14780b4ecc8SPaolo Bonzini return rc; 14880b4ecc8SPaolo Bonzini } 14980b4ecc8SPaolo Bonzini } 15080b4ecc8SPaolo Bonzini 15180b4ecc8SPaolo Bonzini return 0; 15280b4ecc8SPaolo Bonzini } 15380b4ecc8SPaolo Bonzini static int msi_msix_update(XenPCIPassthroughState *s, 15480b4ecc8SPaolo Bonzini uint64_t addr, 15580b4ecc8SPaolo Bonzini uint32_t data, 15680b4ecc8SPaolo Bonzini int pirq, 15780b4ecc8SPaolo Bonzini bool is_msix, 15880b4ecc8SPaolo Bonzini int msix_entry, 159a8036336SRoger Pau Monne int *old_pirq, 160a8036336SRoger Pau Monne bool masked) 16180b4ecc8SPaolo Bonzini { 16280b4ecc8SPaolo Bonzini PCIDevice *d = &s->dev; 16380b4ecc8SPaolo Bonzini uint8_t gvec = msi_vector(data); 16480b4ecc8SPaolo Bonzini uint32_t gflags = msi_gflags(data, addr); 16580b4ecc8SPaolo Bonzini int rc = 0; 16680b4ecc8SPaolo Bonzini uint64_t table_addr = 0; 16780b4ecc8SPaolo Bonzini 16880b4ecc8SPaolo Bonzini XEN_PT_LOG(d, "Updating MSI%s with pirq %d gvec %#x gflags %#x" 16980b4ecc8SPaolo Bonzini " (entry: %#x)\n", 17080b4ecc8SPaolo Bonzini is_msix ? "-X" : "", pirq, gvec, gflags, msix_entry); 17180b4ecc8SPaolo Bonzini 17280b4ecc8SPaolo Bonzini if (is_msix) { 17380b4ecc8SPaolo Bonzini table_addr = s->msix->mmio_base_addr; 17480b4ecc8SPaolo Bonzini } 17580b4ecc8SPaolo Bonzini 176a8036336SRoger Pau Monne gflags |= masked ? 0 : (1u << XEN_PT_GFLAGSSHIFT_UNMASKED); 177a8036336SRoger Pau Monne 17880b4ecc8SPaolo Bonzini rc = xc_domain_update_msi_irq(xen_xc, xen_domid, gvec, 17980b4ecc8SPaolo Bonzini pirq, gflags, table_addr); 18080b4ecc8SPaolo Bonzini 18180b4ecc8SPaolo Bonzini if (rc) { 1823782f60dSJan Beulich XEN_PT_ERR(d, "Updating of MSI%s failed. (err: %d)\n", 1833782f60dSJan Beulich is_msix ? "-X" : "", errno); 18480b4ecc8SPaolo Bonzini 18580b4ecc8SPaolo Bonzini if (xc_physdev_unmap_pirq(xen_xc, xen_domid, *old_pirq)) { 1863782f60dSJan Beulich XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (err: %d)\n", 1873782f60dSJan Beulich is_msix ? "-X" : "", *old_pirq, errno); 18880b4ecc8SPaolo Bonzini } 18980b4ecc8SPaolo Bonzini *old_pirq = XEN_PT_UNASSIGNED_PIRQ; 19080b4ecc8SPaolo Bonzini } 19180b4ecc8SPaolo Bonzini return rc; 19280b4ecc8SPaolo Bonzini } 19380b4ecc8SPaolo Bonzini 19480b4ecc8SPaolo Bonzini static int msi_msix_disable(XenPCIPassthroughState *s, 19580b4ecc8SPaolo Bonzini uint64_t addr, 19680b4ecc8SPaolo Bonzini uint32_t data, 19780b4ecc8SPaolo Bonzini int pirq, 19880b4ecc8SPaolo Bonzini bool is_msix, 19980b4ecc8SPaolo Bonzini bool is_binded) 20080b4ecc8SPaolo Bonzini { 20180b4ecc8SPaolo Bonzini PCIDevice *d = &s->dev; 20280b4ecc8SPaolo Bonzini uint8_t gvec = msi_vector(data); 20380b4ecc8SPaolo Bonzini uint32_t gflags = msi_gflags(data, addr); 20480b4ecc8SPaolo Bonzini int rc = 0; 20580b4ecc8SPaolo Bonzini 20680b4ecc8SPaolo Bonzini if (pirq == XEN_PT_UNASSIGNED_PIRQ) { 20780b4ecc8SPaolo Bonzini return 0; 20880b4ecc8SPaolo Bonzini } 20980b4ecc8SPaolo Bonzini 21080b4ecc8SPaolo Bonzini if (is_binded) { 21180b4ecc8SPaolo Bonzini XEN_PT_LOG(d, "Unbind MSI%s with pirq %d, gvec %#x\n", 21280b4ecc8SPaolo Bonzini is_msix ? "-X" : "", pirq, gvec); 21380b4ecc8SPaolo Bonzini rc = xc_domain_unbind_msi_irq(xen_xc, xen_domid, gvec, pirq, gflags); 21480b4ecc8SPaolo Bonzini if (rc) { 2153782f60dSJan Beulich XEN_PT_ERR(d, "Unbinding of MSI%s failed. (err: %d, pirq: %d, gvec: %#x)\n", 2163782f60dSJan Beulich is_msix ? "-X" : "", errno, pirq, gvec); 21780b4ecc8SPaolo Bonzini return rc; 21880b4ecc8SPaolo Bonzini } 21980b4ecc8SPaolo Bonzini } 22080b4ecc8SPaolo Bonzini 22180b4ecc8SPaolo Bonzini XEN_PT_LOG(d, "Unmap MSI%s pirq %d\n", is_msix ? "-X" : "", pirq); 22280b4ecc8SPaolo Bonzini rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, pirq); 22380b4ecc8SPaolo Bonzini if (rc) { 2243782f60dSJan Beulich XEN_PT_ERR(d, "Unmapping of MSI%s pirq %d failed. (err: %i)\n", 2253782f60dSJan Beulich is_msix ? "-X" : "", pirq, errno); 22680b4ecc8SPaolo Bonzini return rc; 22780b4ecc8SPaolo Bonzini } 22880b4ecc8SPaolo Bonzini 22980b4ecc8SPaolo Bonzini return 0; 23080b4ecc8SPaolo Bonzini } 23180b4ecc8SPaolo Bonzini 23280b4ecc8SPaolo Bonzini /* 23380b4ecc8SPaolo Bonzini * MSI virtualization functions 23480b4ecc8SPaolo Bonzini */ 23580b4ecc8SPaolo Bonzini 236cf8124f0SKonrad Rzeszutek Wilk static int xen_pt_msi_set_enable(XenPCIPassthroughState *s, bool enable) 23780b4ecc8SPaolo Bonzini { 23880b4ecc8SPaolo Bonzini XEN_PT_LOG(&s->dev, "%s MSI.\n", enable ? "enabling" : "disabling"); 23980b4ecc8SPaolo Bonzini 24080b4ecc8SPaolo Bonzini if (!s->msi) { 24180b4ecc8SPaolo Bonzini return -1; 24280b4ecc8SPaolo Bonzini } 24380b4ecc8SPaolo Bonzini 24480b4ecc8SPaolo Bonzini return msi_msix_enable(s, s->msi->ctrl_offset, PCI_MSI_FLAGS_ENABLE, 24580b4ecc8SPaolo Bonzini enable); 24680b4ecc8SPaolo Bonzini } 24780b4ecc8SPaolo Bonzini 24880b4ecc8SPaolo Bonzini /* setup physical msi, but don't enable it */ 24980b4ecc8SPaolo Bonzini int xen_pt_msi_setup(XenPCIPassthroughState *s) 25080b4ecc8SPaolo Bonzini { 25180b4ecc8SPaolo Bonzini int pirq = XEN_PT_UNASSIGNED_PIRQ; 25280b4ecc8SPaolo Bonzini int rc = 0; 25380b4ecc8SPaolo Bonzini XenPTMSI *msi = s->msi; 25480b4ecc8SPaolo Bonzini 25580b4ecc8SPaolo Bonzini if (msi->initialized) { 25680b4ecc8SPaolo Bonzini XEN_PT_ERR(&s->dev, 25780b4ecc8SPaolo Bonzini "Setup physical MSI when it has been properly initialized.\n"); 25880b4ecc8SPaolo Bonzini return -1; 25980b4ecc8SPaolo Bonzini } 26080b4ecc8SPaolo Bonzini 26180b4ecc8SPaolo Bonzini rc = msi_msix_setup(s, msi_addr64(msi), msi->data, &pirq, false, 0, true); 26280b4ecc8SPaolo Bonzini if (rc) { 26380b4ecc8SPaolo Bonzini return rc; 26480b4ecc8SPaolo Bonzini } 26580b4ecc8SPaolo Bonzini 26680b4ecc8SPaolo Bonzini if (pirq < 0) { 26780b4ecc8SPaolo Bonzini XEN_PT_ERR(&s->dev, "Invalid pirq number: %d.\n", pirq); 26880b4ecc8SPaolo Bonzini return -1; 26980b4ecc8SPaolo Bonzini } 27080b4ecc8SPaolo Bonzini 27180b4ecc8SPaolo Bonzini msi->pirq = pirq; 27280b4ecc8SPaolo Bonzini XEN_PT_LOG(&s->dev, "MSI mapped with pirq %d.\n", pirq); 27380b4ecc8SPaolo Bonzini 27480b4ecc8SPaolo Bonzini return 0; 27580b4ecc8SPaolo Bonzini } 27680b4ecc8SPaolo Bonzini 27780b4ecc8SPaolo Bonzini int xen_pt_msi_update(XenPCIPassthroughState *s) 27880b4ecc8SPaolo Bonzini { 27980b4ecc8SPaolo Bonzini XenPTMSI *msi = s->msi; 280a8036336SRoger Pau Monne 281a8036336SRoger Pau Monne /* Current MSI emulation in QEMU only supports 1 vector */ 28280b4ecc8SPaolo Bonzini return msi_msix_update(s, msi_addr64(msi), msi->data, msi->pirq, 283a8036336SRoger Pau Monne false, 0, &msi->pirq, msi->mask & 1); 28480b4ecc8SPaolo Bonzini } 28580b4ecc8SPaolo Bonzini 28680b4ecc8SPaolo Bonzini void xen_pt_msi_disable(XenPCIPassthroughState *s) 28780b4ecc8SPaolo Bonzini { 28880b4ecc8SPaolo Bonzini XenPTMSI *msi = s->msi; 28980b4ecc8SPaolo Bonzini 29080b4ecc8SPaolo Bonzini if (!msi) { 29180b4ecc8SPaolo Bonzini return; 29280b4ecc8SPaolo Bonzini } 29380b4ecc8SPaolo Bonzini 294fe2da64cSKonrad Rzeszutek Wilk (void)xen_pt_msi_set_enable(s, false); 29580b4ecc8SPaolo Bonzini 29680b4ecc8SPaolo Bonzini msi_msix_disable(s, msi_addr64(msi), msi->data, msi->pirq, false, 29780b4ecc8SPaolo Bonzini msi->initialized); 29880b4ecc8SPaolo Bonzini 29980b4ecc8SPaolo Bonzini /* clear msi info */ 300c976437cSZhenzhong Duan msi->flags &= ~PCI_MSI_FLAGS_ENABLE; 301c976437cSZhenzhong Duan msi->initialized = false; 30280b4ecc8SPaolo Bonzini msi->mapped = false; 30380b4ecc8SPaolo Bonzini msi->pirq = XEN_PT_UNASSIGNED_PIRQ; 30480b4ecc8SPaolo Bonzini } 30580b4ecc8SPaolo Bonzini 30680b4ecc8SPaolo Bonzini /* 30780b4ecc8SPaolo Bonzini * MSI-X virtualization functions 30880b4ecc8SPaolo Bonzini */ 30980b4ecc8SPaolo Bonzini 31080b4ecc8SPaolo Bonzini static int msix_set_enable(XenPCIPassthroughState *s, bool enabled) 31180b4ecc8SPaolo Bonzini { 31280b4ecc8SPaolo Bonzini XEN_PT_LOG(&s->dev, "%s MSI-X.\n", enabled ? "enabling" : "disabling"); 31380b4ecc8SPaolo Bonzini 31480b4ecc8SPaolo Bonzini if (!s->msix) { 31580b4ecc8SPaolo Bonzini return -1; 31680b4ecc8SPaolo Bonzini } 31780b4ecc8SPaolo Bonzini 31880b4ecc8SPaolo Bonzini return msi_msix_enable(s, s->msix->ctrl_offset, PCI_MSIX_FLAGS_ENABLE, 31980b4ecc8SPaolo Bonzini enabled); 32080b4ecc8SPaolo Bonzini } 32180b4ecc8SPaolo Bonzini 322f0ada360SJan Beulich static int xen_pt_msix_update_one(XenPCIPassthroughState *s, int entry_nr, 323f0ada360SJan Beulich uint32_t vec_ctrl) 32480b4ecc8SPaolo Bonzini { 32580b4ecc8SPaolo Bonzini XenPTMSIXEntry *entry = NULL; 32680b4ecc8SPaolo Bonzini int pirq; 32780b4ecc8SPaolo Bonzini int rc; 32880b4ecc8SPaolo Bonzini 32980b4ecc8SPaolo Bonzini if (entry_nr < 0 || entry_nr >= s->msix->total_entries) { 33080b4ecc8SPaolo Bonzini return -EINVAL; 33180b4ecc8SPaolo Bonzini } 33280b4ecc8SPaolo Bonzini 33380b4ecc8SPaolo Bonzini entry = &s->msix->msix_entry[entry_nr]; 33480b4ecc8SPaolo Bonzini 33580b4ecc8SPaolo Bonzini if (!entry->updated) { 33680b4ecc8SPaolo Bonzini return 0; 33780b4ecc8SPaolo Bonzini } 33880b4ecc8SPaolo Bonzini 33980b4ecc8SPaolo Bonzini pirq = entry->pirq; 34080b4ecc8SPaolo Bonzini 341f0ada360SJan Beulich /* 342f0ada360SJan Beulich * Update the entry addr and data to the latest values only when the 343f0ada360SJan Beulich * entry is masked or they are all masked, as required by the spec. 344f0ada360SJan Beulich * Addr and data changes while the MSI-X entry is unmasked get deferred 345f0ada360SJan Beulich * until the next masked -> unmasked transition. 346f0ada360SJan Beulich */ 347f0ada360SJan Beulich if (pirq == XEN_PT_UNASSIGNED_PIRQ || s->msix->maskall || 348f0ada360SJan Beulich (vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { 349f0ada360SJan Beulich entry->addr = entry->latch(LOWER_ADDR) | 350f0ada360SJan Beulich ((uint64_t)entry->latch(UPPER_ADDR) << 32); 351f0ada360SJan Beulich entry->data = entry->latch(DATA); 352f0ada360SJan Beulich } 353f0ada360SJan Beulich 35480b4ecc8SPaolo Bonzini rc = msi_msix_setup(s, entry->addr, entry->data, &pirq, true, entry_nr, 35580b4ecc8SPaolo Bonzini entry->pirq == XEN_PT_UNASSIGNED_PIRQ); 35680b4ecc8SPaolo Bonzini if (rc) { 35780b4ecc8SPaolo Bonzini return rc; 35880b4ecc8SPaolo Bonzini } 35980b4ecc8SPaolo Bonzini if (entry->pirq == XEN_PT_UNASSIGNED_PIRQ) { 36080b4ecc8SPaolo Bonzini entry->pirq = pirq; 36180b4ecc8SPaolo Bonzini } 36280b4ecc8SPaolo Bonzini 36380b4ecc8SPaolo Bonzini rc = msi_msix_update(s, entry->addr, entry->data, pirq, true, 364a8036336SRoger Pau Monne entry_nr, &entry->pirq, 365a8036336SRoger Pau Monne vec_ctrl & PCI_MSIX_ENTRY_CTRL_MASKBIT); 36680b4ecc8SPaolo Bonzini 36780b4ecc8SPaolo Bonzini if (!rc) { 36880b4ecc8SPaolo Bonzini entry->updated = false; 36980b4ecc8SPaolo Bonzini } 37080b4ecc8SPaolo Bonzini 37180b4ecc8SPaolo Bonzini return rc; 37280b4ecc8SPaolo Bonzini } 37380b4ecc8SPaolo Bonzini 37480b4ecc8SPaolo Bonzini int xen_pt_msix_update(XenPCIPassthroughState *s) 37580b4ecc8SPaolo Bonzini { 37680b4ecc8SPaolo Bonzini XenPTMSIX *msix = s->msix; 37780b4ecc8SPaolo Bonzini int i; 37880b4ecc8SPaolo Bonzini 37980b4ecc8SPaolo Bonzini for (i = 0; i < msix->total_entries; i++) { 380f0ada360SJan Beulich xen_pt_msix_update_one(s, i, msix->msix_entry[i].latch(VECTOR_CTRL)); 38180b4ecc8SPaolo Bonzini } 38280b4ecc8SPaolo Bonzini 38380b4ecc8SPaolo Bonzini return 0; 38480b4ecc8SPaolo Bonzini } 38580b4ecc8SPaolo Bonzini 38680b4ecc8SPaolo Bonzini void xen_pt_msix_disable(XenPCIPassthroughState *s) 38780b4ecc8SPaolo Bonzini { 38880b4ecc8SPaolo Bonzini int i = 0; 38980b4ecc8SPaolo Bonzini 39080b4ecc8SPaolo Bonzini msix_set_enable(s, false); 39180b4ecc8SPaolo Bonzini 39280b4ecc8SPaolo Bonzini for (i = 0; i < s->msix->total_entries; i++) { 39380b4ecc8SPaolo Bonzini XenPTMSIXEntry *entry = &s->msix->msix_entry[i]; 39480b4ecc8SPaolo Bonzini 39580b4ecc8SPaolo Bonzini msi_msix_disable(s, entry->addr, entry->data, entry->pirq, true, true); 39680b4ecc8SPaolo Bonzini 39780b4ecc8SPaolo Bonzini /* clear MSI-X info */ 39880b4ecc8SPaolo Bonzini entry->pirq = XEN_PT_UNASSIGNED_PIRQ; 39980b4ecc8SPaolo Bonzini entry->updated = false; 40080b4ecc8SPaolo Bonzini } 40180b4ecc8SPaolo Bonzini } 40280b4ecc8SPaolo Bonzini 40380b4ecc8SPaolo Bonzini int xen_pt_msix_update_remap(XenPCIPassthroughState *s, int bar_index) 40480b4ecc8SPaolo Bonzini { 40580b4ecc8SPaolo Bonzini XenPTMSIXEntry *entry; 40680b4ecc8SPaolo Bonzini int i, ret; 40780b4ecc8SPaolo Bonzini 40880b4ecc8SPaolo Bonzini if (!(s->msix && s->msix->bar_index == bar_index)) { 40980b4ecc8SPaolo Bonzini return 0; 41080b4ecc8SPaolo Bonzini } 41180b4ecc8SPaolo Bonzini 41280b4ecc8SPaolo Bonzini for (i = 0; i < s->msix->total_entries; i++) { 41380b4ecc8SPaolo Bonzini entry = &s->msix->msix_entry[i]; 41480b4ecc8SPaolo Bonzini if (entry->pirq != XEN_PT_UNASSIGNED_PIRQ) { 41580b4ecc8SPaolo Bonzini ret = xc_domain_unbind_pt_irq(xen_xc, xen_domid, entry->pirq, 41680b4ecc8SPaolo Bonzini PT_IRQ_TYPE_MSI, 0, 0, 0, 0); 41780b4ecc8SPaolo Bonzini if (ret) { 4183782f60dSJan Beulich XEN_PT_ERR(&s->dev, "unbind MSI-X entry %d failed (err: %d)\n", 4193782f60dSJan Beulich entry->pirq, errno); 42080b4ecc8SPaolo Bonzini } 42180b4ecc8SPaolo Bonzini entry->updated = true; 42280b4ecc8SPaolo Bonzini } 42380b4ecc8SPaolo Bonzini } 42480b4ecc8SPaolo Bonzini return xen_pt_msix_update(s); 42580b4ecc8SPaolo Bonzini } 42680b4ecc8SPaolo Bonzini 42780b4ecc8SPaolo Bonzini static uint32_t get_entry_value(XenPTMSIXEntry *e, int offset) 42880b4ecc8SPaolo Bonzini { 429bdfe5159SJan Beulich assert(!(offset % sizeof(*e->latch))); 430bdfe5159SJan Beulich return e->latch[offset / sizeof(*e->latch)]; 43180b4ecc8SPaolo Bonzini } 43280b4ecc8SPaolo Bonzini 43380b4ecc8SPaolo Bonzini static void set_entry_value(XenPTMSIXEntry *e, int offset, uint32_t val) 43480b4ecc8SPaolo Bonzini { 435bdfe5159SJan Beulich assert(!(offset % sizeof(*e->latch))); 436f0ada360SJan Beulich e->latch[offset / sizeof(*e->latch)] = val; 43780b4ecc8SPaolo Bonzini } 43880b4ecc8SPaolo Bonzini 43980b4ecc8SPaolo Bonzini static void pci_msix_write(void *opaque, hwaddr addr, 44080b4ecc8SPaolo Bonzini uint64_t val, unsigned size) 44180b4ecc8SPaolo Bonzini { 44280b4ecc8SPaolo Bonzini XenPCIPassthroughState *s = opaque; 44380b4ecc8SPaolo Bonzini XenPTMSIX *msix = s->msix; 44480b4ecc8SPaolo Bonzini XenPTMSIXEntry *entry; 445b38ec5eeSJan Beulich unsigned int entry_nr, offset; 44680b4ecc8SPaolo Bonzini 44780b4ecc8SPaolo Bonzini entry_nr = addr / PCI_MSIX_ENTRY_SIZE; 448b38ec5eeSJan Beulich if (entry_nr >= msix->total_entries) { 44980b4ecc8SPaolo Bonzini return; 45080b4ecc8SPaolo Bonzini } 45180b4ecc8SPaolo Bonzini entry = &msix->msix_entry[entry_nr]; 45280b4ecc8SPaolo Bonzini offset = addr % PCI_MSIX_ENTRY_SIZE; 45380b4ecc8SPaolo Bonzini 45480b4ecc8SPaolo Bonzini if (offset != PCI_MSIX_ENTRY_VECTOR_CTRL) { 455c976437cSZhenzhong Duan if (get_entry_value(entry, offset) == val 456c976437cSZhenzhong Duan && entry->pirq != XEN_PT_UNASSIGNED_PIRQ) { 45780b4ecc8SPaolo Bonzini return; 45880b4ecc8SPaolo Bonzini } 45980b4ecc8SPaolo Bonzini 460f0ada360SJan Beulich entry->updated = true; 461f0ada360SJan Beulich } else if (msix->enabled && entry->updated && 462f0ada360SJan Beulich !(val & PCI_MSIX_ENTRY_CTRL_MASKBIT)) { 463f0ada360SJan Beulich const volatile uint32_t *vec_ctrl; 464f0ada360SJan Beulich 46580b4ecc8SPaolo Bonzini /* 46680b4ecc8SPaolo Bonzini * If Xen intercepts the mask bit access, entry->vec_ctrl may not be 46780b4ecc8SPaolo Bonzini * up-to-date. Read from hardware directly. 46880b4ecc8SPaolo Bonzini */ 46980b4ecc8SPaolo Bonzini vec_ctrl = s->msix->phys_iomem_base + entry_nr * PCI_MSIX_ENTRY_SIZE 47080b4ecc8SPaolo Bonzini + PCI_MSIX_ENTRY_VECTOR_CTRL; 471f0ada360SJan Beulich xen_pt_msix_update_one(s, entry_nr, *vec_ctrl); 47280b4ecc8SPaolo Bonzini } 47380b4ecc8SPaolo Bonzini 47480b4ecc8SPaolo Bonzini set_entry_value(entry, offset, val); 47580b4ecc8SPaolo Bonzini } 47680b4ecc8SPaolo Bonzini 47780b4ecc8SPaolo Bonzini static uint64_t pci_msix_read(void *opaque, hwaddr addr, 47880b4ecc8SPaolo Bonzini unsigned size) 47980b4ecc8SPaolo Bonzini { 48080b4ecc8SPaolo Bonzini XenPCIPassthroughState *s = opaque; 48180b4ecc8SPaolo Bonzini XenPTMSIX *msix = s->msix; 48280b4ecc8SPaolo Bonzini int entry_nr, offset; 48380b4ecc8SPaolo Bonzini 48480b4ecc8SPaolo Bonzini entry_nr = addr / PCI_MSIX_ENTRY_SIZE; 48580b4ecc8SPaolo Bonzini if (entry_nr < 0) { 48680b4ecc8SPaolo Bonzini XEN_PT_ERR(&s->dev, "asked MSI-X entry '%i' invalid!\n", entry_nr); 48780b4ecc8SPaolo Bonzini return 0; 48880b4ecc8SPaolo Bonzini } 48980b4ecc8SPaolo Bonzini 49080b4ecc8SPaolo Bonzini offset = addr % PCI_MSIX_ENTRY_SIZE; 49180b4ecc8SPaolo Bonzini 49280b4ecc8SPaolo Bonzini if (addr < msix->total_entries * PCI_MSIX_ENTRY_SIZE) { 49380b4ecc8SPaolo Bonzini return get_entry_value(&msix->msix_entry[entry_nr], offset); 49480b4ecc8SPaolo Bonzini } else { 49580b4ecc8SPaolo Bonzini /* Pending Bit Array (PBA) */ 49680b4ecc8SPaolo Bonzini return *(uint32_t *)(msix->phys_iomem_base + addr); 49780b4ecc8SPaolo Bonzini } 49880b4ecc8SPaolo Bonzini } 49980b4ecc8SPaolo Bonzini 500bdfe5159SJan Beulich static bool pci_msix_accepts(void *opaque, hwaddr addr, 501*8372d383SPeter Maydell unsigned size, bool is_write, 502*8372d383SPeter Maydell MemTxAttrs attrs) 503bdfe5159SJan Beulich { 504bdfe5159SJan Beulich return !(addr & (size - 1)); 505bdfe5159SJan Beulich } 506bdfe5159SJan Beulich 50780b4ecc8SPaolo Bonzini static const MemoryRegionOps pci_msix_ops = { 50880b4ecc8SPaolo Bonzini .read = pci_msix_read, 50980b4ecc8SPaolo Bonzini .write = pci_msix_write, 51080b4ecc8SPaolo Bonzini .endianness = DEVICE_NATIVE_ENDIAN, 51180b4ecc8SPaolo Bonzini .valid = { 51280b4ecc8SPaolo Bonzini .min_access_size = 4, 51380b4ecc8SPaolo Bonzini .max_access_size = 4, 51480b4ecc8SPaolo Bonzini .unaligned = false, 515bdfe5159SJan Beulich .accepts = pci_msix_accepts 51680b4ecc8SPaolo Bonzini }, 517bdfe5159SJan Beulich .impl = { 518bdfe5159SJan Beulich .min_access_size = 4, 519bdfe5159SJan Beulich .max_access_size = 4, 520bdfe5159SJan Beulich .unaligned = false 521bdfe5159SJan Beulich } 52280b4ecc8SPaolo Bonzini }; 52380b4ecc8SPaolo Bonzini 52480b4ecc8SPaolo Bonzini int xen_pt_msix_init(XenPCIPassthroughState *s, uint32_t base) 52580b4ecc8SPaolo Bonzini { 52680b4ecc8SPaolo Bonzini uint8_t id = 0; 52780b4ecc8SPaolo Bonzini uint16_t control = 0; 52880b4ecc8SPaolo Bonzini uint32_t table_off = 0; 52980b4ecc8SPaolo Bonzini int i, total_entries, bar_index; 53080b4ecc8SPaolo Bonzini XenHostPCIDevice *hd = &s->real_device; 53180b4ecc8SPaolo Bonzini PCIDevice *d = &s->dev; 53280b4ecc8SPaolo Bonzini int fd = -1; 53380b4ecc8SPaolo Bonzini XenPTMSIX *msix = NULL; 53480b4ecc8SPaolo Bonzini int rc = 0; 53580b4ecc8SPaolo Bonzini 53680b4ecc8SPaolo Bonzini rc = xen_host_pci_get_byte(hd, base + PCI_CAP_LIST_ID, &id); 53780b4ecc8SPaolo Bonzini if (rc) { 53880b4ecc8SPaolo Bonzini return rc; 53980b4ecc8SPaolo Bonzini } 54080b4ecc8SPaolo Bonzini 54180b4ecc8SPaolo Bonzini if (id != PCI_CAP_ID_MSIX) { 54280b4ecc8SPaolo Bonzini XEN_PT_ERR(d, "Invalid id %#x base %#x\n", id, base); 54380b4ecc8SPaolo Bonzini return -1; 54480b4ecc8SPaolo Bonzini } 54580b4ecc8SPaolo Bonzini 54664c7c117SPeter Maydell rc = xen_host_pci_get_word(hd, base + PCI_MSIX_FLAGS, &control); 54764c7c117SPeter Maydell if (rc) { 54864c7c117SPeter Maydell XEN_PT_ERR(d, "Failed to read PCI_MSIX_FLAGS field\n"); 54964c7c117SPeter Maydell return rc; 55064c7c117SPeter Maydell } 55180b4ecc8SPaolo Bonzini total_entries = control & PCI_MSIX_FLAGS_QSIZE; 55280b4ecc8SPaolo Bonzini total_entries += 1; 55380b4ecc8SPaolo Bonzini 55480b4ecc8SPaolo Bonzini s->msix = g_malloc0(sizeof (XenPTMSIX) 55580b4ecc8SPaolo Bonzini + total_entries * sizeof (XenPTMSIXEntry)); 55680b4ecc8SPaolo Bonzini msix = s->msix; 55780b4ecc8SPaolo Bonzini 55880b4ecc8SPaolo Bonzini msix->total_entries = total_entries; 55980b4ecc8SPaolo Bonzini for (i = 0; i < total_entries; i++) { 56080b4ecc8SPaolo Bonzini msix->msix_entry[i].pirq = XEN_PT_UNASSIGNED_PIRQ; 56180b4ecc8SPaolo Bonzini } 56280b4ecc8SPaolo Bonzini 56322fc860bSPaolo Bonzini memory_region_init_io(&msix->mmio, OBJECT(s), &pci_msix_ops, 56422fc860bSPaolo Bonzini s, "xen-pci-pt-msix", 56580b4ecc8SPaolo Bonzini (total_entries * PCI_MSIX_ENTRY_SIZE 56680b4ecc8SPaolo Bonzini + XC_PAGE_SIZE - 1) 56780b4ecc8SPaolo Bonzini & XC_PAGE_MASK); 56880b4ecc8SPaolo Bonzini 56964c7c117SPeter Maydell rc = xen_host_pci_get_long(hd, base + PCI_MSIX_TABLE, &table_off); 57064c7c117SPeter Maydell if (rc) { 57164c7c117SPeter Maydell XEN_PT_ERR(d, "Failed to read PCI_MSIX_TABLE field\n"); 57264c7c117SPeter Maydell goto error_out; 57364c7c117SPeter Maydell } 57480b4ecc8SPaolo Bonzini bar_index = msix->bar_index = table_off & PCI_MSIX_FLAGS_BIRMASK; 57580b4ecc8SPaolo Bonzini table_off = table_off & ~PCI_MSIX_FLAGS_BIRMASK; 57680b4ecc8SPaolo Bonzini msix->table_base = s->real_device.io_regions[bar_index].base_addr; 57780b4ecc8SPaolo Bonzini XEN_PT_LOG(d, "get MSI-X table BAR base 0x%"PRIx64"\n", msix->table_base); 57880b4ecc8SPaolo Bonzini 57980b4ecc8SPaolo Bonzini fd = open("/dev/mem", O_RDWR); 58080b4ecc8SPaolo Bonzini if (fd == -1) { 58180b4ecc8SPaolo Bonzini rc = -errno; 58280b4ecc8SPaolo Bonzini XEN_PT_ERR(d, "Can't open /dev/mem: %s\n", strerror(errno)); 58380b4ecc8SPaolo Bonzini goto error_out; 58480b4ecc8SPaolo Bonzini } 58580b4ecc8SPaolo Bonzini XEN_PT_LOG(d, "table_off = %#x, total_entries = %d\n", 58680b4ecc8SPaolo Bonzini table_off, total_entries); 58780b4ecc8SPaolo Bonzini msix->table_offset_adjust = table_off & 0x0fff; 58880b4ecc8SPaolo Bonzini msix->phys_iomem_base = 58980b4ecc8SPaolo Bonzini mmap(NULL, 59080b4ecc8SPaolo Bonzini total_entries * PCI_MSIX_ENTRY_SIZE + msix->table_offset_adjust, 59180b4ecc8SPaolo Bonzini PROT_READ, 59280b4ecc8SPaolo Bonzini MAP_SHARED | MAP_LOCKED, 59380b4ecc8SPaolo Bonzini fd, 59480b4ecc8SPaolo Bonzini msix->table_base + table_off - msix->table_offset_adjust); 59580b4ecc8SPaolo Bonzini close(fd); 59680b4ecc8SPaolo Bonzini if (msix->phys_iomem_base == MAP_FAILED) { 59780b4ecc8SPaolo Bonzini rc = -errno; 59880b4ecc8SPaolo Bonzini XEN_PT_ERR(d, "Can't map physical MSI-X table: %s\n", strerror(errno)); 59980b4ecc8SPaolo Bonzini goto error_out; 60080b4ecc8SPaolo Bonzini } 60180b4ecc8SPaolo Bonzini msix->phys_iomem_base = (char *)msix->phys_iomem_base 60280b4ecc8SPaolo Bonzini + msix->table_offset_adjust; 60380b4ecc8SPaolo Bonzini 60480b4ecc8SPaolo Bonzini XEN_PT_LOG(d, "mapping physical MSI-X table to %p\n", 60580b4ecc8SPaolo Bonzini msix->phys_iomem_base); 60680b4ecc8SPaolo Bonzini 60780b4ecc8SPaolo Bonzini memory_region_add_subregion_overlap(&s->bar[bar_index], table_off, 60880b4ecc8SPaolo Bonzini &msix->mmio, 60980b4ecc8SPaolo Bonzini 2); /* Priority: pci default + 1 */ 61080b4ecc8SPaolo Bonzini 61180b4ecc8SPaolo Bonzini return 0; 61280b4ecc8SPaolo Bonzini 61380b4ecc8SPaolo Bonzini error_out: 61480b4ecc8SPaolo Bonzini g_free(s->msix); 61580b4ecc8SPaolo Bonzini s->msix = NULL; 61680b4ecc8SPaolo Bonzini return rc; 61780b4ecc8SPaolo Bonzini } 61880b4ecc8SPaolo Bonzini 6194e494de6SLan Tianyu void xen_pt_msix_unmap(XenPCIPassthroughState *s) 62080b4ecc8SPaolo Bonzini { 62180b4ecc8SPaolo Bonzini XenPTMSIX *msix = s->msix; 62280b4ecc8SPaolo Bonzini 62380b4ecc8SPaolo Bonzini if (!msix) { 62480b4ecc8SPaolo Bonzini return; 62580b4ecc8SPaolo Bonzini } 62680b4ecc8SPaolo Bonzini 62780b4ecc8SPaolo Bonzini /* unmap the MSI-X memory mapped register area */ 62880b4ecc8SPaolo Bonzini if (msix->phys_iomem_base) { 62980b4ecc8SPaolo Bonzini XEN_PT_LOG(&s->dev, "unmapping physical MSI-X table from %p\n", 63080b4ecc8SPaolo Bonzini msix->phys_iomem_base); 63180b4ecc8SPaolo Bonzini munmap(msix->phys_iomem_base, msix->total_entries * PCI_MSIX_ENTRY_SIZE 63280b4ecc8SPaolo Bonzini + msix->table_offset_adjust); 63380b4ecc8SPaolo Bonzini } 63480b4ecc8SPaolo Bonzini 63580b4ecc8SPaolo Bonzini memory_region_del_subregion(&s->bar[msix->bar_index], &msix->mmio); 6364e494de6SLan Tianyu } 6374e494de6SLan Tianyu 6384e494de6SLan Tianyu void xen_pt_msix_delete(XenPCIPassthroughState *s) 6394e494de6SLan Tianyu { 6404e494de6SLan Tianyu XenPTMSIX *msix = s->msix; 6414e494de6SLan Tianyu 6424e494de6SLan Tianyu if (!msix) { 6434e494de6SLan Tianyu return; 6444e494de6SLan Tianyu } 6454e494de6SLan Tianyu 6464e494de6SLan Tianyu object_unparent(OBJECT(&msix->mmio)); 64780b4ecc8SPaolo Bonzini 64880b4ecc8SPaolo Bonzini g_free(s->msix); 64980b4ecc8SPaolo Bonzini s->msix = NULL; 65080b4ecc8SPaolo Bonzini } 651