1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b5401a96SAlex Nixon /*
3996c34aeSKonrad Rzeszutek Wilk * Xen PCI - handle PCI (INTx) and MSI infrastructure calls for PV, HVM and
4996c34aeSKonrad Rzeszutek Wilk * initial domain support. We also handle the DSDT _PRT callbacks for GSI's
5996c34aeSKonrad Rzeszutek Wilk * used in HVM and initial domain mode (PV does not parse ACPI, so it has no
6996c34aeSKonrad Rzeszutek Wilk * concept of GSIs). Under PV we hook under the pnbbios API for IRQs and
7996c34aeSKonrad Rzeszutek Wilk * 0xcf8 PCI configuration read/write.
8b5401a96SAlex Nixon *
9b5401a96SAlex Nixon * Author: Ryan Wilson <hap9@epoch.ncsc.mil>
10996c34aeSKonrad Rzeszutek Wilk * Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
11996c34aeSKonrad Rzeszutek Wilk * Stefano Stabellini <stefano.stabellini@eu.citrix.com>
12b5401a96SAlex Nixon */
13eb008eb6SPaul Gortmaker #include <linux/export.h>
14b5401a96SAlex Nixon #include <linux/init.h>
15b5401a96SAlex Nixon #include <linux/pci.h>
16b5401a96SAlex Nixon #include <linux/acpi.h>
17b5401a96SAlex Nixon
18b5401a96SAlex Nixon #include <linux/io.h>
190e058e52SStefano Stabellini #include <asm/io_apic.h>
20b5401a96SAlex Nixon #include <asm/pci_x86.h>
21b5401a96SAlex Nixon
22b5401a96SAlex Nixon #include <asm/xen/hypervisor.h>
23b5401a96SAlex Nixon
243942b740SStefano Stabellini #include <xen/features.h>
25b5401a96SAlex Nixon #include <xen/events.h>
26a67efff2SOleksandr Andrushchenko #include <xen/pci.h>
27b5401a96SAlex Nixon #include <asm/xen/pci.h>
2814520c92SBoris Ostrovsky #include <asm/xen/cpuid.h>
2914520c92SBoris Ostrovsky #include <asm/apic.h>
30ee87e155SRandy Dunlap #include <asm/acpi.h>
3195d76accSJiang Liu #include <asm/i8259.h>
32b5401a96SAlex Nixon
xen_pcifront_enable_irq(struct pci_dev * dev)33fef6e262SKonrad Rzeszutek Wilk static int xen_pcifront_enable_irq(struct pci_dev *dev)
34fef6e262SKonrad Rzeszutek Wilk {
35fef6e262SKonrad Rzeszutek Wilk int rc;
36fef6e262SKonrad Rzeszutek Wilk int share = 1;
37fef6e262SKonrad Rzeszutek Wilk int pirq;
38fef6e262SKonrad Rzeszutek Wilk u8 gsi;
39fef6e262SKonrad Rzeszutek Wilk
40fef6e262SKonrad Rzeszutek Wilk rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &gsi);
41*56d4e762SIlpo Järvinen if (rc) {
42fef6e262SKonrad Rzeszutek Wilk dev_warn(&dev->dev, "Xen PCI: failed to read interrupt line: %d\n",
43fef6e262SKonrad Rzeszutek Wilk rc);
44*56d4e762SIlpo Järvinen return pcibios_err_to_errno(rc);
45fef6e262SKonrad Rzeszutek Wilk }
4678316adaSKonrad Rzeszutek Wilk /* In PV DomU the Xen PCI backend puts the PIRQ in the interrupt line.*/
4778316adaSKonrad Rzeszutek Wilk pirq = gsi;
48fef6e262SKonrad Rzeszutek Wilk
4995d76accSJiang Liu if (gsi < nr_legacy_irqs())
50fef6e262SKonrad Rzeszutek Wilk share = 0;
51fef6e262SKonrad Rzeszutek Wilk
52fef6e262SKonrad Rzeszutek Wilk rc = xen_bind_pirq_gsi_to_irq(gsi, pirq, share, "pcifront");
53fef6e262SKonrad Rzeszutek Wilk if (rc < 0) {
54fef6e262SKonrad Rzeszutek Wilk dev_warn(&dev->dev, "Xen PCI: failed to bind GSI%d (PIRQ%d) to IRQ: %d\n",
55fef6e262SKonrad Rzeszutek Wilk gsi, pirq, rc);
56fef6e262SKonrad Rzeszutek Wilk return rc;
57fef6e262SKonrad Rzeszutek Wilk }
58fef6e262SKonrad Rzeszutek Wilk
59fef6e262SKonrad Rzeszutek Wilk dev->irq = rc;
60fef6e262SKonrad Rzeszutek Wilk dev_info(&dev->dev, "Xen PCI mapped GSI%d to IRQ%d\n", gsi, dev->irq);
61fef6e262SKonrad Rzeszutek Wilk return 0;
62fef6e262SKonrad Rzeszutek Wilk }
63fef6e262SKonrad Rzeszutek Wilk
6442a1de56SStefano Stabellini #ifdef CONFIG_ACPI
xen_register_pirq(u32 gsi,int triggering,bool set_pirq)6560369a4fSWei Liu static int xen_register_pirq(u32 gsi, int triggering, bool set_pirq)
6642a1de56SStefano Stabellini {
671c026a18SColin Ian King int rc, pirq = -1, irq;
6842a1de56SStefano Stabellini struct physdev_map_pirq map_irq;
6942a1de56SStefano Stabellini int shareable = 0;
7042a1de56SStefano Stabellini char *name;
7142a1de56SStefano Stabellini
7268c2c39aSStefano Stabellini irq = xen_irq_from_gsi(gsi);
7368c2c39aSStefano Stabellini if (irq > 0)
7468c2c39aSStefano Stabellini return irq;
7568c2c39aSStefano Stabellini
7678316adaSKonrad Rzeszutek Wilk if (set_pirq)
7778316adaSKonrad Rzeszutek Wilk pirq = gsi;
7878316adaSKonrad Rzeszutek Wilk
7938aa66fcSJeremy Fitzhardinge map_irq.domid = DOMID_SELF;
8038aa66fcSJeremy Fitzhardinge map_irq.type = MAP_PIRQ_TYPE_GSI;
8138aa66fcSJeremy Fitzhardinge map_irq.index = gsi;
82f4d0635bSIan Campbell map_irq.pirq = pirq;
8338aa66fcSJeremy Fitzhardinge
8438aa66fcSJeremy Fitzhardinge rc = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq, &map_irq);
8538aa66fcSJeremy Fitzhardinge if (rc) {
8638aa66fcSJeremy Fitzhardinge printk(KERN_WARNING "xen map irq failed %d\n", rc);
8738aa66fcSJeremy Fitzhardinge return -1;
8838aa66fcSJeremy Fitzhardinge }
8938aa66fcSJeremy Fitzhardinge
9030bd35edSKonrad Rzeszutek Wilk if (triggering == ACPI_EDGE_SENSITIVE) {
9130bd35edSKonrad Rzeszutek Wilk shareable = 0;
9230bd35edSKonrad Rzeszutek Wilk name = "ioapic-edge";
9330bd35edSKonrad Rzeszutek Wilk } else {
9430bd35edSKonrad Rzeszutek Wilk shareable = 1;
9530bd35edSKonrad Rzeszutek Wilk name = "ioapic-level";
9630bd35edSKonrad Rzeszutek Wilk }
9730bd35edSKonrad Rzeszutek Wilk
98ed89eb63SKonrad Rzeszutek Wilk irq = xen_bind_pirq_gsi_to_irq(gsi, map_irq.pirq, shareable, name);
9930bd35edSKonrad Rzeszutek Wilk if (irq < 0)
10030bd35edSKonrad Rzeszutek Wilk goto out;
10130bd35edSKonrad Rzeszutek Wilk
102ed89eb63SKonrad Rzeszutek Wilk printk(KERN_DEBUG "xen: --> pirq=%d -> irq=%d (gsi=%d)\n", map_irq.pirq, irq, gsi);
10338aa66fcSJeremy Fitzhardinge out:
10438aa66fcSJeremy Fitzhardinge return irq;
10538aa66fcSJeremy Fitzhardinge }
10638aa66fcSJeremy Fitzhardinge
acpi_register_gsi_xen_hvm(struct device * dev,u32 gsi,int trigger,int polarity)107ed89eb63SKonrad Rzeszutek Wilk static int acpi_register_gsi_xen_hvm(struct device *dev, u32 gsi,
108ed89eb63SKonrad Rzeszutek Wilk int trigger, int polarity)
109ed89eb63SKonrad Rzeszutek Wilk {
110ed89eb63SKonrad Rzeszutek Wilk if (!xen_hvm_domain())
111ed89eb63SKonrad Rzeszutek Wilk return -1;
112ed89eb63SKonrad Rzeszutek Wilk
11360369a4fSWei Liu return xen_register_pirq(gsi, trigger,
11478316adaSKonrad Rzeszutek Wilk false /* no mapping of GSI to PIRQ */);
115ed89eb63SKonrad Rzeszutek Wilk }
116ed89eb63SKonrad Rzeszutek Wilk
117cae7d81aSJan Beulich #ifdef CONFIG_XEN_PV_DOM0
xen_register_gsi(u32 gsi,int triggering,int polarity)11860369a4fSWei Liu static int xen_register_gsi(u32 gsi, int triggering, int polarity)
11938aa66fcSJeremy Fitzhardinge {
12038aa66fcSJeremy Fitzhardinge int rc, irq;
12138aa66fcSJeremy Fitzhardinge struct physdev_setup_gsi setup_gsi;
12238aa66fcSJeremy Fitzhardinge
12338aa66fcSJeremy Fitzhardinge if (!xen_pv_domain())
12438aa66fcSJeremy Fitzhardinge return -1;
12538aa66fcSJeremy Fitzhardinge
12638aa66fcSJeremy Fitzhardinge printk(KERN_DEBUG "xen: registering gsi %u triggering %d polarity %d\n",
12738aa66fcSJeremy Fitzhardinge gsi, triggering, polarity);
12838aa66fcSJeremy Fitzhardinge
12960369a4fSWei Liu irq = xen_register_pirq(gsi, triggering, true);
13038aa66fcSJeremy Fitzhardinge
13138aa66fcSJeremy Fitzhardinge setup_gsi.gsi = gsi;
13238aa66fcSJeremy Fitzhardinge setup_gsi.triggering = (triggering == ACPI_EDGE_SENSITIVE ? 0 : 1);
13338aa66fcSJeremy Fitzhardinge setup_gsi.polarity = (polarity == ACPI_ACTIVE_HIGH ? 0 : 1);
13438aa66fcSJeremy Fitzhardinge
13538aa66fcSJeremy Fitzhardinge rc = HYPERVISOR_physdev_op(PHYSDEVOP_setup_gsi, &setup_gsi);
13638aa66fcSJeremy Fitzhardinge if (rc == -EEXIST)
13738aa66fcSJeremy Fitzhardinge printk(KERN_INFO "Already setup the GSI :%d\n", gsi);
13838aa66fcSJeremy Fitzhardinge else if (rc) {
13938aa66fcSJeremy Fitzhardinge printk(KERN_ERR "Failed to setup GSI :%d, err_code:%d\n",
14038aa66fcSJeremy Fitzhardinge gsi, rc);
14138aa66fcSJeremy Fitzhardinge }
14238aa66fcSJeremy Fitzhardinge
14338aa66fcSJeremy Fitzhardinge return irq;
14438aa66fcSJeremy Fitzhardinge }
14538aa66fcSJeremy Fitzhardinge
acpi_register_gsi_xen(struct device * dev,u32 gsi,int trigger,int polarity)146fef6e262SKonrad Rzeszutek Wilk static int acpi_register_gsi_xen(struct device *dev, u32 gsi,
147fef6e262SKonrad Rzeszutek Wilk int trigger, int polarity)
148fef6e262SKonrad Rzeszutek Wilk {
14960369a4fSWei Liu return xen_register_gsi(gsi, trigger, polarity);
150fef6e262SKonrad Rzeszutek Wilk }
151fef6e262SKonrad Rzeszutek Wilk #endif
152d92edd81SKonrad Rzeszutek Wilk #endif
153fef6e262SKonrad Rzeszutek Wilk
154fef6e262SKonrad Rzeszutek Wilk #if defined(CONFIG_PCI_MSI)
155fef6e262SKonrad Rzeszutek Wilk #include <linux/msi.h>
156fef6e262SKonrad Rzeszutek Wilk
157fef6e262SKonrad Rzeszutek Wilk struct xen_pci_frontend_ops *xen_pci_frontend;
158fef6e262SKonrad Rzeszutek Wilk EXPORT_SYMBOL_GPL(xen_pci_frontend);
159fef6e262SKonrad Rzeszutek Wilk
160874a2013SThomas Gleixner struct xen_msi_ops {
161874a2013SThomas Gleixner int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type);
162874a2013SThomas Gleixner void (*teardown_msi_irqs)(struct pci_dev *dev);
163874a2013SThomas Gleixner };
164874a2013SThomas Gleixner
165874a2013SThomas Gleixner static struct xen_msi_ops xen_msi_ops __ro_after_init;
166874a2013SThomas Gleixner
xen_setup_msi_irqs(struct pci_dev * dev,int nvec,int type)167fef6e262SKonrad Rzeszutek Wilk static int xen_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
168fef6e262SKonrad Rzeszutek Wilk {
169fef6e262SKonrad Rzeszutek Wilk int irq, ret, i;
170fef6e262SKonrad Rzeszutek Wilk struct msi_desc *msidesc;
171fef6e262SKonrad Rzeszutek Wilk int *v;
172fef6e262SKonrad Rzeszutek Wilk
173884ac297SKonrad Rzeszutek Wilk if (type == PCI_CAP_ID_MSI && nvec > 1)
174884ac297SKonrad Rzeszutek Wilk return 1;
175884ac297SKonrad Rzeszutek Wilk
1766396bb22SKees Cook v = kcalloc(max(1, nvec), sizeof(int), GFP_KERNEL);
177fef6e262SKonrad Rzeszutek Wilk if (!v)
178fef6e262SKonrad Rzeszutek Wilk return -ENOMEM;
179fef6e262SKonrad Rzeszutek Wilk
180fef6e262SKonrad Rzeszutek Wilk if (type == PCI_CAP_ID_MSIX)
181fef6e262SKonrad Rzeszutek Wilk ret = xen_pci_frontend_enable_msix(dev, v, nvec);
182fef6e262SKonrad Rzeszutek Wilk else
183fef6e262SKonrad Rzeszutek Wilk ret = xen_pci_frontend_enable_msi(dev, v);
184fef6e262SKonrad Rzeszutek Wilk if (ret)
185fef6e262SKonrad Rzeszutek Wilk goto error;
186fef6e262SKonrad Rzeszutek Wilk i = 0;
187f2948df5SThomas Gleixner msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_NOTASSOCIATED) {
188dec02deaSJan Beulich irq = xen_bind_pirq_msi_to_irq(dev, msidesc, v[i],
1894892c9b4SRoger Pau Monne (type == PCI_CAP_ID_MSI) ? nvec : 1,
190fef6e262SKonrad Rzeszutek Wilk (type == PCI_CAP_ID_MSIX) ?
191fef6e262SKonrad Rzeszutek Wilk "pcifront-msi-x" :
192fef6e262SKonrad Rzeszutek Wilk "pcifront-msi",
193fef6e262SKonrad Rzeszutek Wilk DOMID_SELF);
194e6599225SKonrad Rzeszutek Wilk if (irq < 0) {
195e6599225SKonrad Rzeszutek Wilk ret = irq;
196fef6e262SKonrad Rzeszutek Wilk goto free;
197e6599225SKonrad Rzeszutek Wilk }
198fef6e262SKonrad Rzeszutek Wilk i++;
199fef6e262SKonrad Rzeszutek Wilk }
200fef6e262SKonrad Rzeszutek Wilk kfree(v);
201335b4223SMaximilian Heyne return msi_device_populate_sysfs(&dev->dev);
202fef6e262SKonrad Rzeszutek Wilk
203fef6e262SKonrad Rzeszutek Wilk error:
2042cfec6a2SKonrad Rzeszutek Wilk if (ret == -ENOSYS)
205fef6e262SKonrad Rzeszutek Wilk dev_err(&dev->dev, "Xen PCI frontend has not registered MSI/MSI-X support!\n");
2062cfec6a2SKonrad Rzeszutek Wilk else if (ret)
2072cfec6a2SKonrad Rzeszutek Wilk dev_err(&dev->dev, "Xen PCI frontend error: %d!\n", ret);
208fef6e262SKonrad Rzeszutek Wilk free:
209fef6e262SKonrad Rzeszutek Wilk kfree(v);
210fef6e262SKonrad Rzeszutek Wilk return ret;
211fef6e262SKonrad Rzeszutek Wilk }
212fef6e262SKonrad Rzeszutek Wilk
xen_msi_compose_msg(struct pci_dev * pdev,unsigned int pirq,struct msi_msg * msg)213fef6e262SKonrad Rzeszutek Wilk static void xen_msi_compose_msg(struct pci_dev *pdev, unsigned int pirq,
214fef6e262SKonrad Rzeszutek Wilk struct msi_msg *msg)
215fef6e262SKonrad Rzeszutek Wilk {
21641bb2115SThomas Gleixner /*
21741bb2115SThomas Gleixner * We set vector == 0 to tell the hypervisor we don't care about
21841bb2115SThomas Gleixner * it, but we want a pirq setup instead. We use the dest_id fields
21941bb2115SThomas Gleixner * to pass the pirq that we want.
22041bb2115SThomas Gleixner */
22141bb2115SThomas Gleixner memset(msg, 0, sizeof(*msg));
22241bb2115SThomas Gleixner msg->address_hi = X86_MSI_BASE_ADDRESS_HIGH;
22341bb2115SThomas Gleixner msg->arch_addr_hi.destid_8_31 = pirq >> 8;
22441bb2115SThomas Gleixner msg->arch_addr_lo.destid_0_7 = pirq & 0xFF;
22541bb2115SThomas Gleixner msg->arch_addr_lo.base_address = X86_MSI_BASE_ADDRESS_LOW;
22641bb2115SThomas Gleixner msg->arch_data.delivery_mode = APIC_DELIVERY_MODE_EXTINT;
227fef6e262SKonrad Rzeszutek Wilk }
228fef6e262SKonrad Rzeszutek Wilk
xen_hvm_setup_msi_irqs(struct pci_dev * dev,int nvec,int type)229fef6e262SKonrad Rzeszutek Wilk static int xen_hvm_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
230fef6e262SKonrad Rzeszutek Wilk {
231fef6e262SKonrad Rzeszutek Wilk int irq, pirq;
232fef6e262SKonrad Rzeszutek Wilk struct msi_desc *msidesc;
233fef6e262SKonrad Rzeszutek Wilk struct msi_msg msg;
234fef6e262SKonrad Rzeszutek Wilk
235884ac297SKonrad Rzeszutek Wilk if (type == PCI_CAP_ID_MSI && nvec > 1)
236884ac297SKonrad Rzeszutek Wilk return 1;
237884ac297SKonrad Rzeszutek Wilk
238f2948df5SThomas Gleixner msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_NOTASSOCIATED) {
239fef6e262SKonrad Rzeszutek Wilk pirq = xen_allocate_pirq_msi(dev, msidesc);
240e6599225SKonrad Rzeszutek Wilk if (pirq < 0) {
241e6599225SKonrad Rzeszutek Wilk irq = -ENODEV;
242fef6e262SKonrad Rzeszutek Wilk goto error;
243e6599225SKonrad Rzeszutek Wilk }
244fef6e262SKonrad Rzeszutek Wilk xen_msi_compose_msg(dev, pirq, &msg);
24583a18912SJiang Liu __pci_write_msi_msg(msidesc, &msg);
246fef6e262SKonrad Rzeszutek Wilk dev_dbg(&dev->dev, "xen: msi bound to pirq=%d\n", pirq);
247dec02deaSJan Beulich irq = xen_bind_pirq_msi_to_irq(dev, msidesc, pirq,
2484892c9b4SRoger Pau Monne (type == PCI_CAP_ID_MSI) ? nvec : 1,
249fef6e262SKonrad Rzeszutek Wilk (type == PCI_CAP_ID_MSIX) ?
250fef6e262SKonrad Rzeszutek Wilk "msi-x" : "msi",
251fef6e262SKonrad Rzeszutek Wilk DOMID_SELF);
252fef6e262SKonrad Rzeszutek Wilk if (irq < 0)
253fef6e262SKonrad Rzeszutek Wilk goto error;
254fef6e262SKonrad Rzeszutek Wilk dev_dbg(&dev->dev,
255fef6e262SKonrad Rzeszutek Wilk "xen: msi --> pirq=%d --> irq=%d\n", pirq, irq);
256fef6e262SKonrad Rzeszutek Wilk }
257335b4223SMaximilian Heyne return msi_device_populate_sysfs(&dev->dev);
258fef6e262SKonrad Rzeszutek Wilk
259fef6e262SKonrad Rzeszutek Wilk error:
260577f79e4SKonrad Rzeszutek Wilk dev_err(&dev->dev, "Failed to create MSI%s! ret=%d!\n",
261577f79e4SKonrad Rzeszutek Wilk type == PCI_CAP_ID_MSI ? "" : "-X", irq);
262e6599225SKonrad Rzeszutek Wilk return irq;
263fef6e262SKonrad Rzeszutek Wilk }
264fef6e262SKonrad Rzeszutek Wilk
265cae7d81aSJan Beulich #ifdef CONFIG_XEN_PV_DOM0
26655e901fcSJan Beulich static bool __read_mostly pci_seg_supported = true;
267809f9267SStefano Stabellini
xen_initdom_setup_msi_irqs(struct pci_dev * dev,int nvec,int type)268fef6e262SKonrad Rzeszutek Wilk static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
269fef6e262SKonrad Rzeszutek Wilk {
270fef6e262SKonrad Rzeszutek Wilk int ret = 0;
271fef6e262SKonrad Rzeszutek Wilk struct msi_desc *msidesc;
272fef6e262SKonrad Rzeszutek Wilk
273f2948df5SThomas Gleixner msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_NOTASSOCIATED) {
274fef6e262SKonrad Rzeszutek Wilk struct physdev_map_pirq map_irq;
275fef6e262SKonrad Rzeszutek Wilk domid_t domid;
276fef6e262SKonrad Rzeszutek Wilk
277fef6e262SKonrad Rzeszutek Wilk domid = ret = xen_find_device_domain_owner(dev);
278fef6e262SKonrad Rzeszutek Wilk /* N.B. Casting int's -ENODEV to uint16_t results in 0xFFED,
279fef6e262SKonrad Rzeszutek Wilk * hence check ret value for < 0. */
280fef6e262SKonrad Rzeszutek Wilk if (ret < 0)
281fef6e262SKonrad Rzeszutek Wilk domid = DOMID_SELF;
282fef6e262SKonrad Rzeszutek Wilk
283fef6e262SKonrad Rzeszutek Wilk memset(&map_irq, 0, sizeof(map_irq));
284fef6e262SKonrad Rzeszutek Wilk map_irq.domid = domid;
28555e901fcSJan Beulich map_irq.type = MAP_PIRQ_TYPE_MSI_SEG;
286fef6e262SKonrad Rzeszutek Wilk map_irq.index = -1;
287fef6e262SKonrad Rzeszutek Wilk map_irq.pirq = -1;
28855e901fcSJan Beulich map_irq.bus = dev->bus->number |
28955e901fcSJan Beulich (pci_domain_nr(dev->bus) << 16);
290fef6e262SKonrad Rzeszutek Wilk map_irq.devfn = dev->devfn;
291fef6e262SKonrad Rzeszutek Wilk
2924892c9b4SRoger Pau Monne if (type == PCI_CAP_ID_MSI && nvec > 1) {
2934892c9b4SRoger Pau Monne map_irq.type = MAP_PIRQ_TYPE_MULTI_MSI;
2944892c9b4SRoger Pau Monne map_irq.entry_nr = nvec;
2954892c9b4SRoger Pau Monne } else if (type == PCI_CAP_ID_MSIX) {
296fef6e262SKonrad Rzeszutek Wilk int pos;
2976a878e50SYijing Wang unsigned long flags;
298fef6e262SKonrad Rzeszutek Wilk u32 table_offset, bir;
299fef6e262SKonrad Rzeszutek Wilk
3007c86617dSBjorn Helgaas pos = dev->msix_cap;
301fef6e262SKonrad Rzeszutek Wilk pci_read_config_dword(dev, pos + PCI_MSIX_TABLE,
302fef6e262SKonrad Rzeszutek Wilk &table_offset);
3034be6bfe2SBjorn Helgaas bir = (u8)(table_offset & PCI_MSIX_TABLE_BIR);
3046a878e50SYijing Wang flags = pci_resource_flags(dev, bir);
3056a878e50SYijing Wang if (!flags || (flags & IORESOURCE_UNSET))
3066a878e50SYijing Wang return -EINVAL;
307fef6e262SKonrad Rzeszutek Wilk
308fef6e262SKonrad Rzeszutek Wilk map_irq.table_base = pci_resource_start(dev, bir);
309173ffad7SThomas Gleixner map_irq.entry_nr = msidesc->msi_index;
310fef6e262SKonrad Rzeszutek Wilk }
311fef6e262SKonrad Rzeszutek Wilk
31255e901fcSJan Beulich ret = -EINVAL;
31355e901fcSJan Beulich if (pci_seg_supported)
31455e901fcSJan Beulich ret = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq,
31555e901fcSJan Beulich &map_irq);
3164892c9b4SRoger Pau Monne if (type == PCI_CAP_ID_MSI && nvec > 1 && ret) {
3174892c9b4SRoger Pau Monne /*
3184892c9b4SRoger Pau Monne * If MAP_PIRQ_TYPE_MULTI_MSI is not available
3194892c9b4SRoger Pau Monne * there's nothing else we can do in this case.
3204892c9b4SRoger Pau Monne * Just set ret > 0 so driver can retry with
3214892c9b4SRoger Pau Monne * single MSI.
3224892c9b4SRoger Pau Monne */
3234892c9b4SRoger Pau Monne ret = 1;
3244892c9b4SRoger Pau Monne goto out;
3254892c9b4SRoger Pau Monne }
32655e901fcSJan Beulich if (ret == -EINVAL && !pci_domain_nr(dev->bus)) {
32755e901fcSJan Beulich map_irq.type = MAP_PIRQ_TYPE_MSI;
32855e901fcSJan Beulich map_irq.index = -1;
32955e901fcSJan Beulich map_irq.pirq = -1;
33055e901fcSJan Beulich map_irq.bus = dev->bus->number;
33155e901fcSJan Beulich ret = HYPERVISOR_physdev_op(PHYSDEVOP_map_pirq,
33255e901fcSJan Beulich &map_irq);
33355e901fcSJan Beulich if (ret != -EINVAL)
33455e901fcSJan Beulich pci_seg_supported = false;
33555e901fcSJan Beulich }
336fef6e262SKonrad Rzeszutek Wilk if (ret) {
337fef6e262SKonrad Rzeszutek Wilk dev_warn(&dev->dev, "xen map irq failed %d for %d domain\n",
338fef6e262SKonrad Rzeszutek Wilk ret, domid);
339fef6e262SKonrad Rzeszutek Wilk goto out;
340fef6e262SKonrad Rzeszutek Wilk }
341fef6e262SKonrad Rzeszutek Wilk
3424892c9b4SRoger Pau Monne ret = xen_bind_pirq_msi_to_irq(dev, msidesc, map_irq.pirq,
3434892c9b4SRoger Pau Monne (type == PCI_CAP_ID_MSI) ? nvec : 1,
3444892c9b4SRoger Pau Monne (type == PCI_CAP_ID_MSIX) ? "msi-x" : "msi",
345fef6e262SKonrad Rzeszutek Wilk domid);
346fef6e262SKonrad Rzeszutek Wilk if (ret < 0)
347fef6e262SKonrad Rzeszutek Wilk goto out;
348fef6e262SKonrad Rzeszutek Wilk }
349335b4223SMaximilian Heyne ret = msi_device_populate_sysfs(&dev->dev);
350fef6e262SKonrad Rzeszutek Wilk out:
351fef6e262SKonrad Rzeszutek Wilk return ret;
352fef6e262SKonrad Rzeszutek Wilk }
3538605c684STang Liang
xen_initdom_restore_msi(struct pci_dev * dev)354ae72f315SThomas Gleixner bool xen_initdom_restore_msi(struct pci_dev *dev)
3558605c684STang Liang {
3568605c684STang Liang int ret = 0;
3578605c684STang Liang
358ae72f315SThomas Gleixner if (!xen_initial_domain())
359ae72f315SThomas Gleixner return true;
360ae72f315SThomas Gleixner
3618605c684STang Liang if (pci_seg_supported) {
3628605c684STang Liang struct physdev_pci_device restore_ext;
3638605c684STang Liang
3648605c684STang Liang restore_ext.seg = pci_domain_nr(dev->bus);
3658605c684STang Liang restore_ext.bus = dev->bus->number;
3668605c684STang Liang restore_ext.devfn = dev->devfn;
3678605c684STang Liang ret = HYPERVISOR_physdev_op(PHYSDEVOP_restore_msi_ext,
3688605c684STang Liang &restore_ext);
3698605c684STang Liang if (ret == -ENOSYS)
3708605c684STang Liang pci_seg_supported = false;
3718605c684STang Liang WARN(ret && ret != -ENOSYS, "restore_msi_ext -> %d\n", ret);
3728605c684STang Liang }
3738605c684STang Liang if (!pci_seg_supported) {
3748605c684STang Liang struct physdev_restore_msi restore;
3758605c684STang Liang
3768605c684STang Liang restore.bus = dev->bus->number;
3778605c684STang Liang restore.devfn = dev->devfn;
3788605c684STang Liang ret = HYPERVISOR_physdev_op(PHYSDEVOP_restore_msi, &restore);
3798605c684STang Liang WARN(ret && ret != -ENOSYS, "restore_msi -> %d\n", ret);
3808605c684STang Liang }
381ae72f315SThomas Gleixner return false;
3828605c684STang Liang }
383cae7d81aSJan Beulich #else /* CONFIG_XEN_PV_DOM0 */
38470b59379SThomas Gleixner #define xen_initdom_setup_msi_irqs NULL
385cae7d81aSJan Beulich #endif /* !CONFIG_XEN_PV_DOM0 */
386fef6e262SKonrad Rzeszutek Wilk
xen_teardown_msi_irqs(struct pci_dev * dev)387fef6e262SKonrad Rzeszutek Wilk static void xen_teardown_msi_irqs(struct pci_dev *dev)
388fef6e262SKonrad Rzeszutek Wilk {
389fef6e262SKonrad Rzeszutek Wilk struct msi_desc *msidesc;
3907d4d892dSThomas Gleixner int i;
391fef6e262SKonrad Rzeszutek Wilk
392f2948df5SThomas Gleixner msi_for_each_desc(msidesc, &dev->dev, MSI_DESC_ASSOCIATED) {
3937d4d892dSThomas Gleixner for (i = 0; i < msidesc->nvec_used; i++)
3947d4d892dSThomas Gleixner xen_destroy_irq(msidesc->irq + i);
3956c796996SThomas Gleixner msidesc->irq = 0;
3967d4d892dSThomas Gleixner }
397335b4223SMaximilian Heyne
398335b4223SMaximilian Heyne msi_device_destroy_sysfs(&dev->dev);
3997d4d892dSThomas Gleixner }
4007d4d892dSThomas Gleixner
xen_pv_teardown_msi_irqs(struct pci_dev * dev)4017d4d892dSThomas Gleixner static void xen_pv_teardown_msi_irqs(struct pci_dev *dev)
4027d4d892dSThomas Gleixner {
4030bcfade9SThomas Gleixner if (dev->msix_enabled)
404fef6e262SKonrad Rzeszutek Wilk xen_pci_frontend_disable_msix(dev);
405fef6e262SKonrad Rzeszutek Wilk else
406fef6e262SKonrad Rzeszutek Wilk xen_pci_frontend_disable_msi(dev);
407fef6e262SKonrad Rzeszutek Wilk
4087d4d892dSThomas Gleixner xen_teardown_msi_irqs(dev);
409fef6e262SKonrad Rzeszutek Wilk }
410fef6e262SKonrad Rzeszutek Wilk
xen_msi_domain_alloc_irqs(struct irq_domain * domain,struct device * dev,int nvec)4112e4386ebSThomas Gleixner static int xen_msi_domain_alloc_irqs(struct irq_domain *domain,
4122e4386ebSThomas Gleixner struct device *dev, int nvec)
4132e4386ebSThomas Gleixner {
4142e4386ebSThomas Gleixner int type;
4152e4386ebSThomas Gleixner
4162e4386ebSThomas Gleixner if (WARN_ON_ONCE(!dev_is_pci(dev)))
4172e4386ebSThomas Gleixner return -EINVAL;
4182e4386ebSThomas Gleixner
4190bcfade9SThomas Gleixner type = to_pci_dev(dev)->msix_enabled ? PCI_CAP_ID_MSIX : PCI_CAP_ID_MSI;
4202e4386ebSThomas Gleixner
421874a2013SThomas Gleixner return xen_msi_ops.setup_msi_irqs(to_pci_dev(dev), nvec, type);
4222e4386ebSThomas Gleixner }
4232e4386ebSThomas Gleixner
xen_msi_domain_free_irqs(struct irq_domain * domain,struct device * dev)4242e4386ebSThomas Gleixner static void xen_msi_domain_free_irqs(struct irq_domain *domain,
4252e4386ebSThomas Gleixner struct device *dev)
4262e4386ebSThomas Gleixner {
4272e4386ebSThomas Gleixner if (WARN_ON_ONCE(!dev_is_pci(dev)))
4282e4386ebSThomas Gleixner return;
4292e4386ebSThomas Gleixner
430874a2013SThomas Gleixner xen_msi_ops.teardown_msi_irqs(to_pci_dev(dev));
4312e4386ebSThomas Gleixner }
4322e4386ebSThomas Gleixner
4332e4386ebSThomas Gleixner static struct msi_domain_ops xen_pci_msi_domain_ops = {
4342e4386ebSThomas Gleixner .domain_alloc_irqs = xen_msi_domain_alloc_irqs,
4352e4386ebSThomas Gleixner .domain_free_irqs = xen_msi_domain_free_irqs,
4362e4386ebSThomas Gleixner };
4372e4386ebSThomas Gleixner
4382e4386ebSThomas Gleixner static struct msi_domain_info xen_pci_msi_domain_info = {
4396c796996SThomas Gleixner .flags = MSI_FLAG_PCI_MSIX | MSI_FLAG_FREE_MSI_DESCS | MSI_FLAG_DEV_SYSFS,
4402e4386ebSThomas Gleixner .ops = &xen_pci_msi_domain_ops,
4412e4386ebSThomas Gleixner };
4422e4386ebSThomas Gleixner
4432e4386ebSThomas Gleixner /*
4442e4386ebSThomas Gleixner * This irq domain is a blatant violation of the irq domain design, but
4452e4386ebSThomas Gleixner * distangling XEN into real irq domains is not a job for mere mortals with
4462e4386ebSThomas Gleixner * limited XENology. But it's the least dangerous way for a mere mortal to
4472e4386ebSThomas Gleixner * get rid of the arch_*_msi_irqs() hackery in order to store the irq
4482e4386ebSThomas Gleixner * domain pointer in struct device. This irq domain wrappery allows to do
4492e4386ebSThomas Gleixner * that without breaking XEN terminally.
4502e4386ebSThomas Gleixner */
xen_create_pci_msi_domain(void)4512e4386ebSThomas Gleixner static __init struct irq_domain *xen_create_pci_msi_domain(void)
4522e4386ebSThomas Gleixner {
4532e4386ebSThomas Gleixner struct irq_domain *d = NULL;
4542e4386ebSThomas Gleixner struct fwnode_handle *fn;
4552e4386ebSThomas Gleixner
4562e4386ebSThomas Gleixner fn = irq_domain_alloc_named_fwnode("XEN-MSI");
4572e4386ebSThomas Gleixner if (fn)
4582e4386ebSThomas Gleixner d = msi_create_irq_domain(fn, &xen_pci_msi_domain_info, NULL);
4592e4386ebSThomas Gleixner
4602e4386ebSThomas Gleixner /* FIXME: No idea how to survive if this fails */
4612e4386ebSThomas Gleixner BUG_ON(!d);
4622e4386ebSThomas Gleixner
4632e4386ebSThomas Gleixner return d;
4642e4386ebSThomas Gleixner }
4652e4386ebSThomas Gleixner
xen_setup_pci_msi(void)46670b59379SThomas Gleixner static __init void xen_setup_pci_msi(void)
46770b59379SThomas Gleixner {
46870b59379SThomas Gleixner if (xen_pv_domain()) {
469ae72f315SThomas Gleixner if (xen_initial_domain())
470874a2013SThomas Gleixner xen_msi_ops.setup_msi_irqs = xen_initdom_setup_msi_irqs;
471ae72f315SThomas Gleixner else
472874a2013SThomas Gleixner xen_msi_ops.setup_msi_irqs = xen_setup_msi_irqs;
473874a2013SThomas Gleixner xen_msi_ops.teardown_msi_irqs = xen_pv_teardown_msi_irqs;
47470b59379SThomas Gleixner } else if (xen_hvm_domain()) {
475874a2013SThomas Gleixner xen_msi_ops.setup_msi_irqs = xen_hvm_setup_msi_irqs;
476874a2013SThomas Gleixner xen_msi_ops.teardown_msi_irqs = xen_teardown_msi_irqs;
47770b59379SThomas Gleixner } else {
47870b59379SThomas Gleixner WARN_ON_ONCE(1);
47970b59379SThomas Gleixner return;
48070b59379SThomas Gleixner }
48170b59379SThomas Gleixner
4822e4386ebSThomas Gleixner /*
4832e4386ebSThomas Gleixner * Override the PCI/MSI irq domain init function. No point
4842e4386ebSThomas Gleixner * in allocating the native domain and never use it.
4852e4386ebSThomas Gleixner */
4862e4386ebSThomas Gleixner x86_init.irqs.create_pci_msi_domain = xen_create_pci_msi_domain;
4877e0815b3SThomas Gleixner /*
4887e0815b3SThomas Gleixner * With XEN PIRQ/Eventchannels in use PCI/MSI[-X] masking is solely
4897e0815b3SThomas Gleixner * controlled by the hypervisor.
4907e0815b3SThomas Gleixner */
4917e0815b3SThomas Gleixner pci_msi_ignore_mask = 1;
49270b59379SThomas Gleixner }
49370b59379SThomas Gleixner
49470b59379SThomas Gleixner #else /* CONFIG_PCI_MSI */
xen_setup_pci_msi(void)49570b59379SThomas Gleixner static inline void xen_setup_pci_msi(void) { }
49670b59379SThomas Gleixner #endif /* CONFIG_PCI_MSI */
497fef6e262SKonrad Rzeszutek Wilk
pci_xen_init(void)498fef6e262SKonrad Rzeszutek Wilk int __init pci_xen_init(void)
499fef6e262SKonrad Rzeszutek Wilk {
500fef6e262SKonrad Rzeszutek Wilk if (!xen_pv_domain() || xen_initial_domain())
501fef6e262SKonrad Rzeszutek Wilk return -ENODEV;
502fef6e262SKonrad Rzeszutek Wilk
503fef6e262SKonrad Rzeszutek Wilk printk(KERN_INFO "PCI: setting up Xen PCI frontend stub\n");
504fef6e262SKonrad Rzeszutek Wilk
505fef6e262SKonrad Rzeszutek Wilk pcibios_set_cache_line_size();
506fef6e262SKonrad Rzeszutek Wilk
507fef6e262SKonrad Rzeszutek Wilk pcibios_enable_irq = xen_pcifront_enable_irq;
508fef6e262SKonrad Rzeszutek Wilk pcibios_disable_irq = NULL;
509fef6e262SKonrad Rzeszutek Wilk
510fef6e262SKonrad Rzeszutek Wilk /* Keep ACPI out of the picture */
51188e9ca16SAndy Shevchenko acpi_noirq_set();
512fef6e262SKonrad Rzeszutek Wilk
51370b59379SThomas Gleixner xen_setup_pci_msi();
514fef6e262SKonrad Rzeszutek Wilk return 0;
515fef6e262SKonrad Rzeszutek Wilk }
516fef6e262SKonrad Rzeszutek Wilk
517066d79e4SBoris Ostrovsky #ifdef CONFIG_PCI_MSI
xen_hvm_msi_init(void)5182905c50bSThomas Gleixner static void __init xen_hvm_msi_init(void)
519066d79e4SBoris Ostrovsky {
52049062454SThomas Gleixner if (!apic_is_disabled) {
52114520c92SBoris Ostrovsky /*
52214520c92SBoris Ostrovsky * If hardware supports (x2)APIC virtualization (as indicated
52314520c92SBoris Ostrovsky * by hypervisor's leaf 4) then we don't need to use pirqs/
52414520c92SBoris Ostrovsky * event channels for MSI handling and instead use regular
52514520c92SBoris Ostrovsky * APIC processing
52614520c92SBoris Ostrovsky */
52714520c92SBoris Ostrovsky uint32_t eax = cpuid_eax(xen_cpuid_base() + 4);
52814520c92SBoris Ostrovsky
52914520c92SBoris Ostrovsky if (((eax & XEN_HVM_CPUID_X2APIC_VIRT) && x2apic_mode) ||
53093984fbdSBorislav Petkov ((eax & XEN_HVM_CPUID_APIC_ACCESS_VIRT) && boot_cpu_has(X86_FEATURE_APIC)))
53114520c92SBoris Ostrovsky return;
53214520c92SBoris Ostrovsky }
53370b59379SThomas Gleixner xen_setup_pci_msi();
534066d79e4SBoris Ostrovsky }
535066d79e4SBoris Ostrovsky #endif
536066d79e4SBoris Ostrovsky
pci_xen_hvm_init(void)537fef6e262SKonrad Rzeszutek Wilk int __init pci_xen_hvm_init(void)
538fef6e262SKonrad Rzeszutek Wilk {
53984d582d2SBoris Ostrovsky if (!xen_have_vector_callback || !xen_feature(XENFEAT_hvm_pirqs))
540fef6e262SKonrad Rzeszutek Wilk return 0;
541fef6e262SKonrad Rzeszutek Wilk
542fef6e262SKonrad Rzeszutek Wilk #ifdef CONFIG_ACPI
543fef6e262SKonrad Rzeszutek Wilk /*
544fef6e262SKonrad Rzeszutek Wilk * We don't want to change the actual ACPI delivery model,
545fef6e262SKonrad Rzeszutek Wilk * just how GSIs get registered.
546fef6e262SKonrad Rzeszutek Wilk */
547fef6e262SKonrad Rzeszutek Wilk __acpi_register_gsi = acpi_register_gsi_xen_hvm;
5488abb850aSJiang Liu __acpi_unregister_gsi = NULL;
549fef6e262SKonrad Rzeszutek Wilk #endif
550fef6e262SKonrad Rzeszutek Wilk
551fef6e262SKonrad Rzeszutek Wilk #ifdef CONFIG_PCI_MSI
552066d79e4SBoris Ostrovsky /*
553066d79e4SBoris Ostrovsky * We need to wait until after x2apic is initialized
554066d79e4SBoris Ostrovsky * before we can set MSI IRQ ops.
555066d79e4SBoris Ostrovsky */
5562905c50bSThomas Gleixner x86_platform.apic_post_init = xen_hvm_msi_init;
557fef6e262SKonrad Rzeszutek Wilk #endif
558fef6e262SKonrad Rzeszutek Wilk return 0;
559fef6e262SKonrad Rzeszutek Wilk }
560fef6e262SKonrad Rzeszutek Wilk
561cae7d81aSJan Beulich #ifdef CONFIG_XEN_PV_DOM0
pci_xen_initial_domain(void)562a0ee0567SKonrad Rzeszutek Wilk int __init pci_xen_initial_domain(void)
56338aa66fcSJeremy Fitzhardinge {
56478316adaSKonrad Rzeszutek Wilk int irq;
565a0ee0567SKonrad Rzeszutek Wilk
56670b59379SThomas Gleixner xen_setup_pci_msi();
56738aa66fcSJeremy Fitzhardinge __acpi_register_gsi = acpi_register_gsi_xen;
5688abb850aSJiang Liu __acpi_unregister_gsi = NULL;
569702f9260SStefano Stabellini /*
570702f9260SStefano Stabellini * Pre-allocate the legacy IRQs. Use NR_LEGACY_IRQS here
571702f9260SStefano Stabellini * because we don't have a PIC and thus nr_legacy_irqs() is zero.
572702f9260SStefano Stabellini */
573702f9260SStefano Stabellini for (irq = 0; irq < NR_IRQS_LEGACY; irq++) {
57438aa66fcSJeremy Fitzhardinge int trigger, polarity;
57538aa66fcSJeremy Fitzhardinge
57638aa66fcSJeremy Fitzhardinge if (acpi_get_override_irq(irq, &trigger, &polarity) == -1)
57738aa66fcSJeremy Fitzhardinge continue;
57838aa66fcSJeremy Fitzhardinge
57960369a4fSWei Liu xen_register_pirq(irq,
580ed89eb63SKonrad Rzeszutek Wilk trigger ? ACPI_LEVEL_SENSITIVE : ACPI_EDGE_SENSITIVE,
58178316adaSKonrad Rzeszutek Wilk true /* Map GSI to PIRQ */);
58238aa66fcSJeremy Fitzhardinge }
5839b6519dbSKonrad Rzeszutek Wilk if (0 == nr_ioapics) {
58495d76accSJiang Liu for (irq = 0; irq < nr_legacy_irqs(); irq++)
58578316adaSKonrad Rzeszutek Wilk xen_bind_pirq_gsi_to_irq(irq, irq, 0, "xt-pic");
5869b6519dbSKonrad Rzeszutek Wilk }
587a0ee0567SKonrad Rzeszutek Wilk return 0;
58838aa66fcSJeremy Fitzhardinge }
589cae7d81aSJan Beulich #endif
590cae7d81aSJan Beulich
591