xref: /freebsd/sys/dev/xen/pcifront/pcifront.c (revision 5b56413d)
1*5b56413dSWarner Losh /*-
2*5b56413dSWarner Losh  * SPDX-License-Identifier: BSD-3-Clause
3*5b56413dSWarner Losh  *
4*5b56413dSWarner Losh  * Copyright (c) 2006, Cisco Systems, Inc.
5*5b56413dSWarner Losh  * All rights reserved.
6*5b56413dSWarner Losh  *
7*5b56413dSWarner Losh  * Redistribution and use in source and binary forms, with or without
8*5b56413dSWarner Losh  * modification, are permitted provided that the following conditions
9*5b56413dSWarner Losh  * are met:
10*5b56413dSWarner Losh  *
11*5b56413dSWarner Losh  * 1. Redistributions of source code must retain the above copyright
12*5b56413dSWarner Losh  *    notice, this list of conditions and the following disclaimer.
13*5b56413dSWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
14*5b56413dSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
15*5b56413dSWarner Losh  *    documentation and/or other materials provided with the distribution.
16*5b56413dSWarner Losh  * 3. Neither the name of Cisco Systems, Inc. nor the names of its contributors
17*5b56413dSWarner Losh  *    may be used to endorse or promote products derived from this software
18*5b56413dSWarner Losh  *    without specific prior written permission.
19*5b56413dSWarner Losh  *
20*5b56413dSWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21*5b56413dSWarner Losh  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22*5b56413dSWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23*5b56413dSWarner Losh  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24*5b56413dSWarner Losh  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25*5b56413dSWarner Losh  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26*5b56413dSWarner Losh  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27*5b56413dSWarner Losh  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28*5b56413dSWarner Losh  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29*5b56413dSWarner Losh  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30*5b56413dSWarner Losh  * POSSIBILITY OF SUCH DAMAGE.
31*5b56413dSWarner Losh  */
32*5b56413dSWarner Losh 
33*5b56413dSWarner Losh #include <sys/cdefs.h>
34*5b56413dSWarner Losh __FBSDID("$FreeBSD$");
35*5b56413dSWarner Losh 
36*5b56413dSWarner Losh #include <sys/param.h>
37*5b56413dSWarner Losh #include <sys/module.h>
38*5b56413dSWarner Losh #include <sys/systm.h>
39*5b56413dSWarner Losh #include <sys/mbuf.h>
40*5b56413dSWarner Losh #include <sys/malloc.h>
41*5b56413dSWarner Losh #include <sys/kernel.h>
42*5b56413dSWarner Losh #include <sys/socket.h>
43*5b56413dSWarner Losh #include <sys/queue.h>
44*5b56413dSWarner Losh 
45*5b56413dSWarner Losh #include <machine/vmparam.h>
46*5b56413dSWarner Losh #include <vm/vm.h>
47*5b56413dSWarner Losh #include <vm/pmap.h>
48*5b56413dSWarner Losh 
49*5b56413dSWarner Losh #include <machine/bus.h>
50*5b56413dSWarner Losh #include <machine/resource.h>
51*5b56413dSWarner Losh #include <machine/frame.h>
52*5b56413dSWarner Losh 
53*5b56413dSWarner Losh #include <sys/bus.h>
54*5b56413dSWarner Losh #include <sys/rman.h>
55*5b56413dSWarner Losh 
56*5b56413dSWarner Losh #include <machine/intr_machdep.h>
57*5b56413dSWarner Losh 
58*5b56413dSWarner Losh #include <machine/xen-os.h>
59*5b56413dSWarner Losh #include <machine/hypervisor.h>
60*5b56413dSWarner Losh #include <machine/hypervisor-ifs.h>
61*5b56413dSWarner Losh #include <machine/xen_intr.h>
62*5b56413dSWarner Losh #include <machine/evtchn.h>
63*5b56413dSWarner Losh #include <machine/xenbus.h>
64*5b56413dSWarner Losh #include <machine/gnttab.h>
65*5b56413dSWarner Losh #include <machine/xen-public/memory.h>
66*5b56413dSWarner Losh #include <machine/xen-public/io/pciif.h>
67*5b56413dSWarner Losh 
68*5b56413dSWarner Losh #include <sys/pciio.h>
69*5b56413dSWarner Losh #include <dev/pci/pcivar.h>
70*5b56413dSWarner Losh #include "pcib_if.h"
71*5b56413dSWarner Losh 
72*5b56413dSWarner Losh #ifdef XEN_PCIDEV_FE_DEBUG
73*5b56413dSWarner Losh #define DPRINTF(fmt, args...) \
74*5b56413dSWarner Losh     printf("pcifront (%s:%d): " fmt, __FUNCTION__, __LINE__, ##args)
75*5b56413dSWarner Losh #else
76*5b56413dSWarner Losh #define DPRINTF(fmt, args...) ((void)0)
77*5b56413dSWarner Losh #endif
78*5b56413dSWarner Losh #define WPRINTF(fmt, args...) \
79*5b56413dSWarner Losh     printf("pcifront (%s:%d): " fmt, __FUNCTION__, __LINE__, ##args)
80*5b56413dSWarner Losh 
81*5b56413dSWarner Losh #define INVALID_GRANT_REF (0)
82*5b56413dSWarner Losh #define INVALID_EVTCHN    (-1)
83*5b56413dSWarner Losh #define virt_to_mfn(x) (vtophys(x) >> PAGE_SHIFT)
84*5b56413dSWarner Losh 
85*5b56413dSWarner Losh struct pcifront_device {
86*5b56413dSWarner Losh 	STAILQ_ENTRY(pcifront_device) next;
87*5b56413dSWarner Losh 
88*5b56413dSWarner Losh 	struct xenbus_device *xdev;
89*5b56413dSWarner Losh 
90*5b56413dSWarner Losh 	int unit;
91*5b56413dSWarner Losh 	int evtchn;
92*5b56413dSWarner Losh 	int gnt_ref;
93*5b56413dSWarner Losh 
94*5b56413dSWarner Losh 	/* Lock this when doing any operations in sh_info */
95*5b56413dSWarner Losh 	struct mtx sh_info_lock;
96*5b56413dSWarner Losh 	struct xen_pci_sharedinfo *sh_info;
97*5b56413dSWarner Losh 
98*5b56413dSWarner Losh 	device_t ndev;
99*5b56413dSWarner Losh 
100*5b56413dSWarner Losh 	int ref_cnt;
101*5b56413dSWarner Losh };
102*5b56413dSWarner Losh 
103*5b56413dSWarner Losh static STAILQ_HEAD(pcifront_dlist, pcifront_device) pdev_list = STAILQ_HEAD_INITIALIZER(pdev_list);
104*5b56413dSWarner Losh 
105*5b56413dSWarner Losh struct xpcib_softc {
106*5b56413dSWarner Losh 	int domain;
107*5b56413dSWarner Losh 	int bus;
108*5b56413dSWarner Losh 	struct pcifront_device *pdev;
109*5b56413dSWarner Losh };
110*5b56413dSWarner Losh 
111*5b56413dSWarner Losh /* Allocate a PCI device structure */
112*5b56413dSWarner Losh static struct pcifront_device *
alloc_pdev(struct xenbus_device * xdev)113*5b56413dSWarner Losh alloc_pdev(struct xenbus_device *xdev)
114*5b56413dSWarner Losh {
115*5b56413dSWarner Losh 	struct pcifront_device *pdev = NULL;
116*5b56413dSWarner Losh 	int err, unit;
117*5b56413dSWarner Losh 
118*5b56413dSWarner Losh 	err = sscanf(xdev->nodename, "device/pci/%d", &unit);
119*5b56413dSWarner Losh 	if (err != 1) {
120*5b56413dSWarner Losh 		if (err == 0)
121*5b56413dSWarner Losh 			err = -EINVAL;
122*5b56413dSWarner Losh 		xenbus_dev_fatal(pdev->xdev, err, "Error scanning pci device instance number");
123*5b56413dSWarner Losh 		goto out;
124*5b56413dSWarner Losh 	}
125*5b56413dSWarner Losh 
126*5b56413dSWarner Losh 	pdev = (struct pcifront_device *)malloc(sizeof(struct pcifront_device), M_DEVBUF, M_NOWAIT);
127*5b56413dSWarner Losh 	if (pdev == NULL) {
128*5b56413dSWarner Losh 		err = -ENOMEM;
129*5b56413dSWarner Losh 		xenbus_dev_fatal(xdev, err, "Error allocating pcifront_device struct");
130*5b56413dSWarner Losh 		goto out;
131*5b56413dSWarner Losh 	}
132*5b56413dSWarner Losh 	pdev->unit = unit;
133*5b56413dSWarner Losh 	pdev->xdev = xdev;
134*5b56413dSWarner Losh 	pdev->ref_cnt = 1;
135*5b56413dSWarner Losh 
136*5b56413dSWarner Losh 	pdev->sh_info = (struct xen_pci_sharedinfo *)malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
137*5b56413dSWarner Losh 	if (pdev->sh_info == NULL) {
138*5b56413dSWarner Losh 		free(pdev, M_DEVBUF);
139*5b56413dSWarner Losh 		pdev = NULL;
140*5b56413dSWarner Losh 		err = -ENOMEM;
141*5b56413dSWarner Losh 		xenbus_dev_fatal(xdev, err, "Error allocating sh_info struct");
142*5b56413dSWarner Losh 		goto out;
143*5b56413dSWarner Losh 	}
144*5b56413dSWarner Losh 	pdev->sh_info->flags = 0;
145*5b56413dSWarner Losh 
146*5b56413dSWarner Losh 	xdev->data = pdev;
147*5b56413dSWarner Losh 
148*5b56413dSWarner Losh 	mtx_init(&pdev->sh_info_lock, "info_lock", "pci shared dev info lock", MTX_DEF);
149*5b56413dSWarner Losh 
150*5b56413dSWarner Losh 	pdev->evtchn = INVALID_EVTCHN;
151*5b56413dSWarner Losh 	pdev->gnt_ref = INVALID_GRANT_REF;
152*5b56413dSWarner Losh 
153*5b56413dSWarner Losh 	STAILQ_INSERT_TAIL(&pdev_list, pdev, next);
154*5b56413dSWarner Losh 
155*5b56413dSWarner Losh 	DPRINTF("Allocated pdev @ 0x%p (unit=%d)\n", pdev, unit);
156*5b56413dSWarner Losh 
157*5b56413dSWarner Losh  out:
158*5b56413dSWarner Losh 	return pdev;
159*5b56413dSWarner Losh }
160*5b56413dSWarner Losh 
161*5b56413dSWarner Losh /* Hold a reference to a pcifront device */
162*5b56413dSWarner Losh static void
get_pdev(struct pcifront_device * pdev)163*5b56413dSWarner Losh get_pdev(struct pcifront_device *pdev)
164*5b56413dSWarner Losh {
165*5b56413dSWarner Losh 	pdev->ref_cnt++;
166*5b56413dSWarner Losh }
167*5b56413dSWarner Losh 
168*5b56413dSWarner Losh /* Release a reference to a pcifront device */
169*5b56413dSWarner Losh static void
put_pdev(struct pcifront_device * pdev)170*5b56413dSWarner Losh put_pdev(struct pcifront_device *pdev)
171*5b56413dSWarner Losh {
172*5b56413dSWarner Losh 	if (--pdev->ref_cnt > 0)
173*5b56413dSWarner Losh 		return;
174*5b56413dSWarner Losh 
175*5b56413dSWarner Losh 	DPRINTF("freeing pdev @ 0x%p (ref_cnt=%d)\n", pdev, pdev->ref_cnt);
176*5b56413dSWarner Losh 
177*5b56413dSWarner Losh 	if (pdev->evtchn != INVALID_EVTCHN)
178*5b56413dSWarner Losh 		xenbus_free_evtchn(pdev->xdev, pdev->evtchn);
179*5b56413dSWarner Losh 
180*5b56413dSWarner Losh 	if (pdev->gnt_ref != INVALID_GRANT_REF)
181*5b56413dSWarner Losh 		gnttab_end_foreign_access(pdev->gnt_ref, 0, (void *)pdev->sh_info);
182*5b56413dSWarner Losh 
183*5b56413dSWarner Losh 	pdev->xdev->data = NULL;
184*5b56413dSWarner Losh 
185*5b56413dSWarner Losh 	free(pdev, M_DEVBUF);
186*5b56413dSWarner Losh }
187*5b56413dSWarner Losh 
188*5b56413dSWarner Losh /* Write to the xenbus info needed by backend */
189*5b56413dSWarner Losh static int
pcifront_publish_info(struct pcifront_device * pdev)190*5b56413dSWarner Losh pcifront_publish_info(struct pcifront_device *pdev)
191*5b56413dSWarner Losh {
192*5b56413dSWarner Losh 	int err = 0;
193*5b56413dSWarner Losh 	struct xenbus_transaction *trans;
194*5b56413dSWarner Losh 
195*5b56413dSWarner Losh 	err = xenbus_grant_ring(pdev->xdev, virt_to_mfn(pdev->sh_info));
196*5b56413dSWarner Losh 	if (err < 0) {
197*5b56413dSWarner Losh 		WPRINTF("error granting access to ring page\n");
198*5b56413dSWarner Losh 		goto out;
199*5b56413dSWarner Losh 	}
200*5b56413dSWarner Losh 
201*5b56413dSWarner Losh 	pdev->gnt_ref = err;
202*5b56413dSWarner Losh 
203*5b56413dSWarner Losh 	err = xenbus_alloc_evtchn(pdev->xdev, &pdev->evtchn);
204*5b56413dSWarner Losh 	if (err)
205*5b56413dSWarner Losh 		goto out;
206*5b56413dSWarner Losh 
207*5b56413dSWarner Losh  do_publish:
208*5b56413dSWarner Losh 	trans = xenbus_transaction_start();
209*5b56413dSWarner Losh 	if (IS_ERR(trans)) {
210*5b56413dSWarner Losh 		xenbus_dev_fatal(pdev->xdev, err,
211*5b56413dSWarner Losh 						 "Error writing configuration for backend "
212*5b56413dSWarner Losh 						 "(start transaction)");
213*5b56413dSWarner Losh 		goto out;
214*5b56413dSWarner Losh 	}
215*5b56413dSWarner Losh 
216*5b56413dSWarner Losh 	err = xenbus_printf(trans, pdev->xdev->nodename,
217*5b56413dSWarner Losh 						"pci-op-ref", "%u", pdev->gnt_ref);
218*5b56413dSWarner Losh 	if (!err)
219*5b56413dSWarner Losh 		err = xenbus_printf(trans, pdev->xdev->nodename,
220*5b56413dSWarner Losh 							"event-channel", "%u", pdev->evtchn);
221*5b56413dSWarner Losh 	if (!err)
222*5b56413dSWarner Losh 		err = xenbus_printf(trans, pdev->xdev->nodename,
223*5b56413dSWarner Losh 							"magic", XEN_PCI_MAGIC);
224*5b56413dSWarner Losh 	if (!err)
225*5b56413dSWarner Losh 		err = xenbus_switch_state(pdev->xdev, trans,
226*5b56413dSWarner Losh 								  XenbusStateInitialised);
227*5b56413dSWarner Losh 
228*5b56413dSWarner Losh 	if (err) {
229*5b56413dSWarner Losh 		xenbus_transaction_end(trans, 1);
230*5b56413dSWarner Losh 		xenbus_dev_fatal(pdev->xdev, err,
231*5b56413dSWarner Losh 						 "Error writing configuration for backend");
232*5b56413dSWarner Losh 		goto out;
233*5b56413dSWarner Losh 	} else {
234*5b56413dSWarner Losh 		err = xenbus_transaction_end(trans, 0);
235*5b56413dSWarner Losh 		if (err == -EAGAIN)
236*5b56413dSWarner Losh 			goto do_publish;
237*5b56413dSWarner Losh 		else if (err) {
238*5b56413dSWarner Losh 			xenbus_dev_fatal(pdev->xdev, err,
239*5b56413dSWarner Losh 							 "Error completing transaction for backend");
240*5b56413dSWarner Losh 			goto out;
241*5b56413dSWarner Losh 		}
242*5b56413dSWarner Losh 	}
243*5b56413dSWarner Losh 
244*5b56413dSWarner Losh  out:
245*5b56413dSWarner Losh 	return err;
246*5b56413dSWarner Losh }
247*5b56413dSWarner Losh 
248*5b56413dSWarner Losh /* The backend is now connected so complete the connection process on our side */
249*5b56413dSWarner Losh static int
pcifront_connect(struct pcifront_device * pdev)250*5b56413dSWarner Losh pcifront_connect(struct pcifront_device *pdev)
251*5b56413dSWarner Losh {
252*5b56413dSWarner Losh 	device_t nexus;
253*5b56413dSWarner Losh 	devclass_t nexus_devclass;
254*5b56413dSWarner Losh 
255*5b56413dSWarner Losh 	/* We will add our device as a child of the nexus0 device */
256*5b56413dSWarner Losh 	if (!(nexus_devclass = devclass_find("nexus")) ||
257*5b56413dSWarner Losh 		!(nexus = devclass_get_device(nexus_devclass, 0))) {
258*5b56413dSWarner Losh 		WPRINTF("could not find nexus0!\n");
259*5b56413dSWarner Losh 		return -1;
260*5b56413dSWarner Losh 	}
261*5b56413dSWarner Losh 
262*5b56413dSWarner Losh 	/* Create a newbus device representing this frontend instance */
263*5b56413dSWarner Losh 	pdev->ndev = BUS_ADD_CHILD(nexus, 0, "xpcife", pdev->unit);
264*5b56413dSWarner Losh 	if (!pdev->ndev) {
265*5b56413dSWarner Losh 		WPRINTF("could not create xpcife%d!\n", pdev->unit);
266*5b56413dSWarner Losh 		return -EFAULT;
267*5b56413dSWarner Losh 	}
268*5b56413dSWarner Losh 	get_pdev(pdev);
269*5b56413dSWarner Losh 	device_set_ivars(pdev->ndev, pdev);
270*5b56413dSWarner Losh 
271*5b56413dSWarner Losh 	/* Good to go connected now */
272*5b56413dSWarner Losh 	xenbus_switch_state(pdev->xdev, NULL, XenbusStateConnected);
273*5b56413dSWarner Losh 
274*5b56413dSWarner Losh 	printf("pcifront: connected to %s\n", pdev->xdev->nodename);
275*5b56413dSWarner Losh 
276*5b56413dSWarner Losh 	mtx_lock(&Giant);
277*5b56413dSWarner Losh 	device_probe_and_attach(pdev->ndev);
278*5b56413dSWarner Losh 	mtx_unlock(&Giant);
279*5b56413dSWarner Losh 
280*5b56413dSWarner Losh 	return 0;
281*5b56413dSWarner Losh }
282*5b56413dSWarner Losh 
283*5b56413dSWarner Losh /* The backend is closing so process a disconnect */
284*5b56413dSWarner Losh static int
pcifront_disconnect(struct pcifront_device * pdev)285*5b56413dSWarner Losh pcifront_disconnect(struct pcifront_device *pdev)
286*5b56413dSWarner Losh {
287*5b56413dSWarner Losh 	int err = 0;
288*5b56413dSWarner Losh 	XenbusState prev_state;
289*5b56413dSWarner Losh 
290*5b56413dSWarner Losh 	prev_state = xenbus_read_driver_state(pdev->xdev->nodename);
291*5b56413dSWarner Losh 
292*5b56413dSWarner Losh 	if (prev_state < XenbusStateClosing) {
293*5b56413dSWarner Losh 		err = xenbus_switch_state(pdev->xdev, NULL, XenbusStateClosing);
294*5b56413dSWarner Losh 		if (!err && prev_state == XenbusStateConnected) {
295*5b56413dSWarner Losh 			/* TODO - need to detach the newbus devices */
296*5b56413dSWarner Losh 		}
297*5b56413dSWarner Losh 	}
298*5b56413dSWarner Losh 
299*5b56413dSWarner Losh 	return err;
300*5b56413dSWarner Losh }
301*5b56413dSWarner Losh 
302*5b56413dSWarner Losh /* Process a probe from the xenbus */
303*5b56413dSWarner Losh static int
pcifront_probe(struct xenbus_device * xdev,const struct xenbus_device_id * id)304*5b56413dSWarner Losh pcifront_probe(struct xenbus_device *xdev,
305*5b56413dSWarner Losh 			   const struct xenbus_device_id *id)
306*5b56413dSWarner Losh {
307*5b56413dSWarner Losh 	int err = 0;
308*5b56413dSWarner Losh 	struct pcifront_device *pdev;
309*5b56413dSWarner Losh 
310*5b56413dSWarner Losh 	DPRINTF("xenbus probing\n");
311*5b56413dSWarner Losh 
312*5b56413dSWarner Losh 	if ((pdev = alloc_pdev(xdev)) == NULL)
313*5b56413dSWarner Losh 		goto out;
314*5b56413dSWarner Losh 
315*5b56413dSWarner Losh 	err = pcifront_publish_info(pdev);
316*5b56413dSWarner Losh 
317*5b56413dSWarner Losh  out:
318*5b56413dSWarner Losh 	if (err)
319*5b56413dSWarner Losh 		put_pdev(pdev);
320*5b56413dSWarner Losh 	return err;
321*5b56413dSWarner Losh }
322*5b56413dSWarner Losh 
323*5b56413dSWarner Losh /* Remove the xenbus PCI device */
324*5b56413dSWarner Losh static int
pcifront_remove(struct xenbus_device * xdev)325*5b56413dSWarner Losh pcifront_remove(struct xenbus_device *xdev)
326*5b56413dSWarner Losh {
327*5b56413dSWarner Losh 	DPRINTF("removing xenbus device node (%s)\n", xdev->nodename);
328*5b56413dSWarner Losh 	if (xdev->data)
329*5b56413dSWarner Losh 		put_pdev(xdev->data);
330*5b56413dSWarner Losh 	return 0;
331*5b56413dSWarner Losh }
332*5b56413dSWarner Losh 
333*5b56413dSWarner Losh /* Called by xenbus when our backend node changes state */
334*5b56413dSWarner Losh static void
pcifront_backend_changed(struct xenbus_device * xdev,XenbusState be_state)335*5b56413dSWarner Losh pcifront_backend_changed(struct xenbus_device *xdev,
336*5b56413dSWarner Losh 						 XenbusState be_state)
337*5b56413dSWarner Losh {
338*5b56413dSWarner Losh 	struct pcifront_device *pdev = xdev->data;
339*5b56413dSWarner Losh 
340*5b56413dSWarner Losh 	switch (be_state) {
341*5b56413dSWarner Losh 	case XenbusStateClosing:
342*5b56413dSWarner Losh 		DPRINTF("backend closing (%s)\n", xdev->nodename);
343*5b56413dSWarner Losh 		pcifront_disconnect(pdev);
344*5b56413dSWarner Losh 		break;
345*5b56413dSWarner Losh 
346*5b56413dSWarner Losh 	case XenbusStateClosed:
347*5b56413dSWarner Losh 		DPRINTF("backend closed (%s)\n", xdev->nodename);
348*5b56413dSWarner Losh 		pcifront_disconnect(pdev);
349*5b56413dSWarner Losh 		break;
350*5b56413dSWarner Losh 
351*5b56413dSWarner Losh 	case XenbusStateConnected:
352*5b56413dSWarner Losh 		DPRINTF("backend connected (%s)\n", xdev->nodename);
353*5b56413dSWarner Losh 		pcifront_connect(pdev);
354*5b56413dSWarner Losh 		break;
355*5b56413dSWarner Losh 
356*5b56413dSWarner Losh 	default:
357*5b56413dSWarner Losh 		break;
358*5b56413dSWarner Losh 	}
359*5b56413dSWarner Losh }
360*5b56413dSWarner Losh 
361*5b56413dSWarner Losh /* Process PCI operation */
362*5b56413dSWarner Losh static int
do_pci_op(struct pcifront_device * pdev,struct xen_pci_op * op)363*5b56413dSWarner Losh do_pci_op(struct pcifront_device *pdev, struct xen_pci_op *op)
364*5b56413dSWarner Losh {
365*5b56413dSWarner Losh 	int err = 0;
366*5b56413dSWarner Losh 	struct xen_pci_op *active_op = &pdev->sh_info->op;
367*5b56413dSWarner Losh 	evtchn_port_t port = pdev->evtchn;
368*5b56413dSWarner Losh 	time_t timeout;
369*5b56413dSWarner Losh 
370*5b56413dSWarner Losh 	mtx_lock(&pdev->sh_info_lock);
371*5b56413dSWarner Losh 
372*5b56413dSWarner Losh 	memcpy(active_op, op, sizeof(struct xen_pci_op));
373*5b56413dSWarner Losh 
374*5b56413dSWarner Losh 	/* Go */
375*5b56413dSWarner Losh 	wmb();
376*5b56413dSWarner Losh 	set_bit(_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags);
377*5b56413dSWarner Losh 	notify_remote_via_evtchn(port);
378*5b56413dSWarner Losh 
379*5b56413dSWarner Losh 	timeout = time_uptime + 2;
380*5b56413dSWarner Losh 
381*5b56413dSWarner Losh 	clear_evtchn(port);
382*5b56413dSWarner Losh 
383*5b56413dSWarner Losh 	/* Spin while waiting for the answer */
384*5b56413dSWarner Losh 	while (test_bit
385*5b56413dSWarner Losh 	       (_XEN_PCIF_active, (unsigned long *)&pdev->sh_info->flags)) {
386*5b56413dSWarner Losh 		int err = HYPERVISOR_poll(&port, 1, 3 * hz);
387*5b56413dSWarner Losh 		if (err)
388*5b56413dSWarner Losh 			panic("Failed HYPERVISOR_poll: err=%d", err);
389*5b56413dSWarner Losh 		clear_evtchn(port);
390*5b56413dSWarner Losh 		if (time_uptime > timeout) {
391*5b56413dSWarner Losh 			WPRINTF("pciback not responding!!!\n");
392*5b56413dSWarner Losh 			clear_bit(_XEN_PCIF_active,
393*5b56413dSWarner Losh 				  (unsigned long *)&pdev->sh_info->flags);
394*5b56413dSWarner Losh 			err = XEN_PCI_ERR_dev_not_found;
395*5b56413dSWarner Losh 			goto out;
396*5b56413dSWarner Losh 		}
397*5b56413dSWarner Losh 	}
398*5b56413dSWarner Losh 
399*5b56413dSWarner Losh 	memcpy(op, active_op, sizeof(struct xen_pci_op));
400*5b56413dSWarner Losh 
401*5b56413dSWarner Losh 	err = op->err;
402*5b56413dSWarner Losh  out:
403*5b56413dSWarner Losh 	mtx_unlock(&pdev->sh_info_lock);
404*5b56413dSWarner Losh 	return err;
405*5b56413dSWarner Losh }
406*5b56413dSWarner Losh 
407*5b56413dSWarner Losh /* ** XenBus Driver registration ** */
408*5b56413dSWarner Losh 
409*5b56413dSWarner Losh static struct xenbus_device_id pcifront_ids[] = {
410*5b56413dSWarner Losh 	{ "pci" },
411*5b56413dSWarner Losh 	{ "" }
412*5b56413dSWarner Losh };
413*5b56413dSWarner Losh 
414*5b56413dSWarner Losh static struct xenbus_driver pcifront = {
415*5b56413dSWarner Losh 	.name = "pcifront",
416*5b56413dSWarner Losh 	.ids = pcifront_ids,
417*5b56413dSWarner Losh 	.probe = pcifront_probe,
418*5b56413dSWarner Losh 	.remove = pcifront_remove,
419*5b56413dSWarner Losh 	.otherend_changed = pcifront_backend_changed,
420*5b56413dSWarner Losh };
421*5b56413dSWarner Losh 
422*5b56413dSWarner Losh /* Register the driver with xenbus during sys init */
423*5b56413dSWarner Losh static void
pcifront_init(void * unused)424*5b56413dSWarner Losh pcifront_init(void *unused)
425*5b56413dSWarner Losh {
426*5b56413dSWarner Losh 	if ((xen_start_info->flags & SIF_INITDOMAIN))
427*5b56413dSWarner Losh 		return;
428*5b56413dSWarner Losh 
429*5b56413dSWarner Losh 	DPRINTF("xenbus registering\n");
430*5b56413dSWarner Losh 
431*5b56413dSWarner Losh 	xenbus_register_frontend(&pcifront);
432*5b56413dSWarner Losh }
433*5b56413dSWarner Losh 
SYSINIT(pciif,SI_SUB_PSEUDO,SI_ORDER_ANY,pcifront_init,NULL)434*5b56413dSWarner Losh SYSINIT(pciif, SI_SUB_PSEUDO, SI_ORDER_ANY, pcifront_init, NULL)
435*5b56413dSWarner Losh 
436*5b56413dSWarner Losh /* Newbus xpcife device driver probe */
437*5b56413dSWarner Losh static int
438*5b56413dSWarner Losh xpcife_probe(device_t dev)
439*5b56413dSWarner Losh {
440*5b56413dSWarner Losh #ifdef XEN_PCIDEV_FE_DEBUG
441*5b56413dSWarner Losh 	struct pcifront_device *pdev = (struct pcifront_device *)device_get_ivars(dev);
442*5b56413dSWarner Losh 	DPRINTF("xpcife probe (unit=%d)\n", pdev->unit);
443*5b56413dSWarner Losh #endif
444*5b56413dSWarner Losh 	return (BUS_PROBE_NOWILDCARD);
445*5b56413dSWarner Losh }
446*5b56413dSWarner Losh 
447*5b56413dSWarner Losh /* Newbus xpcife device driver attach */
448*5b56413dSWarner Losh static int
xpcife_attach(device_t dev)449*5b56413dSWarner Losh xpcife_attach(device_t dev)
450*5b56413dSWarner Losh {
451*5b56413dSWarner Losh 	struct pcifront_device *pdev = (struct pcifront_device *)device_get_ivars(dev);
452*5b56413dSWarner Losh 	int i, num_roots, len, err;
453*5b56413dSWarner Losh 	char str[64];
454*5b56413dSWarner Losh 	unsigned int domain, bus;
455*5b56413dSWarner Losh 
456*5b56413dSWarner Losh 	DPRINTF("xpcife attach (unit=%d)\n", pdev->unit);
457*5b56413dSWarner Losh 
458*5b56413dSWarner Losh 	err = xenbus_scanf(NULL, pdev->xdev->otherend,
459*5b56413dSWarner Losh 					   "root_num", "%d", &num_roots);
460*5b56413dSWarner Losh 	if (err != 1) {
461*5b56413dSWarner Losh 		if (err == 0)
462*5b56413dSWarner Losh 			err = -EINVAL;
463*5b56413dSWarner Losh 		xenbus_dev_fatal(pdev->xdev, err,
464*5b56413dSWarner Losh 						 "Error reading number of PCI roots");
465*5b56413dSWarner Losh 		goto out;
466*5b56413dSWarner Losh 	}
467*5b56413dSWarner Losh 
468*5b56413dSWarner Losh 	/* Add a pcib device for each root */
469*5b56413dSWarner Losh 	for (i = 0; i < num_roots; i++) {
470*5b56413dSWarner Losh 		device_t child;
471*5b56413dSWarner Losh 
472*5b56413dSWarner Losh 		len = snprintf(str, sizeof(str), "root-%d", i);
473*5b56413dSWarner Losh 		if (unlikely(len >= (sizeof(str) - 1))) {
474*5b56413dSWarner Losh 			err = -ENOMEM;
475*5b56413dSWarner Losh 			goto out;
476*5b56413dSWarner Losh 		}
477*5b56413dSWarner Losh 
478*5b56413dSWarner Losh 		err = xenbus_scanf(NULL, pdev->xdev->otherend, str,
479*5b56413dSWarner Losh 						   "%x:%x", &domain, &bus);
480*5b56413dSWarner Losh 		if (err != 2) {
481*5b56413dSWarner Losh 			if (err >= 0)
482*5b56413dSWarner Losh 				err = -EINVAL;
483*5b56413dSWarner Losh 			xenbus_dev_fatal(pdev->xdev, err,
484*5b56413dSWarner Losh 							 "Error reading PCI root %d", i);
485*5b56413dSWarner Losh 			goto out;
486*5b56413dSWarner Losh 		}
487*5b56413dSWarner Losh 		err = 0;
488*5b56413dSWarner Losh 		if (domain != pdev->xdev->otherend_id) {
489*5b56413dSWarner Losh 			err = -EINVAL;
490*5b56413dSWarner Losh 			xenbus_dev_fatal(pdev->xdev, err,
491*5b56413dSWarner Losh 							 "Domain mismatch %d != %d", domain, pdev->xdev->otherend_id);
492*5b56413dSWarner Losh 			goto out;
493*5b56413dSWarner Losh 		}
494*5b56413dSWarner Losh 
495*5b56413dSWarner Losh 		child = device_add_child(dev, "pcib", bus);
496*5b56413dSWarner Losh 		if (!child) {
497*5b56413dSWarner Losh 			err = -ENOMEM;
498*5b56413dSWarner Losh 			xenbus_dev_fatal(pdev->xdev, err,
499*5b56413dSWarner Losh 							 "Unable to create pcib%d", bus);
500*5b56413dSWarner Losh 			goto out;
501*5b56413dSWarner Losh 		}
502*5b56413dSWarner Losh 	}
503*5b56413dSWarner Losh 
504*5b56413dSWarner Losh  out:
505*5b56413dSWarner Losh 	return bus_generic_attach(dev);
506*5b56413dSWarner Losh }
507*5b56413dSWarner Losh 
508*5b56413dSWarner Losh static devclass_t xpcife_devclass;
509*5b56413dSWarner Losh 
510*5b56413dSWarner Losh static device_method_t xpcife_methods[] = {
511*5b56413dSWarner Losh 	/* Device interface */
512*5b56413dSWarner Losh 	DEVMETHOD(device_probe, xpcife_probe),
513*5b56413dSWarner Losh 	DEVMETHOD(device_attach, xpcife_attach),
514*5b56413dSWarner Losh 	DEVMETHOD(device_detach,	bus_generic_detach),
515*5b56413dSWarner Losh 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
516*5b56413dSWarner Losh 	DEVMETHOD(device_suspend,	bus_generic_suspend),
517*5b56413dSWarner Losh 	DEVMETHOD(device_resume,	bus_generic_resume),
518*5b56413dSWarner Losh     /* Bus interface */
519*5b56413dSWarner Losh     DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
520*5b56413dSWarner Losh     DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
521*5b56413dSWarner Losh     DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
522*5b56413dSWarner Losh     DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
523*5b56413dSWarner Losh     DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
524*5b56413dSWarner Losh     DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
525*5b56413dSWarner Losh 
526*5b56413dSWarner Losh 	DEVMETHOD_END
527*5b56413dSWarner Losh };
528*5b56413dSWarner Losh 
529*5b56413dSWarner Losh static driver_t xpcife_driver = {
530*5b56413dSWarner Losh 	"xpcife",
531*5b56413dSWarner Losh 	xpcife_methods,
532*5b56413dSWarner Losh 	0,
533*5b56413dSWarner Losh };
534*5b56413dSWarner Losh 
535*5b56413dSWarner Losh DRIVER_MODULE(xpcife, nexus, xpcife_driver, xpcife_devclass, 0, 0);
536*5b56413dSWarner Losh 
537*5b56413dSWarner Losh /* Newbus xen pcib device driver probe */
538*5b56413dSWarner Losh static int
xpcib_probe(device_t dev)539*5b56413dSWarner Losh xpcib_probe(device_t dev)
540*5b56413dSWarner Losh {
541*5b56413dSWarner Losh 	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
542*5b56413dSWarner Losh 	struct pcifront_device *pdev = (struct pcifront_device *)device_get_ivars(device_get_parent(dev));
543*5b56413dSWarner Losh 
544*5b56413dSWarner Losh 	DPRINTF("xpcib probe (bus=%d)\n", device_get_unit(dev));
545*5b56413dSWarner Losh 
546*5b56413dSWarner Losh 	sc->domain = pdev->xdev->otherend_id;
547*5b56413dSWarner Losh 	sc->bus = device_get_unit(dev);
548*5b56413dSWarner Losh 	sc->pdev = pdev;
549*5b56413dSWarner Losh 
550*5b56413dSWarner Losh 	return 0;
551*5b56413dSWarner Losh }
552*5b56413dSWarner Losh 
553*5b56413dSWarner Losh /* Newbus xen pcib device driver attach */
554*5b56413dSWarner Losh static int
xpcib_attach(device_t dev)555*5b56413dSWarner Losh xpcib_attach(device_t dev)
556*5b56413dSWarner Losh {
557*5b56413dSWarner Losh 	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
558*5b56413dSWarner Losh 
559*5b56413dSWarner Losh 	DPRINTF("xpcib attach (bus=%d)\n", sc->bus);
560*5b56413dSWarner Losh 
561*5b56413dSWarner Losh 	device_add_child(dev, "pci", DEVICE_UNIT_ANY);
562*5b56413dSWarner Losh 	return bus_generic_attach(dev);
563*5b56413dSWarner Losh }
564*5b56413dSWarner Losh 
565*5b56413dSWarner Losh static int
xpcib_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)566*5b56413dSWarner Losh xpcib_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
567*5b56413dSWarner Losh {
568*5b56413dSWarner Losh 	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
569*5b56413dSWarner Losh 	switch (which) {
570*5b56413dSWarner Losh 	case  PCIB_IVAR_BUS:
571*5b56413dSWarner Losh 		*result = sc->bus;
572*5b56413dSWarner Losh 		return 0;
573*5b56413dSWarner Losh 	}
574*5b56413dSWarner Losh 	return ENOENT;
575*5b56413dSWarner Losh }
576*5b56413dSWarner Losh 
577*5b56413dSWarner Losh /* Return the number of slots supported */
578*5b56413dSWarner Losh static int
xpcib_maxslots(device_t dev)579*5b56413dSWarner Losh xpcib_maxslots(device_t dev)
580*5b56413dSWarner Losh {
581*5b56413dSWarner Losh 	return 31;
582*5b56413dSWarner Losh }
583*5b56413dSWarner Losh 
584*5b56413dSWarner Losh #define PCI_DEVFN(slot,func)	((((slot) & 0x1f) << 3) | ((func) & 0x07))
585*5b56413dSWarner Losh 
586*5b56413dSWarner Losh /* Read configuration space register */
587*5b56413dSWarner Losh static u_int32_t
xpcib_read_config(device_t dev,int bus,int slot,int func,int reg,int bytes)588*5b56413dSWarner Losh xpcib_read_config(device_t dev, int bus, int slot, int func,
589*5b56413dSWarner Losh 				  int reg, int bytes)
590*5b56413dSWarner Losh {
591*5b56413dSWarner Losh 	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
592*5b56413dSWarner Losh 	struct xen_pci_op op = {
593*5b56413dSWarner Losh 		.cmd    = XEN_PCI_OP_conf_read,
594*5b56413dSWarner Losh 		.domain = sc->domain,
595*5b56413dSWarner Losh 		.bus    = sc->bus,
596*5b56413dSWarner Losh 		.devfn  = PCI_DEVFN(slot, func),
597*5b56413dSWarner Losh 		.offset = reg,
598*5b56413dSWarner Losh 		.size   = bytes,
599*5b56413dSWarner Losh 	};
600*5b56413dSWarner Losh 	int err;
601*5b56413dSWarner Losh 
602*5b56413dSWarner Losh 	err = do_pci_op(sc->pdev, &op);
603*5b56413dSWarner Losh 
604*5b56413dSWarner Losh 	DPRINTF("read config (b=%d, s=%d, f=%d, reg=%d, len=%d, val=%x, err=%d)\n",
605*5b56413dSWarner Losh 			bus, slot, func, reg, bytes, op.value, err);
606*5b56413dSWarner Losh 
607*5b56413dSWarner Losh 	if (err)
608*5b56413dSWarner Losh 		op.value = ~0;
609*5b56413dSWarner Losh 
610*5b56413dSWarner Losh 	return op.value;
611*5b56413dSWarner Losh }
612*5b56413dSWarner Losh 
613*5b56413dSWarner Losh /* Write configuration space register */
614*5b56413dSWarner Losh static void
xpcib_write_config(device_t dev,int bus,int slot,int func,int reg,u_int32_t data,int bytes)615*5b56413dSWarner Losh xpcib_write_config(device_t dev, int bus, int slot, int func,
616*5b56413dSWarner Losh 				   int reg, u_int32_t data, int bytes)
617*5b56413dSWarner Losh {
618*5b56413dSWarner Losh 	struct xpcib_softc *sc = (struct xpcib_softc *)device_get_softc(dev);
619*5b56413dSWarner Losh 	struct xen_pci_op op = {
620*5b56413dSWarner Losh 		.cmd    = XEN_PCI_OP_conf_write,
621*5b56413dSWarner Losh 		.domain = sc->domain,
622*5b56413dSWarner Losh 		.bus    = sc->bus,
623*5b56413dSWarner Losh 		.devfn  = PCI_DEVFN(slot, func),
624*5b56413dSWarner Losh 		.offset = reg,
625*5b56413dSWarner Losh 		.size   = bytes,
626*5b56413dSWarner Losh 		.value  = data,
627*5b56413dSWarner Losh 	};
628*5b56413dSWarner Losh 	int err;
629*5b56413dSWarner Losh 
630*5b56413dSWarner Losh 	err = do_pci_op(sc->pdev, &op);
631*5b56413dSWarner Losh 
632*5b56413dSWarner Losh 	DPRINTF("write config (b=%d, s=%d, f=%d, reg=%d, len=%d, val=%x, err=%d)\n",
633*5b56413dSWarner Losh 			bus, slot, func, reg, bytes, data, err);
634*5b56413dSWarner Losh }
635*5b56413dSWarner Losh 
636*5b56413dSWarner Losh static int
xpcib_route_interrupt(device_t pcib,device_t dev,int pin)637*5b56413dSWarner Losh xpcib_route_interrupt(device_t pcib, device_t dev, int pin)
638*5b56413dSWarner Losh {
639*5b56413dSWarner Losh 	struct pci_devinfo *dinfo = device_get_ivars(dev);
640*5b56413dSWarner Losh 	pcicfgregs *cfg = &dinfo->cfg;
641*5b56413dSWarner Losh 
642*5b56413dSWarner Losh 	DPRINTF("route intr (pin=%d, line=%d)\n", pin, cfg->intline);
643*5b56413dSWarner Losh 
644*5b56413dSWarner Losh 	return cfg->intline;
645*5b56413dSWarner Losh }
646*5b56413dSWarner Losh 
647*5b56413dSWarner Losh static device_method_t xpcib_methods[] = {
648*5b56413dSWarner Losh     /* Device interface */
649*5b56413dSWarner Losh     DEVMETHOD(device_probe,		xpcib_probe),
650*5b56413dSWarner Losh     DEVMETHOD(device_attach,		xpcib_attach),
651*5b56413dSWarner Losh     DEVMETHOD(device_detach,		bus_generic_detach),
652*5b56413dSWarner Losh     DEVMETHOD(device_shutdown,		bus_generic_shutdown),
653*5b56413dSWarner Losh     DEVMETHOD(device_suspend,		bus_generic_suspend),
654*5b56413dSWarner Losh     DEVMETHOD(device_resume,		bus_generic_resume),
655*5b56413dSWarner Losh 
656*5b56413dSWarner Losh     /* Bus interface */
657*5b56413dSWarner Losh     DEVMETHOD(bus_read_ivar,		xpcib_read_ivar),
658*5b56413dSWarner Losh     DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
659*5b56413dSWarner Losh     DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
660*5b56413dSWarner Losh     DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
661*5b56413dSWarner Losh     DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
662*5b56413dSWarner Losh     DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
663*5b56413dSWarner Losh     DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
664*5b56413dSWarner Losh 
665*5b56413dSWarner Losh     /* pcib interface */
666*5b56413dSWarner Losh     DEVMETHOD(pcib_maxslots,		xpcib_maxslots),
667*5b56413dSWarner Losh     DEVMETHOD(pcib_read_config,		xpcib_read_config),
668*5b56413dSWarner Losh     DEVMETHOD(pcib_write_config,	xpcib_write_config),
669*5b56413dSWarner Losh     DEVMETHOD(pcib_route_interrupt,	xpcib_route_interrupt),
670*5b56413dSWarner Losh 	DEVMETHOD(pcib_request_feature,	pcib_request_feature_allow),
671*5b56413dSWarner Losh 
672*5b56413dSWarner Losh     DEVMETHOD_END
673*5b56413dSWarner Losh };
674*5b56413dSWarner Losh 
675*5b56413dSWarner Losh static devclass_t xpcib_devclass;
676*5b56413dSWarner Losh 
677*5b56413dSWarner Losh DEFINE_CLASS_0(pcib, xpcib_driver, xpcib_methods, sizeof(struct xpcib_softc));
678*5b56413dSWarner Losh DRIVER_MODULE(pcib, xpcife, xpcib_driver, xpcib_devclass, 0, 0);
679*5b56413dSWarner Losh 
680*5b56413dSWarner Losh /*
681*5b56413dSWarner Losh  * Local variables:
682*5b56413dSWarner Losh  * mode: C
683*5b56413dSWarner Losh  * c-set-style: "BSD"
684*5b56413dSWarner Losh  * c-basic-offset: 4
685*5b56413dSWarner Losh  * tab-width: 4
686*5b56413dSWarner Losh  * indent-tabs-mode: t
687*5b56413dSWarner Losh  * End:
688*5b56413dSWarner Losh  */
689