xref: /titanic_50/usr/src/uts/common/xen/io/xenbus_dev.c (revision 193974072f41a843678abf5f61979c748687e66b)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
23b26a64aeSjohnlev  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev /*
28843e1988Sjohnlev  * xenbus_dev.c
29843e1988Sjohnlev  *
30843e1988Sjohnlev  * Driver giving user-space access to the kernel's xenbus connection
31843e1988Sjohnlev  * to xenstore.
32843e1988Sjohnlev  *
33843e1988Sjohnlev  * Copyright (c) 2005, Christian Limpach
34843e1988Sjohnlev  * Copyright (c) 2005, Rusty Russell, IBM Corporation
35843e1988Sjohnlev  *
36843e1988Sjohnlev  * This file may be distributed separately from the Linux kernel, or
37843e1988Sjohnlev  * incorporated into other software packages, subject to the following license:
38843e1988Sjohnlev  *
39843e1988Sjohnlev  * Permission is hereby granted, free of charge, to any person obtaining a copy
40843e1988Sjohnlev  * of this source file (the "Software"), to deal in the Software without
41843e1988Sjohnlev  * restriction, including without limitation the rights to use, copy, modify,
42843e1988Sjohnlev  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
43843e1988Sjohnlev  * and to permit persons to whom the Software is furnished to do so, subject to
44843e1988Sjohnlev  * the following conditions:
45843e1988Sjohnlev  *
46843e1988Sjohnlev  * The above copyright notice and this permission notice shall be included in
47843e1988Sjohnlev  * all copies or substantial portions of the Software.
48843e1988Sjohnlev  *
49843e1988Sjohnlev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
50843e1988Sjohnlev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
51843e1988Sjohnlev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
52843e1988Sjohnlev  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
53843e1988Sjohnlev  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
54843e1988Sjohnlev  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
55843e1988Sjohnlev  * IN THE SOFTWARE.
56843e1988Sjohnlev  */
57843e1988Sjohnlev 
58843e1988Sjohnlev 
59843e1988Sjohnlev #include <sys/types.h>
60843e1988Sjohnlev #include <sys/sysmacros.h>
61843e1988Sjohnlev #include <sys/conf.h>
62843e1988Sjohnlev #include <sys/stat.h>
63843e1988Sjohnlev #include <sys/modctl.h>
64843e1988Sjohnlev #include <sys/uio.h>
65843e1988Sjohnlev #include <sys/list.h>
66843e1988Sjohnlev #include <sys/file.h>
67843e1988Sjohnlev #include <sys/errno.h>
68843e1988Sjohnlev #include <sys/open.h>
69843e1988Sjohnlev #include <sys/cred.h>
70843e1988Sjohnlev #include <sys/condvar.h>
71843e1988Sjohnlev #include <sys/ddi.h>
72843e1988Sjohnlev #include <sys/sunddi.h>
73b26a64aeSjohnlev #include <sys/policy.h>
74b26a64aeSjohnlev 
75551bc2a6Smrj #ifdef XPV_HVM_DRIVER
76551bc2a6Smrj #include <public/io/xenbus.h>
77551bc2a6Smrj #include <public/io/xs_wire.h>
78551bc2a6Smrj #include <sys/xpv_support.h>
79551bc2a6Smrj #endif
80843e1988Sjohnlev #include <sys/hypervisor.h>
81551bc2a6Smrj #include <xen/sys/xenbus.h>
82843e1988Sjohnlev #include <xen/sys/xenbus_comms.h>
83843e1988Sjohnlev #include <xen/sys/xenbus_impl.h>
84843e1988Sjohnlev #include <xen/public/io/xs_wire.h>
85843e1988Sjohnlev 
86843e1988Sjohnlev #ifdef DEBUG
87843e1988Sjohnlev #define	XENBUSDRV_DBPRINT(fmt) { if (xenbusdrv_debug) cmn_err fmt; }
88843e1988Sjohnlev #else
89843e1988Sjohnlev #define	XENBUSDRV_DBPRINT(fmt)
90843e1988Sjohnlev #endif /* ifdef DEBUG */
91843e1988Sjohnlev 
92843e1988Sjohnlev /* Some handy macros */
93843e1988Sjohnlev #define	XENBUSDRV_MASK_READ_IDX(idx)	((idx) & (PAGESIZE - 1))
94843e1988Sjohnlev #define	XENBUSDRV_MINOR2INST(minor)	((int)(minor))
95843e1988Sjohnlev #define	XENBUSDRV_NCLONES 		256
96843e1988Sjohnlev #define	XENBUSDRV_INST2SOFTS(instance)	\
97843e1988Sjohnlev 	((xenbus_dev_t *)ddi_get_soft_state(xenbusdrv_statep, (instance)))
98843e1988Sjohnlev 
99843e1988Sjohnlev static int xenbusdrv_debug = 0;
100843e1988Sjohnlev static int xenbusdrv_clone_tab[XENBUSDRV_NCLONES];
101843e1988Sjohnlev static dev_info_t *xenbusdrv_dip;
102843e1988Sjohnlev static kmutex_t xenbusdrv_clone_tab_mutex;
103843e1988Sjohnlev 
104843e1988Sjohnlev struct xenbus_dev_transaction {
105843e1988Sjohnlev 	list_t list;
106843e1988Sjohnlev 	xenbus_transaction_t handle;
107843e1988Sjohnlev };
108843e1988Sjohnlev 
109843e1988Sjohnlev /* Soft state data structure for xenbus driver */
110843e1988Sjohnlev struct xenbus_dev_data {
111843e1988Sjohnlev 	dev_info_t *dip;
112843e1988Sjohnlev 
113843e1988Sjohnlev 	/* In-progress transaction. */
114843e1988Sjohnlev 	list_t transactions;
115843e1988Sjohnlev 
116843e1988Sjohnlev 	/* Partial request. */
117843e1988Sjohnlev 	unsigned int len;
118843e1988Sjohnlev 	union {
119843e1988Sjohnlev 		struct xsd_sockmsg msg;
120843e1988Sjohnlev 		char buffer[MMU_PAGESIZE];
121843e1988Sjohnlev 	} u;
122843e1988Sjohnlev 
123843e1988Sjohnlev 	/* Response queue. */
124843e1988Sjohnlev 	char read_buffer[MMU_PAGESIZE];
125843e1988Sjohnlev 	unsigned int read_cons, read_prod;
126843e1988Sjohnlev 	kcondvar_t read_cv;
127843e1988Sjohnlev 	kmutex_t read_mutex;
128843e1988Sjohnlev 	int xenstore_inst;
129843e1988Sjohnlev };
130843e1988Sjohnlev typedef struct xenbus_dev_data xenbus_dev_t;
131843e1988Sjohnlev static void *xenbusdrv_statep;
132843e1988Sjohnlev 
133843e1988Sjohnlev static int xenbusdrv_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
134843e1988Sjohnlev static int xenbusdrv_attach(dev_info_t *, ddi_attach_cmd_t);
135843e1988Sjohnlev static int xenbusdrv_detach(dev_info_t *, ddi_detach_cmd_t);
136843e1988Sjohnlev static int xenbusdrv_open(dev_t *, int, int, cred_t *);
137843e1988Sjohnlev static int xenbusdrv_close(dev_t, int, int, cred_t *);
138843e1988Sjohnlev static int xenbusdrv_read(dev_t, struct uio *, cred_t *);
139843e1988Sjohnlev static int xenbusdrv_write(dev_t, struct uio *, cred_t *);
140843e1988Sjohnlev static int xenbusdrv_devmap(dev_t, devmap_cookie_t, offset_t, size_t, size_t *,
141843e1988Sjohnlev     uint_t);
142b26a64aeSjohnlev static int xenbusdrv_segmap(dev_t, off_t, ddi_as_handle_t, caddr_t *, off_t,
143b26a64aeSjohnlev     uint_t, uint_t, uint_t, cred_t *);
144843e1988Sjohnlev static int xenbusdrv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
145843e1988Sjohnlev static int xenbusdrv_queue_reply(xenbus_dev_t *, const struct xsd_sockmsg *,
146843e1988Sjohnlev     const char *);
147843e1988Sjohnlev 
148843e1988Sjohnlev /* Solaris driver framework */
149843e1988Sjohnlev 
150843e1988Sjohnlev static 	struct cb_ops xenbusdrv_cb_ops = {
151843e1988Sjohnlev 	xenbusdrv_open,			/* cb_open */
152843e1988Sjohnlev 	xenbusdrv_close,		/* cb_close */
153843e1988Sjohnlev 	nodev,				/* cb_strategy */
154843e1988Sjohnlev 	nodev,				/* cb_print */
155843e1988Sjohnlev 	nodev,				/* cb_dump */
156843e1988Sjohnlev 	xenbusdrv_read,			/* cb_read */
157843e1988Sjohnlev 	xenbusdrv_write,		/* cb_write */
158843e1988Sjohnlev 	xenbusdrv_ioctl,		/* cb_ioctl */
159843e1988Sjohnlev 	xenbusdrv_devmap,		/* cb_devmap */
160843e1988Sjohnlev 	NULL,				/* cb_mmap */
161b26a64aeSjohnlev 	xenbusdrv_segmap,		/* cb_segmap */
162843e1988Sjohnlev 	nochpoll,			/* cb_chpoll */
163843e1988Sjohnlev 	ddi_prop_op,			/* cb_prop_op */
164843e1988Sjohnlev 	0,				/* cb_stream */
165843e1988Sjohnlev 	D_DEVMAP | D_NEW | D_MP,	/* cb_flag */
166843e1988Sjohnlev 	CB_REV
167843e1988Sjohnlev };
168843e1988Sjohnlev 
169843e1988Sjohnlev static struct dev_ops xenbusdrv_dev_ops = {
170843e1988Sjohnlev 	DEVO_REV,		/* devo_rev */
171843e1988Sjohnlev 	0,			/* devo_refcnt */
172843e1988Sjohnlev 	xenbusdrv_info,		/* devo_getinfo */
173843e1988Sjohnlev 	nulldev,		/* devo_identify */
174843e1988Sjohnlev 	nulldev,		/* devo_probe */
175843e1988Sjohnlev 	xenbusdrv_attach,	/* devo_attach */
176843e1988Sjohnlev 	xenbusdrv_detach,	/* devo_detach */
177843e1988Sjohnlev 	nodev,			/* devo_reset */
178843e1988Sjohnlev 	&xenbusdrv_cb_ops,	/* devo_cb_ops */
179843e1988Sjohnlev 	NULL,			/* devo_bus_ops */
180*19397407SSherry Moore 	NULL,			/* devo_power */
181*19397407SSherry Moore 	ddi_quiesce_not_needed,		/* devo_quiesce */
182843e1988Sjohnlev };
183843e1988Sjohnlev 
184843e1988Sjohnlev static struct modldrv modldrv = {
185843e1988Sjohnlev 	&mod_driverops,		/* Type of module.  This one is a driver */
186*19397407SSherry Moore 	"virtual bus driver",	/* Name of the module. */
187843e1988Sjohnlev 	&xenbusdrv_dev_ops	/* driver ops */
188843e1988Sjohnlev };
189843e1988Sjohnlev 
190843e1988Sjohnlev static struct modlinkage modlinkage = {
191843e1988Sjohnlev 	MODREV_1,
192843e1988Sjohnlev 	&modldrv,
193843e1988Sjohnlev 	NULL
194843e1988Sjohnlev };
195843e1988Sjohnlev 
196843e1988Sjohnlev int
_init(void)197843e1988Sjohnlev _init(void)
198843e1988Sjohnlev {
199843e1988Sjohnlev 	int e;
200843e1988Sjohnlev 
201843e1988Sjohnlev 	e = ddi_soft_state_init(&xenbusdrv_statep, sizeof (xenbus_dev_t), 1);
202843e1988Sjohnlev 	if (e)
203843e1988Sjohnlev 		return (e);
204843e1988Sjohnlev 
205843e1988Sjohnlev 	e = mod_install(&modlinkage);
206843e1988Sjohnlev 	if (e)
207843e1988Sjohnlev 		ddi_soft_state_fini(&xenbusdrv_statep);
208843e1988Sjohnlev 
209843e1988Sjohnlev 	return (e);
210843e1988Sjohnlev }
211843e1988Sjohnlev 
212843e1988Sjohnlev int
_fini(void)213843e1988Sjohnlev _fini(void)
214843e1988Sjohnlev {
215843e1988Sjohnlev 	int e;
216843e1988Sjohnlev 
217843e1988Sjohnlev 	e = mod_remove(&modlinkage);
218843e1988Sjohnlev 	if (e)
219843e1988Sjohnlev 		return (e);
220843e1988Sjohnlev 
221843e1988Sjohnlev 	ddi_soft_state_fini(&xenbusdrv_statep);
222843e1988Sjohnlev 
223843e1988Sjohnlev 	return (0);
224843e1988Sjohnlev }
225843e1988Sjohnlev 
226843e1988Sjohnlev int
_info(struct modinfo * modinfop)227843e1988Sjohnlev _info(struct modinfo *modinfop)
228843e1988Sjohnlev {
229843e1988Sjohnlev 	return (mod_info(&modlinkage, modinfop));
230843e1988Sjohnlev }
231843e1988Sjohnlev 
232843e1988Sjohnlev /* ARGSUSED */
233843e1988Sjohnlev static int
xenbusdrv_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)234843e1988Sjohnlev xenbusdrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
235843e1988Sjohnlev {
236843e1988Sjohnlev 	dev_t	dev = (dev_t)arg;
237843e1988Sjohnlev 	minor_t	minor = getminor(dev);
238843e1988Sjohnlev 	int	retval;
239843e1988Sjohnlev 
240843e1988Sjohnlev 	switch (cmd) {
241843e1988Sjohnlev 	case DDI_INFO_DEVT2DEVINFO:
242843e1988Sjohnlev 		if (minor != 0 || xenbusdrv_dip == NULL) {
243843e1988Sjohnlev 			*result = (void *)NULL;
244843e1988Sjohnlev 			retval = DDI_FAILURE;
245843e1988Sjohnlev 		} else {
246843e1988Sjohnlev 			*result = (void *)xenbusdrv_dip;
247843e1988Sjohnlev 			retval = DDI_SUCCESS;
248843e1988Sjohnlev 		}
249843e1988Sjohnlev 		break;
250843e1988Sjohnlev 	case DDI_INFO_DEVT2INSTANCE:
251843e1988Sjohnlev 		*result = (void *)0;
252843e1988Sjohnlev 		retval = DDI_SUCCESS;
253843e1988Sjohnlev 		break;
254843e1988Sjohnlev 	default:
255843e1988Sjohnlev 		retval = DDI_FAILURE;
256843e1988Sjohnlev 	}
257843e1988Sjohnlev 	return (retval);
258843e1988Sjohnlev }
259843e1988Sjohnlev 
260843e1988Sjohnlev static int
xenbusdrv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)261843e1988Sjohnlev xenbusdrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
262843e1988Sjohnlev {
263843e1988Sjohnlev 	int	error;
264843e1988Sjohnlev 	int	unit = ddi_get_instance(dip);
265843e1988Sjohnlev 
266843e1988Sjohnlev 
267843e1988Sjohnlev 	switch (cmd) {
268843e1988Sjohnlev 	case DDI_ATTACH:
269843e1988Sjohnlev 		break;
270843e1988Sjohnlev 	case DDI_RESUME:
271843e1988Sjohnlev 		return (DDI_SUCCESS);
272843e1988Sjohnlev 	default:
273843e1988Sjohnlev 		cmn_err(CE_WARN, "xenbus_attach: unknown cmd 0x%x\n", cmd);
274843e1988Sjohnlev 		return (DDI_FAILURE);
275843e1988Sjohnlev 	}
276843e1988Sjohnlev 
277843e1988Sjohnlev 	/* DDI_ATTACH */
278843e1988Sjohnlev 
279843e1988Sjohnlev 	/*
280843e1988Sjohnlev 	 * only one instance - but we clone using the open routine
281843e1988Sjohnlev 	 */
282843e1988Sjohnlev 	if (ddi_get_instance(dip) > 0)
283843e1988Sjohnlev 		return (DDI_FAILURE);
284843e1988Sjohnlev 
285843e1988Sjohnlev 	mutex_init(&xenbusdrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
286843e1988Sjohnlev 	    NULL);
287843e1988Sjohnlev 
288843e1988Sjohnlev 	error = ddi_create_minor_node(dip, "xenbus", S_IFCHR, unit,
289843e1988Sjohnlev 	    DDI_PSEUDO, NULL);
290843e1988Sjohnlev 	if (error != DDI_SUCCESS)
291843e1988Sjohnlev 		goto fail;
292843e1988Sjohnlev 
293843e1988Sjohnlev 	/*
294843e1988Sjohnlev 	 * save dip for getinfo
295843e1988Sjohnlev 	 */
296843e1988Sjohnlev 	xenbusdrv_dip = dip;
297843e1988Sjohnlev 	ddi_report_dev(dip);
298843e1988Sjohnlev 
299551bc2a6Smrj #ifndef XPV_HVM_DRIVER
300843e1988Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info))
301843e1988Sjohnlev 		xs_dom0_init();
302551bc2a6Smrj #endif
303843e1988Sjohnlev 
304843e1988Sjohnlev 	return (DDI_SUCCESS);
305843e1988Sjohnlev 
306843e1988Sjohnlev fail:
307843e1988Sjohnlev 	(void) xenbusdrv_detach(dip, DDI_DETACH);
308843e1988Sjohnlev 	return (error);
309843e1988Sjohnlev }
310843e1988Sjohnlev 
311843e1988Sjohnlev static int
xenbusdrv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)312843e1988Sjohnlev xenbusdrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
313843e1988Sjohnlev {
314843e1988Sjohnlev 	/*
315843e1988Sjohnlev 	 * again, only one instance
316843e1988Sjohnlev 	 */
317843e1988Sjohnlev 	if (ddi_get_instance(dip) > 0)
318843e1988Sjohnlev 		return (DDI_FAILURE);
319843e1988Sjohnlev 
320843e1988Sjohnlev 	switch (cmd) {
321843e1988Sjohnlev 	case DDI_DETACH:
322843e1988Sjohnlev 		ddi_remove_minor_node(dip, NULL);
323843e1988Sjohnlev 		mutex_destroy(&xenbusdrv_clone_tab_mutex);
324843e1988Sjohnlev 		xenbusdrv_dip = NULL;
325843e1988Sjohnlev 		return (DDI_SUCCESS);
326843e1988Sjohnlev 	case DDI_SUSPEND:
327843e1988Sjohnlev 		return (DDI_SUCCESS);
328843e1988Sjohnlev 	default:
329843e1988Sjohnlev 		cmn_err(CE_WARN, "xenbus_detach: unknown cmd 0x%x\n", cmd);
330843e1988Sjohnlev 		return (DDI_FAILURE);
331843e1988Sjohnlev 	}
332843e1988Sjohnlev }
333843e1988Sjohnlev 
334843e1988Sjohnlev /* ARGSUSED */
335843e1988Sjohnlev static int
xenbusdrv_open(dev_t * devp,int flag,int otyp,cred_t * cr)336b26a64aeSjohnlev xenbusdrv_open(dev_t *devp, int flag, int otyp, cred_t *cr)
337843e1988Sjohnlev {
338843e1988Sjohnlev 	xenbus_dev_t *xbs;
339843e1988Sjohnlev 	minor_t minor = getminor(*devp);
340843e1988Sjohnlev 
341843e1988Sjohnlev 	if (otyp == OTYP_BLK)
342843e1988Sjohnlev 		return (ENXIO);
343843e1988Sjohnlev 
344843e1988Sjohnlev 	/*
345843e1988Sjohnlev 	 * only allow open on minor = 0 - the clone device
346843e1988Sjohnlev 	 */
347843e1988Sjohnlev 	if (minor != 0)
348843e1988Sjohnlev 		return (ENXIO);
349843e1988Sjohnlev 
350843e1988Sjohnlev 	/*
351843e1988Sjohnlev 	 * find a free slot and grab it
352843e1988Sjohnlev 	 */
353843e1988Sjohnlev 	mutex_enter(&xenbusdrv_clone_tab_mutex);
354843e1988Sjohnlev 	for (minor = 1; minor < XENBUSDRV_NCLONES; minor++) {
355843e1988Sjohnlev 		if (xenbusdrv_clone_tab[minor] == 0) {
356843e1988Sjohnlev 			xenbusdrv_clone_tab[minor] = 1;
357843e1988Sjohnlev 			break;
358843e1988Sjohnlev 		}
359843e1988Sjohnlev 	}
360843e1988Sjohnlev 	mutex_exit(&xenbusdrv_clone_tab_mutex);
361843e1988Sjohnlev 	if (minor == XENBUSDRV_NCLONES)
362843e1988Sjohnlev 		return (EAGAIN);
363843e1988Sjohnlev 
364843e1988Sjohnlev 	/* Allocate softstate structure */
365843e1988Sjohnlev 	if (ddi_soft_state_zalloc(xenbusdrv_statep,
366843e1988Sjohnlev 	    XENBUSDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
367843e1988Sjohnlev 		mutex_enter(&xenbusdrv_clone_tab_mutex);
368843e1988Sjohnlev 		xenbusdrv_clone_tab[minor] = 0;
369843e1988Sjohnlev 		mutex_exit(&xenbusdrv_clone_tab_mutex);
370843e1988Sjohnlev 		return (EAGAIN);
371843e1988Sjohnlev 	}
372843e1988Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
373843e1988Sjohnlev 
374843e1988Sjohnlev 	/* ... and init it */
375843e1988Sjohnlev 	xbs->dip = xenbusdrv_dip;
376843e1988Sjohnlev 	mutex_init(&xbs->read_mutex, NULL, MUTEX_DRIVER, NULL);
377843e1988Sjohnlev 	cv_init(&xbs->read_cv, NULL, CV_DEFAULT, NULL);
378843e1988Sjohnlev 	list_create(&xbs->transactions, sizeof (struct xenbus_dev_transaction),
379843e1988Sjohnlev 	    offsetof(struct xenbus_dev_transaction, list));
380843e1988Sjohnlev 
381843e1988Sjohnlev 	/* clone driver */
382843e1988Sjohnlev 	*devp = makedevice(getmajor(*devp), minor);
383843e1988Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv open succeeded, minor=%d",
384843e1988Sjohnlev 	    minor));
385843e1988Sjohnlev 
386843e1988Sjohnlev 	return (0);
387843e1988Sjohnlev }
388843e1988Sjohnlev 
389843e1988Sjohnlev /* ARGSUSED */
390843e1988Sjohnlev static int
xenbusdrv_close(dev_t dev,int flag,int otyp,struct cred * cr)391b26a64aeSjohnlev xenbusdrv_close(dev_t dev, int flag, int otyp, struct cred *cr)
392843e1988Sjohnlev {
393843e1988Sjohnlev 	xenbus_dev_t *xbs;
394843e1988Sjohnlev 	minor_t minor = getminor(dev);
395843e1988Sjohnlev 	struct xenbus_dev_transaction *trans;
396843e1988Sjohnlev 
397843e1988Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
398843e1988Sjohnlev 	if (xbs == NULL)
399843e1988Sjohnlev 		return (ENXIO);
400843e1988Sjohnlev 
401843e1988Sjohnlev #ifdef notyet
402843e1988Sjohnlev 	/*
403843e1988Sjohnlev 	 * XXPV - would like to be able to notify xenstore down here, but
404843e1988Sjohnlev 	 * as the daemon is currently written, it doesn't leave the device
405843e1988Sjohnlev 	 * open after initial setup, so we have no way of knowing if it has
406843e1988Sjohnlev 	 * gone away.
407843e1988Sjohnlev 	 */
408843e1988Sjohnlev 	if (xbs->xenstore_inst)
409843e1988Sjohnlev 		xs_notify_xenstore_down();
410843e1988Sjohnlev #endif
411843e1988Sjohnlev 	/* free pending transaction */
412843e1988Sjohnlev 	while (trans = (struct xenbus_dev_transaction *)
413843e1988Sjohnlev 	    list_head(&xbs->transactions)) {
414843e1988Sjohnlev 		(void) xenbus_transaction_end(trans->handle, 1);
415843e1988Sjohnlev 		list_remove(&xbs->transactions, (void *)trans);
416843e1988Sjohnlev 		kmem_free(trans, sizeof (*trans));
417843e1988Sjohnlev 	}
418843e1988Sjohnlev 
419843e1988Sjohnlev 	mutex_destroy(&xbs->read_mutex);
420843e1988Sjohnlev 	cv_destroy(&xbs->read_cv);
421843e1988Sjohnlev 	ddi_soft_state_free(xenbusdrv_statep, XENBUSDRV_MINOR2INST(minor));
422843e1988Sjohnlev 
423843e1988Sjohnlev 	/*
424843e1988Sjohnlev 	 * free clone tab slot
425843e1988Sjohnlev 	 */
426843e1988Sjohnlev 	mutex_enter(&xenbusdrv_clone_tab_mutex);
427843e1988Sjohnlev 	xenbusdrv_clone_tab[minor] = 0;
428843e1988Sjohnlev 	mutex_exit(&xenbusdrv_clone_tab_mutex);
429843e1988Sjohnlev 
430843e1988Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv close succeeded, minor=%d",
431843e1988Sjohnlev 	    minor));
432843e1988Sjohnlev 
433843e1988Sjohnlev 	return (0);
434843e1988Sjohnlev }
435843e1988Sjohnlev 
436843e1988Sjohnlev /* ARGSUSED */
437843e1988Sjohnlev static int
xenbusdrv_read(dev_t dev,struct uio * uiop,cred_t * cr)438b26a64aeSjohnlev xenbusdrv_read(dev_t dev, struct uio *uiop, cred_t *cr)
439843e1988Sjohnlev {
440843e1988Sjohnlev 	xenbus_dev_t *xbs;
441843e1988Sjohnlev 	size_t len;
442843e1988Sjohnlev 	int res, ret;
443843e1988Sjohnlev 	int idx;
444843e1988Sjohnlev 
445843e1988Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_read called"));
446843e1988Sjohnlev 
447b26a64aeSjohnlev 	if (secpolicy_xvm_control(cr))
448b26a64aeSjohnlev 		return (EPERM);
449b26a64aeSjohnlev 
450843e1988Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
451843e1988Sjohnlev 
452843e1988Sjohnlev 	mutex_enter(&xbs->read_mutex);
453843e1988Sjohnlev 
454843e1988Sjohnlev 	/* check if we have something to read */
455843e1988Sjohnlev 	while (xbs->read_prod == xbs->read_cons) {
456843e1988Sjohnlev 		if (cv_wait_sig(&xbs->read_cv, &xbs->read_mutex) == 0) {
457843e1988Sjohnlev 			mutex_exit(&xbs->read_mutex);
458843e1988Sjohnlev 			return (EINTR);
459843e1988Sjohnlev 		}
460843e1988Sjohnlev 	}
461843e1988Sjohnlev 
462843e1988Sjohnlev 	idx = XENBUSDRV_MASK_READ_IDX(xbs->read_cons);
463843e1988Sjohnlev 	res = uiop->uio_resid;
464843e1988Sjohnlev 
465843e1988Sjohnlev 	len = xbs->read_prod - xbs->read_cons;
466843e1988Sjohnlev 
467843e1988Sjohnlev 	if (len > (sizeof (xbs->read_buffer) - idx))
468843e1988Sjohnlev 		len = sizeof (xbs->read_buffer) - idx;
469843e1988Sjohnlev 	if (len > res)
470843e1988Sjohnlev 		len = res;
471843e1988Sjohnlev 
472843e1988Sjohnlev 	ret = uiomove(xbs->read_buffer + idx, len, UIO_READ, uiop);
473843e1988Sjohnlev 	xbs->read_cons += res - uiop->uio_resid;
474843e1988Sjohnlev 	mutex_exit(&xbs->read_mutex);
475843e1988Sjohnlev 
476843e1988Sjohnlev 	return (ret);
477843e1988Sjohnlev }
478843e1988Sjohnlev 
479843e1988Sjohnlev /*
480843e1988Sjohnlev  * prepare data for xenbusdrv_read()
481843e1988Sjohnlev  */
482843e1988Sjohnlev static int
xenbusdrv_queue_reply(xenbus_dev_t * xbs,const struct xsd_sockmsg * msg,const char * reply)483843e1988Sjohnlev xenbusdrv_queue_reply(xenbus_dev_t *xbs, const struct xsd_sockmsg *msg,
484843e1988Sjohnlev     const char *reply)
485843e1988Sjohnlev {
486843e1988Sjohnlev 	int i;
487843e1988Sjohnlev 	int remaining;
488843e1988Sjohnlev 
489843e1988Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply called"));
490843e1988Sjohnlev 
491843e1988Sjohnlev 	mutex_enter(&xbs->read_mutex);
492843e1988Sjohnlev 
493843e1988Sjohnlev 	remaining = sizeof (xbs->read_buffer) -
494843e1988Sjohnlev 	    (xbs->read_prod - xbs->read_cons);
495843e1988Sjohnlev 
496843e1988Sjohnlev 	if (sizeof (*msg) + msg->len > remaining) {
497843e1988Sjohnlev 		mutex_exit(&xbs->read_mutex);
498843e1988Sjohnlev 		return (EOVERFLOW);
499843e1988Sjohnlev 	}
500843e1988Sjohnlev 
501843e1988Sjohnlev 	for (i = 0; i < sizeof (*msg); i++, xbs->read_prod++) {
502843e1988Sjohnlev 		xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
503843e1988Sjohnlev 		    ((char *)msg)[i];
504843e1988Sjohnlev 	}
505843e1988Sjohnlev 
506843e1988Sjohnlev 	for (i = 0; i < msg->len; i++, xbs->read_prod++) {
507843e1988Sjohnlev 		xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
508843e1988Sjohnlev 		    reply[i];
509843e1988Sjohnlev 	}
510843e1988Sjohnlev 
511843e1988Sjohnlev 	cv_broadcast(&xbs->read_cv);
512843e1988Sjohnlev 
513843e1988Sjohnlev 	mutex_exit(&xbs->read_mutex);
514843e1988Sjohnlev 
515843e1988Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply exited"));
516843e1988Sjohnlev 
517843e1988Sjohnlev 	return (0);
518843e1988Sjohnlev }
519843e1988Sjohnlev 
520843e1988Sjohnlev /* ARGSUSED */
521843e1988Sjohnlev static int
xenbusdrv_write(dev_t dev,struct uio * uiop,cred_t * cr)522b26a64aeSjohnlev xenbusdrv_write(dev_t dev, struct uio *uiop, cred_t *cr)
523843e1988Sjohnlev {
524843e1988Sjohnlev 	xenbus_dev_t *xbs;
525843e1988Sjohnlev 	struct xenbus_dev_transaction *trans;
526843e1988Sjohnlev 	void *reply;
527843e1988Sjohnlev 	size_t len;
528843e1988Sjohnlev 	int rc = 0;
529843e1988Sjohnlev 
530843e1988Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_write called"));
531843e1988Sjohnlev 
532b26a64aeSjohnlev 	if (secpolicy_xvm_control(cr))
533b26a64aeSjohnlev 		return (EPERM);
534b26a64aeSjohnlev 
535843e1988Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
536843e1988Sjohnlev 	len = uiop->uio_resid;
537843e1988Sjohnlev 
538843e1988Sjohnlev 	if ((len + xbs->len) > sizeof (xbs->u.buffer)) {
539843e1988Sjohnlev 		XENBUSDRV_DBPRINT((CE_WARN, "Request is too big"));
540843e1988Sjohnlev 		rc = EINVAL;
541843e1988Sjohnlev 		goto out;
542843e1988Sjohnlev 	}
543843e1988Sjohnlev 
544843e1988Sjohnlev 	if (uiomove(xbs->u.buffer + xbs->len, len, UIO_WRITE, uiop) != 0) {
545843e1988Sjohnlev 		XENBUSDRV_DBPRINT((CE_WARN, "Uiomove failed"));
546843e1988Sjohnlev 		rc = EFAULT;
547843e1988Sjohnlev 		goto out;
548843e1988Sjohnlev 	}
549843e1988Sjohnlev 
550843e1988Sjohnlev 	xbs->len += len;
551843e1988Sjohnlev 
552843e1988Sjohnlev 	if (xbs->len < (sizeof (xbs->u.msg)) ||
553843e1988Sjohnlev 	    xbs->len < (sizeof (xbs->u.msg) + xbs->u.msg.len)) {
554843e1988Sjohnlev 		XENBUSDRV_DBPRINT((CE_NOTE, "Partial request"));
555843e1988Sjohnlev 		return (0);
556843e1988Sjohnlev 	}
557843e1988Sjohnlev 
558843e1988Sjohnlev 	switch (xbs->u.msg.type) {
559843e1988Sjohnlev 	case XS_TRANSACTION_START:
560843e1988Sjohnlev 	case XS_TRANSACTION_END:
561843e1988Sjohnlev 	case XS_DIRECTORY:
562843e1988Sjohnlev 	case XS_READ:
563843e1988Sjohnlev 	case XS_GET_PERMS:
564843e1988Sjohnlev 	case XS_RELEASE:
565843e1988Sjohnlev 	case XS_GET_DOMAIN_PATH:
566843e1988Sjohnlev 	case XS_WRITE:
567843e1988Sjohnlev 	case XS_MKDIR:
568843e1988Sjohnlev 	case XS_RM:
569843e1988Sjohnlev 	case XS_SET_PERMS:
570843e1988Sjohnlev 		/* send the request to xenstore and get feedback */
571843e1988Sjohnlev 		rc = xenbus_dev_request_and_reply(&xbs->u.msg, &reply);
572843e1988Sjohnlev 		if (rc) {
573843e1988Sjohnlev 			XENBUSDRV_DBPRINT((CE_WARN,
574843e1988Sjohnlev 			    "xenbus_dev_request_and_reply failed"));
575843e1988Sjohnlev 			goto out;
576843e1988Sjohnlev 		}
577843e1988Sjohnlev 
578843e1988Sjohnlev 		/* handle transaction start/end */
579843e1988Sjohnlev 		if (xbs->u.msg.type == XS_TRANSACTION_START) {
580843e1988Sjohnlev 			trans = kmem_alloc(sizeof (*trans), KM_SLEEP);
581843e1988Sjohnlev 			(void) ddi_strtoul((char *)reply, NULL, 0,
582843e1988Sjohnlev 			    (unsigned long *)&trans->handle);
583843e1988Sjohnlev 			list_insert_tail(&xbs->transactions, (void *)trans);
584843e1988Sjohnlev 		} else if (xbs->u.msg.type == XS_TRANSACTION_END) {
585843e1988Sjohnlev 			/* try to find out the ending transaction */
586843e1988Sjohnlev 			for (trans = (struct xenbus_dev_transaction *)
587843e1988Sjohnlev 			    list_head(&xbs->transactions); trans;
588843e1988Sjohnlev 			    trans = (struct xenbus_dev_transaction *)
589843e1988Sjohnlev 			    list_next(&xbs->transactions, (void *)trans))
590843e1988Sjohnlev 				if (trans->handle ==
591843e1988Sjohnlev 				    (xenbus_transaction_t)
592843e1988Sjohnlev 				    xbs->u.msg.tx_id)
593843e1988Sjohnlev 					break;
594843e1988Sjohnlev 			ASSERT(trans);
595843e1988Sjohnlev 			/* free it, if we find it */
596843e1988Sjohnlev 			list_remove(&xbs->transactions, (void *)trans);
597843e1988Sjohnlev 			kmem_free(trans, sizeof (*trans));
598843e1988Sjohnlev 		}
599843e1988Sjohnlev 
600843e1988Sjohnlev 		/* prepare data for xenbusdrv_read() to get */
601843e1988Sjohnlev 		rc = xenbusdrv_queue_reply(xbs, &xbs->u.msg, reply);
602843e1988Sjohnlev 
603843e1988Sjohnlev 		kmem_free(reply, xbs->u.msg.len + 1);
604843e1988Sjohnlev 		break;
605843e1988Sjohnlev 	default:
606843e1988Sjohnlev 		rc = EINVAL;
607843e1988Sjohnlev 	}
608843e1988Sjohnlev 
609843e1988Sjohnlev out:
610843e1988Sjohnlev 	xbs->len = 0;
611843e1988Sjohnlev 	return (rc);
612843e1988Sjohnlev }
613843e1988Sjohnlev 
614843e1988Sjohnlev /*ARGSUSED*/
615843e1988Sjohnlev static int
xenbusdrv_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)616843e1988Sjohnlev xenbusdrv_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
617843e1988Sjohnlev     size_t *maplen, uint_t model)
618843e1988Sjohnlev {
619843e1988Sjohnlev 	xenbus_dev_t *xbs;
620843e1988Sjohnlev 	int err;
621843e1988Sjohnlev 
622843e1988Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
623843e1988Sjohnlev 
624843e1988Sjohnlev 	if (off != 0 || len != PAGESIZE)
625843e1988Sjohnlev 		return (-1);
626843e1988Sjohnlev 
627843e1988Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info))
628843e1988Sjohnlev 		return (-1);
629843e1988Sjohnlev 
630843e1988Sjohnlev 	err = devmap_umem_setup(dhp, xbs->dip, NULL, xb_xenstore_cookie(),
631843e1988Sjohnlev 	    0, PAGESIZE, PROT_READ | PROT_WRITE | PROT_USER, 0, NULL);
632843e1988Sjohnlev 
633843e1988Sjohnlev 	if (err)
634843e1988Sjohnlev 		return (err);
635843e1988Sjohnlev 
636843e1988Sjohnlev 	*maplen = PAGESIZE;
637843e1988Sjohnlev 
638843e1988Sjohnlev 	return (0);
639843e1988Sjohnlev }
640843e1988Sjohnlev 
641b26a64aeSjohnlev static int
xenbusdrv_segmap(dev_t dev,off_t off,ddi_as_handle_t as,caddr_t * addrp,off_t len,uint_t prot,uint_t maxprot,uint_t flags,cred_t * cr)642b26a64aeSjohnlev xenbusdrv_segmap(dev_t dev, off_t off, ddi_as_handle_t as, caddr_t *addrp,
643b26a64aeSjohnlev     off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr)
644b26a64aeSjohnlev {
645b26a64aeSjohnlev 
646b26a64aeSjohnlev 	if (secpolicy_xvm_control(cr))
647b26a64aeSjohnlev 		return (EPERM);
648b26a64aeSjohnlev 
649b26a64aeSjohnlev 	return (ddi_devmap_segmap(dev, off, as, addrp, len, prot,
650b26a64aeSjohnlev 	    maxprot, flags, cr));
651b26a64aeSjohnlev }
652b26a64aeSjohnlev 
653843e1988Sjohnlev /*ARGSUSED*/
654843e1988Sjohnlev static int
xenbusdrv_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rvalp)655b26a64aeSjohnlev xenbusdrv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr,
656843e1988Sjohnlev     int *rvalp)
657843e1988Sjohnlev {
658843e1988Sjohnlev 	xenbus_dev_t *xbs;
659843e1988Sjohnlev 
660b26a64aeSjohnlev 	if (secpolicy_xvm_control(cr))
661b26a64aeSjohnlev 		return (EPERM);
662b26a64aeSjohnlev 
663843e1988Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
664843e1988Sjohnlev 	switch (cmd) {
665843e1988Sjohnlev 	case IOCTL_XENBUS_XENSTORE_EVTCHN:
666843e1988Sjohnlev 		*rvalp = xen_info->store_evtchn;
667843e1988Sjohnlev 		break;
668843e1988Sjohnlev 	case IOCTL_XENBUS_NOTIFY_UP:
669843e1988Sjohnlev 		xs_notify_xenstore_up();
670843e1988Sjohnlev 		xbs->xenstore_inst = 1;
671843e1988Sjohnlev 		break;
672843e1988Sjohnlev 	default:
673843e1988Sjohnlev 		return (EINVAL);
674843e1988Sjohnlev 	}
675843e1988Sjohnlev 
676843e1988Sjohnlev 	return (0);
677843e1988Sjohnlev }
678