xref: /onnv-gate/usr/src/uts/common/xen/io/xenbus_dev.c (revision 7656:2621e50fdf4a)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*
236784Sjohnlev  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
245084Sjohnlev  * Use is subject to license terms.
255084Sjohnlev  */
265084Sjohnlev 
275084Sjohnlev /*
285084Sjohnlev  * xenbus_dev.c
295084Sjohnlev  *
305084Sjohnlev  * Driver giving user-space access to the kernel's xenbus connection
315084Sjohnlev  * to xenstore.
325084Sjohnlev  *
335084Sjohnlev  * Copyright (c) 2005, Christian Limpach
345084Sjohnlev  * Copyright (c) 2005, Rusty Russell, IBM Corporation
355084Sjohnlev  *
365084Sjohnlev  * This file may be distributed separately from the Linux kernel, or
375084Sjohnlev  * incorporated into other software packages, subject to the following license:
385084Sjohnlev  *
395084Sjohnlev  * Permission is hereby granted, free of charge, to any person obtaining a copy
405084Sjohnlev  * of this source file (the "Software"), to deal in the Software without
415084Sjohnlev  * restriction, including without limitation the rights to use, copy, modify,
425084Sjohnlev  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
435084Sjohnlev  * and to permit persons to whom the Software is furnished to do so, subject to
445084Sjohnlev  * the following conditions:
455084Sjohnlev  *
465084Sjohnlev  * The above copyright notice and this permission notice shall be included in
475084Sjohnlev  * all copies or substantial portions of the Software.
485084Sjohnlev  *
495084Sjohnlev  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
505084Sjohnlev  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
515084Sjohnlev  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
525084Sjohnlev  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
535084Sjohnlev  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
545084Sjohnlev  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
555084Sjohnlev  * IN THE SOFTWARE.
565084Sjohnlev  */
575084Sjohnlev 
585084Sjohnlev 
595084Sjohnlev #include <sys/types.h>
605084Sjohnlev #include <sys/sysmacros.h>
615084Sjohnlev #include <sys/conf.h>
625084Sjohnlev #include <sys/stat.h>
635084Sjohnlev #include <sys/modctl.h>
645084Sjohnlev #include <sys/uio.h>
655084Sjohnlev #include <sys/list.h>
665084Sjohnlev #include <sys/file.h>
675084Sjohnlev #include <sys/errno.h>
685084Sjohnlev #include <sys/open.h>
695084Sjohnlev #include <sys/cred.h>
705084Sjohnlev #include <sys/condvar.h>
715084Sjohnlev #include <sys/ddi.h>
725084Sjohnlev #include <sys/sunddi.h>
736784Sjohnlev #include <sys/policy.h>
746784Sjohnlev 
755741Smrj #ifdef XPV_HVM_DRIVER
765741Smrj #include <public/io/xenbus.h>
775741Smrj #include <public/io/xs_wire.h>
785741Smrj #include <sys/xpv_support.h>
795741Smrj #endif
805084Sjohnlev #include <sys/hypervisor.h>
815741Smrj #include <xen/sys/xenbus.h>
825084Sjohnlev #include <xen/sys/xenbus_comms.h>
835084Sjohnlev #include <xen/sys/xenbus_impl.h>
845084Sjohnlev #include <xen/public/io/xs_wire.h>
855084Sjohnlev 
865084Sjohnlev #ifdef DEBUG
875084Sjohnlev #define	XENBUSDRV_DBPRINT(fmt) { if (xenbusdrv_debug) cmn_err fmt; }
885084Sjohnlev #else
895084Sjohnlev #define	XENBUSDRV_DBPRINT(fmt)
905084Sjohnlev #endif /* ifdef DEBUG */
915084Sjohnlev 
925084Sjohnlev /* Some handy macros */
935084Sjohnlev #define	XENBUSDRV_MASK_READ_IDX(idx)	((idx) & (PAGESIZE - 1))
945084Sjohnlev #define	XENBUSDRV_MINOR2INST(minor)	((int)(minor))
955084Sjohnlev #define	XENBUSDRV_NCLONES 		256
965084Sjohnlev #define	XENBUSDRV_INST2SOFTS(instance)	\
975084Sjohnlev 	((xenbus_dev_t *)ddi_get_soft_state(xenbusdrv_statep, (instance)))
985084Sjohnlev 
995084Sjohnlev static int xenbusdrv_debug = 0;
1005084Sjohnlev static int xenbusdrv_clone_tab[XENBUSDRV_NCLONES];
1015084Sjohnlev static dev_info_t *xenbusdrv_dip;
1025084Sjohnlev static kmutex_t xenbusdrv_clone_tab_mutex;
1035084Sjohnlev 
1045084Sjohnlev struct xenbus_dev_transaction {
1055084Sjohnlev 	list_t list;
1065084Sjohnlev 	xenbus_transaction_t handle;
1075084Sjohnlev };
1085084Sjohnlev 
1095084Sjohnlev /* Soft state data structure for xenbus driver */
1105084Sjohnlev struct xenbus_dev_data {
1115084Sjohnlev 	dev_info_t *dip;
1125084Sjohnlev 
1135084Sjohnlev 	/* In-progress transaction. */
1145084Sjohnlev 	list_t transactions;
1155084Sjohnlev 
1165084Sjohnlev 	/* Partial request. */
1175084Sjohnlev 	unsigned int len;
1185084Sjohnlev 	union {
1195084Sjohnlev 		struct xsd_sockmsg msg;
1205084Sjohnlev 		char buffer[MMU_PAGESIZE];
1215084Sjohnlev 	} u;
1225084Sjohnlev 
1235084Sjohnlev 	/* Response queue. */
1245084Sjohnlev 	char read_buffer[MMU_PAGESIZE];
1255084Sjohnlev 	unsigned int read_cons, read_prod;
1265084Sjohnlev 	kcondvar_t read_cv;
1275084Sjohnlev 	kmutex_t read_mutex;
1285084Sjohnlev 	int xenstore_inst;
1295084Sjohnlev };
1305084Sjohnlev typedef struct xenbus_dev_data xenbus_dev_t;
1315084Sjohnlev static void *xenbusdrv_statep;
1325084Sjohnlev 
1335084Sjohnlev static int xenbusdrv_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
1345084Sjohnlev static int xenbusdrv_attach(dev_info_t *, ddi_attach_cmd_t);
1355084Sjohnlev static int xenbusdrv_detach(dev_info_t *, ddi_detach_cmd_t);
1365084Sjohnlev static int xenbusdrv_open(dev_t *, int, int, cred_t *);
1375084Sjohnlev static int xenbusdrv_close(dev_t, int, int, cred_t *);
1385084Sjohnlev static int xenbusdrv_read(dev_t, struct uio *, cred_t *);
1395084Sjohnlev static int xenbusdrv_write(dev_t, struct uio *, cred_t *);
1405084Sjohnlev static int xenbusdrv_devmap(dev_t, devmap_cookie_t, offset_t, size_t, size_t *,
1415084Sjohnlev     uint_t);
1426784Sjohnlev static int xenbusdrv_segmap(dev_t, off_t, ddi_as_handle_t, caddr_t *, off_t,
1436784Sjohnlev     uint_t, uint_t, uint_t, cred_t *);
1445084Sjohnlev static int xenbusdrv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1455084Sjohnlev static int xenbusdrv_queue_reply(xenbus_dev_t *, const struct xsd_sockmsg *,
1465084Sjohnlev     const char *);
1475084Sjohnlev 
1485084Sjohnlev /* Solaris driver framework */
1495084Sjohnlev 
1505084Sjohnlev static 	struct cb_ops xenbusdrv_cb_ops = {
1515084Sjohnlev 	xenbusdrv_open,			/* cb_open */
1525084Sjohnlev 	xenbusdrv_close,		/* cb_close */
1535084Sjohnlev 	nodev,				/* cb_strategy */
1545084Sjohnlev 	nodev,				/* cb_print */
1555084Sjohnlev 	nodev,				/* cb_dump */
1565084Sjohnlev 	xenbusdrv_read,			/* cb_read */
1575084Sjohnlev 	xenbusdrv_write,		/* cb_write */
1585084Sjohnlev 	xenbusdrv_ioctl,		/* cb_ioctl */
1595084Sjohnlev 	xenbusdrv_devmap,		/* cb_devmap */
1605084Sjohnlev 	NULL,				/* cb_mmap */
1616784Sjohnlev 	xenbusdrv_segmap,		/* cb_segmap */
1625084Sjohnlev 	nochpoll,			/* cb_chpoll */
1635084Sjohnlev 	ddi_prop_op,			/* cb_prop_op */
1645084Sjohnlev 	0,				/* cb_stream */
1655084Sjohnlev 	D_DEVMAP | D_NEW | D_MP,	/* cb_flag */
1665084Sjohnlev 	CB_REV
1675084Sjohnlev };
1685084Sjohnlev 
1695084Sjohnlev static struct dev_ops xenbusdrv_dev_ops = {
1705084Sjohnlev 	DEVO_REV,		/* devo_rev */
1715084Sjohnlev 	0,			/* devo_refcnt */
1725084Sjohnlev 	xenbusdrv_info,		/* devo_getinfo */
1735084Sjohnlev 	nulldev,		/* devo_identify */
1745084Sjohnlev 	nulldev,		/* devo_probe */
1755084Sjohnlev 	xenbusdrv_attach,	/* devo_attach */
1765084Sjohnlev 	xenbusdrv_detach,	/* devo_detach */
1775084Sjohnlev 	nodev,			/* devo_reset */
1785084Sjohnlev 	&xenbusdrv_cb_ops,	/* devo_cb_ops */
1795084Sjohnlev 	NULL,			/* devo_bus_ops */
180*7656SSherry.Moore@Sun.COM 	NULL,			/* devo_power */
181*7656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* devo_quiesce */
1825084Sjohnlev };
1835084Sjohnlev 
1845084Sjohnlev static struct modldrv modldrv = {
1855084Sjohnlev 	&mod_driverops,		/* Type of module.  This one is a driver */
186*7656SSherry.Moore@Sun.COM 	"virtual bus driver",	/* Name of the module. */
1875084Sjohnlev 	&xenbusdrv_dev_ops	/* driver ops */
1885084Sjohnlev };
1895084Sjohnlev 
1905084Sjohnlev static struct modlinkage modlinkage = {
1915084Sjohnlev 	MODREV_1,
1925084Sjohnlev 	&modldrv,
1935084Sjohnlev 	NULL
1945084Sjohnlev };
1955084Sjohnlev 
1965084Sjohnlev int
_init(void)1975084Sjohnlev _init(void)
1985084Sjohnlev {
1995084Sjohnlev 	int e;
2005084Sjohnlev 
2015084Sjohnlev 	e = ddi_soft_state_init(&xenbusdrv_statep, sizeof (xenbus_dev_t), 1);
2025084Sjohnlev 	if (e)
2035084Sjohnlev 		return (e);
2045084Sjohnlev 
2055084Sjohnlev 	e = mod_install(&modlinkage);
2065084Sjohnlev 	if (e)
2075084Sjohnlev 		ddi_soft_state_fini(&xenbusdrv_statep);
2085084Sjohnlev 
2095084Sjohnlev 	return (e);
2105084Sjohnlev }
2115084Sjohnlev 
2125084Sjohnlev int
_fini(void)2135084Sjohnlev _fini(void)
2145084Sjohnlev {
2155084Sjohnlev 	int e;
2165084Sjohnlev 
2175084Sjohnlev 	e = mod_remove(&modlinkage);
2185084Sjohnlev 	if (e)
2195084Sjohnlev 		return (e);
2205084Sjohnlev 
2215084Sjohnlev 	ddi_soft_state_fini(&xenbusdrv_statep);
2225084Sjohnlev 
2235084Sjohnlev 	return (0);
2245084Sjohnlev }
2255084Sjohnlev 
2265084Sjohnlev int
_info(struct modinfo * modinfop)2275084Sjohnlev _info(struct modinfo *modinfop)
2285084Sjohnlev {
2295084Sjohnlev 	return (mod_info(&modlinkage, modinfop));
2305084Sjohnlev }
2315084Sjohnlev 
2325084Sjohnlev /* ARGSUSED */
2335084Sjohnlev static int
xenbusdrv_info(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)2345084Sjohnlev xenbusdrv_info(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
2355084Sjohnlev {
2365084Sjohnlev 	dev_t	dev = (dev_t)arg;
2375084Sjohnlev 	minor_t	minor = getminor(dev);
2385084Sjohnlev 	int	retval;
2395084Sjohnlev 
2405084Sjohnlev 	switch (cmd) {
2415084Sjohnlev 	case DDI_INFO_DEVT2DEVINFO:
2425084Sjohnlev 		if (minor != 0 || xenbusdrv_dip == NULL) {
2435084Sjohnlev 			*result = (void *)NULL;
2445084Sjohnlev 			retval = DDI_FAILURE;
2455084Sjohnlev 		} else {
2465084Sjohnlev 			*result = (void *)xenbusdrv_dip;
2475084Sjohnlev 			retval = DDI_SUCCESS;
2485084Sjohnlev 		}
2495084Sjohnlev 		break;
2505084Sjohnlev 	case DDI_INFO_DEVT2INSTANCE:
2515084Sjohnlev 		*result = (void *)0;
2525084Sjohnlev 		retval = DDI_SUCCESS;
2535084Sjohnlev 		break;
2545084Sjohnlev 	default:
2555084Sjohnlev 		retval = DDI_FAILURE;
2565084Sjohnlev 	}
2575084Sjohnlev 	return (retval);
2585084Sjohnlev }
2595084Sjohnlev 
2605084Sjohnlev static int
xenbusdrv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2615084Sjohnlev xenbusdrv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2625084Sjohnlev {
2635084Sjohnlev 	int	error;
2645084Sjohnlev 	int	unit = ddi_get_instance(dip);
2655084Sjohnlev 
2665084Sjohnlev 
2675084Sjohnlev 	switch (cmd) {
2685084Sjohnlev 	case DDI_ATTACH:
2695084Sjohnlev 		break;
2705084Sjohnlev 	case DDI_RESUME:
2715084Sjohnlev 		return (DDI_SUCCESS);
2725084Sjohnlev 	default:
2735084Sjohnlev 		cmn_err(CE_WARN, "xenbus_attach: unknown cmd 0x%x\n", cmd);
2745084Sjohnlev 		return (DDI_FAILURE);
2755084Sjohnlev 	}
2765084Sjohnlev 
2775084Sjohnlev 	/* DDI_ATTACH */
2785084Sjohnlev 
2795084Sjohnlev 	/*
2805084Sjohnlev 	 * only one instance - but we clone using the open routine
2815084Sjohnlev 	 */
2825084Sjohnlev 	if (ddi_get_instance(dip) > 0)
2835084Sjohnlev 		return (DDI_FAILURE);
2845084Sjohnlev 
2855084Sjohnlev 	mutex_init(&xenbusdrv_clone_tab_mutex, NULL, MUTEX_DRIVER,
2865084Sjohnlev 	    NULL);
2875084Sjohnlev 
2885084Sjohnlev 	error = ddi_create_minor_node(dip, "xenbus", S_IFCHR, unit,
2895084Sjohnlev 	    DDI_PSEUDO, NULL);
2905084Sjohnlev 	if (error != DDI_SUCCESS)
2915084Sjohnlev 		goto fail;
2925084Sjohnlev 
2935084Sjohnlev 	/*
2945084Sjohnlev 	 * save dip for getinfo
2955084Sjohnlev 	 */
2965084Sjohnlev 	xenbusdrv_dip = dip;
2975084Sjohnlev 	ddi_report_dev(dip);
2985084Sjohnlev 
2995741Smrj #ifndef XPV_HVM_DRIVER
3005084Sjohnlev 	if (DOMAIN_IS_INITDOMAIN(xen_info))
3015084Sjohnlev 		xs_dom0_init();
3025741Smrj #endif
3035084Sjohnlev 
3045084Sjohnlev 	return (DDI_SUCCESS);
3055084Sjohnlev 
3065084Sjohnlev fail:
3075084Sjohnlev 	(void) xenbusdrv_detach(dip, DDI_DETACH);
3085084Sjohnlev 	return (error);
3095084Sjohnlev }
3105084Sjohnlev 
3115084Sjohnlev static int
xenbusdrv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3125084Sjohnlev xenbusdrv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3135084Sjohnlev {
3145084Sjohnlev 	/*
3155084Sjohnlev 	 * again, only one instance
3165084Sjohnlev 	 */
3175084Sjohnlev 	if (ddi_get_instance(dip) > 0)
3185084Sjohnlev 		return (DDI_FAILURE);
3195084Sjohnlev 
3205084Sjohnlev 	switch (cmd) {
3215084Sjohnlev 	case DDI_DETACH:
3225084Sjohnlev 		ddi_remove_minor_node(dip, NULL);
3235084Sjohnlev 		mutex_destroy(&xenbusdrv_clone_tab_mutex);
3245084Sjohnlev 		xenbusdrv_dip = NULL;
3255084Sjohnlev 		return (DDI_SUCCESS);
3265084Sjohnlev 	case DDI_SUSPEND:
3275084Sjohnlev 		return (DDI_SUCCESS);
3285084Sjohnlev 	default:
3295084Sjohnlev 		cmn_err(CE_WARN, "xenbus_detach: unknown cmd 0x%x\n", cmd);
3305084Sjohnlev 		return (DDI_FAILURE);
3315084Sjohnlev 	}
3325084Sjohnlev }
3335084Sjohnlev 
3345084Sjohnlev /* ARGSUSED */
3355084Sjohnlev static int
xenbusdrv_open(dev_t * devp,int flag,int otyp,cred_t * cr)3366784Sjohnlev xenbusdrv_open(dev_t *devp, int flag, int otyp, cred_t *cr)
3375084Sjohnlev {
3385084Sjohnlev 	xenbus_dev_t *xbs;
3395084Sjohnlev 	minor_t minor = getminor(*devp);
3405084Sjohnlev 
3415084Sjohnlev 	if (otyp == OTYP_BLK)
3425084Sjohnlev 		return (ENXIO);
3435084Sjohnlev 
3445084Sjohnlev 	/*
3455084Sjohnlev 	 * only allow open on minor = 0 - the clone device
3465084Sjohnlev 	 */
3475084Sjohnlev 	if (minor != 0)
3485084Sjohnlev 		return (ENXIO);
3495084Sjohnlev 
3505084Sjohnlev 	/*
3515084Sjohnlev 	 * find a free slot and grab it
3525084Sjohnlev 	 */
3535084Sjohnlev 	mutex_enter(&xenbusdrv_clone_tab_mutex);
3545084Sjohnlev 	for (minor = 1; minor < XENBUSDRV_NCLONES; minor++) {
3555084Sjohnlev 		if (xenbusdrv_clone_tab[minor] == 0) {
3565084Sjohnlev 			xenbusdrv_clone_tab[minor] = 1;
3575084Sjohnlev 			break;
3585084Sjohnlev 		}
3595084Sjohnlev 	}
3605084Sjohnlev 	mutex_exit(&xenbusdrv_clone_tab_mutex);
3615084Sjohnlev 	if (minor == XENBUSDRV_NCLONES)
3625084Sjohnlev 		return (EAGAIN);
3635084Sjohnlev 
3645084Sjohnlev 	/* Allocate softstate structure */
3655084Sjohnlev 	if (ddi_soft_state_zalloc(xenbusdrv_statep,
3665084Sjohnlev 	    XENBUSDRV_MINOR2INST(minor)) != DDI_SUCCESS) {
3675084Sjohnlev 		mutex_enter(&xenbusdrv_clone_tab_mutex);
3685084Sjohnlev 		xenbusdrv_clone_tab[minor] = 0;
3695084Sjohnlev 		mutex_exit(&xenbusdrv_clone_tab_mutex);
3705084Sjohnlev 		return (EAGAIN);
3715084Sjohnlev 	}
3725084Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
3735084Sjohnlev 
3745084Sjohnlev 	/* ... and init it */
3755084Sjohnlev 	xbs->dip = xenbusdrv_dip;
3765084Sjohnlev 	mutex_init(&xbs->read_mutex, NULL, MUTEX_DRIVER, NULL);
3775084Sjohnlev 	cv_init(&xbs->read_cv, NULL, CV_DEFAULT, NULL);
3785084Sjohnlev 	list_create(&xbs->transactions, sizeof (struct xenbus_dev_transaction),
3795084Sjohnlev 	    offsetof(struct xenbus_dev_transaction, list));
3805084Sjohnlev 
3815084Sjohnlev 	/* clone driver */
3825084Sjohnlev 	*devp = makedevice(getmajor(*devp), minor);
3835084Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv open succeeded, minor=%d",
3845084Sjohnlev 	    minor));
3855084Sjohnlev 
3865084Sjohnlev 	return (0);
3875084Sjohnlev }
3885084Sjohnlev 
3895084Sjohnlev /* ARGSUSED */
3905084Sjohnlev static int
xenbusdrv_close(dev_t dev,int flag,int otyp,struct cred * cr)3916784Sjohnlev xenbusdrv_close(dev_t dev, int flag, int otyp, struct cred *cr)
3925084Sjohnlev {
3935084Sjohnlev 	xenbus_dev_t *xbs;
3945084Sjohnlev 	minor_t minor = getminor(dev);
3955084Sjohnlev 	struct xenbus_dev_transaction *trans;
3965084Sjohnlev 
3975084Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(minor));
3985084Sjohnlev 	if (xbs == NULL)
3995084Sjohnlev 		return (ENXIO);
4005084Sjohnlev 
4015084Sjohnlev #ifdef notyet
4025084Sjohnlev 	/*
4035084Sjohnlev 	 * XXPV - would like to be able to notify xenstore down here, but
4045084Sjohnlev 	 * as the daemon is currently written, it doesn't leave the device
4055084Sjohnlev 	 * open after initial setup, so we have no way of knowing if it has
4065084Sjohnlev 	 * gone away.
4075084Sjohnlev 	 */
4085084Sjohnlev 	if (xbs->xenstore_inst)
4095084Sjohnlev 		xs_notify_xenstore_down();
4105084Sjohnlev #endif
4115084Sjohnlev 	/* free pending transaction */
4125084Sjohnlev 	while (trans = (struct xenbus_dev_transaction *)
4135084Sjohnlev 	    list_head(&xbs->transactions)) {
4145084Sjohnlev 		(void) xenbus_transaction_end(trans->handle, 1);
4155084Sjohnlev 		list_remove(&xbs->transactions, (void *)trans);
4165084Sjohnlev 		kmem_free(trans, sizeof (*trans));
4175084Sjohnlev 	}
4185084Sjohnlev 
4195084Sjohnlev 	mutex_destroy(&xbs->read_mutex);
4205084Sjohnlev 	cv_destroy(&xbs->read_cv);
4215084Sjohnlev 	ddi_soft_state_free(xenbusdrv_statep, XENBUSDRV_MINOR2INST(minor));
4225084Sjohnlev 
4235084Sjohnlev 	/*
4245084Sjohnlev 	 * free clone tab slot
4255084Sjohnlev 	 */
4265084Sjohnlev 	mutex_enter(&xenbusdrv_clone_tab_mutex);
4275084Sjohnlev 	xenbusdrv_clone_tab[minor] = 0;
4285084Sjohnlev 	mutex_exit(&xenbusdrv_clone_tab_mutex);
4295084Sjohnlev 
4305084Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "Xenbus drv close succeeded, minor=%d",
4315084Sjohnlev 	    minor));
4325084Sjohnlev 
4335084Sjohnlev 	return (0);
4345084Sjohnlev }
4355084Sjohnlev 
4365084Sjohnlev /* ARGSUSED */
4375084Sjohnlev static int
xenbusdrv_read(dev_t dev,struct uio * uiop,cred_t * cr)4386784Sjohnlev xenbusdrv_read(dev_t dev, struct uio *uiop, cred_t *cr)
4395084Sjohnlev {
4405084Sjohnlev 	xenbus_dev_t *xbs;
4415084Sjohnlev 	size_t len;
4425084Sjohnlev 	int res, ret;
4435084Sjohnlev 	int idx;
4445084Sjohnlev 
4455084Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_read called"));
4465084Sjohnlev 
4476784Sjohnlev 	if (secpolicy_xvm_control(cr))
4486784Sjohnlev 		return (EPERM);
4496784Sjohnlev 
4505084Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
4515084Sjohnlev 
4525084Sjohnlev 	mutex_enter(&xbs->read_mutex);
4535084Sjohnlev 
4545084Sjohnlev 	/* check if we have something to read */
4555084Sjohnlev 	while (xbs->read_prod == xbs->read_cons) {
4565084Sjohnlev 		if (cv_wait_sig(&xbs->read_cv, &xbs->read_mutex) == 0) {
4575084Sjohnlev 			mutex_exit(&xbs->read_mutex);
4585084Sjohnlev 			return (EINTR);
4595084Sjohnlev 		}
4605084Sjohnlev 	}
4615084Sjohnlev 
4625084Sjohnlev 	idx = XENBUSDRV_MASK_READ_IDX(xbs->read_cons);
4635084Sjohnlev 	res = uiop->uio_resid;
4645084Sjohnlev 
4655084Sjohnlev 	len = xbs->read_prod - xbs->read_cons;
4665084Sjohnlev 
4675084Sjohnlev 	if (len > (sizeof (xbs->read_buffer) - idx))
4685084Sjohnlev 		len = sizeof (xbs->read_buffer) - idx;
4695084Sjohnlev 	if (len > res)
4705084Sjohnlev 		len = res;
4715084Sjohnlev 
4725084Sjohnlev 	ret = uiomove(xbs->read_buffer + idx, len, UIO_READ, uiop);
4735084Sjohnlev 	xbs->read_cons += res - uiop->uio_resid;
4745084Sjohnlev 	mutex_exit(&xbs->read_mutex);
4755084Sjohnlev 
4765084Sjohnlev 	return (ret);
4775084Sjohnlev }
4785084Sjohnlev 
4795084Sjohnlev /*
4805084Sjohnlev  * prepare data for xenbusdrv_read()
4815084Sjohnlev  */
4825084Sjohnlev static int
xenbusdrv_queue_reply(xenbus_dev_t * xbs,const struct xsd_sockmsg * msg,const char * reply)4835084Sjohnlev xenbusdrv_queue_reply(xenbus_dev_t *xbs, const struct xsd_sockmsg *msg,
4845084Sjohnlev     const char *reply)
4855084Sjohnlev {
4865084Sjohnlev 	int i;
4875084Sjohnlev 	int remaining;
4885084Sjohnlev 
4895084Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply called"));
4905084Sjohnlev 
4915084Sjohnlev 	mutex_enter(&xbs->read_mutex);
4925084Sjohnlev 
4935084Sjohnlev 	remaining = sizeof (xbs->read_buffer) -
4945084Sjohnlev 	    (xbs->read_prod - xbs->read_cons);
4955084Sjohnlev 
4965084Sjohnlev 	if (sizeof (*msg) + msg->len > remaining) {
4975084Sjohnlev 		mutex_exit(&xbs->read_mutex);
4985084Sjohnlev 		return (EOVERFLOW);
4995084Sjohnlev 	}
5005084Sjohnlev 
5015084Sjohnlev 	for (i = 0; i < sizeof (*msg); i++, xbs->read_prod++) {
5025084Sjohnlev 		xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
5035084Sjohnlev 		    ((char *)msg)[i];
5045084Sjohnlev 	}
5055084Sjohnlev 
5065084Sjohnlev 	for (i = 0; i < msg->len; i++, xbs->read_prod++) {
5075084Sjohnlev 		xbs->read_buffer[XENBUSDRV_MASK_READ_IDX(xbs->read_prod)] =
5085084Sjohnlev 		    reply[i];
5095084Sjohnlev 	}
5105084Sjohnlev 
5115084Sjohnlev 	cv_broadcast(&xbs->read_cv);
5125084Sjohnlev 
5135084Sjohnlev 	mutex_exit(&xbs->read_mutex);
5145084Sjohnlev 
5155084Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_queue_reply exited"));
5165084Sjohnlev 
5175084Sjohnlev 	return (0);
5185084Sjohnlev }
5195084Sjohnlev 
5205084Sjohnlev /* ARGSUSED */
5215084Sjohnlev static int
xenbusdrv_write(dev_t dev,struct uio * uiop,cred_t * cr)5226784Sjohnlev xenbusdrv_write(dev_t dev, struct uio *uiop, cred_t *cr)
5235084Sjohnlev {
5245084Sjohnlev 	xenbus_dev_t *xbs;
5255084Sjohnlev 	struct xenbus_dev_transaction *trans;
5265084Sjohnlev 	void *reply;
5275084Sjohnlev 	size_t len;
5285084Sjohnlev 	int rc = 0;
5295084Sjohnlev 
5305084Sjohnlev 	XENBUSDRV_DBPRINT((CE_NOTE, "xenbusdrv_write called"));
5315084Sjohnlev 
5326784Sjohnlev 	if (secpolicy_xvm_control(cr))
5336784Sjohnlev 		return (EPERM);
5346784Sjohnlev 
5355084Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
5365084Sjohnlev 	len = uiop->uio_resid;
5375084Sjohnlev 
5385084Sjohnlev 	if ((len + xbs->len) > sizeof (xbs->u.buffer)) {
5395084Sjohnlev 		XENBUSDRV_DBPRINT((CE_WARN, "Request is too big"));
5405084Sjohnlev 		rc = EINVAL;
5415084Sjohnlev 		goto out;
5425084Sjohnlev 	}
5435084Sjohnlev 
5445084Sjohnlev 	if (uiomove(xbs->u.buffer + xbs->len, len, UIO_WRITE, uiop) != 0) {
5455084Sjohnlev 		XENBUSDRV_DBPRINT((CE_WARN, "Uiomove failed"));
5465084Sjohnlev 		rc = EFAULT;
5475084Sjohnlev 		goto out;
5485084Sjohnlev 	}
5495084Sjohnlev 
5505084Sjohnlev 	xbs->len += len;
5515084Sjohnlev 
5525084Sjohnlev 	if (xbs->len < (sizeof (xbs->u.msg)) ||
5535084Sjohnlev 	    xbs->len < (sizeof (xbs->u.msg) + xbs->u.msg.len)) {
5545084Sjohnlev 		XENBUSDRV_DBPRINT((CE_NOTE, "Partial request"));
5555084Sjohnlev 		return (0);
5565084Sjohnlev 	}
5575084Sjohnlev 
5585084Sjohnlev 	switch (xbs->u.msg.type) {
5595084Sjohnlev 	case XS_TRANSACTION_START:
5605084Sjohnlev 	case XS_TRANSACTION_END:
5615084Sjohnlev 	case XS_DIRECTORY:
5625084Sjohnlev 	case XS_READ:
5635084Sjohnlev 	case XS_GET_PERMS:
5645084Sjohnlev 	case XS_RELEASE:
5655084Sjohnlev 	case XS_GET_DOMAIN_PATH:
5665084Sjohnlev 	case XS_WRITE:
5675084Sjohnlev 	case XS_MKDIR:
5685084Sjohnlev 	case XS_RM:
5695084Sjohnlev 	case XS_SET_PERMS:
5705084Sjohnlev 		/* send the request to xenstore and get feedback */
5715084Sjohnlev 		rc = xenbus_dev_request_and_reply(&xbs->u.msg, &reply);
5725084Sjohnlev 		if (rc) {
5735084Sjohnlev 			XENBUSDRV_DBPRINT((CE_WARN,
5745084Sjohnlev 			    "xenbus_dev_request_and_reply failed"));
5755084Sjohnlev 			goto out;
5765084Sjohnlev 		}
5775084Sjohnlev 
5785084Sjohnlev 		/* handle transaction start/end */
5795084Sjohnlev 		if (xbs->u.msg.type == XS_TRANSACTION_START) {
5805084Sjohnlev 			trans = kmem_alloc(sizeof (*trans), KM_SLEEP);
5815084Sjohnlev 			(void) ddi_strtoul((char *)reply, NULL, 0,
5825084Sjohnlev 			    (unsigned long *)&trans->handle);
5835084Sjohnlev 			list_insert_tail(&xbs->transactions, (void *)trans);
5845084Sjohnlev 		} else if (xbs->u.msg.type == XS_TRANSACTION_END) {
5855084Sjohnlev 			/* try to find out the ending transaction */
5865084Sjohnlev 			for (trans = (struct xenbus_dev_transaction *)
5875084Sjohnlev 			    list_head(&xbs->transactions); trans;
5885084Sjohnlev 			    trans = (struct xenbus_dev_transaction *)
5895084Sjohnlev 			    list_next(&xbs->transactions, (void *)trans))
5905084Sjohnlev 				if (trans->handle ==
5915084Sjohnlev 				    (xenbus_transaction_t)
5925084Sjohnlev 				    xbs->u.msg.tx_id)
5935084Sjohnlev 					break;
5945084Sjohnlev 			ASSERT(trans);
5955084Sjohnlev 			/* free it, if we find it */
5965084Sjohnlev 			list_remove(&xbs->transactions, (void *)trans);
5975084Sjohnlev 			kmem_free(trans, sizeof (*trans));
5985084Sjohnlev 		}
5995084Sjohnlev 
6005084Sjohnlev 		/* prepare data for xenbusdrv_read() to get */
6015084Sjohnlev 		rc = xenbusdrv_queue_reply(xbs, &xbs->u.msg, reply);
6025084Sjohnlev 
6035084Sjohnlev 		kmem_free(reply, xbs->u.msg.len + 1);
6045084Sjohnlev 		break;
6055084Sjohnlev 	default:
6065084Sjohnlev 		rc = EINVAL;
6075084Sjohnlev 	}
6085084Sjohnlev 
6095084Sjohnlev out:
6105084Sjohnlev 	xbs->len = 0;
6115084Sjohnlev 	return (rc);
6125084Sjohnlev }
6135084Sjohnlev 
6145084Sjohnlev /*ARGSUSED*/
6155084Sjohnlev static int
xenbusdrv_devmap(dev_t dev,devmap_cookie_t dhp,offset_t off,size_t len,size_t * maplen,uint_t model)6165084Sjohnlev xenbusdrv_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
6175084Sjohnlev     size_t *maplen, uint_t model)
6185084Sjohnlev {
6195084Sjohnlev 	xenbus_dev_t *xbs;
6205084Sjohnlev 	int err;
6215084Sjohnlev 
6225084Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
6235084Sjohnlev 
6245084Sjohnlev 	if (off != 0 || len != PAGESIZE)
6255084Sjohnlev 		return (-1);
6265084Sjohnlev 
6275084Sjohnlev 	if (!DOMAIN_IS_INITDOMAIN(xen_info))
6285084Sjohnlev 		return (-1);
6295084Sjohnlev 
6305084Sjohnlev 	err = devmap_umem_setup(dhp, xbs->dip, NULL, xb_xenstore_cookie(),
6315084Sjohnlev 	    0, PAGESIZE, PROT_READ | PROT_WRITE | PROT_USER, 0, NULL);
6325084Sjohnlev 
6335084Sjohnlev 	if (err)
6345084Sjohnlev 		return (err);
6355084Sjohnlev 
6365084Sjohnlev 	*maplen = PAGESIZE;
6375084Sjohnlev 
6385084Sjohnlev 	return (0);
6395084Sjohnlev }
6405084Sjohnlev 
6416784Sjohnlev 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)6426784Sjohnlev xenbusdrv_segmap(dev_t dev, off_t off, ddi_as_handle_t as, caddr_t *addrp,
6436784Sjohnlev     off_t len, uint_t prot, uint_t maxprot, uint_t flags, cred_t *cr)
6446784Sjohnlev {
6456784Sjohnlev 
6466784Sjohnlev 	if (secpolicy_xvm_control(cr))
6476784Sjohnlev 		return (EPERM);
6486784Sjohnlev 
6496784Sjohnlev 	return (ddi_devmap_segmap(dev, off, as, addrp, len, prot,
6506784Sjohnlev 	    maxprot, flags, cr));
6516784Sjohnlev }
6526784Sjohnlev 
6535084Sjohnlev /*ARGSUSED*/
6545084Sjohnlev static int
xenbusdrv_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cr,int * rvalp)6556784Sjohnlev xenbusdrv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cr,
6565084Sjohnlev     int *rvalp)
6575084Sjohnlev {
6585084Sjohnlev 	xenbus_dev_t *xbs;
6595084Sjohnlev 
6606784Sjohnlev 	if (secpolicy_xvm_control(cr))
6616784Sjohnlev 		return (EPERM);
6626784Sjohnlev 
6635084Sjohnlev 	xbs = XENBUSDRV_INST2SOFTS(XENBUSDRV_MINOR2INST(getminor(dev)));
6645084Sjohnlev 	switch (cmd) {
6655084Sjohnlev 	case IOCTL_XENBUS_XENSTORE_EVTCHN:
6665084Sjohnlev 		*rvalp = xen_info->store_evtchn;
6675084Sjohnlev 		break;
6685084Sjohnlev 	case IOCTL_XENBUS_NOTIFY_UP:
6695084Sjohnlev 		xs_notify_xenstore_up();
6705084Sjohnlev 		xbs->xenstore_inst = 1;
6715084Sjohnlev 		break;
6725084Sjohnlev 	default:
6735084Sjohnlev 		return (EINVAL);
6745084Sjohnlev 	}
6755084Sjohnlev 
6765084Sjohnlev 	return (0);
6775084Sjohnlev }
678