162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Xen PCI - handle PCI (INTx) and MSI infrastructure calls for PV, HVM and 462306a36Sopenharmony_ci * initial domain support. We also handle the DSDT _PRT callbacks for GSI's 562306a36Sopenharmony_ci * used in HVM and initial domain mode (PV does not parse ACPI, so it has no 662306a36Sopenharmony_ci * concept of GSIs). Under PV we hook under the pnbbios API for IRQs and 762306a36Sopenharmony_ci * 0xcf8 PCI configuration read/write. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Author: Ryan Wilson <hap9@epoch.ncsc.mil> 1062306a36Sopenharmony_ci * Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> 1162306a36Sopenharmony_ci * Stefano Stabellini <stefano.stabellini@eu.citrix.com> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/pci.h> 1662306a36Sopenharmony_ci#include <linux/acpi.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/io.h> 1962306a36Sopenharmony_ci#include <asm/io_apic.h> 2062306a36Sopenharmony_ci#include <asm/pci_x86.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/xen/hypervisor.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <xen/features.h> 2562306a36Sopenharmony_ci#include <xen/events.h> 2662306a36Sopenharmony_ci#include <xen/pci.h> 2762306a36Sopenharmony_ci#include <asm/xen/pci.h> 2862306a36Sopenharmony_ci#include <asm/xen/cpuid.h> 2962306a36Sopenharmony_ci#include <asm/apic.h> 3062306a36Sopenharmony_ci#include <asm/acpi.h> 3162306a36Sopenharmony_ci#include <asm/i8259.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int xen_pcifront_enable_irq(struct pci_dev *dev) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci int rc; 3662306a36Sopenharmony_ci int share = 1; 3762306a36Sopenharmony_ci int pirq; 3862306a36Sopenharmony_ci u8 gsi; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &gsi); 4162306a36Sopenharmony_ci if (rc < 0) { 4262306a36Sopenharmony_ci dev_warn(&dev->dev, "Xen PCI: failed to read interrupt line: %d\n", 4362306a36Sopenharmony_ci rc); 4462306a36Sopenharmony_ci return rc; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci /* In PV DomU the Xen PCI backend puts the PIRQ in the interrupt line.*/ 4762306a36Sopenharmony_ci pirq = gsi; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (gsi < nr_legacy_irqs()) 5062306a36Sopenharmony_ci share = 0; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci rc = xen_bind_pirq_gsi_to_irq(gsi, pirq, share, "pcifront"); 5362306a36Sopenharmony_ci if (rc < 0) { 5462306a36Sopenharmony_ci dev_warn(&dev->dev, "Xen PCI: failed to bind GSI%d (PIRQ%d) to IRQ: %d\n", 5562306a36Sopenharmony_ci gsi, pirq, rc); 5662306a36Sopenharmony_ci return rc; 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci dev->irq = rc; 6062306a36Sopenharmony_ci dev_info(&dev->dev, "Xen PCI mapped GSI%d to IRQ%d\n", gsi, dev->irq); 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#ifdef CONFIG_ACPI 6562306a36Sopenharmony_cistatic int xen_register_pirq(u32 gsi, int triggering, bool set_pirq) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci int rc, pirq = -1, irq; 6862306a36Sopenharmony_ci struct physdev_map_pirq map_irq; 6962306a36Sopenharmony_ci int shareable = 0; 7062306a36Sopenharmony_ci char *name; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci irq = xen_irq_from_gsi(gsi); 7362306a36Sopenharmony_ci if (irq > 0) 7462306a36Sopenharmony_ci return irq; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (set_pirq) 7762306a36Sopenharmony_ci pirq = gsi; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci map_irq.domid = DOMID_SELF; 8062306a36Sopenharmony_ci map_irq.type = MAP_PIRQ_TYPE_GSI; 8162306a36Sopenharmony_ci map_irq.index = gsi; 8262306a36Sopenharmony_ci map_irq.pirq = pirq; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq); 8562306a36Sopenharmony_ci if (rc) { 8662306a36Sopenharmony_ci printk(KERN_WARNING "xen map irq failed %d\n", rc); 8762306a36Sopenharmony_ci return -1; 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci if (triggering == ACPI_EDGE_SENSITIVE) { 9162306a36Sopenharmony_ci shareable = 0; 9262306a36Sopenharmony_ci name = "ioapic-edge"; 9362306a36Sopenharmony_ci } else { 9462306a36Sopenharmony_ci shareable = 1; 9562306a36Sopenharmony_ci name = "ioapic-level"; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci irq = xen_bind_pirq_gsi_to_irq(gsi, map_irq.pirq, shareable, name); 9962306a36Sopenharmony_ci if (irq < 0) 10062306a36Sopenharmony_ci goto out; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci printk(KERN_DEBUG "xen: --> pirq=%d -> irq=%d (gsi=%d)\n", map_irq.pirq, irq, gsi); 10362306a36Sopenharmony_ciout: 10462306a36Sopenharmony_ci return irq; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi, 10862306a36Sopenharmony_ci int trigger, int polarity) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci if (!xen_hvm_domain()) 11162306a36Sopenharmony_ci return -1; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return xen_register_pirq(gsi, trigger, 11462306a36Sopenharmony_ci false /* no mapping of GSI to PIRQ */); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#ifdef CONFIG_XEN_PV_DOM0 11862306a36Sopenharmony_cistatic int xen_register_gsi(u32 gsi, int triggering, int polarity) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci int rc, irq; 12162306a36Sopenharmony_ci struct physdev_setup_gsi setup_gsi; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!xen_pv_domain()) 12462306a36Sopenharmony_ci return -1; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci printk(KERN_DEBUG "xen: registering gsi %u triggering %d polarity %d\n", 12762306a36Sopenharmony_ci gsi, triggering, polarity); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci irq = xen_register_pirq(gsi, triggering, true); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci setup_gsi.gsi = gsi; 13262306a36Sopenharmony_ci setup_gsi.triggering = (triggering == ACPI_EDGE_SENSITIVE ? 0 : 1); 13362306a36Sopenharmony_ci setup_gsi.polarity = (polarity == ACPI_ACTIVE_HIGH ? 0 : 1); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci rc = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi); 13662306a36Sopenharmony_ci if (rc == -EEXIST) 13762306a36Sopenharmony_ci printk(KERN_INFO "Already setup the GSI :%d\n", gsi); 13862306a36Sopenharmony_ci else if (rc) { 13962306a36Sopenharmony_ci printk(KERN_ERR "Failed to setup GSI :%d, err_code:%d\n", 14062306a36Sopenharmony_ci gsi, rc); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return irq; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic int acpi_register_gsi_xen(struct device *dev, u32 gsi, 14762306a36Sopenharmony_ci int trigger, int polarity) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci return xen_register_gsi(gsi, trigger, polarity); 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci#endif 15262306a36Sopenharmony_ci#endif 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#if defined(CONFIG_PCI_MSI) 15562306a36Sopenharmony_ci#include <linux/msi.h> 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistruct xen_pci_frontend_ops *xen_pci_frontend; 15862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(xen_pci_frontend); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistruct xen_msi_ops { 16162306a36Sopenharmony_ci int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type); 16262306a36Sopenharmony_ci void (*teardown_msi_irqs)(struct pci_dev *dev); 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic struct xen_msi_ops xen_msi_ops __ro_after_init; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci int irq, ret, i; 17062306a36Sopenharmony_ci struct msi_desc *msidesc; 17162306a36Sopenharmony_ci int *v; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (type == PCI_CAP_ID_MSI && nvec > 1) 17462306a36Sopenharmony_ci return 1; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci v = kcalloc(max(1, nvec), sizeof(int), GFP_KERNEL); 17762306a36Sopenharmony_ci if (!v) 17862306a36Sopenharmony_ci return -ENOMEM; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (type == PCI_CAP_ID_MSIX) 18162306a36Sopenharmony_ci ret = xen_pci_frontend_enable_msix(dev, v, nvec); 18262306a36Sopenharmony_ci else 18362306a36Sopenharmony_ci ret = xen_pci_frontend_enable_msi(dev, v); 18462306a36Sopenharmony_ci if (ret) 18562306a36Sopenharmony_ci goto error; 18662306a36Sopenharmony_ci i = 0; 18762306a36Sopenharmony_ci msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_NOTASSOCIATED) { 18862306a36Sopenharmony_ci irq = xen_bind_pirq_msi_to_irq(dev, msidesc, v[i], 18962306a36Sopenharmony_ci (type == PCI_CAP_ID_MSI) ? nvec : 1, 19062306a36Sopenharmony_ci (type == PCI_CAP_ID_MSIX) ? 19162306a36Sopenharmony_ci "pcifront-msi-x" : 19262306a36Sopenharmony_ci "pcifront-msi", 19362306a36Sopenharmony_ci DOMID_SELF); 19462306a36Sopenharmony_ci if (irq < 0) { 19562306a36Sopenharmony_ci ret = irq; 19662306a36Sopenharmony_ci goto free; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci i++; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci kfree(v); 20162306a36Sopenharmony_ci return msi_device_populate_sysfs(&dev->dev); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cierror: 20462306a36Sopenharmony_ci if (ret == -ENOSYS) 20562306a36Sopenharmony_ci dev_err(&dev->dev, "Xen PCI frontend has not registered MSI/MSI-X support!\n"); 20662306a36Sopenharmony_ci else if (ret) 20762306a36Sopenharmony_ci dev_err(&dev->dev, "Xen PCI frontend error: %d!\n", ret); 20862306a36Sopenharmony_cifree: 20962306a36Sopenharmony_ci kfree(v); 21062306a36Sopenharmony_ci return ret; 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void xen_msi_compose_msg(struct pci_dev *pdev, unsigned int pirq, 21462306a36Sopenharmony_ci struct msi_msg *msg) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci /* 21762306a36Sopenharmony_ci * We set vector == 0 to tell the hypervisor we don't care about 21862306a36Sopenharmony_ci * it, but we want a pirq setup instead. We use the dest_id fields 21962306a36Sopenharmony_ci * to pass the pirq that we want. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci memset(msg, 0, sizeof(*msg)); 22262306a36Sopenharmony_ci msg->address_hi = X86_MSI_BASE_ADDRESS_HIGH; 22362306a36Sopenharmony_ci msg->arch_addr_hi.destid_8_31 = pirq >> 8; 22462306a36Sopenharmony_ci msg->arch_addr_lo.destid_0_7 = pirq & 0xFF; 22562306a36Sopenharmony_ci msg->arch_addr_lo.base_address = X86_MSI_BASE_ADDRESS_LOW; 22662306a36Sopenharmony_ci msg->arch_data.delivery_mode = APIC_DELIVERY_MODE_EXTINT; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int irq, pirq; 23262306a36Sopenharmony_ci struct msi_desc *msidesc; 23362306a36Sopenharmony_ci struct msi_msg msg; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (type == PCI_CAP_ID_MSI && nvec > 1) 23662306a36Sopenharmony_ci return 1; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_NOTASSOCIATED) { 23962306a36Sopenharmony_ci pirq = xen_allocate_pirq_msi(dev, msidesc); 24062306a36Sopenharmony_ci if (pirq < 0) { 24162306a36Sopenharmony_ci irq = -ENODEV; 24262306a36Sopenharmony_ci goto error; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci xen_msi_compose_msg(dev, pirq, &msg); 24562306a36Sopenharmony_ci __pci_write_msi_msg(msidesc, &msg); 24662306a36Sopenharmony_ci dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq); 24762306a36Sopenharmony_ci irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq, 24862306a36Sopenharmony_ci (type == PCI_CAP_ID_MSI) ? nvec : 1, 24962306a36Sopenharmony_ci (type == PCI_CAP_ID_MSIX) ? 25062306a36Sopenharmony_ci "msi-x" : "msi", 25162306a36Sopenharmony_ci DOMID_SELF); 25262306a36Sopenharmony_ci if (irq < 0) 25362306a36Sopenharmony_ci goto error; 25462306a36Sopenharmony_ci dev_dbg(&dev->dev, 25562306a36Sopenharmony_ci "xen: msi --> pirq=%d --> irq=%d\n", pirq, irq); 25662306a36Sopenharmony_ci } 25762306a36Sopenharmony_ci return msi_device_populate_sysfs(&dev->dev); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cierror: 26062306a36Sopenharmony_ci dev_err(&dev->dev, "Failed to create MSI%s! ret=%d!\n", 26162306a36Sopenharmony_ci type == PCI_CAP_ID_MSI ? "" : "-X", irq); 26262306a36Sopenharmony_ci return irq; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci#ifdef CONFIG_XEN_PV_DOM0 26662306a36Sopenharmony_cistatic bool __read_mostly pci_seg_supported = true; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci int ret = 0; 27162306a36Sopenharmony_ci struct msi_desc *msidesc; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_NOTASSOCIATED) { 27462306a36Sopenharmony_ci struct physdev_map_pirq map_irq; 27562306a36Sopenharmony_ci domid_t domid; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci domid = ret = xen_find_device_domain_owner(dev); 27862306a36Sopenharmony_ci /* N.B. Casting int's -ENODEV to uint16_t results in 0xFFED, 27962306a36Sopenharmony_ci * hence check ret value for < 0. */ 28062306a36Sopenharmony_ci if (ret < 0) 28162306a36Sopenharmony_ci domid = DOMID_SELF; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci memset(&map_irq, 0, sizeof(map_irq)); 28462306a36Sopenharmony_ci map_irq.domid = domid; 28562306a36Sopenharmony_ci map_irq.type = MAP_PIRQ_TYPE_MSI_SEG; 28662306a36Sopenharmony_ci map_irq.index = -1; 28762306a36Sopenharmony_ci map_irq.pirq = -1; 28862306a36Sopenharmony_ci map_irq.bus = dev->bus->number | 28962306a36Sopenharmony_ci (pci_domain_nr(dev->bus) << 16); 29062306a36Sopenharmony_ci map_irq.devfn = dev->devfn; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (type == PCI_CAP_ID_MSI && nvec > 1) { 29362306a36Sopenharmony_ci map_irq.type = MAP_PIRQ_TYPE_MULTI_MSI; 29462306a36Sopenharmony_ci map_irq.entry_nr = nvec; 29562306a36Sopenharmony_ci } else if (type == PCI_CAP_ID_MSIX) { 29662306a36Sopenharmony_ci int pos; 29762306a36Sopenharmony_ci unsigned long flags; 29862306a36Sopenharmony_ci u32 table_offset, bir; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci pos = dev->msix_cap; 30162306a36Sopenharmony_ci pci_read_config_dword(dev, pos + PCI_MSIX_TABLE, 30262306a36Sopenharmony_ci &table_offset); 30362306a36Sopenharmony_ci bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR); 30462306a36Sopenharmony_ci flags = pci_resource_flags(dev, bir); 30562306a36Sopenharmony_ci if (!flags || (flags & IORESOURCE_UNSET)) 30662306a36Sopenharmony_ci return -EINVAL; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci map_irq.table_base = pci_resource_start(dev, bir); 30962306a36Sopenharmony_ci map_irq.entry_nr = msidesc->msi_index; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ret = -EINVAL; 31362306a36Sopenharmony_ci if (pci_seg_supported) 31462306a36Sopenharmony_ci ret = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, 31562306a36Sopenharmony_ci &map_irq); 31662306a36Sopenharmony_ci if (type == PCI_CAP_ID_MSI && nvec > 1 && ret) { 31762306a36Sopenharmony_ci /* 31862306a36Sopenharmony_ci * If MAP_PIRQ_TYPE_MULTI_MSI is not available 31962306a36Sopenharmony_ci * there's nothing else we can do in this case. 32062306a36Sopenharmony_ci * Just set ret > 0 so driver can retry with 32162306a36Sopenharmony_ci * single MSI. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci ret = 1; 32462306a36Sopenharmony_ci goto out; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci if (ret == -EINVAL && !pci_domain_nr(dev->bus)) { 32762306a36Sopenharmony_ci map_irq.type = MAP_PIRQ_TYPE_MSI; 32862306a36Sopenharmony_ci map_irq.index = -1; 32962306a36Sopenharmony_ci map_irq.pirq = -1; 33062306a36Sopenharmony_ci map_irq.bus = dev->bus->number; 33162306a36Sopenharmony_ci ret = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, 33262306a36Sopenharmony_ci &map_irq); 33362306a36Sopenharmony_ci if (ret != -EINVAL) 33462306a36Sopenharmony_ci pci_seg_supported = false; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci if (ret) { 33762306a36Sopenharmony_ci dev_warn(&dev->dev, "xen map irq failed %d for %d domain\n", 33862306a36Sopenharmony_ci ret, domid); 33962306a36Sopenharmony_ci goto out; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci ret = xen_bind_pirq_msi_to_irq(dev, msidesc, map_irq.pirq, 34362306a36Sopenharmony_ci (type == PCI_CAP_ID_MSI) ? nvec : 1, 34462306a36Sopenharmony_ci (type == PCI_CAP_ID_MSIX) ? "msi-x" : "msi", 34562306a36Sopenharmony_ci domid); 34662306a36Sopenharmony_ci if (ret < 0) 34762306a36Sopenharmony_ci goto out; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci ret = msi_device_populate_sysfs(&dev->dev); 35062306a36Sopenharmony_ciout: 35162306a36Sopenharmony_ci return ret; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cibool xen_initdom_restore_msi(struct pci_dev *dev) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci int ret = 0; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!xen_initial_domain()) 35962306a36Sopenharmony_ci return true; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (pci_seg_supported) { 36262306a36Sopenharmony_ci struct physdev_pci_device restore_ext; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci restore_ext.seg = pci_domain_nr(dev->bus); 36562306a36Sopenharmony_ci restore_ext.bus = dev->bus->number; 36662306a36Sopenharmony_ci restore_ext.devfn = dev->devfn; 36762306a36Sopenharmony_ci ret = HYPERVISOR_physdev_op(PHYSDEVOP_restore_msi_ext, 36862306a36Sopenharmony_ci &restore_ext); 36962306a36Sopenharmony_ci if (ret == -ENOSYS) 37062306a36Sopenharmony_ci pci_seg_supported = false; 37162306a36Sopenharmony_ci WARN(ret && ret != -ENOSYS, "restore_msi_ext -> %d\n", ret); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci if (!pci_seg_supported) { 37462306a36Sopenharmony_ci struct physdev_restore_msi restore; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci restore.bus = dev->bus->number; 37762306a36Sopenharmony_ci restore.devfn = dev->devfn; 37862306a36Sopenharmony_ci ret = HYPERVISOR_physdev_op(PHYSDEVOP_restore_msi, &restore); 37962306a36Sopenharmony_ci WARN(ret && ret != -ENOSYS, "restore_msi -> %d\n", ret); 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci return false; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci#else /* CONFIG_XEN_PV_DOM0 */ 38462306a36Sopenharmony_ci#define xen_initdom_setup_msi_irqs NULL 38562306a36Sopenharmony_ci#endif /* !CONFIG_XEN_PV_DOM0 */ 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void xen_teardown_msi_irqs(struct pci_dev *dev) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct msi_desc *msidesc; 39062306a36Sopenharmony_ci int i; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_ASSOCIATED) { 39362306a36Sopenharmony_ci for (i = 0; i < msidesc->nvec_used; i++) 39462306a36Sopenharmony_ci xen_destroy_irq(msidesc->irq + i); 39562306a36Sopenharmony_ci msidesc->irq = 0; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci msi_device_destroy_sysfs(&dev->dev); 39962306a36Sopenharmony_ci} 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_cistatic void xen_pv_teardown_msi_irqs(struct pci_dev *dev) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci if (dev->msix_enabled) 40462306a36Sopenharmony_ci xen_pci_frontend_disable_msix(dev); 40562306a36Sopenharmony_ci else 40662306a36Sopenharmony_ci xen_pci_frontend_disable_msi(dev); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci xen_teardown_msi_irqs(dev); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int xen_msi_domain_alloc_irqs(struct irq_domain *domain, 41262306a36Sopenharmony_ci struct device *dev, int nvec) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci int type; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (WARN_ON_ONCE(!dev_is_pci(dev))) 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci type = to_pci_dev(dev)->msix_enabled ? PCI_CAP_ID_MSIX : PCI_CAP_ID_MSI; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return xen_msi_ops.setup_msi_irqs(to_pci_dev(dev), nvec, type); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void xen_msi_domain_free_irqs(struct irq_domain *domain, 42562306a36Sopenharmony_ci struct device *dev) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci if (WARN_ON_ONCE(!dev_is_pci(dev))) 42862306a36Sopenharmony_ci return; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci xen_msi_ops.teardown_msi_irqs(to_pci_dev(dev)); 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic struct msi_domain_ops xen_pci_msi_domain_ops = { 43462306a36Sopenharmony_ci .domain_alloc_irqs = xen_msi_domain_alloc_irqs, 43562306a36Sopenharmony_ci .domain_free_irqs = xen_msi_domain_free_irqs, 43662306a36Sopenharmony_ci}; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic struct msi_domain_info xen_pci_msi_domain_info = { 43962306a36Sopenharmony_ci .flags = MSI_FLAG_PCI_MSIX | MSI_FLAG_FREE_MSI_DESCS | MSI_FLAG_DEV_SYSFS, 44062306a36Sopenharmony_ci .ops = &xen_pci_msi_domain_ops, 44162306a36Sopenharmony_ci}; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci/* 44462306a36Sopenharmony_ci * This irq domain is a blatant violation of the irq domain design, but 44562306a36Sopenharmony_ci * distangling XEN into real irq domains is not a job for mere mortals with 44662306a36Sopenharmony_ci * limited XENology. But it's the least dangerous way for a mere mortal to 44762306a36Sopenharmony_ci * get rid of the arch_*_msi_irqs() hackery in order to store the irq 44862306a36Sopenharmony_ci * domain pointer in struct device. This irq domain wrappery allows to do 44962306a36Sopenharmony_ci * that without breaking XEN terminally. 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_cistatic __init struct irq_domain *xen_create_pci_msi_domain(void) 45262306a36Sopenharmony_ci{ 45362306a36Sopenharmony_ci struct irq_domain *d = NULL; 45462306a36Sopenharmony_ci struct fwnode_handle *fn; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci fn = irq_domain_alloc_named_fwnode("XEN-MSI"); 45762306a36Sopenharmony_ci if (fn) 45862306a36Sopenharmony_ci d = msi_create_irq_domain(fn, &xen_pci_msi_domain_info, NULL); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* FIXME: No idea how to survive if this fails */ 46162306a36Sopenharmony_ci BUG_ON(!d); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return d; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic __init void xen_setup_pci_msi(void) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci if (xen_pv_domain()) { 46962306a36Sopenharmony_ci if (xen_initial_domain()) 47062306a36Sopenharmony_ci xen_msi_ops.setup_msi_irqs = xen_initdom_setup_msi_irqs; 47162306a36Sopenharmony_ci else 47262306a36Sopenharmony_ci xen_msi_ops.setup_msi_irqs = xen_setup_msi_irqs; 47362306a36Sopenharmony_ci xen_msi_ops.teardown_msi_irqs = xen_pv_teardown_msi_irqs; 47462306a36Sopenharmony_ci } else if (xen_hvm_domain()) { 47562306a36Sopenharmony_ci xen_msi_ops.setup_msi_irqs = xen_hvm_setup_msi_irqs; 47662306a36Sopenharmony_ci xen_msi_ops.teardown_msi_irqs = xen_teardown_msi_irqs; 47762306a36Sopenharmony_ci } else { 47862306a36Sopenharmony_ci WARN_ON_ONCE(1); 47962306a36Sopenharmony_ci return; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* 48362306a36Sopenharmony_ci * Override the PCI/MSI irq domain init function. No point 48462306a36Sopenharmony_ci * in allocating the native domain and never use it. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_ci x86_init.irqs.create_pci_msi_domain = xen_create_pci_msi_domain; 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * With XEN PIRQ/Eventchannels in use PCI/MSI[-X] masking is solely 48962306a36Sopenharmony_ci * controlled by the hypervisor. 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_ci pci_msi_ignore_mask = 1; 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci#else /* CONFIG_PCI_MSI */ 49562306a36Sopenharmony_cistatic inline void xen_setup_pci_msi(void) { } 49662306a36Sopenharmony_ci#endif /* CONFIG_PCI_MSI */ 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ciint __init pci_xen_init(void) 49962306a36Sopenharmony_ci{ 50062306a36Sopenharmony_ci if (!xen_pv_domain() || xen_initial_domain()) 50162306a36Sopenharmony_ci return -ENODEV; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci printk(KERN_INFO "PCI: setting up Xen PCI frontend stub\n"); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci pcibios_set_cache_line_size(); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci pcibios_enable_irq = xen_pcifront_enable_irq; 50862306a36Sopenharmony_ci pcibios_disable_irq = NULL; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Keep ACPI out of the picture */ 51162306a36Sopenharmony_ci acpi_noirq_set(); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci xen_setup_pci_msi(); 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 51862306a36Sopenharmony_cistatic void __init xen_hvm_msi_init(void) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci if (!apic_is_disabled) { 52162306a36Sopenharmony_ci /* 52262306a36Sopenharmony_ci * If hardware supports (x2)APIC virtualization (as indicated 52362306a36Sopenharmony_ci * by hypervisor's leaf 4) then we don't need to use pirqs/ 52462306a36Sopenharmony_ci * event channels for MSI handling and instead use regular 52562306a36Sopenharmony_ci * APIC processing 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci uint32_t eax = cpuid_eax(xen_cpuid_base() + 4); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (((eax & XEN_HVM_CPUID_X2APIC_VIRT) && x2apic_mode) || 53062306a36Sopenharmony_ci ((eax & XEN_HVM_CPUID_APIC_ACCESS_VIRT) && boot_cpu_has(X86_FEATURE_APIC))) 53162306a36Sopenharmony_ci return; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci xen_setup_pci_msi(); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci#endif 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ciint __init pci_xen_hvm_init(void) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci if (!xen_have_vector_callback || !xen_feature(XENFEAT_hvm_pirqs)) 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci#ifdef CONFIG_ACPI 54362306a36Sopenharmony_ci /* 54462306a36Sopenharmony_ci * We don't want to change the actual ACPI delivery model, 54562306a36Sopenharmony_ci * just how GSIs get registered. 54662306a36Sopenharmony_ci */ 54762306a36Sopenharmony_ci __acpi_register_gsi = acpi_register_gsi_xen_hvm; 54862306a36Sopenharmony_ci __acpi_unregister_gsi = NULL; 54962306a36Sopenharmony_ci#endif 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 55262306a36Sopenharmony_ci /* 55362306a36Sopenharmony_ci * We need to wait until after x2apic is initialized 55462306a36Sopenharmony_ci * before we can set MSI IRQ ops. 55562306a36Sopenharmony_ci */ 55662306a36Sopenharmony_ci x86_platform.apic_post_init = xen_hvm_msi_init; 55762306a36Sopenharmony_ci#endif 55862306a36Sopenharmony_ci return 0; 55962306a36Sopenharmony_ci} 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci#ifdef CONFIG_XEN_PV_DOM0 56262306a36Sopenharmony_ciint __init pci_xen_initial_domain(void) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci int irq; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci xen_setup_pci_msi(); 56762306a36Sopenharmony_ci __acpi_register_gsi = acpi_register_gsi_xen; 56862306a36Sopenharmony_ci __acpi_unregister_gsi = NULL; 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * Pre-allocate the legacy IRQs. Use NR_LEGACY_IRQS here 57162306a36Sopenharmony_ci * because we don't have a PIC and thus nr_legacy_irqs() is zero. 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_ci for (irq = 0; irq < NR_IRQS_LEGACY; irq++) { 57462306a36Sopenharmony_ci int trigger, polarity; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (acpi_get_override_irq(irq, &trigger, &polarity) == -1) 57762306a36Sopenharmony_ci continue; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci xen_register_pirq(irq, 58062306a36Sopenharmony_ci trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE, 58162306a36Sopenharmony_ci true /* Map GSI to PIRQ */); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci if (0 == nr_ioapics) { 58462306a36Sopenharmony_ci for (irq = 0; irq < nr_legacy_irqs(); irq++) 58562306a36Sopenharmony_ci xen_bind_pirq_gsi_to_irq(irq, irq, 0, "xt-pic"); 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci#endif 59062306a36Sopenharmony_ci 591