aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorStefano Stabellini <stefano.stabellini@eu.citrix.com>2010-05-17 12:08:21 -0400
committerJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>2010-07-22 19:46:09 -0400
commit183d03cc4ff39e0f0d952c09aa96d0abfd6e0c3c (patch)
tree75947fc4a9ac69e902663c9cb618993b7c656cff /drivers
parent38e20b07efd541a959de367dc90a17f92ce2e8a6 (diff)
xen: Xen PCI platform device driver.
Add the xen pci platform device driver that is responsible for initializing the grant table and xenbus in PV on HVM mode. Few changes to xenbus and grant table are necessary to allow the delayed initialization in HVM mode. Grant table needs few additional modifications to work in HVM mode. The Xen PCI platform device raises an irq every time an event has been delivered to us. However these interrupts are only delivered to vcpu 0. The Xen PCI platform interrupt handler calls xen_hvm_evtchn_do_upcall that is a little wrapper around __xen_evtchn_do_upcall, the traditional Xen upcall handler, the very same used with traditional PV guests. When running on HVM the event channel upcall is never called while in progress because it is a normal Linux irq handler (and we cannot switch the irq chip wholesale to the Xen PV ones as we are running QEMU and might have passed in PCI devices), therefore we cannot be sure that evtchn_upcall_pending is 0 when returning. For this reason if evtchn_upcall_pending is set by Xen we need to loop again on the event channels set pending otherwise we might loose some event channel deliveries. Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com> Signed-off-by: Sheng Yang <sheng@linux.intel.com> Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/xen/Kconfig9
-rw-r--r--drivers/xen/Makefile3
-rw-r--r--drivers/xen/events.c8
-rw-r--r--drivers/xen/grant-table.c77
-rw-r--r--drivers/xen/manage.c1
-rw-r--r--drivers/xen/platform-pci.c181
-rw-r--r--drivers/xen/xenbus/xenbus_probe.c22
7 files changed, 283 insertions, 18 deletions
diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index fad3df2c1276..8f84b108b491 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -62,4 +62,13 @@ config XEN_SYS_HYPERVISOR
62 virtual environment, /sys/hypervisor will still be present, 62 virtual environment, /sys/hypervisor will still be present,
63 but will have no xen contents. 63 but will have no xen contents.
64 64
65config XEN_PLATFORM_PCI
66 tristate "xen platform pci device driver"
67 depends on XEN
68 default m
69 help
70 Driver for the Xen PCI Platform device: it is responsible for
71 initializing xenbus and grant_table when running in a Xen HVM
72 domain. As a consequence this driver is required to run any Xen PV
73 frontend on Xen HVM.
65endmenu 74endmenu
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 7c284342f30f..e392fb776af3 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -9,4 +9,5 @@ obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
9obj-$(CONFIG_XEN_BALLOON) += balloon.o 9obj-$(CONFIG_XEN_BALLOON) += balloon.o
10obj-$(CONFIG_XEN_DEV_EVTCHN) += evtchn.o 10obj-$(CONFIG_XEN_DEV_EVTCHN) += evtchn.o
11obj-$(CONFIG_XENFS) += xenfs/ 11obj-$(CONFIG_XENFS) += xenfs/
12obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o \ No newline at end of file 12obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o
13obj-$(CONFIG_XEN_PLATFORM_PCI) += platform-pci.o
diff --git a/drivers/xen/events.c b/drivers/xen/events.c
index d659480125f0..7c64473c9f3f 100644
--- a/drivers/xen/events.c
+++ b/drivers/xen/events.c
@@ -665,7 +665,7 @@ static void __xen_evtchn_do_upcall(void)
665 665
666 count = __get_cpu_var(xed_nesting_count); 666 count = __get_cpu_var(xed_nesting_count);
667 __get_cpu_var(xed_nesting_count) = 0; 667 __get_cpu_var(xed_nesting_count) = 0;
668 } while(count != 1); 668 } while (count != 1 || vcpu_info->evtchn_upcall_pending);
669 669
670out: 670out:
671 671
@@ -689,6 +689,7 @@ void xen_hvm_evtchn_do_upcall(void)
689{ 689{
690 __xen_evtchn_do_upcall(); 690 __xen_evtchn_do_upcall();
691} 691}
692EXPORT_SYMBOL_GPL(xen_hvm_evtchn_do_upcall);
692 693
693/* Rebind a new event channel to an existing irq. */ 694/* Rebind a new event channel to an existing irq. */
694void rebind_evtchn_irq(int evtchn, int irq) 695void rebind_evtchn_irq(int evtchn, int irq)
@@ -725,7 +726,10 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
725 struct evtchn_bind_vcpu bind_vcpu; 726 struct evtchn_bind_vcpu bind_vcpu;
726 int evtchn = evtchn_from_irq(irq); 727 int evtchn = evtchn_from_irq(irq);
727 728
728 if (!VALID_EVTCHN(evtchn)) 729 /* events delivered via platform PCI interrupts are always
730 * routed to vcpu 0 */
731 if (!VALID_EVTCHN(evtchn) ||
732 (xen_hvm_domain() && !xen_have_vector_callback))
729 return -1; 733 return -1;
730 734
731 /* Send future instances of this interrupt to other vcpu. */ 735 /* Send future instances of this interrupt to other vcpu. */
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index f66db3b91d61..6c4531816496 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -37,11 +37,13 @@
37#include <linux/slab.h> 37#include <linux/slab.h>
38#include <linux/vmalloc.h> 38#include <linux/vmalloc.h>
39#include <linux/uaccess.h> 39#include <linux/uaccess.h>
40#include <linux/io.h>
40 41
41#include <xen/xen.h> 42#include <xen/xen.h>
42#include <xen/interface/xen.h> 43#include <xen/interface/xen.h>
43#include <xen/page.h> 44#include <xen/page.h>
44#include <xen/grant_table.h> 45#include <xen/grant_table.h>
46#include <xen/interface/memory.h>
45#include <asm/xen/hypercall.h> 47#include <asm/xen/hypercall.h>
46 48
47#include <asm/pgtable.h> 49#include <asm/pgtable.h>
@@ -59,6 +61,8 @@ static unsigned int boot_max_nr_grant_frames;
59static int gnttab_free_count; 61static int gnttab_free_count;
60static grant_ref_t gnttab_free_head; 62static grant_ref_t gnttab_free_head;
61static DEFINE_SPINLOCK(gnttab_list_lock); 63static DEFINE_SPINLOCK(gnttab_list_lock);
64unsigned long xen_hvm_resume_frames;
65EXPORT_SYMBOL_GPL(xen_hvm_resume_frames);
62 66
63static struct grant_entry *shared; 67static struct grant_entry *shared;
64 68
@@ -433,7 +437,7 @@ static unsigned int __max_nr_grant_frames(void)
433 return query.max_nr_frames; 437 return query.max_nr_frames;
434} 438}
435 439
436static inline unsigned int max_nr_grant_frames(void) 440unsigned int gnttab_max_grant_frames(void)
437{ 441{
438 unsigned int xen_max = __max_nr_grant_frames(); 442 unsigned int xen_max = __max_nr_grant_frames();
439 443
@@ -441,6 +445,7 @@ static inline unsigned int max_nr_grant_frames(void)
441 return boot_max_nr_grant_frames; 445 return boot_max_nr_grant_frames;
442 return xen_max; 446 return xen_max;
443} 447}
448EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
444 449
445static int gnttab_map(unsigned int start_idx, unsigned int end_idx) 450static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
446{ 451{
@@ -449,6 +454,30 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
449 unsigned int nr_gframes = end_idx + 1; 454 unsigned int nr_gframes = end_idx + 1;
450 int rc; 455 int rc;
451 456
457 if (xen_hvm_domain()) {
458 struct xen_add_to_physmap xatp;
459 unsigned int i = end_idx;
460 rc = 0;
461 /*
462 * Loop backwards, so that the first hypercall has the largest
463 * index, ensuring that the table will grow only once.
464 */
465 do {
466 xatp.domid = DOMID_SELF;
467 xatp.idx = i;
468 xatp.space = XENMAPSPACE_grant_table;
469 xatp.gpfn = (xen_hvm_resume_frames >> PAGE_SHIFT) + i;
470 rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp);
471 if (rc != 0) {
472 printk(KERN_WARNING
473 "grant table add_to_physmap failed, err=%d\n", rc);
474 break;
475 }
476 } while (i-- > start_idx);
477
478 return rc;
479 }
480
452 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC); 481 frames = kmalloc(nr_gframes * sizeof(unsigned long), GFP_ATOMIC);
453 if (!frames) 482 if (!frames)
454 return -ENOMEM; 483 return -ENOMEM;
@@ -465,7 +494,7 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
465 494
466 BUG_ON(rc || setup.status); 495 BUG_ON(rc || setup.status);
467 496
468 rc = arch_gnttab_map_shared(frames, nr_gframes, max_nr_grant_frames(), 497 rc = arch_gnttab_map_shared(frames, nr_gframes, gnttab_max_grant_frames(),
469 &shared); 498 &shared);
470 BUG_ON(rc); 499 BUG_ON(rc);
471 500
@@ -476,9 +505,27 @@ static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
476 505
477int gnttab_resume(void) 506int gnttab_resume(void)
478{ 507{
479 if (max_nr_grant_frames() < nr_grant_frames) 508 unsigned int max_nr_gframes;
509
510 max_nr_gframes = gnttab_max_grant_frames();
511 if (max_nr_gframes < nr_grant_frames)
480 return -ENOSYS; 512 return -ENOSYS;
481 return gnttab_map(0, nr_grant_frames - 1); 513
514 if (xen_pv_domain())
515 return gnttab_map(0, nr_grant_frames - 1);
516
517 if (!shared) {
518 shared = ioremap(xen_hvm_resume_frames, PAGE_SIZE * max_nr_gframes);
519 if (shared == NULL) {
520 printk(KERN_WARNING
521 "Failed to ioremap gnttab share frames!");
522 return -ENOMEM;
523 }
524 }
525
526 gnttab_map(0, nr_grant_frames - 1);
527
528 return 0;
482} 529}
483 530
484int gnttab_suspend(void) 531int gnttab_suspend(void)
@@ -495,7 +542,7 @@ static int gnttab_expand(unsigned int req_entries)
495 cur = nr_grant_frames; 542 cur = nr_grant_frames;
496 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) / 543 extra = ((req_entries + (GREFS_PER_GRANT_FRAME-1)) /
497 GREFS_PER_GRANT_FRAME); 544 GREFS_PER_GRANT_FRAME);
498 if (cur + extra > max_nr_grant_frames()) 545 if (cur + extra > gnttab_max_grant_frames())
499 return -ENOSPC; 546 return -ENOSPC;
500 547
501 rc = gnttab_map(cur, cur + extra - 1); 548 rc = gnttab_map(cur, cur + extra - 1);
@@ -505,15 +552,12 @@ static int gnttab_expand(unsigned int req_entries)
505 return rc; 552 return rc;
506} 553}
507 554
508static int __devinit gnttab_init(void) 555int gnttab_init(void)
509{ 556{
510 int i; 557 int i;
511 unsigned int max_nr_glist_frames, nr_glist_frames; 558 unsigned int max_nr_glist_frames, nr_glist_frames;
512 unsigned int nr_init_grefs; 559 unsigned int nr_init_grefs;
513 560
514 if (!xen_domain())
515 return -ENODEV;
516
517 nr_grant_frames = 1; 561 nr_grant_frames = 1;
518 boot_max_nr_grant_frames = __max_nr_grant_frames(); 562 boot_max_nr_grant_frames = __max_nr_grant_frames();
519 563
@@ -556,5 +600,18 @@ static int __devinit gnttab_init(void)
556 kfree(gnttab_list); 600 kfree(gnttab_list);
557 return -ENOMEM; 601 return -ENOMEM;
558} 602}
603EXPORT_SYMBOL_GPL(gnttab_init);
604
605static int __devinit __gnttab_init(void)
606{
607 /* Delay grant-table initialization in the PV on HVM case */
608 if (xen_hvm_domain())
609 return 0;
610
611 if (!xen_pv_domain())
612 return -ENODEV;
613
614 return gnttab_init();
615}
559 616
560core_initcall(gnttab_init); 617core_initcall(__gnttab_init);
diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c
index 07e857b0de13..af9c5594d315 100644
--- a/drivers/xen/manage.c
+++ b/drivers/xen/manage.c
@@ -264,5 +264,6 @@ static int __init setup_shutdown_event(void)
264 264
265 return 0; 265 return 0;
266} 266}
267EXPORT_SYMBOL_GPL(xen_setup_shutdown_event);
267 268
268subsys_initcall(setup_shutdown_event); 269subsys_initcall(setup_shutdown_event);
diff --git a/drivers/xen/platform-pci.c b/drivers/xen/platform-pci.c
new file mode 100644
index 000000000000..a0ee5d06f715
--- /dev/null
+++ b/drivers/xen/platform-pci.c
@@ -0,0 +1,181 @@
1/******************************************************************************
2 * platform-pci.c
3 *
4 * Xen platform PCI device driver
5 * Copyright (c) 2005, Intel Corporation.
6 * Copyright (c) 2007, XenSource Inc.
7 * Copyright (c) 2010, Citrix
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms and conditions of the GNU General Public License,
11 * version 2, as published by the Free Software Foundation.
12 *
13 * This program is distributed in the hope it will be useful, but WITHOUT
14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
16 * more details.
17 *
18 * You should have received a copy of the GNU General Public License along with
19 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
20 * Place - Suite 330, Boston, MA 02111-1307 USA.
21 *
22 */
23
24
25#include <linux/interrupt.h>
26#include <linux/io.h>
27#include <linux/module.h>
28#include <linux/pci.h>
29
30#include <xen/grant_table.h>
31#include <xen/xenbus.h>
32#include <xen/events.h>
33#include <xen/hvm.h>
34
35#define DRV_NAME "xen-platform-pci"
36
37MODULE_AUTHOR("ssmith@xensource.com and stefano.stabellini@eu.citrix.com");
38MODULE_DESCRIPTION("Xen platform PCI device");
39MODULE_LICENSE("GPL");
40
41static unsigned long platform_mmio;
42static unsigned long platform_mmio_alloc;
43static unsigned long platform_mmiolen;
44
45unsigned long alloc_xen_mmio(unsigned long len)
46{
47 unsigned long addr;
48
49 addr = platform_mmio + platform_mmio_alloc;
50 platform_mmio_alloc += len;
51 BUG_ON(platform_mmio_alloc > platform_mmiolen);
52
53 return addr;
54}
55
56static uint64_t get_callback_via(struct pci_dev *pdev)
57{
58 u8 pin;
59 int irq;
60
61 irq = pdev->irq;
62 if (irq < 16)
63 return irq; /* ISA IRQ */
64
65 pin = pdev->pin;
66
67 /* We don't know the GSI. Specify the PCI INTx line instead. */
68 return ((uint64_t)0x01 << 56) | /* PCI INTx identifier */
69 ((uint64_t)pci_domain_nr(pdev->bus) << 32) |
70 ((uint64_t)pdev->bus->number << 16) |
71 ((uint64_t)(pdev->devfn & 0xff) << 8) |
72 ((uint64_t)(pin - 1) & 3);
73}
74
75static irqreturn_t do_hvm_evtchn_intr(int irq, void *dev_id)
76{
77 xen_hvm_evtchn_do_upcall();
78 return IRQ_HANDLED;
79}
80
81static int xen_allocate_irq(struct pci_dev *pdev)
82{
83 return request_irq(pdev->irq, do_hvm_evtchn_intr,
84 IRQF_DISABLED | IRQF_NOBALANCING | IRQF_TRIGGER_RISING,
85 "xen-platform-pci", pdev);
86}
87
88static int __devinit platform_pci_init(struct pci_dev *pdev,
89 const struct pci_device_id *ent)
90{
91 int i, ret;
92 long ioaddr, iolen;
93 long mmio_addr, mmio_len;
94 uint64_t callback_via;
95 unsigned int max_nr_gframes;
96
97 i = pci_enable_device(pdev);
98 if (i)
99 return i;
100
101 ioaddr = pci_resource_start(pdev, 0);
102 iolen = pci_resource_len(pdev, 0);
103
104 mmio_addr = pci_resource_start(pdev, 1);
105 mmio_len = pci_resource_len(pdev, 1);
106
107 if (mmio_addr == 0 || ioaddr == 0) {
108 dev_err(&pdev->dev, "no resources found\n");
109 ret = -ENOENT;
110 goto pci_out;
111 }
112
113 if (request_mem_region(mmio_addr, mmio_len, DRV_NAME) == NULL) {
114 dev_err(&pdev->dev, "MEM I/O resource 0x%lx @ 0x%lx busy\n",
115 mmio_addr, mmio_len);
116 ret = -EBUSY;
117 goto pci_out;
118 }
119
120 if (request_region(ioaddr, iolen, DRV_NAME) == NULL) {
121 dev_err(&pdev->dev, "I/O resource 0x%lx @ 0x%lx busy\n",
122 iolen, ioaddr);
123 ret = -EBUSY;
124 goto mem_out;
125 }
126
127 platform_mmio = mmio_addr;
128 platform_mmiolen = mmio_len;
129
130 if (!xen_have_vector_callback) {
131 ret = xen_allocate_irq(pdev);
132 if (ret) {
133 dev_warn(&pdev->dev, "request_irq failed err=%d\n", ret);
134 goto out;
135 }
136 callback_via = get_callback_via(pdev);
137 ret = xen_set_callback_via(callback_via);
138 if (ret) {
139 dev_warn(&pdev->dev, "Unable to set the evtchn callback "
140 "err=%d\n", ret);
141 goto out;
142 }
143 }
144
145 max_nr_gframes = gnttab_max_grant_frames();
146 xen_hvm_resume_frames = alloc_xen_mmio(PAGE_SIZE * max_nr_gframes);
147 ret = gnttab_init();
148 if (ret)
149 goto out;
150 xenbus_probe(NULL);
151 return 0;
152
153out:
154 release_region(ioaddr, iolen);
155mem_out:
156 release_mem_region(mmio_addr, mmio_len);
157pci_out:
158 pci_disable_device(pdev);
159 return ret;
160}
161
162static struct pci_device_id platform_pci_tbl[] __devinitdata = {
163 {PCI_VENDOR_ID_XEN, PCI_DEVICE_ID_XEN_PLATFORM,
164 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
165 {0,}
166};
167
168MODULE_DEVICE_TABLE(pci, platform_pci_tbl);
169
170static struct pci_driver platform_driver = {
171 .name = DRV_NAME,
172 .probe = platform_pci_init,
173 .id_table = platform_pci_tbl,
174};
175
176static int __init platform_pci_module_init(void)
177{
178 return pci_register_driver(&platform_driver);
179}
180
181module_init(platform_pci_module_init);
diff --git a/drivers/xen/xenbus/xenbus_probe.c b/drivers/xen/xenbus/xenbus_probe.c
index d96fa75b45ec..a9e83c438cbb 100644
--- a/drivers/xen/xenbus/xenbus_probe.c
+++ b/drivers/xen/xenbus/xenbus_probe.c
@@ -781,8 +781,23 @@ void xenbus_probe(struct work_struct *unused)
781 /* Notify others that xenstore is up */ 781 /* Notify others that xenstore is up */
782 blocking_notifier_call_chain(&xenstore_chain, 0, NULL); 782 blocking_notifier_call_chain(&xenstore_chain, 0, NULL);
783} 783}
784EXPORT_SYMBOL_GPL(xenbus_probe);
784 785
785static int __init xenbus_probe_init(void) 786static int __init xenbus_probe_initcall(void)
787{
788 if (!xen_domain())
789 return -ENODEV;
790
791 if (xen_initial_domain() || xen_hvm_domain())
792 return 0;
793
794 xenbus_probe(NULL);
795 return 0;
796}
797
798device_initcall(xenbus_probe_initcall);
799
800static int __init xenbus_init(void)
786{ 801{
787 int err = 0; 802 int err = 0;
788 803
@@ -834,9 +849,6 @@ static int __init xenbus_probe_init(void)
834 goto out_unreg_back; 849 goto out_unreg_back;
835 } 850 }
836 851
837 if (!xen_initial_domain())
838 xenbus_probe(NULL);
839
840#ifdef CONFIG_XEN_COMPAT_XENFS 852#ifdef CONFIG_XEN_COMPAT_XENFS
841 /* 853 /*
842 * Create xenfs mountpoint in /proc for compatibility with 854 * Create xenfs mountpoint in /proc for compatibility with
@@ -857,7 +869,7 @@ static int __init xenbus_probe_init(void)
857 return err; 869 return err;
858} 870}
859 871
860postcore_initcall(xenbus_probe_init); 872postcore_initcall(xenbus_init);
861 873
862MODULE_LICENSE("GPL"); 874MODULE_LICENSE("GPL");
863 875