ia64/xen-unstable

view unmodified_drivers/linux-2.6/platform-pci/platform-pci.c @ 14659:e3dc8cea5bc0

[HVM] Save/restore: PV-on-HVM driver save/restore support
Signed-off-by: Zhai Edwin <edwin.zhai@intel.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Thu Mar 29 16:27:52 2007 +0000 (2007-03-29)
parents a68a3c6cac5f
children 1a347b19142a
line source
1 /******************************************************************************
2 * evtchn-pci.c
3 * xen event channel fake PCI device driver
4 * Copyright (C) 2005, Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
17 * Place - Suite 330, Boston, MA 02111-1307 USA.
18 *
19 */
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/sched.h>
24 #include <linux/errno.h>
25 #include <linux/pci.h>
26 #include <linux/init.h>
27 #include <linux/version.h>
28 #include <linux/interrupt.h>
29 #include <linux/vmalloc.h>
30 #include <linux/mm.h>
31 #include <asm/system.h>
32 #include <asm/io.h>
33 #include <asm/irq.h>
34 #include <asm/uaccess.h>
35 #include <asm/hypervisor.h>
36 #include <asm/pgtable.h>
37 #include <xen/interface/memory.h>
38 #include <xen/features.h>
39 #include <xen/gnttab.h>
40 #ifdef __ia64__
41 #include <asm/xen/xencomm.h>
42 #endif
44 #include "platform-pci.h"
46 #ifdef HAVE_XEN_PLATFORM_COMPAT_H
47 #include <xen/platform-compat.h>
48 #endif
50 #define DRV_NAME "xen-platform-pci"
51 #define DRV_VERSION "0.10"
52 #define DRV_RELDATE "03/03/2005"
54 char *hypercall_stubs;
55 EXPORT_SYMBOL(hypercall_stubs);
57 // Used to be xiaofeng.ling@intel.com
58 MODULE_AUTHOR("ssmith@xensource.com");
59 MODULE_DESCRIPTION("Xen platform PCI device");
60 MODULE_LICENSE("GPL");
62 unsigned long *phys_to_machine_mapping;
63 EXPORT_SYMBOL(phys_to_machine_mapping);
65 static unsigned long shared_info_frame;
66 static uint64_t callback_via;
68 static int __devinit init_xen_info(void)
69 {
70 struct xen_add_to_physmap xatp;
71 extern void *shared_info_area;
73 #ifdef __ia64__
74 xencomm_init();
75 #endif
77 setup_xen_features();
79 shared_info_frame = alloc_xen_mmio(PAGE_SIZE) >> PAGE_SHIFT;
80 xatp.domid = DOMID_SELF;
81 xatp.idx = 0;
82 xatp.space = XENMAPSPACE_shared_info;
83 xatp.gpfn = shared_info_frame;
84 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
85 BUG();
87 shared_info_area =
88 ioremap(shared_info_frame << PAGE_SHIFT, PAGE_SIZE);
89 if (shared_info_area == NULL)
90 panic("can't map shared info\n");
92 phys_to_machine_mapping = NULL;
94 gnttab_init();
96 return 0;
97 }
99 static void __devexit platform_pci_remove(struct pci_dev *pdev)
100 {
101 long ioaddr, iolen;
102 long mmio_addr, mmio_len;
104 ioaddr = pci_resource_start(pdev, 0);
105 iolen = pci_resource_len(pdev, 0);
106 mmio_addr = pci_resource_start(pdev, 1);
107 mmio_len = pci_resource_len(pdev, 1);
109 release_region(ioaddr, iolen);
110 release_mem_region(mmio_addr, mmio_len);
112 pci_set_drvdata(pdev, NULL);
113 free_irq(pdev->irq, pdev);
114 }
116 static unsigned long platform_mmio;
117 static unsigned long platform_mmio_alloc;
118 static unsigned long platform_mmiolen;
120 unsigned long alloc_xen_mmio(unsigned long len)
121 {
122 unsigned long addr;
124 addr = platform_mmio + platform_mmio_alloc;
125 platform_mmio_alloc += len;
126 BUG_ON(platform_mmio_alloc > platform_mmiolen);
128 return addr;
129 }
131 #ifndef __ia64__
132 /* Lifted from hvmloader.c */
133 static int get_hypercall_stubs(void)
134 {
135 uint32_t eax, ebx, ecx, edx, pages, msr, i;
136 char signature[13];
138 cpuid(0x40000000, &eax, &ebx, &ecx, &edx);
139 *(uint32_t*)(signature + 0) = ebx;
140 *(uint32_t*)(signature + 4) = ecx;
141 *(uint32_t*)(signature + 8) = edx;
142 signature[12] = 0;
144 if (strcmp("XenVMMXenVMM", signature) || (eax < 0x40000002)) {
145 printk(KERN_WARNING
146 "Detected Xen platform device but not Xen VMM?"
147 " (sig %s, eax %x)\n",
148 signature, eax);
149 return -EINVAL;
150 }
152 cpuid(0x40000001, &eax, &ebx, &ecx, &edx);
154 printk(KERN_INFO "Xen version %d.%d.\n", eax >> 16, eax & 0xffff);
156 cpuid(0x40000002, &pages, &msr, &ecx, &edx);
158 printk(KERN_INFO "Hypercall area is %u pages.\n", pages);
160 /* Use __vmalloc() because vmalloc_exec() is not an exported symbol. */
161 /* PAGE_KERNEL_EXEC also is not exported, hence we use PAGE_KERNEL. */
162 /* hypercall_stubs = vmalloc_exec(pages * PAGE_SIZE); */
163 hypercall_stubs = __vmalloc(pages * PAGE_SIZE,
164 GFP_KERNEL | __GFP_HIGHMEM,
165 __pgprot(__PAGE_KERNEL & ~_PAGE_NX));
166 if (hypercall_stubs == NULL)
167 return -ENOMEM;
169 for (i = 0; i < pages; i++) {
170 unsigned long pfn;
171 pfn = vmalloc_to_pfn((char *)hypercall_stubs + i*PAGE_SIZE);
172 wrmsrl(msr, ((u64)pfn << PAGE_SHIFT) + i);
173 }
175 return 0;
176 }
177 #else /* __ia64__ */
178 #define get_hypercall_stubs() (0)
179 #endif
181 static uint64_t get_callback_via(struct pci_dev *pdev)
182 {
183 u8 pin;
184 int irq;
186 #ifdef __ia64__
187 for (irq = 0; irq < 16; irq++) {
188 if (isa_irq_to_vector(irq) == pdev->irq)
189 return irq; /* ISA IRQ */
190 }
191 #else /* !__ia64__ */
192 irq = pdev->irq;
193 if (irq < 16)
194 return irq; /* ISA IRQ */
195 #endif
197 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
198 pin = pdev->pin;
199 #else
200 pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
201 #endif
203 /* We don't know the GSI. Specify the PCI INTx line instead. */
204 return (((uint64_t)0x01 << 56) | /* PCI INTx identifier */
205 ((uint64_t)pci_domain_nr(pdev->bus) << 32) |
206 ((uint64_t)pdev->bus->number << 16) |
207 ((uint64_t)(pdev->devfn & 0xff) << 8) |
208 ((uint64_t)(pin - 1) & 3));
209 }
211 /* Invalidate foreign mappings (e.g., in qemu-based device model). */
212 static uint16_t invlmap_port;
213 void xen_invalidate_foreign_mappings(void)
214 {
215 outb(0, invlmap_port);
216 }
217 EXPORT_SYMBOL(xen_invalidate_foreign_mappings);
219 static int __devinit platform_pci_init(struct pci_dev *pdev,
220 const struct pci_device_id *ent)
221 {
222 int i, ret;
223 long ioaddr, iolen;
224 long mmio_addr, mmio_len;
226 i = pci_enable_device(pdev);
227 if (i)
228 return i;
230 ioaddr = pci_resource_start(pdev, 0);
231 iolen = pci_resource_len(pdev, 0);
233 mmio_addr = pci_resource_start(pdev, 1);
234 mmio_len = pci_resource_len(pdev, 1);
236 callback_via = get_callback_via(pdev);
238 if (mmio_addr == 0 || ioaddr == 0 || callback_via == 0) {
239 printk(KERN_WARNING DRV_NAME ":no resources found\n");
240 return -ENOENT;
241 }
243 invlmap_port = ioaddr;
245 if (request_mem_region(mmio_addr, mmio_len, DRV_NAME) == NULL)
246 {
247 printk(KERN_ERR ":MEM I/O resource 0x%lx @ 0x%lx busy\n",
248 mmio_addr, mmio_len);
249 return -EBUSY;
250 }
252 if (request_region(ioaddr, iolen, DRV_NAME) == NULL)
253 {
254 printk(KERN_ERR DRV_NAME ":I/O resource 0x%lx @ 0x%lx busy\n",
255 iolen, ioaddr);
256 release_mem_region(mmio_addr, mmio_len);
257 return -EBUSY;
258 }
260 platform_mmio = mmio_addr;
261 platform_mmiolen = mmio_len;
263 ret = get_hypercall_stubs();
264 if (ret < 0)
265 goto out;
267 if ((ret = init_xen_info()))
268 goto out;
270 if ((ret = request_irq(pdev->irq, evtchn_interrupt,
271 SA_SHIRQ | SA_SAMPLE_RANDOM,
272 "xen-platform-pci", pdev)))
273 goto out;
275 if ((ret = set_callback_via(callback_via)))
276 goto out;
278 out:
279 if (ret) {
280 release_mem_region(mmio_addr, mmio_len);
281 release_region(ioaddr, iolen);
282 }
284 return ret;
285 }
287 #define XEN_PLATFORM_VENDOR_ID 0x5853
288 #define XEN_PLATFORM_DEVICE_ID 0x0001
289 static struct pci_device_id platform_pci_tbl[] __devinitdata = {
290 {XEN_PLATFORM_VENDOR_ID, XEN_PLATFORM_DEVICE_ID,
291 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
292 /* Continue to recognise the old ID for now */
293 {0xfffd, 0x0101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
294 {0,}
295 };
297 MODULE_DEVICE_TABLE(pci, platform_pci_tbl);
299 static struct pci_driver platform_driver = {
300 name: DRV_NAME,
301 probe: platform_pci_init,
302 remove: __devexit_p(platform_pci_remove),
303 id_table: platform_pci_tbl,
304 };
306 static int pci_device_registered;
308 void platform_pci_suspend(void)
309 {
310 gnttab_suspend();
311 }
312 EXPORT_SYMBOL_GPL(platform_pci_suspend);
314 void platform_pci_resume(void)
315 {
316 struct xen_add_to_physmap xatp;
317 phys_to_machine_mapping = NULL;
319 /* do 2 things for PV driver restore on HVM
320 * 1: rebuild share info
321 * 2: set callback irq again
322 */
323 xatp.domid = DOMID_SELF;
324 xatp.idx = 0;
325 xatp.space = XENMAPSPACE_shared_info;
326 xatp.gpfn = shared_info_frame;
327 if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
328 BUG();
330 if (( set_callback_via(callback_via)))
331 printk("platform_pci_resume failure!\n");
333 gnttab_resume();
334 }
335 EXPORT_SYMBOL_GPL(platform_pci_resume);
337 static int __init platform_pci_module_init(void)
338 {
339 int rc;
341 rc = pci_module_init(&platform_driver);
342 if (rc)
343 printk(KERN_INFO DRV_NAME ":No platform pci device model found\n");
344 else
345 pci_device_registered = 1;
347 return rc;
348 }
350 static void __exit platform_pci_module_cleanup(void)
351 {
352 printk(KERN_INFO DRV_NAME ":Do platform module cleanup\n");
353 /* disable hypervisor for callback irq */
354 set_callback_via(0);
355 if (pci_device_registered)
356 pci_unregister_driver(&platform_driver);
357 }
359 module_init(platform_pci_module_init);
360 module_exit(platform_pci_module_cleanup);