xref: /titanic_51/usr/src/uts/sun4v/io/glvc/glvc.c (revision d3d50737e566cade9a08d73d2af95105ac7cd960)
14ab777b1Swh94709 /*
24ab777b1Swh94709  * CDDL HEADER START
34ab777b1Swh94709  *
44ab777b1Swh94709  * The contents of this file are subject to the terms of the
54ab777b1Swh94709  * Common Development and Distribution License (the "License").
64ab777b1Swh94709  * You may not use this file except in compliance with the License.
74ab777b1Swh94709  *
84ab777b1Swh94709  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94ab777b1Swh94709  * or http://www.opensolaris.org/os/licensing.
104ab777b1Swh94709  * See the License for the specific language governing permissions
114ab777b1Swh94709  * and limitations under the License.
124ab777b1Swh94709  *
134ab777b1Swh94709  * When distributing Covered Code, include this CDDL HEADER in each
144ab777b1Swh94709  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154ab777b1Swh94709  * If applicable, add the following below this CDDL HEADER, with the
164ab777b1Swh94709  * fields enclosed by brackets "[]" replaced with your own identifying
174ab777b1Swh94709  * information: Portions Copyright [yyyy] [name of copyright owner]
184ab777b1Swh94709  *
194ab777b1Swh94709  * CDDL HEADER END
204ab777b1Swh94709  */
214ab777b1Swh94709 /*
22*d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234ab777b1Swh94709  * Use is subject to license terms.
244ab777b1Swh94709  */
254ab777b1Swh94709 
264ab777b1Swh94709 
274ab777b1Swh94709 #include <sys/time.h>
284ab777b1Swh94709 #include <sys/errno.h>
294ab777b1Swh94709 #include <sys/kmem.h>
304ab777b1Swh94709 #include <sys/stat.h>
314ab777b1Swh94709 #include <sys/cmn_err.h>
324ab777b1Swh94709 
334ab777b1Swh94709 #include <sys/conf.h>
344ab777b1Swh94709 #include <sys/modctl.h>
354ab777b1Swh94709 #include <sys/devops.h>
364ab777b1Swh94709 #include <sys/ddi.h>
374ab777b1Swh94709 #include <sys/sunddi.h>
384ab777b1Swh94709 #include <sys/callb.h>
394ab777b1Swh94709 #include <sys/disp.h>
404ab777b1Swh94709 #include <sys/strlog.h>
414ab777b1Swh94709 #include <sys/file.h>
424ab777b1Swh94709 
434ab777b1Swh94709 #include <sys/uadmin.h>
444ab777b1Swh94709 #include <sys/machsystm.h>
454ab777b1Swh94709 #include <sys/hypervisor_api.h>
464ab777b1Swh94709 #include <sys/hsvc.h>
474ab777b1Swh94709 #include <sys/glvc.h>
484ab777b1Swh94709 
494ab777b1Swh94709 /*
504ab777b1Swh94709  * Global Variables - can be patched from Solaris
514ab777b1Swh94709  * ==============================================
524ab777b1Swh94709  */
534ab777b1Swh94709 
544ab777b1Swh94709 /* bit defination in virtual device register */
554ab777b1Swh94709 #define	GLVC_REG_RECV		0x0001
564ab777b1Swh94709 #define	GLVC_REG_RECV_ENA	0x0002
574ab777b1Swh94709 #define	GLVC_REG_SEND		0x0004
584ab777b1Swh94709 #define	GLVC_REG_SEND_ENA	0x0008
594ab777b1Swh94709 #define	GLVC_REG_ERR		0x8000
604ab777b1Swh94709 
614ab777b1Swh94709 /*
624ab777b1Swh94709  * For interrupt mode
634ab777b1Swh94709  */
644ab777b1Swh94709 #define	GLVC_MODE_NONE		0
654ab777b1Swh94709 #define	GLVC_POLLING_MODE	1
664ab777b1Swh94709 #define	GLVC_INTR_MODE		2
674ab777b1Swh94709 
684ab777b1Swh94709 /*
694ab777b1Swh94709  * For open
704ab777b1Swh94709  */
714ab777b1Swh94709 #define	GLVC_NO_OPEN		0
724ab777b1Swh94709 #define	GLVC_OPEN		1
734ab777b1Swh94709 #define	GLVC_EXCL_OPEN		2
744ab777b1Swh94709 
754ab777b1Swh94709 /*
764ab777b1Swh94709  * For timeout polling, in microsecond.
774ab777b1Swh94709  */
784ab777b1Swh94709 #define	GLVC_TIMEOUT_POLL	5000000		/* Timeout in intr mode */
794ab777b1Swh94709 #define	GLVC_POLLMODE_POLL	500000		/* Interval in polling mode */
804ab777b1Swh94709 
814ab777b1Swh94709 /*
824ab777b1Swh94709  * For debug printing
834ab777b1Swh94709  */
844ab777b1Swh94709 #define	_PRINTF			printf
854ab777b1Swh94709 #define	DPRINTF(args)		if (glvc_debug) _PRINTF args;
864ab777b1Swh94709 
874ab777b1Swh94709 /*
884ab777b1Swh94709  * Driver entry points
894ab777b1Swh94709  */
904ab777b1Swh94709 static int	glvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
914ab777b1Swh94709 static int	glvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
924ab777b1Swh94709 static int	glvc_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
934ab777b1Swh94709 static int	glvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
944ab777b1Swh94709 static int	glvc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
954ab777b1Swh94709     cred_t *cred_p, int *rval_p);
964ab777b1Swh94709 static int	glvc_read(dev_t dev, struct uio *uiop, cred_t *credp);
974ab777b1Swh94709 static int	glvc_write(dev_t dev, struct uio *uiop, cred_t *credp);
984ab777b1Swh94709 
994ab777b1Swh94709 static struct cb_ops glvc_cb_ops = {
1004ab777b1Swh94709 	glvc_open,	/* open */
1014ab777b1Swh94709 	glvc_close,	/* close */
1024ab777b1Swh94709 	nodev,		/* strategy() */
1034ab777b1Swh94709 	nodev,		/* print() */
1044ab777b1Swh94709 	nodev,		/* dump() */
1054ab777b1Swh94709 	glvc_read,	/* read() */
1064ab777b1Swh94709 	glvc_write,	/* write() */
1074ab777b1Swh94709 	glvc_ioctl,	/* ioctl() */
1084ab777b1Swh94709 	nodev,		/* devmap() */
1094ab777b1Swh94709 	nodev,		/* mmap() */
1104ab777b1Swh94709 	ddi_segmap,	/* segmap() */
1114ab777b1Swh94709 	nochpoll,	/* poll() */
1124ab777b1Swh94709 	ddi_prop_op,    /* prop_op() */
1134ab777b1Swh94709 	NULL,		/* cb_str */
1144ab777b1Swh94709 	D_NEW | D_MP	/* cb_flag */
1154ab777b1Swh94709 };
1164ab777b1Swh94709 
1174ab777b1Swh94709 
1184ab777b1Swh94709 static struct dev_ops glvc_ops = {
1194ab777b1Swh94709 	DEVO_REV,
1204ab777b1Swh94709 	0,			/* ref count */
1214ab777b1Swh94709 	ddi_getinfo_1to1,	/* getinfo() */
1224ab777b1Swh94709 	nulldev,		/* identify() */
1234ab777b1Swh94709 	nulldev,		/* probe() */
1244ab777b1Swh94709 	glvc_attach,		/* attach() */
1254ab777b1Swh94709 	glvc_detach,		/* detach */
1264ab777b1Swh94709 	nodev,			/* reset */
1274ab777b1Swh94709 	&glvc_cb_ops,		/* pointer to cb_ops structure */
1284ab777b1Swh94709 	(struct bus_ops *)NULL,
12919397407SSherry Moore 	nulldev,		/* power() */
13019397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
1314ab777b1Swh94709 };
1324ab777b1Swh94709 
1334ab777b1Swh94709 /*
1344ab777b1Swh94709  * Loadable module support.
1354ab777b1Swh94709  */
1364ab777b1Swh94709 extern struct mod_ops mod_driverops;
1374ab777b1Swh94709 
1384ab777b1Swh94709 static struct modldrv modldrv = {
1394ab777b1Swh94709 	&mod_driverops,			/* Type of module. This is a driver */
14019397407SSherry Moore 	"Sun4v virtual channel driver",	/* Name of the module */
1414ab777b1Swh94709 	&glvc_ops			/* pointer to the dev_ops structure */
1424ab777b1Swh94709 };
1434ab777b1Swh94709 
1444ab777b1Swh94709 static struct modlinkage modlinkage = {
1454ab777b1Swh94709 	MODREV_1,
1464ab777b1Swh94709 	&modldrv,
1474ab777b1Swh94709 	NULL
1484ab777b1Swh94709 };
1494ab777b1Swh94709 
1504ab777b1Swh94709 typedef struct glvc_soft_state {
1514ab777b1Swh94709 	dev_info_t *dip;	/* dev info of myself */
1524ab777b1Swh94709 	uint64_t s_id;		/* service id for this node */
1534ab777b1Swh94709 	uint64_t mtu;		/* max transmit unit size */
1544ab777b1Swh94709 	uint64_t flag;		/* flag register */
1554ab777b1Swh94709 	kmutex_t open_mutex;	/* protect open_state flag */
1564ab777b1Swh94709 	uint8_t open_state;	/* no-open, open or open exclusively */
1574ab777b1Swh94709 	kmutex_t recv_mutex;
1584ab777b1Swh94709 	kmutex_t send_complete_mutex;
1594ab777b1Swh94709 	uint8_t send_complete_flag;	/* 1 = send completed */
1604ab777b1Swh94709 	uint8_t intr_mode;	/* 1 = polling mode, 2 = interrupt mode */
1614ab777b1Swh94709 	clock_t polling_interval;
1624ab777b1Swh94709 	ddi_softintr_t poll_mode_softint_id;
1634ab777b1Swh94709 	kcondvar_t recv_cv;
1644ab777b1Swh94709 	kcondvar_t send_complete_cv;
1654ab777b1Swh94709 	kmutex_t statusreg_mutex;	/* Protects status register */
1664ab777b1Swh94709 	char *mb_recv_buf;
1674ab777b1Swh94709 	char *mb_send_buf;
1684ab777b1Swh94709 	uint64_t mb_recv_buf_pa;
1694ab777b1Swh94709 	uint64_t mb_send_buf_pa;
1704ab777b1Swh94709 } glvc_soft_state_t;
1714ab777b1Swh94709 
1724ab777b1Swh94709 /*
1734ab777b1Swh94709  * Hypervisor VSC api versioning information for glvc driver.
1744ab777b1Swh94709  */
1754ab777b1Swh94709 static uint64_t	glvc_vsc_min_ver; /* Negotiated VSC API minor version */
1764ab777b1Swh94709 static uint_t glvc_vsc_users = 0; /* VSC API users */
1774ab777b1Swh94709 static kmutex_t glvc_vsc_users_mutex;	/* Mutex to protect user count */
1784ab777b1Swh94709 
1794ab777b1Swh94709 static hsvc_info_t glvc_hsvc = {
1804ab777b1Swh94709 	HSVC_REV_1, NULL, HSVC_GROUP_VSC, GLVC_VSC_MAJOR_VER,
1814ab777b1Swh94709 	GLVC_VSC_MINOR_VER, "glvc"
1824ab777b1Swh94709 };
1834ab777b1Swh94709 
1844ab777b1Swh94709 /*
1854ab777b1Swh94709  * Module Variables
1864ab777b1Swh94709  * ================
1874ab777b1Swh94709  */
1884ab777b1Swh94709 
1894ab777b1Swh94709 /*
1904ab777b1Swh94709  * functions local to this driver.
1914ab777b1Swh94709  */
1924ab777b1Swh94709 static int	glvc_add_intr_handlers(dev_info_t *dip);
1934ab777b1Swh94709 static int	glvc_remove_intr_handlers(dev_info_t *dip);
1944ab777b1Swh94709 static uint_t	glvc_intr(caddr_t arg);
1954ab777b1Swh94709 static int	glvc_peek(glvc_soft_state_t *softsp,
1964ab777b1Swh94709     glvc_xport_msg_peek_t *msg_peek);
1974ab777b1Swh94709 static uint_t	glvc_soft_intr(caddr_t arg);
1984ab777b1Swh94709 static int	glvc_emap_h2s(uint64_t hv_errcode);
1994ab777b1Swh94709 static int	glvc_ioctl_opt_op(glvc_soft_state_t *softsp,
2004ab777b1Swh94709     intptr_t arg, int mode);
2014ab777b1Swh94709 
2024ab777b1Swh94709 /*
2034ab777b1Swh94709  * Driver globals
2044ab777b1Swh94709  */
2054ab777b1Swh94709 static void *glvc_ssp; /* pointer to driver soft state */
2064ab777b1Swh94709 
2074ab777b1Swh94709 static uint_t glvc_debug = 0;
2084ab777b1Swh94709 
2094ab777b1Swh94709 int
2104ab777b1Swh94709 _init(void)
2114ab777b1Swh94709 {
2124ab777b1Swh94709 	int	error = 0;
2134ab777b1Swh94709 
2144ab777b1Swh94709 	if ((error = ddi_soft_state_init(&glvc_ssp,
2154ab777b1Swh94709 	    sizeof (glvc_soft_state_t), 1)) != 0)
2164ab777b1Swh94709 		return (error);
2174ab777b1Swh94709 
2184ab777b1Swh94709 	/*
2194ab777b1Swh94709 	 * Initialize the mutex for global data structure
2204ab777b1Swh94709 	 */
2214ab777b1Swh94709 	mutex_init(&glvc_vsc_users_mutex, NULL, MUTEX_DRIVER, NULL);
2224ab777b1Swh94709 
2234ab777b1Swh94709 	error = mod_install(&modlinkage);
2244ab777b1Swh94709 
2254ab777b1Swh94709 	return (error);
2264ab777b1Swh94709 }
2274ab777b1Swh94709 
2284ab777b1Swh94709 
2294ab777b1Swh94709 int
2304ab777b1Swh94709 _info(struct modinfo *modinfop)
2314ab777b1Swh94709 {
2324ab777b1Swh94709 	return (mod_info(&modlinkage, modinfop));
2334ab777b1Swh94709 }
2344ab777b1Swh94709 
2354ab777b1Swh94709 
2364ab777b1Swh94709 int
2374ab777b1Swh94709 _fini(void)
2384ab777b1Swh94709 {
2394ab777b1Swh94709 	int	error = 0;
2404ab777b1Swh94709 
2414ab777b1Swh94709 	error = mod_remove(&modlinkage);
2424ab777b1Swh94709 	if (error)
2434ab777b1Swh94709 		return (error);
2444ab777b1Swh94709 
2454ab777b1Swh94709 	mutex_destroy(&glvc_vsc_users_mutex);
2464ab777b1Swh94709 
2474ab777b1Swh94709 	ddi_soft_state_fini(&glvc_ssp);
2484ab777b1Swh94709 	return (0);
2494ab777b1Swh94709 }
2504ab777b1Swh94709 
2514ab777b1Swh94709 
2524ab777b1Swh94709 static int
2534ab777b1Swh94709 glvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2544ab777b1Swh94709 {
2554ab777b1Swh94709 	int			instance;
2564ab777b1Swh94709 	int			err;
2574ab777b1Swh94709 	glvc_soft_state_t 	*softsp;
2584ab777b1Swh94709 
2594ab777b1Swh94709 	switch (cmd) {
2604ab777b1Swh94709 	case DDI_ATTACH:
2614ab777b1Swh94709 		instance = ddi_get_instance(dip);
2624ab777b1Swh94709 
2634ab777b1Swh94709 		/*
2644ab777b1Swh94709 		 * Negotiate the API version for VSC hypervisor services.
2654ab777b1Swh94709 		 */
2664ab777b1Swh94709 		mutex_enter(&glvc_vsc_users_mutex);
2674ab777b1Swh94709 		if (glvc_vsc_users == 0 &&
2684ab777b1Swh94709 		    (err = hsvc_register(&glvc_hsvc, &glvc_vsc_min_ver))
2694ab777b1Swh94709 		    != 0) {
2704ab777b1Swh94709 			cmn_err(CE_WARN, "%s: cannot negotiate hypervisor "
2714ab777b1Swh94709 			    "services group: 0x%lx major: 0x%lx minor: 0x%lx "
2724ab777b1Swh94709 			    "errno: %d\n", glvc_hsvc.hsvc_modname,
2734ab777b1Swh94709 			    glvc_hsvc.hsvc_group, glvc_hsvc.hsvc_major,
2744ab777b1Swh94709 			    glvc_hsvc.hsvc_minor, err);
2754ab777b1Swh94709 
2764ab777b1Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
2774ab777b1Swh94709 			return (DDI_FAILURE);
2784ab777b1Swh94709 		} else {
2794ab777b1Swh94709 			glvc_vsc_users++;
2804ab777b1Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
2814ab777b1Swh94709 		}
2824ab777b1Swh94709 
2834ab777b1Swh94709 		DPRINTF(("Glvc instance %d negotiated VSC API version, "
2844ab777b1Swh94709 		    " major 0x%lx minor 0x%lx\n",
2854ab777b1Swh94709 		    instance, glvc_hsvc.hsvc_major, glvc_vsc_min_ver));
2864ab777b1Swh94709 
2874ab777b1Swh94709 		if (ddi_soft_state_zalloc(glvc_ssp, instance)
2884ab777b1Swh94709 		    != DDI_SUCCESS) {
2894ab777b1Swh94709 			mutex_enter(&glvc_vsc_users_mutex);
2904ab777b1Swh94709 			if (--glvc_vsc_users == 0)
2914ab777b1Swh94709 				(void) hsvc_unregister(&glvc_hsvc);
2924ab777b1Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
2934ab777b1Swh94709 			return (DDI_FAILURE);
2944ab777b1Swh94709 		}
2954ab777b1Swh94709 
2964ab777b1Swh94709 		softsp = ddi_get_soft_state(glvc_ssp, instance);
2974ab777b1Swh94709 
2984ab777b1Swh94709 		/* Set the dip in the soft state */
2994ab777b1Swh94709 		softsp->dip = dip;
3004ab777b1Swh94709 
3014ab777b1Swh94709 		softsp->open_state = GLVC_NO_OPEN;
3024ab777b1Swh94709 		softsp->send_complete_flag = 1;
3034ab777b1Swh94709 
3044ab777b1Swh94709 		glvc_debug = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
3054ab777b1Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "glvc_debug", glvc_debug);
3064ab777b1Swh94709 
3074ab777b1Swh94709 		if ((softsp->s_id = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
3084ab777b1Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "channel#", -1))
3094ab777b1Swh94709 		    == -1) {
3104ab777b1Swh94709 			cmn_err(CE_WARN, "Failed to get channel#");
3114ab777b1Swh94709 			goto bad;
3124ab777b1Swh94709 		}
3134ab777b1Swh94709 
3144ab777b1Swh94709 		if ((softsp->mtu = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
3154ab777b1Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "mtu", -1))
3164ab777b1Swh94709 		    <= 0) {
3174ab777b1Swh94709 			cmn_err(CE_WARN, "Failed to get mtu");
3184ab777b1Swh94709 			goto bad;
3194ab777b1Swh94709 		}
3204ab777b1Swh94709 
3214ab777b1Swh94709 		softsp->mb_recv_buf =
3224ab777b1Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
3234ab777b1Swh94709 		if (softsp->mb_recv_buf == NULL) {
3244ab777b1Swh94709 			cmn_err(CE_WARN, "Failed to alloc mem for recv buf");
3254ab777b1Swh94709 			goto bad;
3264ab777b1Swh94709 		}
3274ab777b1Swh94709 		softsp->mb_recv_buf_pa =
3284ab777b1Swh94709 		    va_to_pa((caddr_t)softsp->mb_recv_buf);
3294ab777b1Swh94709 
3304ab777b1Swh94709 		softsp->mb_send_buf =
3314ab777b1Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
3324ab777b1Swh94709 		if (softsp->mb_send_buf == NULL) {
3334ab777b1Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
3344ab777b1Swh94709 			cmn_err(CE_WARN, "Failed to alloc mem for send buf");
3354ab777b1Swh94709 			goto bad;
3364ab777b1Swh94709 		}
3374ab777b1Swh94709 		softsp->mb_send_buf_pa =
3384ab777b1Swh94709 		    va_to_pa((caddr_t)softsp->mb_send_buf);
3394ab777b1Swh94709 
3404ab777b1Swh94709 		err = ddi_create_minor_node(dip, "glvc", S_IFCHR,
3414ab777b1Swh94709 		    instance, DDI_PSEUDO, NULL);
3424ab777b1Swh94709 		if (err != DDI_SUCCESS) {
3434ab777b1Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
3444ab777b1Swh94709 			kmem_free(softsp->mb_send_buf, softsp->mtu);
3454ab777b1Swh94709 			cmn_err(CE_WARN, "Failed to create minor node");
3464ab777b1Swh94709 			goto bad;
3474ab777b1Swh94709 		}
3484ab777b1Swh94709 
3494ab777b1Swh94709 		mutex_init(&(softsp->open_mutex), NULL, MUTEX_DRIVER, NULL);
3504ab777b1Swh94709 		mutex_init(&(softsp->recv_mutex), NULL, MUTEX_DRIVER, NULL);
3514ab777b1Swh94709 		mutex_init(&(softsp->send_complete_mutex), NULL,
3524ab777b1Swh94709 		    MUTEX_DRIVER, NULL);
3534ab777b1Swh94709 		mutex_init(&(softsp->statusreg_mutex), NULL,
3544ab777b1Swh94709 		    MUTEX_DRIVER, NULL);
3554ab777b1Swh94709 		cv_init(&(softsp->recv_cv), NULL, CV_DRIVER, NULL);
3564ab777b1Swh94709 		cv_init(&(softsp->send_complete_cv), NULL, CV_DRIVER, NULL);
3574ab777b1Swh94709 
3584ab777b1Swh94709 		/*
3594ab777b1Swh94709 		 * Add the handlers which watch for unsolicited messages
3604ab777b1Swh94709 		 * and post event to Sysevent Framework.
3614ab777b1Swh94709 		 */
3624ab777b1Swh94709 		err = glvc_add_intr_handlers(dip);
3634ab777b1Swh94709 		if (err != DDI_SUCCESS) {
3644ab777b1Swh94709 			cmn_err(CE_WARN, "Failed to add intr handler");
3654ab777b1Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
3664ab777b1Swh94709 			kmem_free(softsp->mb_send_buf, softsp->mtu);
3674ab777b1Swh94709 			ddi_remove_minor_node(dip, NULL);
3684ab777b1Swh94709 			goto bad1;
3694ab777b1Swh94709 		}
3704ab777b1Swh94709 
3714ab777b1Swh94709 		/*
3724ab777b1Swh94709 		 * Trigger soft interrupt to start polling device if
3734ab777b1Swh94709 		 * we are in the polling mode
3744ab777b1Swh94709 		 */
3754ab777b1Swh94709 		if (softsp->intr_mode == GLVC_POLLING_MODE)
3764ab777b1Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
3774ab777b1Swh94709 
3784ab777b1Swh94709 		ddi_report_dev(dip);
3794ab777b1Swh94709 
3804ab777b1Swh94709 		DPRINTF(("glvc instance %d, s_id %lu,"
3814ab777b1Swh94709 		    "mtu %lu attached\n", instance, softsp->s_id,
3824ab777b1Swh94709 		    softsp->mtu));
3834ab777b1Swh94709 
3844ab777b1Swh94709 		return (DDI_SUCCESS);
3854ab777b1Swh94709 	case DDI_RESUME:
3864ab777b1Swh94709 		return (DDI_SUCCESS);
3874ab777b1Swh94709 	default:
3884ab777b1Swh94709 		return (DDI_FAILURE);
3894ab777b1Swh94709 	}
3904ab777b1Swh94709 
3914ab777b1Swh94709 bad1:
3924ab777b1Swh94709 	cv_destroy(&(softsp->send_complete_cv));
3934ab777b1Swh94709 	cv_destroy(&(softsp->recv_cv));
3944ab777b1Swh94709 	mutex_destroy(&(softsp->open_mutex));
3954ab777b1Swh94709 	mutex_destroy(&(softsp->send_complete_mutex));
3964ab777b1Swh94709 	mutex_destroy(&(softsp->recv_mutex));
3974ab777b1Swh94709 	mutex_destroy(&(softsp->statusreg_mutex));
3984ab777b1Swh94709 
3994ab777b1Swh94709 bad:
4004ab777b1Swh94709 	mutex_enter(&glvc_vsc_users_mutex);
4014ab777b1Swh94709 	if (--glvc_vsc_users == 0)
4024ab777b1Swh94709 		(void) hsvc_unregister(&glvc_hsvc);
4034ab777b1Swh94709 	mutex_exit(&glvc_vsc_users_mutex);
4044ab777b1Swh94709 	cmn_err(CE_WARN, "glvc: attach failed for instance %d\n", instance);
4054ab777b1Swh94709 	ddi_soft_state_free(glvc_ssp, instance);
4064ab777b1Swh94709 	return (DDI_FAILURE);
4074ab777b1Swh94709 }
4084ab777b1Swh94709 
4094ab777b1Swh94709 
4104ab777b1Swh94709 static int
4114ab777b1Swh94709 glvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4124ab777b1Swh94709 {
4134ab777b1Swh94709 	int	instance;
4144ab777b1Swh94709 	int	err;
4154ab777b1Swh94709 	glvc_soft_state_t 	*softsp;
4164ab777b1Swh94709 
4174ab777b1Swh94709 
4184ab777b1Swh94709 	switch (cmd) {
4194ab777b1Swh94709 	case DDI_DETACH:
4204ab777b1Swh94709 		instance = ddi_get_instance(dip);
4214ab777b1Swh94709 
4224ab777b1Swh94709 		softsp = ddi_get_soft_state(glvc_ssp, instance);
4234ab777b1Swh94709 
4244ab777b1Swh94709 		cv_destroy(&(softsp->send_complete_cv));
4254ab777b1Swh94709 		cv_destroy(&(softsp->recv_cv));
4264ab777b1Swh94709 		mutex_destroy(&(softsp->open_mutex));
4274ab777b1Swh94709 		mutex_destroy(&(softsp->statusreg_mutex));
4284ab777b1Swh94709 		mutex_destroy(&(softsp->send_complete_mutex));
4294ab777b1Swh94709 		mutex_destroy(&(softsp->recv_mutex));
4304ab777b1Swh94709 
4314ab777b1Swh94709 		kmem_free(softsp->mb_recv_buf, softsp->mtu);
4324ab777b1Swh94709 		kmem_free(softsp->mb_send_buf, softsp->mtu);
4334ab777b1Swh94709 
4344ab777b1Swh94709 		err = glvc_remove_intr_handlers(dip);
4354ab777b1Swh94709 
4364ab777b1Swh94709 		if (err != DDI_SUCCESS) {
4374ab777b1Swh94709 			cmn_err(CE_WARN, "Failed to remove event handlers");
4384ab777b1Swh94709 			return (DDI_FAILURE);
4394ab777b1Swh94709 		}
4404ab777b1Swh94709 
4414ab777b1Swh94709 		ddi_remove_minor_node(dip, NULL);
4424ab777b1Swh94709 
4434ab777b1Swh94709 		ddi_soft_state_free(glvc_ssp, instance);
4444ab777b1Swh94709 
4454ab777b1Swh94709 		mutex_enter(&glvc_vsc_users_mutex);
4464ab777b1Swh94709 		if (--glvc_vsc_users == 0)
4474ab777b1Swh94709 			(void) hsvc_unregister(&glvc_hsvc);
4484ab777b1Swh94709 		mutex_exit(&glvc_vsc_users_mutex);
4494ab777b1Swh94709 
4504ab777b1Swh94709 		return (DDI_SUCCESS);
4514ab777b1Swh94709 	case DDI_SUSPEND:
4524ab777b1Swh94709 		return (DDI_SUCCESS);
4534ab777b1Swh94709 	default:
4544ab777b1Swh94709 		return (DDI_FAILURE);
4554ab777b1Swh94709 	}
4564ab777b1Swh94709 }
4574ab777b1Swh94709 
4584ab777b1Swh94709 static int
4594ab777b1Swh94709 glvc_add_intr_handlers(dev_info_t *dip)
4604ab777b1Swh94709 {
4614ab777b1Swh94709 	int	instance;
4624ab777b1Swh94709 	glvc_soft_state_t	*softsp;
4634ab777b1Swh94709 	int	err = DDI_FAILURE;
4644ab777b1Swh94709 	uint64_t polling_interval;
4654ab777b1Swh94709 
4664ab777b1Swh94709 	instance = ddi_get_instance(dip);
4674ab777b1Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
4684ab777b1Swh94709 
4694ab777b1Swh94709 	if ((uint64_t)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
4704ab777b1Swh94709 	    DDI_PROP_DONTPASS, "flags", -1) != -1) {
4714ab777b1Swh94709 		err = ddi_add_intr(dip, 0, NULL, NULL, glvc_intr,
4724ab777b1Swh94709 		    (caddr_t)softsp);
4734ab777b1Swh94709 		if (err != DDI_SUCCESS)
4744ab777b1Swh94709 			cmn_err(CE_NOTE, "glvc, instance %d"
4754ab777b1Swh94709 			    " ddi_add_intr() failed, using"
4764ab777b1Swh94709 			    " polling mode", instance);
4774ab777b1Swh94709 	}
4784ab777b1Swh94709 
4794ab777b1Swh94709 	if (err == DDI_SUCCESS) {
4804ab777b1Swh94709 		softsp->intr_mode = GLVC_INTR_MODE;
4814ab777b1Swh94709 		polling_interval = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
4824ab777b1Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "intrmode_poll",
4834ab777b1Swh94709 		    GLVC_TIMEOUT_POLL);
4844ab777b1Swh94709 		DPRINTF(("glvc instance %d polling_interval = %lu\n",
4854ab777b1Swh94709 		    instance, polling_interval));
4864ab777b1Swh94709 		softsp->polling_interval = drv_usectohz(polling_interval);
4874ab777b1Swh94709 	} else {
4884ab777b1Swh94709 		DPRINTF(("glvc, instance %d  intr support not found, "
4894ab777b1Swh94709 		    "err = %d , use polling mode", instance, err));
4904ab777b1Swh94709 		softsp->intr_mode = GLVC_POLLING_MODE;
4914ab777b1Swh94709 		polling_interval =
4924ab777b1Swh94709 		    (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
4934ab777b1Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "pollmode_poll",
4944ab777b1Swh94709 		    GLVC_POLLMODE_POLL);
4954ab777b1Swh94709 		DPRINTF(("glvc instance %d polling_interval = %lu\n",
4964ab777b1Swh94709 		    instance, polling_interval));
4974ab777b1Swh94709 		softsp->polling_interval =
4984ab777b1Swh94709 		    drv_usectohz(polling_interval);
4994ab777b1Swh94709 	}
5004ab777b1Swh94709 
5014ab777b1Swh94709 	/* Now enable interrupt bits in the status register */
5024ab777b1Swh94709 	if (softsp->intr_mode == GLVC_INTR_MODE) {
5034ab777b1Swh94709 		err = hv_service_setstatus(softsp->s_id,
5044ab777b1Swh94709 		    GLVC_REG_RECV_ENA|GLVC_REG_SEND_ENA);
5054ab777b1Swh94709 		if (err != H_EOK) {
5064ab777b1Swh94709 			cmn_err(CE_NOTE, "glvc instance %d"
5074ab777b1Swh94709 			    " cannot enable receive interrupt\n",
5084ab777b1Swh94709 			    instance);
5094ab777b1Swh94709 			return (DDI_FAILURE);
5104ab777b1Swh94709 		}
5114ab777b1Swh94709 	}
5124ab777b1Swh94709 
5134ab777b1Swh94709 
5144ab777b1Swh94709 	err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
5154ab777b1Swh94709 	    &softsp->poll_mode_softint_id, NULL, NULL,
5164ab777b1Swh94709 	    glvc_soft_intr, (caddr_t)softsp);
5174ab777b1Swh94709 
5184ab777b1Swh94709 	return (err);
5194ab777b1Swh94709 }
5204ab777b1Swh94709 
5214ab777b1Swh94709 static int
5224ab777b1Swh94709 glvc_remove_intr_handlers(dev_info_t *dip)
5234ab777b1Swh94709 {
5244ab777b1Swh94709 	int	instance;
5254ab777b1Swh94709 	glvc_soft_state_t	*softsp;
5264ab777b1Swh94709 
5274ab777b1Swh94709 	instance = ddi_get_instance(dip);
5284ab777b1Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
5294ab777b1Swh94709 
5304ab777b1Swh94709 	if (softsp->intr_mode ==  GLVC_INTR_MODE)
5314ab777b1Swh94709 		ddi_remove_intr(dip, 0, NULL);
5324ab777b1Swh94709 
5334ab777b1Swh94709 	ddi_remove_softintr(softsp->poll_mode_softint_id);
5344ab777b1Swh94709 
5354ab777b1Swh94709 	softsp->intr_mode = GLVC_MODE_NONE;
5364ab777b1Swh94709 	softsp->polling_interval = 0;
5374ab777b1Swh94709 
5384ab777b1Swh94709 	return (DDI_SUCCESS);
5394ab777b1Swh94709 }
5404ab777b1Swh94709 
5414ab777b1Swh94709 static uint_t
5424ab777b1Swh94709 glvc_soft_intr(caddr_t arg)
5434ab777b1Swh94709 {
5444ab777b1Swh94709 	/*
5454ab777b1Swh94709 	 * Call the interrupt handle routine to check the register
5464ab777b1Swh94709 	 * status.
5474ab777b1Swh94709 	 */
5484ab777b1Swh94709 	(uint_t)glvc_intr(arg);
5494ab777b1Swh94709 
5504ab777b1Swh94709 	return (DDI_INTR_CLAIMED);
5514ab777b1Swh94709 }
5524ab777b1Swh94709 
5534ab777b1Swh94709 /*ARGSUSED*/
5544ab777b1Swh94709 static int
5554ab777b1Swh94709 glvc_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
5564ab777b1Swh94709 {
5574ab777b1Swh94709 	int error = 0;
5584ab777b1Swh94709 	int instance;
5594ab777b1Swh94709 	glvc_soft_state_t *softsp;
5604ab777b1Swh94709 
5614ab777b1Swh94709 	instance = getminor(*dev_p);
5624ab777b1Swh94709 
5634ab777b1Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
5644ab777b1Swh94709 
5654ab777b1Swh94709 	mutex_enter(&softsp->open_mutex);
5664ab777b1Swh94709 
5674ab777b1Swh94709 	switch (softsp->open_state) {
5684ab777b1Swh94709 	case GLVC_NO_OPEN:
5694ab777b1Swh94709 		if (flag & FEXCL)
5704ab777b1Swh94709 			softsp->open_state = GLVC_EXCL_OPEN;
5714ab777b1Swh94709 		else
5724ab777b1Swh94709 			softsp->open_state = GLVC_OPEN;
5734ab777b1Swh94709 		break;
5744ab777b1Swh94709 
5754ab777b1Swh94709 	case GLVC_OPEN:
5764ab777b1Swh94709 		if (flag & FEXCL)
5774ab777b1Swh94709 			error = EBUSY;
5784ab777b1Swh94709 		break;
5794ab777b1Swh94709 
5804ab777b1Swh94709 	case GLVC_EXCL_OPEN:
5814ab777b1Swh94709 		error = EBUSY;
5824ab777b1Swh94709 		break;
5834ab777b1Swh94709 
5844ab777b1Swh94709 	default:
5854ab777b1Swh94709 		/* Should not happen */
5864ab777b1Swh94709 		cmn_err(CE_WARN, "glvc_open: bad open state %d.",
5874ab777b1Swh94709 		    softsp->open_state);
5884ab777b1Swh94709 		error = ENXIO;
5894ab777b1Swh94709 		break;
5904ab777b1Swh94709 	}
5914ab777b1Swh94709 
5924ab777b1Swh94709 	mutex_exit(&softsp->open_mutex);
5934ab777b1Swh94709 
5944ab777b1Swh94709 	return (error);
5954ab777b1Swh94709 }
5964ab777b1Swh94709 
5974ab777b1Swh94709 /*ARGSUSED*/
5984ab777b1Swh94709 static int
5994ab777b1Swh94709 glvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
6004ab777b1Swh94709 {
6014ab777b1Swh94709 	glvc_soft_state_t *softsp;
6024ab777b1Swh94709 	int instance;
6034ab777b1Swh94709 	int error = 0;
6044ab777b1Swh94709 
6054ab777b1Swh94709 	instance = getminor(dev);
6064ab777b1Swh94709 
6074ab777b1Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
6084ab777b1Swh94709 
6094ab777b1Swh94709 	mutex_enter(&softsp->open_mutex);
6104ab777b1Swh94709 	if (softsp->open_state == GLVC_NO_OPEN) {
6114ab777b1Swh94709 		cmn_err(CE_WARN,
6124ab777b1Swh94709 		    "glvc_close: device already closed");
6134ab777b1Swh94709 		error = ENXIO;
6144ab777b1Swh94709 	} else {
6154ab777b1Swh94709 		softsp->open_state = GLVC_NO_OPEN;
6164ab777b1Swh94709 	}
6174ab777b1Swh94709 	mutex_exit(&softsp->open_mutex);
6184ab777b1Swh94709 
6194ab777b1Swh94709 	return (error);
6204ab777b1Swh94709 }
6214ab777b1Swh94709 
6224ab777b1Swh94709 /*ARGSUSED*/
6234ab777b1Swh94709 static int
6244ab777b1Swh94709 glvc_read(dev_t dev, struct uio *uiop, cred_t *credp)
6254ab777b1Swh94709 {
6264ab777b1Swh94709 	glvc_soft_state_t *softsp;
6274ab777b1Swh94709 	int instance;
6284ab777b1Swh94709 	int rv, error = DDI_SUCCESS;
6294ab777b1Swh94709 	uint64_t hverr, recv_count = 0;
6304ab777b1Swh94709 	uint64_t status_reg;
6314ab777b1Swh94709 
6324ab777b1Swh94709 	instance = getminor(dev);
6334ab777b1Swh94709 
6344ab777b1Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
6354ab777b1Swh94709 
6364ab777b1Swh94709 	mutex_enter(&softsp->recv_mutex);
6374ab777b1Swh94709 
6384ab777b1Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
6394ab777b1Swh94709 	DPRINTF(("glvc_read: err = %ld, getstatus = 0x%lx",
6404ab777b1Swh94709 	    hverr, status_reg));
6414ab777b1Swh94709 
6424ab777b1Swh94709 
6434ab777b1Swh94709 	/*
6444ab777b1Swh94709 	 * If no data available, we wait till we get some.
6454ab777b1Swh94709 	 * Notice we still holding the recv_mutex lock at this
6464ab777b1Swh94709 	 * point.
6474ab777b1Swh94709 	 */
6484ab777b1Swh94709 	while (hverr == H_EOK && (status_reg & GLVC_REG_RECV) !=
6494ab777b1Swh94709 	    GLVC_REG_RECV) {
650*d3d50737SRafael Vanoni 		rv =  cv_reltimedwait_sig(&softsp->recv_cv,
651*d3d50737SRafael Vanoni 		    &softsp->recv_mutex, softsp->polling_interval,
652*d3d50737SRafael Vanoni 		    TR_CLOCK_TICK);
6534ab777b1Swh94709 		if (rv == 0) {
6544ab777b1Swh94709 			/*
6554ab777b1Swh94709 			 * We got interrupted.
6564ab777b1Swh94709 			 */
6574ab777b1Swh94709 			mutex_exit(&softsp->recv_mutex);
6584ab777b1Swh94709 			return (EINTR);
6594ab777b1Swh94709 		}
6604ab777b1Swh94709 		if (rv == -1) {
6614ab777b1Swh94709 			/*
6624ab777b1Swh94709 			 * Timeout wait, trigger a soft intr in case
6634ab777b1Swh94709 			 * we miss an interrupt or in polling mode.
6644ab777b1Swh94709 			 */
6654ab777b1Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
6664ab777b1Swh94709 		}
6674ab777b1Swh94709 		hverr = hv_service_getstatus(softsp->s_id, &status_reg);
6684ab777b1Swh94709 		DPRINTF(("glvc_read: err = %ld, getstatus = 0x%lx",
6694ab777b1Swh94709 		    hverr, status_reg));
6704ab777b1Swh94709 	}
6714ab777b1Swh94709 
6724ab777b1Swh94709 	/* Read data into kernel buffer */
6734ab777b1Swh94709 	hverr = hv_service_recv(softsp->s_id, softsp->mb_recv_buf_pa,
6744ab777b1Swh94709 	    softsp->mtu, &recv_count);
6754ab777b1Swh94709 
6764ab777b1Swh94709 	DPRINTF(("Instance %d glvc_read returns error = %ld, recv_count = %lu",
6774ab777b1Swh94709 	    instance, hverr, recv_count));
6784ab777b1Swh94709 
6794ab777b1Swh94709 	if (hverr == H_EOK) {
6804ab777b1Swh94709 		if (uiop->uio_resid < recv_count) {
6814ab777b1Swh94709 			DPRINTF(("Instance %d, glvc_read user buffer "
6824ab777b1Swh94709 			    "size(%lu) smaller than number of bytes "
6834ab777b1Swh94709 			    "received(%lu).", instance, uiop->uio_resid,
6844ab777b1Swh94709 			    recv_count));
6854ab777b1Swh94709 			mutex_exit(&softsp->recv_mutex);
6864ab777b1Swh94709 			return (EINVAL);
6874ab777b1Swh94709 		}
6884ab777b1Swh94709 		/* move data from kernel to user space */
6894ab777b1Swh94709 		error = uiomove(softsp->mb_recv_buf, recv_count,
6904ab777b1Swh94709 		    UIO_READ, uiop);
6914ab777b1Swh94709 	} else {
6924ab777b1Swh94709 		error = glvc_emap_h2s(hverr);
6934ab777b1Swh94709 	}
6944ab777b1Swh94709 
6954ab777b1Swh94709 	/* Clear the RECV data interrupt bit on device register */
6964ab777b1Swh94709 	if (hv_service_clrstatus(softsp->s_id, GLVC_REG_RECV) != H_EOK) {
6974ab777b1Swh94709 		cmn_err(CE_WARN, "glvc_read clear status reg failed");
6984ab777b1Swh94709 	}
6994ab777b1Swh94709 
7004ab777b1Swh94709 	/* Set RECV interrupt enable bit so we can receive interrupt */
7014ab777b1Swh94709 	if (softsp->intr_mode == GLVC_INTR_MODE)
7024ab777b1Swh94709 		if (hv_service_setstatus(softsp->s_id, GLVC_REG_RECV_ENA)
7034ab777b1Swh94709 		    != H_EOK) {
7044ab777b1Swh94709 			cmn_err(CE_WARN, "glvc_read set status reg failed");
7054ab777b1Swh94709 		}
7064ab777b1Swh94709 
7074ab777b1Swh94709 	mutex_exit(&softsp->recv_mutex);
7084ab777b1Swh94709 
7094ab777b1Swh94709 	return (error);
7104ab777b1Swh94709 }
7114ab777b1Swh94709 
7124ab777b1Swh94709 /*ARGSUSED*/
7134ab777b1Swh94709 static int
7144ab777b1Swh94709 glvc_write(dev_t dev, struct uio *uiop, cred_t *credp)
7154ab777b1Swh94709 {
7164ab777b1Swh94709 	glvc_soft_state_t *softsp;
7174ab777b1Swh94709 	int instance;
7184ab777b1Swh94709 	int rv, error = DDI_SUCCESS;
7194ab777b1Swh94709 	uint64_t hverr, send_count = 0;
7204ab777b1Swh94709 
7214ab777b1Swh94709 	instance = getminor(dev);
7224ab777b1Swh94709 
7234ab777b1Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
7244ab777b1Swh94709 
7254ab777b1Swh94709 	if (uiop->uio_resid > softsp->mtu)
7264ab777b1Swh94709 		return (EINVAL);
7274ab777b1Swh94709 
7284ab777b1Swh94709 	send_count = uiop->uio_resid;
7294ab777b1Swh94709 	DPRINTF(("instance %d glvc_write: request to send %lu bytes",
7304ab777b1Swh94709 	    instance, send_count));
7314ab777b1Swh94709 
7324ab777b1Swh94709 	mutex_enter(&softsp->send_complete_mutex);
7334ab777b1Swh94709 	while (softsp->send_complete_flag == 0) {
734*d3d50737SRafael Vanoni 		rv = cv_reltimedwait_sig(&softsp->send_complete_cv,
735*d3d50737SRafael Vanoni 		    &softsp->send_complete_mutex, softsp->polling_interval,
736*d3d50737SRafael Vanoni 		    TR_CLOCK_TICK);
7374ab777b1Swh94709 		if (rv == 0) {
7384ab777b1Swh94709 			/*
7394ab777b1Swh94709 			 * We got interrupted.
7404ab777b1Swh94709 			 */
7414ab777b1Swh94709 			mutex_exit(&softsp->send_complete_mutex);
7424ab777b1Swh94709 			return (EINTR);
7434ab777b1Swh94709 		}
7444ab777b1Swh94709 		if (rv == -1) {
7454ab777b1Swh94709 			/*
7464ab777b1Swh94709 			 * Timeout wait, trigger a soft intr in case
7474ab777b1Swh94709 			 * we miss an interrupt or in polling mode.
7484ab777b1Swh94709 			 */
7494ab777b1Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
7504ab777b1Swh94709 		}
7514ab777b1Swh94709 	}
7524ab777b1Swh94709 
7534ab777b1Swh94709 	/* move data from to user to kernel space */
7544ab777b1Swh94709 	error = uiomove(softsp->mb_send_buf, send_count,
7554ab777b1Swh94709 	    UIO_WRITE, uiop);
7564ab777b1Swh94709 
7574ab777b1Swh94709 	if (error == 0) {
7584ab777b1Swh94709 		hverr = hv_service_send(softsp->s_id,
7594ab777b1Swh94709 		    softsp->mb_send_buf_pa, send_count, &send_count);
7604ab777b1Swh94709 		error = glvc_emap_h2s(hverr);
7614ab777b1Swh94709 	}
7624ab777b1Swh94709 
7634ab777b1Swh94709 	DPRINTF(("instance %d glvc_write write check error = %d,"
7644ab777b1Swh94709 	    " send_count = %lu", instance, error, send_count));
7654ab777b1Swh94709 
7664ab777b1Swh94709 	softsp->send_complete_flag = 0;
7674ab777b1Swh94709 
7684ab777b1Swh94709 	mutex_exit(&softsp->send_complete_mutex);
7694ab777b1Swh94709 
7704ab777b1Swh94709 	return (error);
7714ab777b1Swh94709 }
7724ab777b1Swh94709 
7734ab777b1Swh94709 /*
7744ab777b1Swh94709  * Interrupt handler
7754ab777b1Swh94709  */
7764ab777b1Swh94709 static uint_t
7774ab777b1Swh94709 glvc_intr(caddr_t arg)
7784ab777b1Swh94709 {
7794ab777b1Swh94709 	glvc_soft_state_t *softsp = (glvc_soft_state_t *)arg;
7804ab777b1Swh94709 	uint64_t status_reg;
7814ab777b1Swh94709 	int error = DDI_INTR_UNCLAIMED;
7824ab777b1Swh94709 	uint64_t hverr = H_EOK;
7834ab777b1Swh94709 	uint64_t clr_bits = 0;
7844ab777b1Swh94709 
7854ab777b1Swh94709 	mutex_enter(&softsp->recv_mutex);
7864ab777b1Swh94709 	mutex_enter(&softsp->send_complete_mutex);
7874ab777b1Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
7884ab777b1Swh94709 	DPRINTF(("glvc_intr: err = %ld, getstatus = 0x%lx",
7894ab777b1Swh94709 	    hverr, status_reg));
7904ab777b1Swh94709 
7914ab777b1Swh94709 	/*
7924ab777b1Swh94709 	 * Clear SEND_COMPLETE bit and disable RECV interrupt
7934ab777b1Swh94709 	 */
7944ab777b1Swh94709 	if (status_reg & GLVC_REG_SEND)
7954ab777b1Swh94709 		clr_bits |= GLVC_REG_SEND;
7964ab777b1Swh94709 	if ((softsp->intr_mode == GLVC_INTR_MODE) &&
7974ab777b1Swh94709 	    (status_reg & GLVC_REG_RECV))
7984ab777b1Swh94709 		clr_bits |= GLVC_REG_RECV_ENA;
7994ab777b1Swh94709 
8004ab777b1Swh94709 	if ((hverr = hv_service_clrstatus(softsp->s_id, clr_bits))
8014ab777b1Swh94709 	    != H_EOK) {
8024ab777b1Swh94709 		cmn_err(CE_WARN, "glvc_intr clear status reg failed"
8034ab777b1Swh94709 		    "error = %ld", hverr);
8044ab777b1Swh94709 		mutex_exit(&softsp->send_complete_mutex);
8054ab777b1Swh94709 		mutex_exit(&softsp->recv_mutex);
8064ab777b1Swh94709 		return (DDI_INTR_UNCLAIMED);
8074ab777b1Swh94709 	}
8084ab777b1Swh94709 
8094ab777b1Swh94709 	if (status_reg & GLVC_REG_RECV) {
8104ab777b1Swh94709 		cv_broadcast(&softsp->recv_cv);
8114ab777b1Swh94709 		error = DDI_INTR_CLAIMED;
8124ab777b1Swh94709 	}
8134ab777b1Swh94709 
8144ab777b1Swh94709 	if (status_reg & GLVC_REG_SEND) {
8154ab777b1Swh94709 		softsp->send_complete_flag = 1;
8164ab777b1Swh94709 		cv_broadcast(&softsp->send_complete_cv);
8174ab777b1Swh94709 		error = DDI_INTR_CLAIMED;
8184ab777b1Swh94709 	}
8194ab777b1Swh94709 
8204ab777b1Swh94709 	mutex_exit(&softsp->send_complete_mutex);
8214ab777b1Swh94709 	mutex_exit(&softsp->recv_mutex);
8224ab777b1Swh94709 
8234ab777b1Swh94709 	return (error);
8244ab777b1Swh94709 }
8254ab777b1Swh94709 
8264ab777b1Swh94709 /*
8274ab777b1Swh94709  * Peek to see if there is data received. If no data available,
8284ab777b1Swh94709  * we sleep wait. If there is data, read from hypervisor and copy
8294ab777b1Swh94709  * to ioctl buffer. We don't clear the receive data interrupt bit.
8304ab777b1Swh94709  */
8314ab777b1Swh94709 static int
8324ab777b1Swh94709 glvc_peek(glvc_soft_state_t *softsp, glvc_xport_msg_peek_t *msg_peek)
8334ab777b1Swh94709 {
8344ab777b1Swh94709 	int rv, error = 0;
8354ab777b1Swh94709 	uint64_t hverr = H_EOK;
8364ab777b1Swh94709 	uint64_t recv_count = 0;
8374ab777b1Swh94709 	uint64_t status_reg;
8384ab777b1Swh94709 
8394ab777b1Swh94709 	mutex_enter(&softsp->recv_mutex);
8404ab777b1Swh94709 
8414ab777b1Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
8424ab777b1Swh94709 	DPRINTF(("glvc_peek: err = %ld, getstatus = 0x%lx",
8434ab777b1Swh94709 	    hverr, status_reg));
8444ab777b1Swh94709 
8454ab777b1Swh94709 	/*
8464ab777b1Swh94709 	 * If no data available, we wait till we get some.
8474ab777b1Swh94709 	 * Notice we still holding the recv_mutex lock at
8484ab777b1Swh94709 	 * this point.
8494ab777b1Swh94709 	 */
8504ab777b1Swh94709 	while (hverr == H_EOK && (status_reg & GLVC_REG_RECV) !=
8514ab777b1Swh94709 	    GLVC_REG_RECV) {
852*d3d50737SRafael Vanoni 		rv = cv_reltimedwait_sig(&softsp->recv_cv,
853*d3d50737SRafael Vanoni 		    &softsp->recv_mutex, softsp->polling_interval,
854*d3d50737SRafael Vanoni 		    TR_CLOCK_TICK);
8554ab777b1Swh94709 		if (rv == 0) {
8564ab777b1Swh94709 			/*
8574ab777b1Swh94709 			 * We got interrupted.
8584ab777b1Swh94709 			 */
8594ab777b1Swh94709 			mutex_exit(&softsp->recv_mutex);
8604ab777b1Swh94709 			return (EINTR);
8614ab777b1Swh94709 		}
8624ab777b1Swh94709 		if (rv == -1) {
8634ab777b1Swh94709 			/*
8644ab777b1Swh94709 			 * Timeout wait, trigger a soft intr in case
8654ab777b1Swh94709 			 * we miss an interrupt or in polling mode.
8664ab777b1Swh94709 			 */
8674ab777b1Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
8684ab777b1Swh94709 		}
8694ab777b1Swh94709 		hverr = hv_service_getstatus(softsp->s_id, &status_reg);
8704ab777b1Swh94709 		DPRINTF(("glvc_peek: err = %ld, getstatus = 0x%lx",
8714ab777b1Swh94709 		    hverr, status_reg));
8724ab777b1Swh94709 	}
8734ab777b1Swh94709 
8744ab777b1Swh94709 	/* Read data into kernel buffer */
8754ab777b1Swh94709 	hverr = hv_service_recv(softsp->s_id, softsp->mb_recv_buf_pa,
8764ab777b1Swh94709 	    softsp->mtu, &recv_count);
8774ab777b1Swh94709 	DPRINTF(("glvc_peek recv data, error = %ld, recv_count = %lu",
8784ab777b1Swh94709 	    hverr, recv_count));
8794ab777b1Swh94709 
8804ab777b1Swh94709 	if (hverr == H_EOK && recv_count > 0) {
8814ab777b1Swh94709 		(void *) memcpy(msg_peek->buf,
8824ab777b1Swh94709 		    softsp->mb_recv_buf, recv_count);
8834ab777b1Swh94709 		msg_peek->buflen = recv_count;
8844ab777b1Swh94709 	} else {
8854ab777b1Swh94709 		error = glvc_emap_h2s(hverr);
8864ab777b1Swh94709 	}
8874ab777b1Swh94709 
8884ab777b1Swh94709 	mutex_exit(&softsp->recv_mutex);
8894ab777b1Swh94709 
8904ab777b1Swh94709 	return (error);
8914ab777b1Swh94709 }
8924ab777b1Swh94709 
8934ab777b1Swh94709 static int
8944ab777b1Swh94709 glvc_ioctl_opt_op(glvc_soft_state_t *softsp, intptr_t arg, int mode)
8954ab777b1Swh94709 {
8964ab777b1Swh94709 	glvc_xport_opt_op_t glvc_xport_cmd;
8974ab777b1Swh94709 	uint64_t status_reg;
8984ab777b1Swh94709 	int retval = 0;
8994ab777b1Swh94709 	uint64_t hverr;
9004ab777b1Swh94709 
9014ab777b1Swh94709 	if (ddi_copyin((caddr_t)arg, (caddr_t)&glvc_xport_cmd,
9024ab777b1Swh94709 	    sizeof (glvc_xport_opt_op_t), mode) != 0) {
9034ab777b1Swh94709 		return (EFAULT);
9044ab777b1Swh94709 	}
9054ab777b1Swh94709 
9064ab777b1Swh94709 	switch (glvc_xport_cmd.opt_sel) {
9074ab777b1Swh94709 	case GLVC_XPORT_OPT_MTU_SZ:
9084ab777b1Swh94709 		if (glvc_xport_cmd.op_sel == GLVC_XPORT_OPT_GET) {
9094ab777b1Swh94709 			glvc_xport_cmd.opt_val = softsp->mtu;
9104ab777b1Swh94709 			retval = ddi_copyout((caddr_t)&glvc_xport_cmd,
9114ab777b1Swh94709 			    (caddr_t)arg, sizeof (glvc_xport_opt_op_t),
9124ab777b1Swh94709 			    mode);
9134ab777b1Swh94709 		} else
9144ab777b1Swh94709 			retval = ENOTSUP;
9154ab777b1Swh94709 
9164ab777b1Swh94709 		break;
9174ab777b1Swh94709 
9184ab777b1Swh94709 	case GLVC_XPORT_OPT_REG_STATUS:
9194ab777b1Swh94709 		if (glvc_xport_cmd.op_sel == GLVC_XPORT_OPT_GET) {
9204ab777b1Swh94709 			mutex_enter(&softsp->statusreg_mutex);
9214ab777b1Swh94709 			hverr = hv_service_getstatus(softsp->s_id, &status_reg);
9224ab777b1Swh94709 			mutex_exit(&softsp->statusreg_mutex);
9234ab777b1Swh94709 			if (hverr == H_EOK) {
9244ab777b1Swh94709 				glvc_xport_cmd.opt_val = (uint32_t)status_reg;
9254ab777b1Swh94709 				retval = ddi_copyout((caddr_t)&glvc_xport_cmd,
9264ab777b1Swh94709 				    (caddr_t)arg, sizeof (glvc_xport_opt_op_t),
9274ab777b1Swh94709 				    mode);
9284ab777b1Swh94709 			} else {
9294ab777b1Swh94709 				retval = EIO;
9304ab777b1Swh94709 			}
9314ab777b1Swh94709 		} else {
9324ab777b1Swh94709 			retval = ENOTSUP;
9334ab777b1Swh94709 		}
9344ab777b1Swh94709 
9354ab777b1Swh94709 		break;
9364ab777b1Swh94709 
9374ab777b1Swh94709 	default:
9384ab777b1Swh94709 		retval = ENOTSUP;
9394ab777b1Swh94709 		break;
9404ab777b1Swh94709 	}
9414ab777b1Swh94709 
9424ab777b1Swh94709 	return (retval);
9434ab777b1Swh94709 }
9444ab777b1Swh94709 
9454ab777b1Swh94709 
9464ab777b1Swh94709 /*ARGSUSED*/
9474ab777b1Swh94709 static int
9484ab777b1Swh94709 glvc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
9494ab777b1Swh94709     int *rval_p)
9504ab777b1Swh94709 {
9514ab777b1Swh94709 	glvc_soft_state_t *softsp;
9524ab777b1Swh94709 	int instance = getminor(dev);
9534ab777b1Swh94709 	glvc_xport_msg_peek_t glvc_peek_msg, msg_peek_cmd;
9544ab777b1Swh94709 	glvc_xport_msg_peek32_t msg_peek_cmd32;
9554ab777b1Swh94709 
9564ab777b1Swh94709 	int retval = 0;
9574ab777b1Swh94709 
9584ab777b1Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
9594ab777b1Swh94709 
9604ab777b1Swh94709 	switch (cmd) {
9614ab777b1Swh94709 	case GLVC_XPORT_IOCTL_OPT_OP:
9624ab777b1Swh94709 		retval = glvc_ioctl_opt_op(softsp, arg, mode);
9634ab777b1Swh94709 		break;
9644ab777b1Swh94709 
9654ab777b1Swh94709 	case GLVC_XPORT_IOCTL_DATA_PEEK:
9664ab777b1Swh94709 		glvc_peek_msg.buf =
9674ab777b1Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
9684ab777b1Swh94709 		if (glvc_peek_msg.buf == NULL)
9694ab777b1Swh94709 			return (EBUSY);
9704ab777b1Swh94709 		retval = glvc_peek(softsp, &glvc_peek_msg);
9714ab777b1Swh94709 		if (retval == 0) {
9724ab777b1Swh94709 			switch (ddi_model_convert_from(mode)) {
9734ab777b1Swh94709 			case DDI_MODEL_ILP32:
9744ab777b1Swh94709 				if (ddi_copyin((caddr_t)arg,
9754ab777b1Swh94709 				    (caddr_t)&msg_peek_cmd32,
9764ab777b1Swh94709 				    sizeof (glvc_xport_msg_peek32_t),
9774ab777b1Swh94709 				    mode) == -1) {
9784ab777b1Swh94709 					retval = EFAULT;
9794ab777b1Swh94709 					break;
9804ab777b1Swh94709 				}
9814ab777b1Swh94709 
9824ab777b1Swh94709 				if (msg_peek_cmd32.buflen32 == 0) {
9834ab777b1Swh94709 					retval = EINVAL;
9844ab777b1Swh94709 					break;
9854ab777b1Swh94709 				}
9864ab777b1Swh94709 
9874ab777b1Swh94709 				if (msg_peek_cmd32.buflen32 >
9884ab777b1Swh94709 				    glvc_peek_msg.buflen)
9894ab777b1Swh94709 					msg_peek_cmd32.buflen32 =
9904ab777b1Swh94709 					    glvc_peek_msg.buflen;
9914ab777b1Swh94709 
9924ab777b1Swh94709 				if (ddi_copyout((caddr_t)glvc_peek_msg.buf,
9934ab777b1Swh94709 				    (caddr_t)(uintptr_t)msg_peek_cmd32.buf32,
9944ab777b1Swh94709 				    msg_peek_cmd32.buflen32, mode) == -1) {
9954ab777b1Swh94709 					retval = EFAULT;
9964ab777b1Swh94709 					break;
9974ab777b1Swh94709 				}
9984ab777b1Swh94709 
9994ab777b1Swh94709 				if (ddi_copyout((caddr_t)&msg_peek_cmd32,
10004ab777b1Swh94709 				    (caddr_t)arg,
10014ab777b1Swh94709 				    sizeof (glvc_xport_msg_peek32_t), mode)
10024ab777b1Swh94709 				    == -1)
10034ab777b1Swh94709 					retval = EFAULT;
10044ab777b1Swh94709 				break;
10054ab777b1Swh94709 
10064ab777b1Swh94709 			case DDI_MODEL_NONE:
10074ab777b1Swh94709 				if (ddi_copyin((caddr_t)arg,
10084ab777b1Swh94709 				    (caddr_t)&msg_peek_cmd,
10094ab777b1Swh94709 				    sizeof (glvc_xport_msg_peek_t), mode) == -1)
10104ab777b1Swh94709 					retval = EFAULT;
10114ab777b1Swh94709 
10124ab777b1Swh94709 				if (msg_peek_cmd.buflen == 0) {
10134ab777b1Swh94709 					retval = EINVAL;
10144ab777b1Swh94709 					break;
10154ab777b1Swh94709 				}
10164ab777b1Swh94709 
10174ab777b1Swh94709 				if (msg_peek_cmd.buflen > glvc_peek_msg.buflen)
10184ab777b1Swh94709 					msg_peek_cmd.buflen =
10194ab777b1Swh94709 					    glvc_peek_msg.buflen;
10204ab777b1Swh94709 
10214ab777b1Swh94709 				if (ddi_copyout((caddr_t)glvc_peek_msg.buf,
10224ab777b1Swh94709 				    (caddr_t)msg_peek_cmd.buf,
10234ab777b1Swh94709 				    msg_peek_cmd.buflen, mode) == -1) {
10244ab777b1Swh94709 					retval = EFAULT;
10254ab777b1Swh94709 					break;
10264ab777b1Swh94709 				}
10274ab777b1Swh94709 
10284ab777b1Swh94709 				if (ddi_copyout((caddr_t)&msg_peek_cmd,
10294ab777b1Swh94709 				    (caddr_t)arg,
10304ab777b1Swh94709 				    sizeof (glvc_xport_msg_peek_t), mode) == -1)
10314ab777b1Swh94709 					retval = EFAULT;
10324ab777b1Swh94709 				break;
10334ab777b1Swh94709 
10344ab777b1Swh94709 			default:
10354ab777b1Swh94709 				retval = EFAULT;
10364ab777b1Swh94709 				break;
10374ab777b1Swh94709 			}
10384ab777b1Swh94709 		}
10394ab777b1Swh94709 		kmem_free(glvc_peek_msg.buf, softsp->mtu);
10404ab777b1Swh94709 		break;
10414ab777b1Swh94709 
10424ab777b1Swh94709 	default:
10434ab777b1Swh94709 		retval = ENOTSUP;
10444ab777b1Swh94709 		break;
10454ab777b1Swh94709 	}
10464ab777b1Swh94709 	return (retval);
10474ab777b1Swh94709 }
10484ab777b1Swh94709 
10494ab777b1Swh94709 /*
10504ab777b1Swh94709  * Map hypervisor error code to solaris. Only
10514ab777b1Swh94709  * H_EOK, H_EINVA, H_EWOULDBLOCK and H_EIO are meaningful
10524ab777b1Swh94709  * to this device. All other error codes are mapped to EIO.
10534ab777b1Swh94709  */
10544ab777b1Swh94709 static int
10554ab777b1Swh94709 glvc_emap_h2s(uint64_t hv_errcode)
10564ab777b1Swh94709 {
10574ab777b1Swh94709 	int s_errcode;
10584ab777b1Swh94709 
10594ab777b1Swh94709 	switch (hv_errcode) {
10604ab777b1Swh94709 	case H_EOK:
10614ab777b1Swh94709 		s_errcode = 0;
10624ab777b1Swh94709 		break;
10634ab777b1Swh94709 
10644ab777b1Swh94709 	case H_EINVAL:
10654ab777b1Swh94709 		s_errcode = EINVAL;
10664ab777b1Swh94709 		break;
10674ab777b1Swh94709 
10684ab777b1Swh94709 	case H_EWOULDBLOCK:
10694ab777b1Swh94709 		s_errcode = EWOULDBLOCK;
10704ab777b1Swh94709 		break;
10714ab777b1Swh94709 
10724ab777b1Swh94709 	case H_EIO:
10734ab777b1Swh94709 		s_errcode = EIO;
10744ab777b1Swh94709 		break;
10754ab777b1Swh94709 
10764ab777b1Swh94709 	default:
10774ab777b1Swh94709 		/* should not happen */
10784ab777b1Swh94709 		DPRINTF(("Unexpected device error code %ld received, "
10794ab777b1Swh94709 		    "mapped to EIO", hv_errcode));
10804ab777b1Swh94709 		s_errcode = EIO;
10814ab777b1Swh94709 		break;
10824ab777b1Swh94709 	}
10834ab777b1Swh94709 
10844ab777b1Swh94709 	return (s_errcode);
10854ab777b1Swh94709 }
1086