xref: /openbmc/linux/arch/x86/pci/xen.c (revision 7e24a55b2122746c2eef192296fc84624354f895)
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