]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/dev/xen/xenpci/xenpci.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / dev / xen / xenpci / xenpci.c
1 /*
2  * Copyright (c) 2008 Citrix Systems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/bus.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
34 #include <sys/module.h>
35 #include <sys/proc.h>
36 #include <sys/systm.h>
37 #include <sys/time.h>
38
39 #include <machine/bus.h>
40 #include <machine/resource.h>
41 #include <sys/rman.h>
42
43 #include <machine/stdarg.h>
44 #include <machine/xen/xen-os.h>
45 #include <xen/features.h>
46 #include <xen/hypervisor.h>
47 #include <xen/gnttab.h>
48 #include <xen/xen_intr.h>
49 #include <xen/interface/memory.h>
50 #include <xen/interface/hvm/params.h>
51
52 #include <dev/pci/pcireg.h>
53 #include <dev/pci/pcivar.h>
54
55 #include <vm/vm.h>
56 #include <vm/vm_extern.h>
57 #include <vm/vm_kern.h>
58 #include <vm/pmap.h>
59
60 #include <dev/xen/xenpci/xenpcivar.h>
61
62 /*
63  * These variables are used by the rest of the kernel to access the
64  * hypervisor.
65  */
66 char *hypercall_stubs;
67 shared_info_t *HYPERVISOR_shared_info;
68 static vm_paddr_t shared_info_pa;
69
70 /*
71  * This is used to find our platform device instance.
72  */
73 static devclass_t xenpci_devclass;
74
75 /*
76  * Return the CPUID base address for Xen functions.
77  */
78 static uint32_t
79 xenpci_cpuid_base(void)
80 {
81         uint32_t base, regs[4];
82
83         for (base = 0x40000000; base < 0x40001000; base += 0x100) {
84                 do_cpuid(base, regs);
85                 if (!memcmp("XenVMMXenVMM", &regs[1], 12)
86                     && (regs[0] - base) >= 2)
87                         return (base);
88         }
89         return (0);
90 }
91
92 /*
93  * Allocate and fill in the hypcall page.
94  */
95 static int
96 xenpci_init_hypercall_stubs(device_t dev, struct xenpci_softc * scp)
97 {
98         uint32_t base, regs[4];
99         int i;
100
101         base = xenpci_cpuid_base();
102         if (!base) {
103                 device_printf(dev, "Xen platform device but not Xen VMM\n");
104                 return (EINVAL);
105         }
106
107         if (bootverbose) {
108                 do_cpuid(base + 1, regs);
109                 device_printf(dev, "Xen version %d.%d.\n",
110                     regs[0] >> 16, regs[0] & 0xffff);
111         }
112
113         /*
114          * Find the hypercall pages.
115          */
116         do_cpuid(base + 2, regs);
117         
118         hypercall_stubs = malloc(regs[0] * PAGE_SIZE, M_TEMP, M_WAITOK);
119
120         for (i = 0; i < regs[0]; i++) {
121                 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i);
122         }
123
124         return (0);
125 }
126
127 /*
128  * After a resume, re-initialise the hypercall page.
129  */
130 static void
131 xenpci_resume_hypercall_stubs(device_t dev, struct xenpci_softc * scp)
132 {
133         uint32_t base, regs[4];
134         int i;
135
136         base = xenpci_cpuid_base();
137
138         do_cpuid(base + 2, regs);
139         for (i = 0; i < regs[0]; i++) {
140                 wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i);
141         }
142 }
143
144 /*
145  * Tell the hypervisor how to contact us for event channel callbacks.
146  */
147 static void
148 xenpci_set_callback(device_t dev)
149 {
150         int irq;
151         uint64_t callback;
152         struct xen_hvm_param xhp;
153
154         irq = pci_get_irq(dev);
155         if (irq < 16) {
156                 callback = irq;
157         } else {
158                 callback = (pci_get_intpin(dev) - 1) & 3;
159                 callback |= pci_get_slot(dev) << 11;
160                 callback |= 1ull << 56;
161         }
162
163         xhp.domid = DOMID_SELF;
164         xhp.index = HVM_PARAM_CALLBACK_IRQ;
165         xhp.value = callback;
166         if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp))
167                 panic("Can't set evtchn callback");
168 }
169
170
171 /*
172  * Deallocate anything allocated by xenpci_allocate_resources.
173  */
174 static int
175 xenpci_deallocate_resources(device_t dev)
176 {
177         struct xenpci_softc *scp = device_get_softc(dev);
178
179         if (scp->res_irq != 0) {
180                 bus_deactivate_resource(dev, SYS_RES_IRQ,
181                         scp->rid_irq, scp->res_irq);
182                 bus_release_resource(dev, SYS_RES_IRQ,
183                         scp->rid_irq, scp->res_irq);
184                 scp->res_irq = 0;
185         }
186         if (scp->res_memory != 0) {
187                 bus_deactivate_resource(dev, SYS_RES_MEMORY,
188                         scp->rid_memory, scp->res_memory);
189                 bus_release_resource(dev, SYS_RES_MEMORY,
190                         scp->rid_memory, scp->res_memory);
191                 scp->res_memory = 0;
192         }
193
194         return (0);
195 }
196
197 /*
198  * Allocate irq and memory resources.
199  */
200 static int
201 xenpci_allocate_resources(device_t dev)
202 {
203         struct xenpci_softc *scp = device_get_softc(dev);
204
205         scp->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
206                         &scp->rid_irq, RF_SHAREABLE|RF_ACTIVE);
207         if (scp->res_irq == NULL)
208                 goto errexit;
209
210         scp->rid_memory = PCIR_BAR(1);
211         scp->res_memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
212                         &scp->rid_memory, RF_ACTIVE);
213         if (scp->res_memory == NULL)
214                 goto errexit;
215         return (0);
216
217 errexit:
218         /* Cleanup anything we may have assigned. */
219         xenpci_deallocate_resources(dev);
220         return (ENXIO); /* For want of a better idea. */
221 }
222
223 /*
224  * Allocate a physical address range from our mmio region.
225  */
226 static int
227 xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz,
228     vm_paddr_t *pa)
229 {
230
231         if (scp->phys_next + sz > rman_get_end(scp->res_memory)) {
232                 return (ENOMEM);
233         }
234
235         *pa = scp->phys_next;
236         scp->phys_next += sz;
237
238         return (0);
239 }
240
241 /*
242  * Allocate a physical address range from our mmio region.
243  */
244 int
245 xenpci_alloc_space(size_t sz, vm_paddr_t *pa)
246 {
247         device_t dev = devclass_get_device(xenpci_devclass, 0);
248
249         if (dev) {
250                 return (xenpci_alloc_space_int(device_get_softc(dev),
251                         sz, pa));
252         } else {
253                 return (ENOMEM);
254         }
255 }
256
257 /*
258  * Called very early in the resume sequence - reinitialise the various
259  * bits of Xen machinery including the hypercall page and the shared
260  * info page.
261  */
262 void
263 xenpci_resume()
264 {
265         device_t dev = devclass_get_device(xenpci_devclass, 0);
266         struct xenpci_softc *scp = device_get_softc(dev);
267         struct xen_add_to_physmap xatp;
268
269         xenpci_resume_hypercall_stubs(dev, scp);
270
271         xatp.domid = DOMID_SELF;
272         xatp.idx = 0;
273         xatp.space = XENMAPSPACE_shared_info;
274         xatp.gpfn = shared_info_pa >> PAGE_SHIFT;
275         if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
276                 panic("HYPERVISOR_memory_op failed");
277
278         pmap_kenter((vm_offset_t) HYPERVISOR_shared_info, shared_info_pa);
279
280         xenpci_set_callback(dev);
281
282         gnttab_resume();
283         irq_resume();
284 }
285
286 /*
287  * Probe - just check device ID.
288  */
289 static int
290 xenpci_probe(device_t dev)
291 {
292
293         if (pci_get_devid(dev) != 0x00015853)
294                 return (ENXIO);
295
296         device_set_desc(dev, "Xen Platform Device");
297         return (bus_generic_probe(dev));
298 }
299
300 /*
301  * Attach - find resources and talk to Xen.
302  */
303 static int
304 xenpci_attach(device_t dev)
305 {
306         int error;
307         struct xenpci_softc *scp = device_get_softc(dev);
308         struct xen_add_to_physmap xatp;
309         vm_offset_t shared_va;
310
311         error = xenpci_allocate_resources(dev);
312         if (error)
313                 goto errexit;
314
315         scp->phys_next = rman_get_start(scp->res_memory);
316
317         error = xenpci_init_hypercall_stubs(dev, scp);
318         if (error)
319                 goto errexit;
320
321         setup_xen_features();
322
323         xenpci_alloc_space_int(scp, PAGE_SIZE, &shared_info_pa); 
324
325         xatp.domid = DOMID_SELF;
326         xatp.idx = 0;
327         xatp.space = XENMAPSPACE_shared_info;
328         xatp.gpfn = shared_info_pa >> PAGE_SHIFT;
329         if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
330                 panic("HYPERVISOR_memory_op failed");
331
332         shared_va = kmem_alloc_nofault(kernel_map, PAGE_SIZE);
333         pmap_kenter(shared_va, shared_info_pa);
334         HYPERVISOR_shared_info = (void *) shared_va;
335
336         /*
337          * Hook the irq up to evtchn
338          */
339         xenpci_irq_init(dev, scp);
340         xenpci_set_callback(dev);
341
342         return (bus_generic_attach(dev));
343
344 errexit:
345         /*
346          * Undo anything we may have done.
347          */
348         xenpci_deallocate_resources(dev);
349         return (error);
350 }
351
352 /*
353  * Detach - reverse anything done by attach.
354  */
355 static int
356 xenpci_detach(device_t dev)
357 {
358         struct xenpci_softc *scp = device_get_softc(dev);
359         device_t parent = device_get_parent(dev);
360
361         /*
362          * Take our interrupt handler out of the list of handlers
363          * that can handle this irq.
364          */
365         if (scp->intr_cookie != NULL) {
366                 if (BUS_TEARDOWN_INTR(parent, dev,
367                         scp->res_irq, scp->intr_cookie) != 0)
368                                 printf("intr teardown failed.. continuing\n");
369                 scp->intr_cookie = NULL;
370         }
371
372         /*
373          * Deallocate any system resources we may have
374          * allocated on behalf of this driver.
375          */
376         return (xenpci_deallocate_resources(dev));
377 }
378
379 static device_method_t xenpci_methods[] = {
380         /* Device interface */
381         DEVMETHOD(device_probe,         xenpci_probe),
382         DEVMETHOD(device_attach,        xenpci_attach),
383         DEVMETHOD(device_detach,        xenpci_detach),
384         DEVMETHOD(device_suspend,       bus_generic_suspend),
385         DEVMETHOD(device_resume,        bus_generic_resume),
386
387         /* Bus interface */
388         DEVMETHOD(bus_add_child,        bus_generic_add_child),
389
390         { 0, 0 }
391 };
392
393 static driver_t xenpci_driver = {
394         "xenpci",
395         xenpci_methods,
396         sizeof(struct xenpci_softc),
397 };
398
399 DRIVER_MODULE(xenpci, pci, xenpci_driver, xenpci_devclass, 0, 0);