xenpci.c revision 185803
1/*
2 * Copyright (c) [year] [your name]
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 */
66char *hypercall_stubs;
67shared_info_t *HYPERVISOR_shared_info;
68static vm_paddr_t shared_info_pa;
69
70/*
71 * The softc is automatically allocated by the parent bus using the
72 * size specified in the driver_t declaration below.
73 */
74#define DEVICE2SOFTC(dev) ((struct xenpci_softc *) device_get_softc(dev))
75
76/* Function prototypes (these should all be static). */
77static int xenpci_deallocate_resources(device_t device);
78static int xenpci_allocate_resources(device_t device);
79static int xenpci_attach(device_t device, struct xenpci_softc *scp);
80static int xenpci_detach(device_t device, struct xenpci_softc *scp);
81
82static int xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz,
83    u_long *pa);
84
85static devclass_t xenpci_devclass;
86
87static int	xenpci_pci_probe(device_t);
88static int	xenpci_pci_attach(device_t);
89static int	xenpci_pci_detach(device_t);
90static int	xenpci_pci_resume(device_t);
91
92static device_method_t xenpci_pci_methods[] = {
93	/* Device interface */
94	DEVMETHOD(device_probe,		xenpci_pci_probe),
95	DEVMETHOD(device_attach,	xenpci_pci_attach),
96	DEVMETHOD(device_detach,	xenpci_pci_detach),
97	DEVMETHOD(device_resume,	xenpci_pci_resume),
98
99	/* Bus interface */
100	DEVMETHOD(bus_add_child,	bus_generic_add_child),
101
102	{ 0, 0 }
103};
104
105static driver_t xenpci_pci_driver = {
106	"xenpci",
107	xenpci_pci_methods,
108	sizeof(struct xenpci_softc),
109};
110
111DRIVER_MODULE(xenpci, pci, xenpci_pci_driver, xenpci_devclass, 0, 0);
112
113static struct _pcsid
114{
115	u_int32_t	type;
116	const char	*desc;
117} pci_ids[] = {
118	{ 0x00015853,	"Xen Platform Device"			},
119	{ 0x00000000,	NULL					}
120};
121
122static int
123xenpci_pci_probe (device_t device)
124{
125	u_int32_t	type = pci_get_devid(device);
126	struct _pcsid	*ep = pci_ids;
127
128	while (ep->type && ep->type != type)
129		++ep;
130	if (ep->desc) {
131		device_set_desc(device, ep->desc);
132		return (bus_generic_probe(device));
133	} else
134		return (ENXIO);
135}
136
137static int
138xenpci_pci_attach(device_t device)
139{
140        int	error;
141	struct xenpci_softc *scp = DEVICE2SOFTC(device);
142
143        error = xenpci_attach(device, scp);
144        if (error)
145                xenpci_pci_detach(device);
146        return (error);
147}
148
149static int
150xenpci_pci_detach (device_t device)
151{
152	struct xenpci_softc *scp = DEVICE2SOFTC(device);
153
154        return (xenpci_detach(device, scp));
155}
156
157static int
158xenpci_pci_resume(device_t device)
159{
160
161	return (bus_generic_resume(device));
162}
163
164/*
165 * Common Attachment sub-functions
166 */
167static uint32_t
168xenpci_cpuid_base(void)
169{
170	uint32_t base, regs[4];
171
172	for (base = 0x40000000; base < 0x40001000; base += 0x100) {
173		do_cpuid(base, regs);
174		if (!memcmp("XenVMMXenVMM", &regs[1], 12)
175		    && (regs[0] - base) >= 2)
176			return (base);
177	}
178	return (0);
179}
180
181static int
182xenpci_init_hypercall_stubs(device_t device, struct xenpci_softc * scp)
183{
184	uint32_t base, regs[4];
185	int i;
186
187	base = xenpci_cpuid_base();
188	if (!base) {
189		device_printf(device, "Xen platform device but not Xen VMM\n");
190		return (EINVAL);
191	}
192
193	if (bootverbose) {
194		do_cpuid(base + 1, regs);
195		device_printf(device, "Xen version %d.%d.\n",
196		    regs[0] >> 16, regs[0] & 0xffff);
197	}
198
199	/*
200	 * Find the hypercall pages.
201	 */
202	do_cpuid(base + 2, regs);
203
204	hypercall_stubs = malloc(regs[0] * PAGE_SIZE, M_TEMP, M_WAITOK);
205
206	for (i = 0; i < regs[0]; i++) {
207		wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i);
208	}
209
210	return (0);
211}
212
213static void
214xenpci_resume_hypercall_stubs(device_t device, struct xenpci_softc * scp)
215{
216	uint32_t base, regs[4];
217	int i;
218
219	base = xenpci_cpuid_base();
220
221	do_cpuid(base + 2, regs);
222	for (i = 0; i < regs[0]; i++) {
223		wrmsr(regs[1], vtophys(hypercall_stubs + i * PAGE_SIZE) + i);
224	}
225}
226
227static void
228xenpci_set_callback(device_t device)
229{
230	int irq;
231	uint64_t callback;
232	struct xen_hvm_param xhp;
233
234	irq = pci_get_irq(device);
235	if (irq < 16) {
236		callback = irq;
237	} else {
238		callback = (pci_get_intpin(device) - 1) & 3;
239		callback |= pci_get_slot(device) << 11;
240		callback |= 1ull << 56;
241	}
242
243	xhp.domid = DOMID_SELF;
244	xhp.index = HVM_PARAM_CALLBACK_IRQ;
245	xhp.value = callback;
246	if (HYPERVISOR_hvm_op(HVMOP_set_param, &xhp))
247		panic("Can't set evtchn callback");
248}
249
250static int
251xenpci_attach(device_t device, struct xenpci_softc * scp)
252{
253	struct xen_add_to_physmap xatp;
254	vm_offset_t shared_va;
255
256	if (xenpci_allocate_resources(device))
257		goto errexit;
258
259	scp->phys_next = rman_get_start(scp->res_memory);
260
261	if (xenpci_init_hypercall_stubs(device, scp))
262		goto errexit;
263
264	setup_xen_features();
265
266	xenpci_alloc_space_int(scp, PAGE_SIZE, &shared_info_pa);
267
268	xatp.domid = DOMID_SELF;
269	xatp.idx = 0;
270	xatp.space = XENMAPSPACE_shared_info;
271	xatp.gpfn = shared_info_pa >> PAGE_SHIFT;
272	if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
273		panic("HYPERVISOR_memory_op failed");
274
275	shared_va = kmem_alloc_nofault(kernel_map, PAGE_SIZE);
276	pmap_kenter(shared_va, shared_info_pa);
277	HYPERVISOR_shared_info = (void *) shared_va;
278
279	/*
280	 * Hook the irq up to evtchn
281	 */
282	xenpci_irq_init(device, scp);
283	xenpci_set_callback(device);
284
285	return (bus_generic_attach(device));
286
287errexit:
288	/*
289	 * Undo anything we may have done.
290	 */
291	xenpci_detach(device, scp);
292	return (ENXIO);
293}
294
295static int
296xenpci_detach(device_t device, struct xenpci_softc *scp)
297{
298	device_t parent = device_get_parent(device);
299
300	/*
301	 * Take our interrupt handler out of the list of handlers
302	 * that can handle this irq.
303	 */
304	if (scp->intr_cookie != NULL) {
305		if (BUS_TEARDOWN_INTR(parent, device,
306			scp->res_irq, scp->intr_cookie) != 0)
307				printf("intr teardown failed.. continuing\n");
308		scp->intr_cookie = NULL;
309	}
310
311	/*
312	 * Deallocate any system resources we may have
313	 * allocated on behalf of this driver.
314	 */
315	return xenpci_deallocate_resources(device);
316}
317
318static int
319xenpci_allocate_resources(device_t device)
320{
321	int error;
322	struct xenpci_softc *scp = DEVICE2SOFTC(device);
323
324	scp->res_irq = bus_alloc_resource_any(device, SYS_RES_IRQ,
325			&scp->rid_irq, RF_SHAREABLE|RF_ACTIVE);
326	if (scp->res_irq == NULL)
327		goto errexit;
328
329	scp->rid_ioport = PCIR_BAR(0);
330	scp->res_ioport = bus_alloc_resource_any(device, SYS_RES_IOPORT,
331			&scp->rid_ioport, RF_ACTIVE);
332	if (scp->res_ioport == NULL)
333		goto errexit;
334
335	scp->rid_memory = PCIR_BAR(1);
336	scp->res_memory = bus_alloc_resource_any(device, SYS_RES_MEMORY,
337			&scp->rid_memory, RF_ACTIVE);
338	if (scp->res_memory == NULL)
339		goto errexit;
340	return (0);
341
342errexit:
343	error = ENXIO;
344	/* Cleanup anything we may have assigned. */
345	xenpci_deallocate_resources(device);
346	return (ENXIO); /* For want of a better idea. */
347}
348
349static int
350xenpci_deallocate_resources(device_t device)
351{
352	struct xenpci_softc *scp = DEVICE2SOFTC(device);
353
354	if (scp->res_irq != 0) {
355		bus_deactivate_resource(device, SYS_RES_IRQ,
356			scp->rid_irq, scp->res_irq);
357		bus_release_resource(device, SYS_RES_IRQ,
358			scp->rid_irq, scp->res_irq);
359		scp->res_irq = 0;
360	}
361	if (scp->res_ioport != 0) {
362		bus_deactivate_resource(device, SYS_RES_IOPORT,
363			scp->rid_ioport, scp->res_ioport);
364		bus_release_resource(device, SYS_RES_IOPORT,
365			scp->rid_ioport, scp->res_ioport);
366		scp->res_ioport = 0;
367	}
368	if (scp->res_memory != 0) {
369		bus_deactivate_resource(device, SYS_RES_MEMORY,
370			scp->rid_memory, scp->res_memory);
371		bus_release_resource(device, SYS_RES_MEMORY,
372			scp->rid_memory, scp->res_memory);
373		scp->res_memory = 0;
374	}
375
376	return (0);
377}
378
379static int
380xenpci_alloc_space_int(struct xenpci_softc *scp, size_t sz,
381    vm_paddr_t *pa)
382{
383
384	if (scp->phys_next + sz > rman_get_end(scp->res_memory)) {
385		return (ENOMEM);
386	}
387
388	*pa = scp->phys_next;
389	scp->phys_next += sz;
390
391	return (0);
392}
393
394int
395xenpci_alloc_space(size_t sz, vm_paddr_t *pa)
396{
397	device_t device = devclass_get_device(xenpci_devclass, 0);
398
399	if (device) {
400		return (xenpci_alloc_space_int(DEVICE2SOFTC(device),
401			sz, pa));
402	} else {
403		return (ENOMEM);
404	}
405}
406
407void
408xenpci_resume()
409{
410	device_t device = devclass_get_device(xenpci_devclass, 0);
411	struct xenpci_softc *scp = DEVICE2SOFTC(device);
412	struct xen_add_to_physmap xatp;
413
414	xenpci_resume_hypercall_stubs(device, scp);
415
416	xatp.domid = DOMID_SELF;
417	xatp.idx = 0;
418	xatp.space = XENMAPSPACE_shared_info;
419	xatp.gpfn = shared_info_pa >> PAGE_SHIFT;
420	if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
421		panic("HYPERVISOR_memory_op failed");
422
423	pmap_kenter((vm_offset_t) HYPERVISOR_shared_info, shared_info_pa);
424
425	xenpci_set_callback(device);
426
427	gnttab_resume();
428	irq_resume();
429}
430
431