xref: /freebsd/sys/dev/xen/xenpci/xenpci.c (revision aa64588d28258aef88cc33b8043112e8856948d0)
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);
400