xref: /onnv-gate/usr/src/uts/sun4v/io/glvc/glvc.c (revision 11066:cebb50cbe4f9)
11840Swh94709 /*
21840Swh94709  * CDDL HEADER START
31840Swh94709  *
41840Swh94709  * The contents of this file are subject to the terms of the
51840Swh94709  * Common Development and Distribution License (the "License").
61840Swh94709  * You may not use this file except in compliance with the License.
71840Swh94709  *
81840Swh94709  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91840Swh94709  * or http://www.opensolaris.org/os/licensing.
101840Swh94709  * See the License for the specific language governing permissions
111840Swh94709  * and limitations under the License.
121840Swh94709  *
131840Swh94709  * When distributing Covered Code, include this CDDL HEADER in each
141840Swh94709  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151840Swh94709  * If applicable, add the following below this CDDL HEADER, with the
161840Swh94709  * fields enclosed by brackets "[]" replaced with your own identifying
171840Swh94709  * information: Portions Copyright [yyyy] [name of copyright owner]
181840Swh94709  *
191840Swh94709  * CDDL HEADER END
201840Swh94709  */
211840Swh94709 /*
22*11066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
231840Swh94709  * Use is subject to license terms.
241840Swh94709  */
251840Swh94709 
261840Swh94709 
271840Swh94709 #include <sys/time.h>
281840Swh94709 #include <sys/errno.h>
291840Swh94709 #include <sys/kmem.h>
301840Swh94709 #include <sys/stat.h>
311840Swh94709 #include <sys/cmn_err.h>
321840Swh94709 
331840Swh94709 #include <sys/conf.h>
341840Swh94709 #include <sys/modctl.h>
351840Swh94709 #include <sys/devops.h>
361840Swh94709 #include <sys/ddi.h>
371840Swh94709 #include <sys/sunddi.h>
381840Swh94709 #include <sys/callb.h>
391840Swh94709 #include <sys/disp.h>
401840Swh94709 #include <sys/strlog.h>
411840Swh94709 #include <sys/file.h>
421840Swh94709 
431840Swh94709 #include <sys/uadmin.h>
441840Swh94709 #include <sys/machsystm.h>
451840Swh94709 #include <sys/hypervisor_api.h>
461840Swh94709 #include <sys/hsvc.h>
471840Swh94709 #include <sys/glvc.h>
481840Swh94709 
491840Swh94709 /*
501840Swh94709  * Global Variables - can be patched from Solaris
511840Swh94709  * ==============================================
521840Swh94709  */
531840Swh94709 
541840Swh94709 /* bit defination in virtual device register */
551840Swh94709 #define	GLVC_REG_RECV		0x0001
561840Swh94709 #define	GLVC_REG_RECV_ENA	0x0002
571840Swh94709 #define	GLVC_REG_SEND		0x0004
581840Swh94709 #define	GLVC_REG_SEND_ENA	0x0008
591840Swh94709 #define	GLVC_REG_ERR		0x8000
601840Swh94709 
611840Swh94709 /*
621840Swh94709  * For interrupt mode
631840Swh94709  */
641840Swh94709 #define	GLVC_MODE_NONE		0
651840Swh94709 #define	GLVC_POLLING_MODE	1
661840Swh94709 #define	GLVC_INTR_MODE		2
671840Swh94709 
681840Swh94709 /*
691840Swh94709  * For open
701840Swh94709  */
711840Swh94709 #define	GLVC_NO_OPEN		0
721840Swh94709 #define	GLVC_OPEN		1
731840Swh94709 #define	GLVC_EXCL_OPEN		2
741840Swh94709 
751840Swh94709 /*
761840Swh94709  * For timeout polling, in microsecond.
771840Swh94709  */
781840Swh94709 #define	GLVC_TIMEOUT_POLL	5000000		/* Timeout in intr mode */
791840Swh94709 #define	GLVC_POLLMODE_POLL	500000		/* Interval in polling mode */
801840Swh94709 
811840Swh94709 /*
821840Swh94709  * For debug printing
831840Swh94709  */
841840Swh94709 #define	_PRINTF			printf
851840Swh94709 #define	DPRINTF(args)		if (glvc_debug) _PRINTF args;
861840Swh94709 
871840Swh94709 /*
881840Swh94709  * Driver entry points
891840Swh94709  */
901840Swh94709 static int	glvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
911840Swh94709 static int	glvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
921840Swh94709 static int	glvc_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
931840Swh94709 static int	glvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
941840Swh94709 static int	glvc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
951840Swh94709     cred_t *cred_p, int *rval_p);
961840Swh94709 static int	glvc_read(dev_t dev, struct uio *uiop, cred_t *credp);
971840Swh94709 static int	glvc_write(dev_t dev, struct uio *uiop, cred_t *credp);
981840Swh94709 
991840Swh94709 static struct cb_ops glvc_cb_ops = {
1001840Swh94709 	glvc_open,	/* open */
1011840Swh94709 	glvc_close,	/* close */
1021840Swh94709 	nodev,		/* strategy() */
1031840Swh94709 	nodev,		/* print() */
1041840Swh94709 	nodev,		/* dump() */
1051840Swh94709 	glvc_read,	/* read() */
1061840Swh94709 	glvc_write,	/* write() */
1071840Swh94709 	glvc_ioctl,	/* ioctl() */
1081840Swh94709 	nodev,		/* devmap() */
1091840Swh94709 	nodev,		/* mmap() */
1101840Swh94709 	ddi_segmap,	/* segmap() */
1111840Swh94709 	nochpoll,	/* poll() */
1121840Swh94709 	ddi_prop_op,    /* prop_op() */
1131840Swh94709 	NULL,		/* cb_str */
1141840Swh94709 	D_NEW | D_MP	/* cb_flag */
1151840Swh94709 };
1161840Swh94709 
1171840Swh94709 
1181840Swh94709 static struct dev_ops glvc_ops = {
1191840Swh94709 	DEVO_REV,
1201840Swh94709 	0,			/* ref count */
1211840Swh94709 	ddi_getinfo_1to1,	/* getinfo() */
1221840Swh94709 	nulldev,		/* identify() */
1231840Swh94709 	nulldev,		/* probe() */
1241840Swh94709 	glvc_attach,		/* attach() */
1251840Swh94709 	glvc_detach,		/* detach */
1261840Swh94709 	nodev,			/* reset */
1271840Swh94709 	&glvc_cb_ops,		/* pointer to cb_ops structure */
1281840Swh94709 	(struct bus_ops *)NULL,
1297656SSherry.Moore@Sun.COM 	nulldev,		/* power() */
1307656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
1311840Swh94709 };
1321840Swh94709 
1331840Swh94709 /*
1341840Swh94709  * Loadable module support.
1351840Swh94709  */
1361840Swh94709 extern struct mod_ops mod_driverops;
1371840Swh94709 
1381840Swh94709 static struct modldrv modldrv = {
1391840Swh94709 	&mod_driverops,			/* Type of module. This is a driver */
1407656SSherry.Moore@Sun.COM 	"Sun4v virtual channel driver",	/* Name of the module */
1411840Swh94709 	&glvc_ops			/* pointer to the dev_ops structure */
1421840Swh94709 };
1431840Swh94709 
1441840Swh94709 static struct modlinkage modlinkage = {
1451840Swh94709 	MODREV_1,
1461840Swh94709 	&modldrv,
1471840Swh94709 	NULL
1481840Swh94709 };
1491840Swh94709 
1501840Swh94709 typedef struct glvc_soft_state {
1511840Swh94709 	dev_info_t *dip;	/* dev info of myself */
1521840Swh94709 	uint64_t s_id;		/* service id for this node */
1531840Swh94709 	uint64_t mtu;		/* max transmit unit size */
1541840Swh94709 	uint64_t flag;		/* flag register */
1551840Swh94709 	kmutex_t open_mutex;	/* protect open_state flag */
1561840Swh94709 	uint8_t open_state;	/* no-open, open or open exclusively */
1571840Swh94709 	kmutex_t recv_mutex;
1581840Swh94709 	kmutex_t send_complete_mutex;
1591840Swh94709 	uint8_t send_complete_flag;	/* 1 = send completed */
1601840Swh94709 	uint8_t intr_mode;	/* 1 = polling mode, 2 = interrupt mode */
1611840Swh94709 	clock_t polling_interval;
1621840Swh94709 	ddi_softintr_t poll_mode_softint_id;
1631840Swh94709 	kcondvar_t recv_cv;
1641840Swh94709 	kcondvar_t send_complete_cv;
1651840Swh94709 	kmutex_t statusreg_mutex;	/* Protects status register */
1661840Swh94709 	char *mb_recv_buf;
1671840Swh94709 	char *mb_send_buf;
1681840Swh94709 	uint64_t mb_recv_buf_pa;
1691840Swh94709 	uint64_t mb_send_buf_pa;
1701840Swh94709 } glvc_soft_state_t;
1711840Swh94709 
1721840Swh94709 /*
1731840Swh94709  * Hypervisor VSC api versioning information for glvc driver.
1741840Swh94709  */
1751840Swh94709 static uint64_t	glvc_vsc_min_ver; /* Negotiated VSC API minor version */
1761840Swh94709 static uint_t glvc_vsc_users = 0; /* VSC API users */
1771840Swh94709 static kmutex_t glvc_vsc_users_mutex;	/* Mutex to protect user count */
1781840Swh94709 
1791840Swh94709 static hsvc_info_t glvc_hsvc = {
1801840Swh94709 	HSVC_REV_1, NULL, HSVC_GROUP_VSC, GLVC_VSC_MAJOR_VER,
1811840Swh94709 	GLVC_VSC_MINOR_VER, "glvc"
1821840Swh94709 };
1831840Swh94709 
1841840Swh94709 /*
1851840Swh94709  * Module Variables
1861840Swh94709  * ================
1871840Swh94709  */
1881840Swh94709 
1891840Swh94709 /*
1901840Swh94709  * functions local to this driver.
1911840Swh94709  */
1921840Swh94709 static int	glvc_add_intr_handlers(dev_info_t *dip);
1931840Swh94709 static int	glvc_remove_intr_handlers(dev_info_t *dip);
1941840Swh94709 static uint_t	glvc_intr(caddr_t arg);
1951840Swh94709 static int	glvc_peek(glvc_soft_state_t *softsp,
1961840Swh94709     glvc_xport_msg_peek_t *msg_peek);
1971840Swh94709 static uint_t	glvc_soft_intr(caddr_t arg);
1981840Swh94709 static int	glvc_emap_h2s(uint64_t hv_errcode);
1991840Swh94709 static int	glvc_ioctl_opt_op(glvc_soft_state_t *softsp,
2001840Swh94709     intptr_t arg, int mode);
2011840Swh94709 
2021840Swh94709 /*
2031840Swh94709  * Driver globals
2041840Swh94709  */
2051840Swh94709 static void *glvc_ssp; /* pointer to driver soft state */
2061840Swh94709 
2071840Swh94709 static uint_t glvc_debug = 0;
2081840Swh94709 
2091840Swh94709 int
_init(void)2101840Swh94709 _init(void)
2111840Swh94709 {
2121840Swh94709 	int	error = 0;
2131840Swh94709 
2141840Swh94709 	if ((error = ddi_soft_state_init(&glvc_ssp,
2151840Swh94709 	    sizeof (glvc_soft_state_t), 1)) != 0)
2161840Swh94709 		return (error);
2171840Swh94709 
2181840Swh94709 	/*
2191840Swh94709 	 * Initialize the mutex for global data structure
2201840Swh94709 	 */
2211840Swh94709 	mutex_init(&glvc_vsc_users_mutex, NULL, MUTEX_DRIVER, NULL);
2221840Swh94709 
2231840Swh94709 	error = mod_install(&modlinkage);
2241840Swh94709 
2251840Swh94709 	return (error);
2261840Swh94709 }
2271840Swh94709 
2281840Swh94709 
2291840Swh94709 int
_info(struct modinfo * modinfop)2301840Swh94709 _info(struct modinfo *modinfop)
2311840Swh94709 {
2321840Swh94709 	return (mod_info(&modlinkage, modinfop));
2331840Swh94709 }
2341840Swh94709 
2351840Swh94709 
2361840Swh94709 int
_fini(void)2371840Swh94709 _fini(void)
2381840Swh94709 {
2391840Swh94709 	int	error = 0;
2401840Swh94709 
2411840Swh94709 	error = mod_remove(&modlinkage);
2421840Swh94709 	if (error)
2431840Swh94709 		return (error);
2441840Swh94709 
2451840Swh94709 	mutex_destroy(&glvc_vsc_users_mutex);
2461840Swh94709 
2471840Swh94709 	ddi_soft_state_fini(&glvc_ssp);
2481840Swh94709 	return (0);
2491840Swh94709 }
2501840Swh94709 
2511840Swh94709 
2521840Swh94709 static int
glvc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2531840Swh94709 glvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2541840Swh94709 {
2551840Swh94709 	int			instance;
2561840Swh94709 	int			err;
2571840Swh94709 	glvc_soft_state_t 	*softsp;
2581840Swh94709 
2591840Swh94709 	switch (cmd) {
2601840Swh94709 	case DDI_ATTACH:
2611840Swh94709 		instance = ddi_get_instance(dip);
2621840Swh94709 
2631840Swh94709 		/*
2641840Swh94709 		 * Negotiate the API version for VSC hypervisor services.
2651840Swh94709 		 */
2661840Swh94709 		mutex_enter(&glvc_vsc_users_mutex);
2671840Swh94709 		if (glvc_vsc_users == 0 &&
2681840Swh94709 		    (err = hsvc_register(&glvc_hsvc, &glvc_vsc_min_ver))
2691840Swh94709 		    != 0) {
2701840Swh94709 			cmn_err(CE_WARN, "%s: cannot negotiate hypervisor "
2711840Swh94709 			    "services group: 0x%lx major: 0x%lx minor: 0x%lx "
2721840Swh94709 			    "errno: %d\n", glvc_hsvc.hsvc_modname,
2731840Swh94709 			    glvc_hsvc.hsvc_group, glvc_hsvc.hsvc_major,
2741840Swh94709 			    glvc_hsvc.hsvc_minor, err);
2751840Swh94709 
2761840Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
2771840Swh94709 			return (DDI_FAILURE);
2781840Swh94709 		} else {
2791840Swh94709 			glvc_vsc_users++;
2801840Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
2811840Swh94709 		}
2821840Swh94709 
2831840Swh94709 		DPRINTF(("Glvc instance %d negotiated VSC API version, "
2841840Swh94709 		    " major 0x%lx minor 0x%lx\n",
2851840Swh94709 		    instance, glvc_hsvc.hsvc_major, glvc_vsc_min_ver));
2861840Swh94709 
2871840Swh94709 		if (ddi_soft_state_zalloc(glvc_ssp, instance)
2881840Swh94709 		    != DDI_SUCCESS) {
2891840Swh94709 			mutex_enter(&glvc_vsc_users_mutex);
2901840Swh94709 			if (--glvc_vsc_users == 0)
2911840Swh94709 				(void) hsvc_unregister(&glvc_hsvc);
2921840Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
2931840Swh94709 			return (DDI_FAILURE);
2941840Swh94709 		}
2951840Swh94709 
2961840Swh94709 		softsp = ddi_get_soft_state(glvc_ssp, instance);
2971840Swh94709 
2981840Swh94709 		/* Set the dip in the soft state */
2991840Swh94709 		softsp->dip = dip;
3001840Swh94709 
3011840Swh94709 		softsp->open_state = GLVC_NO_OPEN;
3021840Swh94709 		softsp->send_complete_flag = 1;
3031840Swh94709 
3041840Swh94709 		glvc_debug = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
3051840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "glvc_debug", glvc_debug);
3061840Swh94709 
3071840Swh94709 		if ((softsp->s_id = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
3081840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "channel#", -1))
3091840Swh94709 		    == -1) {
3101840Swh94709 			cmn_err(CE_WARN, "Failed to get channel#");
3111840Swh94709 			goto bad;
3121840Swh94709 		}
3131840Swh94709 
3141840Swh94709 		if ((softsp->mtu = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
3151840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "mtu", -1))
3161840Swh94709 		    <= 0) {
3171840Swh94709 			cmn_err(CE_WARN, "Failed to get mtu");
3181840Swh94709 			goto bad;
3191840Swh94709 		}
3201840Swh94709 
3211840Swh94709 		softsp->mb_recv_buf =
3221840Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
3231840Swh94709 		if (softsp->mb_recv_buf == NULL) {
3241840Swh94709 			cmn_err(CE_WARN, "Failed to alloc mem for recv buf");
3251840Swh94709 			goto bad;
3261840Swh94709 		}
3271840Swh94709 		softsp->mb_recv_buf_pa =
3281840Swh94709 		    va_to_pa((caddr_t)softsp->mb_recv_buf);
3291840Swh94709 
3301840Swh94709 		softsp->mb_send_buf =
3311840Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
3321840Swh94709 		if (softsp->mb_send_buf == NULL) {
3331840Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
3341840Swh94709 			cmn_err(CE_WARN, "Failed to alloc mem for send buf");
3351840Swh94709 			goto bad;
3361840Swh94709 		}
3371840Swh94709 		softsp->mb_send_buf_pa =
3381840Swh94709 		    va_to_pa((caddr_t)softsp->mb_send_buf);
3391840Swh94709 
3401840Swh94709 		err = ddi_create_minor_node(dip, "glvc", S_IFCHR,
3417656SSherry.Moore@Sun.COM 		    instance, DDI_PSEUDO, NULL);
3421840Swh94709 		if (err != DDI_SUCCESS) {
3431840Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
3441840Swh94709 			kmem_free(softsp->mb_send_buf, softsp->mtu);
3451840Swh94709 			cmn_err(CE_WARN, "Failed to create minor node");
3461840Swh94709 			goto bad;
3471840Swh94709 		}
3481840Swh94709 
3491840Swh94709 		mutex_init(&(softsp->open_mutex), NULL, MUTEX_DRIVER, NULL);
3501840Swh94709 		mutex_init(&(softsp->recv_mutex), NULL, MUTEX_DRIVER, NULL);
3511840Swh94709 		mutex_init(&(softsp->send_complete_mutex), NULL,
3521840Swh94709 		    MUTEX_DRIVER, NULL);
3531840Swh94709 		mutex_init(&(softsp->statusreg_mutex), NULL,
3541840Swh94709 		    MUTEX_DRIVER, NULL);
3551840Swh94709 		cv_init(&(softsp->recv_cv), NULL, CV_DRIVER, NULL);
3561840Swh94709 		cv_init(&(softsp->send_complete_cv), NULL, CV_DRIVER, NULL);
3571840Swh94709 
3581840Swh94709 		/*
3591840Swh94709 		 * Add the handlers which watch for unsolicited messages
3601840Swh94709 		 * and post event to Sysevent Framework.
3611840Swh94709 		 */
3621840Swh94709 		err = glvc_add_intr_handlers(dip);
3631840Swh94709 		if (err != DDI_SUCCESS) {
3641840Swh94709 			cmn_err(CE_WARN, "Failed to add intr handler");
3651840Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
3661840Swh94709 			kmem_free(softsp->mb_send_buf, softsp->mtu);
3671840Swh94709 			ddi_remove_minor_node(dip, NULL);
3681840Swh94709 			goto bad1;
3691840Swh94709 		}
3701840Swh94709 
3711840Swh94709 		/*
3721840Swh94709 		 * Trigger soft interrupt to start polling device if
3731840Swh94709 		 * we are in the polling mode
3741840Swh94709 		 */
3751840Swh94709 		if (softsp->intr_mode == GLVC_POLLING_MODE)
3761840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
3771840Swh94709 
3781840Swh94709 		ddi_report_dev(dip);
3791840Swh94709 
3801840Swh94709 		DPRINTF(("glvc instance %d, s_id %lu,"
3811840Swh94709 		    "mtu %lu attached\n", instance, softsp->s_id,
3821840Swh94709 		    softsp->mtu));
3831840Swh94709 
3841840Swh94709 		return (DDI_SUCCESS);
3851840Swh94709 	case DDI_RESUME:
3861840Swh94709 		return (DDI_SUCCESS);
3871840Swh94709 	default:
3881840Swh94709 		return (DDI_FAILURE);
3891840Swh94709 	}
3901840Swh94709 
3911840Swh94709 bad1:
3921840Swh94709 	cv_destroy(&(softsp->send_complete_cv));
3931840Swh94709 	cv_destroy(&(softsp->recv_cv));
3941840Swh94709 	mutex_destroy(&(softsp->open_mutex));
3951840Swh94709 	mutex_destroy(&(softsp->send_complete_mutex));
3961840Swh94709 	mutex_destroy(&(softsp->recv_mutex));
3971840Swh94709 	mutex_destroy(&(softsp->statusreg_mutex));
3981840Swh94709 
3991840Swh94709 bad:
4001840Swh94709 	mutex_enter(&glvc_vsc_users_mutex);
4011840Swh94709 	if (--glvc_vsc_users == 0)
4021840Swh94709 		(void) hsvc_unregister(&glvc_hsvc);
4031840Swh94709 	mutex_exit(&glvc_vsc_users_mutex);
4041840Swh94709 	cmn_err(CE_WARN, "glvc: attach failed for instance %d\n", instance);
4051840Swh94709 	ddi_soft_state_free(glvc_ssp, instance);
4061840Swh94709 	return (DDI_FAILURE);
4071840Swh94709 }
4081840Swh94709 
4091840Swh94709 
4101840Swh94709 static int
glvc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4111840Swh94709 glvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4121840Swh94709 {
4131840Swh94709 	int	instance;
4141840Swh94709 	int	err;
4151840Swh94709 	glvc_soft_state_t 	*softsp;
4161840Swh94709 
4171840Swh94709 
4181840Swh94709 	switch (cmd) {
4191840Swh94709 	case DDI_DETACH:
4201840Swh94709 		instance = ddi_get_instance(dip);
4211840Swh94709 
4221840Swh94709 		softsp = ddi_get_soft_state(glvc_ssp, instance);
4231840Swh94709 
4241840Swh94709 		cv_destroy(&(softsp->send_complete_cv));
4251840Swh94709 		cv_destroy(&(softsp->recv_cv));
4261840Swh94709 		mutex_destroy(&(softsp->open_mutex));
4271840Swh94709 		mutex_destroy(&(softsp->statusreg_mutex));
4281840Swh94709 		mutex_destroy(&(softsp->send_complete_mutex));
4291840Swh94709 		mutex_destroy(&(softsp->recv_mutex));
4301840Swh94709 
4311840Swh94709 		kmem_free(softsp->mb_recv_buf, softsp->mtu);
4321840Swh94709 		kmem_free(softsp->mb_send_buf, softsp->mtu);
4331840Swh94709 
4341840Swh94709 		err = glvc_remove_intr_handlers(dip);
4351840Swh94709 
4361840Swh94709 		if (err != DDI_SUCCESS) {
4371840Swh94709 			cmn_err(CE_WARN, "Failed to remove event handlers");
4381840Swh94709 			return (DDI_FAILURE);
4391840Swh94709 		}
4401840Swh94709 
4411840Swh94709 		ddi_remove_minor_node(dip, NULL);
4421840Swh94709 
4431840Swh94709 		ddi_soft_state_free(glvc_ssp, instance);
4441840Swh94709 
4451840Swh94709 		mutex_enter(&glvc_vsc_users_mutex);
4461840Swh94709 		if (--glvc_vsc_users == 0)
4471840Swh94709 			(void) hsvc_unregister(&glvc_hsvc);
4481840Swh94709 		mutex_exit(&glvc_vsc_users_mutex);
4491840Swh94709 
4501840Swh94709 		return (DDI_SUCCESS);
4511840Swh94709 	case DDI_SUSPEND:
4521840Swh94709 		return (DDI_SUCCESS);
4531840Swh94709 	default:
4541840Swh94709 		return (DDI_FAILURE);
4551840Swh94709 	}
4561840Swh94709 }
4571840Swh94709 
4581840Swh94709 static int
glvc_add_intr_handlers(dev_info_t * dip)4591840Swh94709 glvc_add_intr_handlers(dev_info_t *dip)
4601840Swh94709 {
4611840Swh94709 	int	instance;
4621840Swh94709 	glvc_soft_state_t	*softsp;
4631840Swh94709 	int	err = DDI_FAILURE;
4641840Swh94709 	uint64_t polling_interval;
4651840Swh94709 
4661840Swh94709 	instance = ddi_get_instance(dip);
4671840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
4681840Swh94709 
4691840Swh94709 	if ((uint64_t)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
4701840Swh94709 	    DDI_PROP_DONTPASS, "flags", -1) != -1) {
4711840Swh94709 		err = ddi_add_intr(dip, 0, NULL, NULL, glvc_intr,
4721840Swh94709 		    (caddr_t)softsp);
4731840Swh94709 		if (err != DDI_SUCCESS)
4741840Swh94709 			cmn_err(CE_NOTE, "glvc, instance %d"
4751840Swh94709 			    " ddi_add_intr() failed, using"
4761840Swh94709 			    " polling mode", instance);
4771840Swh94709 	}
4781840Swh94709 
4791840Swh94709 	if (err == DDI_SUCCESS) {
4801840Swh94709 		softsp->intr_mode = GLVC_INTR_MODE;
4811840Swh94709 		polling_interval = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
4821840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "intrmode_poll",
4831840Swh94709 		    GLVC_TIMEOUT_POLL);
4841840Swh94709 		DPRINTF(("glvc instance %d polling_interval = %lu\n",
4851840Swh94709 		    instance, polling_interval));
4861840Swh94709 		softsp->polling_interval = drv_usectohz(polling_interval);
4871840Swh94709 	} else {
4881840Swh94709 		DPRINTF(("glvc, instance %d  intr support not found, "
4891840Swh94709 		    "err = %d , use polling mode", instance, err));
4901840Swh94709 		softsp->intr_mode = GLVC_POLLING_MODE;
4911840Swh94709 		polling_interval =
4921840Swh94709 		    (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
4931840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "pollmode_poll",
4941840Swh94709 		    GLVC_POLLMODE_POLL);
4951840Swh94709 		DPRINTF(("glvc instance %d polling_interval = %lu\n",
4961840Swh94709 		    instance, polling_interval));
4971840Swh94709 		softsp->polling_interval =
4981840Swh94709 		    drv_usectohz(polling_interval);
4991840Swh94709 	}
5001840Swh94709 
5011840Swh94709 	/* Now enable interrupt bits in the status register */
5021840Swh94709 	if (softsp->intr_mode == GLVC_INTR_MODE) {
5031840Swh94709 		err = hv_service_setstatus(softsp->s_id,
5041840Swh94709 		    GLVC_REG_RECV_ENA|GLVC_REG_SEND_ENA);
5051840Swh94709 		if (err != H_EOK) {
5061840Swh94709 			cmn_err(CE_NOTE, "glvc instance %d"
5071840Swh94709 			    " cannot enable receive interrupt\n",
5081840Swh94709 			    instance);
5091840Swh94709 			return (DDI_FAILURE);
5101840Swh94709 		}
5111840Swh94709 	}
5121840Swh94709 
5131840Swh94709 
5141840Swh94709 	err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
5151840Swh94709 	    &softsp->poll_mode_softint_id, NULL, NULL,
5161840Swh94709 	    glvc_soft_intr, (caddr_t)softsp);
5171840Swh94709 
5181840Swh94709 	return (err);
5191840Swh94709 }
5201840Swh94709 
5211840Swh94709 static int
glvc_remove_intr_handlers(dev_info_t * dip)5221840Swh94709 glvc_remove_intr_handlers(dev_info_t *dip)
5231840Swh94709 {
5241840Swh94709 	int	instance;
5251840Swh94709 	glvc_soft_state_t	*softsp;
5261840Swh94709 
5271840Swh94709 	instance = ddi_get_instance(dip);
5281840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
5291840Swh94709 
5301840Swh94709 	if (softsp->intr_mode ==  GLVC_INTR_MODE)
5311840Swh94709 		ddi_remove_intr(dip, 0, NULL);
5321840Swh94709 
5331840Swh94709 	ddi_remove_softintr(softsp->poll_mode_softint_id);
5341840Swh94709 
5351840Swh94709 	softsp->intr_mode = GLVC_MODE_NONE;
5361840Swh94709 	softsp->polling_interval = 0;
5371840Swh94709 
5381840Swh94709 	return (DDI_SUCCESS);
5391840Swh94709 }
5401840Swh94709 
5411840Swh94709 static uint_t
glvc_soft_intr(caddr_t arg)5421840Swh94709 glvc_soft_intr(caddr_t arg)
5431840Swh94709 {
5441840Swh94709 	/*
5451840Swh94709 	 * Call the interrupt handle routine to check the register
5461840Swh94709 	 * status.
5471840Swh94709 	 */
5481840Swh94709 	(uint_t)glvc_intr(arg);
5491840Swh94709 
5501840Swh94709 	return (DDI_INTR_CLAIMED);
5511840Swh94709 }
5521840Swh94709 
5531840Swh94709 /*ARGSUSED*/
5541840Swh94709 static int
glvc_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)5551840Swh94709 glvc_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
5561840Swh94709 {
5571840Swh94709 	int error = 0;
5581840Swh94709 	int instance;
5591840Swh94709 	glvc_soft_state_t *softsp;
5601840Swh94709 
5611840Swh94709 	instance = getminor(*dev_p);
5621840Swh94709 
5631840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
5641840Swh94709 
5651840Swh94709 	mutex_enter(&softsp->open_mutex);
5661840Swh94709 
5671840Swh94709 	switch (softsp->open_state) {
5681840Swh94709 	case GLVC_NO_OPEN:
5691840Swh94709 		if (flag & FEXCL)
5701840Swh94709 			softsp->open_state = GLVC_EXCL_OPEN;
5711840Swh94709 		else
5721840Swh94709 			softsp->open_state = GLVC_OPEN;
5731840Swh94709 		break;
5741840Swh94709 
5751840Swh94709 	case GLVC_OPEN:
5761840Swh94709 		if (flag & FEXCL)
5771840Swh94709 			error = EBUSY;
5781840Swh94709 		break;
5791840Swh94709 
5801840Swh94709 	case GLVC_EXCL_OPEN:
5811840Swh94709 		error = EBUSY;
5821840Swh94709 		break;
5831840Swh94709 
5841840Swh94709 	default:
5851840Swh94709 		/* Should not happen */
5861840Swh94709 		cmn_err(CE_WARN, "glvc_open: bad open state %d.",
5871840Swh94709 		    softsp->open_state);
5881840Swh94709 		error = ENXIO;
5891840Swh94709 		break;
5901840Swh94709 	}
5911840Swh94709 
5921840Swh94709 	mutex_exit(&softsp->open_mutex);
5931840Swh94709 
5941840Swh94709 	return (error);
5951840Swh94709 }
5961840Swh94709 
5971840Swh94709 /*ARGSUSED*/
5981840Swh94709 static int
glvc_close(dev_t dev,int flag,int otyp,cred_t * cred_p)5991840Swh94709 glvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
6001840Swh94709 {
6011840Swh94709 	glvc_soft_state_t *softsp;
6021840Swh94709 	int instance;
6031840Swh94709 	int error = 0;
6041840Swh94709 
6051840Swh94709 	instance = getminor(dev);
6061840Swh94709 
6071840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
6081840Swh94709 
6091840Swh94709 	mutex_enter(&softsp->open_mutex);
6101840Swh94709 	if (softsp->open_state == GLVC_NO_OPEN) {
6111840Swh94709 		cmn_err(CE_WARN,
6121840Swh94709 		    "glvc_close: device already closed");
6131840Swh94709 		error = ENXIO;
6141840Swh94709 	} else {
6151840Swh94709 		softsp->open_state = GLVC_NO_OPEN;
6161840Swh94709 	}
6171840Swh94709 	mutex_exit(&softsp->open_mutex);
6181840Swh94709 
6191840Swh94709 	return (error);
6201840Swh94709 }
6211840Swh94709 
6221840Swh94709 /*ARGSUSED*/
6231840Swh94709 static int
glvc_read(dev_t dev,struct uio * uiop,cred_t * credp)6241840Swh94709 glvc_read(dev_t dev, struct uio *uiop, cred_t *credp)
6251840Swh94709 {
6261840Swh94709 	glvc_soft_state_t *softsp;
6271840Swh94709 	int instance;
6281840Swh94709 	int rv, error = DDI_SUCCESS;
6291840Swh94709 	uint64_t hverr, recv_count = 0;
6301840Swh94709 	uint64_t status_reg;
6311840Swh94709 
6321840Swh94709 	instance = getminor(dev);
6331840Swh94709 
6341840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
6351840Swh94709 
6361840Swh94709 	mutex_enter(&softsp->recv_mutex);
6371840Swh94709 
6381840Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
6391840Swh94709 	DPRINTF(("glvc_read: err = %ld, getstatus = 0x%lx",
6401840Swh94709 	    hverr, status_reg));
6411840Swh94709 
6421840Swh94709 
6431840Swh94709 	/*
6441840Swh94709 	 * If no data available, we wait till we get some.
6451840Swh94709 	 * Notice we still holding the recv_mutex lock at this
6461840Swh94709 	 * point.
6471840Swh94709 	 */
6481840Swh94709 	while (hverr == H_EOK && (status_reg & GLVC_REG_RECV) !=
6491840Swh94709 	    GLVC_REG_RECV) {
650*11066Srafael.vanoni@sun.com 		rv =  cv_reltimedwait_sig(&softsp->recv_cv,
651*11066Srafael.vanoni@sun.com 		    &softsp->recv_mutex, softsp->polling_interval,
652*11066Srafael.vanoni@sun.com 		    TR_CLOCK_TICK);
6531840Swh94709 		if (rv == 0) {
6541840Swh94709 			/*
6551840Swh94709 			 * We got interrupted.
6561840Swh94709 			 */
6571840Swh94709 			mutex_exit(&softsp->recv_mutex);
6581840Swh94709 			return (EINTR);
6591840Swh94709 		}
6601840Swh94709 		if (rv == -1) {
6611840Swh94709 			/*
6621840Swh94709 			 * Timeout wait, trigger a soft intr in case
6631840Swh94709 			 * we miss an interrupt or in polling mode.
6641840Swh94709 			 */
6651840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
6661840Swh94709 		}
6671840Swh94709 		hverr = hv_service_getstatus(softsp->s_id, &status_reg);
6681840Swh94709 		DPRINTF(("glvc_read: err = %ld, getstatus = 0x%lx",
6691840Swh94709 		    hverr, status_reg));
6701840Swh94709 	}
6711840Swh94709 
6721840Swh94709 	/* Read data into kernel buffer */
6731840Swh94709 	hverr = hv_service_recv(softsp->s_id, softsp->mb_recv_buf_pa,
6741840Swh94709 	    softsp->mtu, &recv_count);
6751840Swh94709 
6761840Swh94709 	DPRINTF(("Instance %d glvc_read returns error = %ld, recv_count = %lu",
6771840Swh94709 	    instance, hverr, recv_count));
6781840Swh94709 
6791840Swh94709 	if (hverr == H_EOK) {
6801840Swh94709 		if (uiop->uio_resid < recv_count) {
6811840Swh94709 			DPRINTF(("Instance %d, glvc_read user buffer "
6821840Swh94709 			    "size(%lu) smaller than number of bytes "
6831840Swh94709 			    "received(%lu).", instance, uiop->uio_resid,
6841840Swh94709 			    recv_count));
6851840Swh94709 			mutex_exit(&softsp->recv_mutex);
6861840Swh94709 			return (EINVAL);
6871840Swh94709 		}
6881840Swh94709 		/* move data from kernel to user space */
6891840Swh94709 		error = uiomove(softsp->mb_recv_buf, recv_count,
6901840Swh94709 		    UIO_READ, uiop);
6911840Swh94709 	} else {
6921840Swh94709 		error = glvc_emap_h2s(hverr);
6931840Swh94709 	}
6941840Swh94709 
6951840Swh94709 	/* Clear the RECV data interrupt bit on device register */
6961840Swh94709 	if (hv_service_clrstatus(softsp->s_id, GLVC_REG_RECV) != H_EOK) {
6971840Swh94709 		cmn_err(CE_WARN, "glvc_read clear status reg failed");
6981840Swh94709 	}
6991840Swh94709 
7001840Swh94709 	/* Set RECV interrupt enable bit so we can receive interrupt */
7011840Swh94709 	if (softsp->intr_mode == GLVC_INTR_MODE)
7021840Swh94709 		if (hv_service_setstatus(softsp->s_id, GLVC_REG_RECV_ENA)
7031840Swh94709 		    != H_EOK) {
7041840Swh94709 			cmn_err(CE_WARN, "glvc_read set status reg failed");
7051840Swh94709 		}
7061840Swh94709 
7071840Swh94709 	mutex_exit(&softsp->recv_mutex);
7081840Swh94709 
7091840Swh94709 	return (error);
7101840Swh94709 }
7111840Swh94709 
7121840Swh94709 /*ARGSUSED*/
7131840Swh94709 static int
glvc_write(dev_t dev,struct uio * uiop,cred_t * credp)7141840Swh94709 glvc_write(dev_t dev, struct uio *uiop, cred_t *credp)
7151840Swh94709 {
7161840Swh94709 	glvc_soft_state_t *softsp;
7171840Swh94709 	int instance;
7181840Swh94709 	int rv, error = DDI_SUCCESS;
7191840Swh94709 	uint64_t hverr, send_count = 0;
7201840Swh94709 
7211840Swh94709 	instance = getminor(dev);
7221840Swh94709 
7231840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
7241840Swh94709 
7251840Swh94709 	if (uiop->uio_resid > softsp->mtu)
7261840Swh94709 		return (EINVAL);
7271840Swh94709 
7281840Swh94709 	send_count = uiop->uio_resid;
7291840Swh94709 	DPRINTF(("instance %d glvc_write: request to send %lu bytes",
7301840Swh94709 	    instance, send_count));
7311840Swh94709 
7321840Swh94709 	mutex_enter(&softsp->send_complete_mutex);
7331840Swh94709 	while (softsp->send_complete_flag == 0) {
734*11066Srafael.vanoni@sun.com 		rv = cv_reltimedwait_sig(&softsp->send_complete_cv,
735*11066Srafael.vanoni@sun.com 		    &softsp->send_complete_mutex, softsp->polling_interval,
736*11066Srafael.vanoni@sun.com 		    TR_CLOCK_TICK);
7371840Swh94709 		if (rv == 0) {
7381840Swh94709 			/*
7391840Swh94709 			 * We got interrupted.
7401840Swh94709 			 */
7411840Swh94709 			mutex_exit(&softsp->send_complete_mutex);
7421840Swh94709 			return (EINTR);
7431840Swh94709 		}
7441840Swh94709 		if (rv == -1) {
7451840Swh94709 			/*
7461840Swh94709 			 * Timeout wait, trigger a soft intr in case
7471840Swh94709 			 * we miss an interrupt or in polling mode.
7481840Swh94709 			 */
7491840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
7501840Swh94709 		}
7511840Swh94709 	}
7521840Swh94709 
7531840Swh94709 	/* move data from to user to kernel space */
7541840Swh94709 	error = uiomove(softsp->mb_send_buf, send_count,
7551840Swh94709 	    UIO_WRITE, uiop);
7561840Swh94709 
7571840Swh94709 	if (error == 0) {
7581840Swh94709 		hverr = hv_service_send(softsp->s_id,
7591840Swh94709 		    softsp->mb_send_buf_pa, send_count, &send_count);
7601840Swh94709 		error = glvc_emap_h2s(hverr);
7611840Swh94709 	}
7621840Swh94709 
7631840Swh94709 	DPRINTF(("instance %d glvc_write write check error = %d,"
7641840Swh94709 	    " send_count = %lu", instance, error, send_count));
7651840Swh94709 
7661840Swh94709 	softsp->send_complete_flag = 0;
7671840Swh94709 
7681840Swh94709 	mutex_exit(&softsp->send_complete_mutex);
7691840Swh94709 
7701840Swh94709 	return (error);
7711840Swh94709 }
7721840Swh94709 
7731840Swh94709 /*
7741840Swh94709  * Interrupt handler
7751840Swh94709  */
7761840Swh94709 static uint_t
glvc_intr(caddr_t arg)7771840Swh94709 glvc_intr(caddr_t arg)
7781840Swh94709 {
7791840Swh94709 	glvc_soft_state_t *softsp = (glvc_soft_state_t *)arg;
7801840Swh94709 	uint64_t status_reg;
7811840Swh94709 	int error = DDI_INTR_UNCLAIMED;
7821840Swh94709 	uint64_t hverr = H_EOK;
7831840Swh94709 	uint64_t clr_bits = 0;
7841840Swh94709 
7851840Swh94709 	mutex_enter(&softsp->recv_mutex);
7861840Swh94709 	mutex_enter(&softsp->send_complete_mutex);
7871840Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
7881840Swh94709 	DPRINTF(("glvc_intr: err = %ld, getstatus = 0x%lx",
7891840Swh94709 	    hverr, status_reg));
7901840Swh94709 
7911840Swh94709 	/*
7921840Swh94709 	 * Clear SEND_COMPLETE bit and disable RECV interrupt
7931840Swh94709 	 */
7941840Swh94709 	if (status_reg & GLVC_REG_SEND)
7951840Swh94709 		clr_bits |= GLVC_REG_SEND;
7961840Swh94709 	if ((softsp->intr_mode == GLVC_INTR_MODE) &&
7971840Swh94709 	    (status_reg & GLVC_REG_RECV))
7981840Swh94709 		clr_bits |= GLVC_REG_RECV_ENA;
7991840Swh94709 
8001840Swh94709 	if ((hverr = hv_service_clrstatus(softsp->s_id, clr_bits))
8011840Swh94709 	    != H_EOK) {
8021840Swh94709 		cmn_err(CE_WARN, "glvc_intr clear status reg failed"
8031840Swh94709 		    "error = %ld", hverr);
8041840Swh94709 		mutex_exit(&softsp->send_complete_mutex);
8051840Swh94709 		mutex_exit(&softsp->recv_mutex);
8061840Swh94709 		return (DDI_INTR_UNCLAIMED);
8071840Swh94709 	}
8081840Swh94709 
8091840Swh94709 	if (status_reg & GLVC_REG_RECV) {
8101840Swh94709 		cv_broadcast(&softsp->recv_cv);
8111840Swh94709 		error = DDI_INTR_CLAIMED;
8121840Swh94709 	}
8131840Swh94709 
8141840Swh94709 	if (status_reg & GLVC_REG_SEND) {
8151840Swh94709 		softsp->send_complete_flag = 1;
8161840Swh94709 		cv_broadcast(&softsp->send_complete_cv);
8171840Swh94709 		error = DDI_INTR_CLAIMED;
8181840Swh94709 	}
8191840Swh94709 
8201840Swh94709 	mutex_exit(&softsp->send_complete_mutex);
8211840Swh94709 	mutex_exit(&softsp->recv_mutex);
8221840Swh94709 
8231840Swh94709 	return (error);
8241840Swh94709 }
8251840Swh94709 
8261840Swh94709 /*
8271840Swh94709  * Peek to see if there is data received. If no data available,
8281840Swh94709  * we sleep wait. If there is data, read from hypervisor and copy
8291840Swh94709  * to ioctl buffer. We don't clear the receive data interrupt bit.
8301840Swh94709  */
8311840Swh94709 static int
glvc_peek(glvc_soft_state_t * softsp,glvc_xport_msg_peek_t * msg_peek)8321840Swh94709 glvc_peek(glvc_soft_state_t *softsp, glvc_xport_msg_peek_t *msg_peek)
8331840Swh94709 {
8341840Swh94709 	int rv, error = 0;
8351840Swh94709 	uint64_t hverr = H_EOK;
8361840Swh94709 	uint64_t recv_count = 0;
8371840Swh94709 	uint64_t status_reg;
8381840Swh94709 
8391840Swh94709 	mutex_enter(&softsp->recv_mutex);
8401840Swh94709 
8411840Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
8421840Swh94709 	DPRINTF(("glvc_peek: err = %ld, getstatus = 0x%lx",
8431840Swh94709 	    hverr, status_reg));
8441840Swh94709 
8451840Swh94709 	/*
8461840Swh94709 	 * If no data available, we wait till we get some.
8471840Swh94709 	 * Notice we still holding the recv_mutex lock at
8481840Swh94709 	 * this point.
8491840Swh94709 	 */
8501840Swh94709 	while (hverr == H_EOK && (status_reg & GLVC_REG_RECV) !=
8511840Swh94709 	    GLVC_REG_RECV) {
852*11066Srafael.vanoni@sun.com 		rv = cv_reltimedwait_sig(&softsp->recv_cv,
853*11066Srafael.vanoni@sun.com 		    &softsp->recv_mutex, softsp->polling_interval,
854*11066Srafael.vanoni@sun.com 		    TR_CLOCK_TICK);
8551840Swh94709 		if (rv == 0) {
8561840Swh94709 			/*
8571840Swh94709 			 * We got interrupted.
8581840Swh94709 			 */
8591840Swh94709 			mutex_exit(&softsp->recv_mutex);
8601840Swh94709 			return (EINTR);
8611840Swh94709 		}
8621840Swh94709 		if (rv == -1) {
8631840Swh94709 			/*
8641840Swh94709 			 * Timeout wait, trigger a soft intr in case
8651840Swh94709 			 * we miss an interrupt or in polling mode.
8661840Swh94709 			 */
8671840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
8681840Swh94709 		}
8691840Swh94709 		hverr = hv_service_getstatus(softsp->s_id, &status_reg);
8701840Swh94709 		DPRINTF(("glvc_peek: err = %ld, getstatus = 0x%lx",
8711840Swh94709 		    hverr, status_reg));
8721840Swh94709 	}
8731840Swh94709 
8741840Swh94709 	/* Read data into kernel buffer */
8751840Swh94709 	hverr = hv_service_recv(softsp->s_id, softsp->mb_recv_buf_pa,
8761840Swh94709 	    softsp->mtu, &recv_count);
8771840Swh94709 	DPRINTF(("glvc_peek recv data, error = %ld, recv_count = %lu",
8781840Swh94709 	    hverr, recv_count));
8791840Swh94709 
8801840Swh94709 	if (hverr == H_EOK && recv_count > 0) {
8811840Swh94709 		(void *) memcpy(msg_peek->buf,
8821840Swh94709 		    softsp->mb_recv_buf, recv_count);
8831840Swh94709 		msg_peek->buflen = recv_count;
8841840Swh94709 	} else {
8851840Swh94709 		error = glvc_emap_h2s(hverr);
8861840Swh94709 	}
8871840Swh94709 
8881840Swh94709 	mutex_exit(&softsp->recv_mutex);
8891840Swh94709 
8901840Swh94709 	return (error);
8911840Swh94709 }
8921840Swh94709 
8931840Swh94709 static int
glvc_ioctl_opt_op(glvc_soft_state_t * softsp,intptr_t arg,int mode)8941840Swh94709 glvc_ioctl_opt_op(glvc_soft_state_t *softsp, intptr_t arg, int mode)
8951840Swh94709 {
8961840Swh94709 	glvc_xport_opt_op_t glvc_xport_cmd;
8971840Swh94709 	uint64_t status_reg;
8981840Swh94709 	int retval = 0;
8991840Swh94709 	uint64_t hverr;
9001840Swh94709 
9011840Swh94709 	if (ddi_copyin((caddr_t)arg, (caddr_t)&glvc_xport_cmd,
9021840Swh94709 	    sizeof (glvc_xport_opt_op_t), mode) != 0) {
9031840Swh94709 		return (EFAULT);
9041840Swh94709 	}
9051840Swh94709 
9061840Swh94709 	switch (glvc_xport_cmd.opt_sel) {
9071840Swh94709 	case GLVC_XPORT_OPT_MTU_SZ:
9081840Swh94709 		if (glvc_xport_cmd.op_sel == GLVC_XPORT_OPT_GET) {
9091840Swh94709 			glvc_xport_cmd.opt_val = softsp->mtu;
9101840Swh94709 			retval = ddi_copyout((caddr_t)&glvc_xport_cmd,
9111840Swh94709 			    (caddr_t)arg, sizeof (glvc_xport_opt_op_t),
9121840Swh94709 			    mode);
9131840Swh94709 		} else
9141840Swh94709 			retval = ENOTSUP;
9151840Swh94709 
9161840Swh94709 		break;
9171840Swh94709 
9181840Swh94709 	case GLVC_XPORT_OPT_REG_STATUS:
9191840Swh94709 		if (glvc_xport_cmd.op_sel == GLVC_XPORT_OPT_GET) {
9201840Swh94709 			mutex_enter(&softsp->statusreg_mutex);
9211840Swh94709 			hverr = hv_service_getstatus(softsp->s_id, &status_reg);
9221840Swh94709 			mutex_exit(&softsp->statusreg_mutex);
9231840Swh94709 			if (hverr == H_EOK) {
9241840Swh94709 				glvc_xport_cmd.opt_val = (uint32_t)status_reg;
9251840Swh94709 				retval = ddi_copyout((caddr_t)&glvc_xport_cmd,
9261840Swh94709 				    (caddr_t)arg, sizeof (glvc_xport_opt_op_t),
9271840Swh94709 				    mode);
9281840Swh94709 			} else {
9291840Swh94709 				retval = EIO;
9301840Swh94709 			}
9311840Swh94709 		} else {
9321840Swh94709 			retval = ENOTSUP;
9331840Swh94709 		}
9341840Swh94709 
9351840Swh94709 		break;
9361840Swh94709 
9371840Swh94709 	default:
9381840Swh94709 		retval = ENOTSUP;
9391840Swh94709 		break;
9401840Swh94709 	}
9411840Swh94709 
9421840Swh94709 	return (retval);
9431840Swh94709 }
9441840Swh94709 
9451840Swh94709 
9461840Swh94709 /*ARGSUSED*/
9471840Swh94709 static int
glvc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)9481840Swh94709 glvc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
9491840Swh94709     int *rval_p)
9501840Swh94709 {
9511840Swh94709 	glvc_soft_state_t *softsp;
9521840Swh94709 	int instance = getminor(dev);
9531840Swh94709 	glvc_xport_msg_peek_t glvc_peek_msg, msg_peek_cmd;
9541840Swh94709 	glvc_xport_msg_peek32_t msg_peek_cmd32;
9551840Swh94709 
9561840Swh94709 	int retval = 0;
9571840Swh94709 
9581840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
9591840Swh94709 
9601840Swh94709 	switch (cmd) {
9611840Swh94709 	case GLVC_XPORT_IOCTL_OPT_OP:
9621840Swh94709 		retval = glvc_ioctl_opt_op(softsp, arg, mode);
9631840Swh94709 		break;
9641840Swh94709 
9651840Swh94709 	case GLVC_XPORT_IOCTL_DATA_PEEK:
9661840Swh94709 		glvc_peek_msg.buf =
9671840Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
9681840Swh94709 		if (glvc_peek_msg.buf == NULL)
9691840Swh94709 			return (EBUSY);
9701840Swh94709 		retval = glvc_peek(softsp, &glvc_peek_msg);
9711840Swh94709 		if (retval == 0) {
9721840Swh94709 			switch (ddi_model_convert_from(mode)) {
9731840Swh94709 			case DDI_MODEL_ILP32:
9741840Swh94709 				if (ddi_copyin((caddr_t)arg,
9751840Swh94709 				    (caddr_t)&msg_peek_cmd32,
9761840Swh94709 				    sizeof (glvc_xport_msg_peek32_t),
9771840Swh94709 				    mode) == -1) {
9781840Swh94709 					retval = EFAULT;
9791840Swh94709 					break;
9801840Swh94709 				}
9811840Swh94709 
9821840Swh94709 				if (msg_peek_cmd32.buflen32 == 0) {
9831840Swh94709 					retval = EINVAL;
9841840Swh94709 					break;
9851840Swh94709 				}
9861840Swh94709 
9871840Swh94709 				if (msg_peek_cmd32.buflen32 >
9881840Swh94709 				    glvc_peek_msg.buflen)
9891840Swh94709 					msg_peek_cmd32.buflen32 =
9901840Swh94709 					    glvc_peek_msg.buflen;
9911840Swh94709 
9921840Swh94709 				if (ddi_copyout((caddr_t)glvc_peek_msg.buf,
9931840Swh94709 				    (caddr_t)(uintptr_t)msg_peek_cmd32.buf32,
9941840Swh94709 				    msg_peek_cmd32.buflen32, mode) == -1) {
9951840Swh94709 					retval = EFAULT;
9961840Swh94709 					break;
9971840Swh94709 				}
9981840Swh94709 
9991840Swh94709 				if (ddi_copyout((caddr_t)&msg_peek_cmd32,
10001840Swh94709 				    (caddr_t)arg,
10011840Swh94709 				    sizeof (glvc_xport_msg_peek32_t), mode)
10021840Swh94709 				    == -1)
10031840Swh94709 					retval = EFAULT;
10041840Swh94709 				break;
10051840Swh94709 
10061840Swh94709 			case DDI_MODEL_NONE:
10071840Swh94709 				if (ddi_copyin((caddr_t)arg,
10081840Swh94709 				    (caddr_t)&msg_peek_cmd,
10091840Swh94709 				    sizeof (glvc_xport_msg_peek_t), mode) == -1)
10101840Swh94709 					retval = EFAULT;
10111840Swh94709 
10121840Swh94709 				if (msg_peek_cmd.buflen == 0) {
10131840Swh94709 					retval = EINVAL;
10141840Swh94709 					break;
10151840Swh94709 				}
10161840Swh94709 
10171840Swh94709 				if (msg_peek_cmd.buflen > glvc_peek_msg.buflen)
10181840Swh94709 					msg_peek_cmd.buflen =
10191840Swh94709 					    glvc_peek_msg.buflen;
10201840Swh94709 
10211840Swh94709 				if (ddi_copyout((caddr_t)glvc_peek_msg.buf,
10221840Swh94709 				    (caddr_t)msg_peek_cmd.buf,
10231840Swh94709 				    msg_peek_cmd.buflen, mode) == -1) {
10241840Swh94709 					retval = EFAULT;
10251840Swh94709 					break;
10261840Swh94709 				}
10271840Swh94709 
10281840Swh94709 				if (ddi_copyout((caddr_t)&msg_peek_cmd,
10291840Swh94709 				    (caddr_t)arg,
10301840Swh94709 				    sizeof (glvc_xport_msg_peek_t), mode) == -1)
10311840Swh94709 					retval = EFAULT;
10321840Swh94709 				break;
10331840Swh94709 
10341840Swh94709 			default:
10351840Swh94709 				retval = EFAULT;
10361840Swh94709 				break;
10371840Swh94709 			}
10381840Swh94709 		}
10391840Swh94709 		kmem_free(glvc_peek_msg.buf, softsp->mtu);
10401840Swh94709 		break;
10411840Swh94709 
10421840Swh94709 	default:
10431840Swh94709 		retval = ENOTSUP;
10441840Swh94709 		break;
10451840Swh94709 	}
10461840Swh94709 	return (retval);
10471840Swh94709 }
10481840Swh94709 
10491840Swh94709 /*
10501840Swh94709  * Map hypervisor error code to solaris. Only
10511840Swh94709  * H_EOK, H_EINVA, H_EWOULDBLOCK and H_EIO are meaningful
10521840Swh94709  * to this device. All other error codes are mapped to EIO.
10531840Swh94709  */
10541840Swh94709 static int
glvc_emap_h2s(uint64_t hv_errcode)10551840Swh94709 glvc_emap_h2s(uint64_t hv_errcode)
10561840Swh94709 {
10571840Swh94709 	int s_errcode;
10581840Swh94709 
10591840Swh94709 	switch (hv_errcode) {
10601840Swh94709 	case H_EOK:
10611840Swh94709 		s_errcode = 0;
10621840Swh94709 		break;
10631840Swh94709 
10641840Swh94709 	case H_EINVAL:
10651840Swh94709 		s_errcode = EINVAL;
10661840Swh94709 		break;
10671840Swh94709 
10681840Swh94709 	case H_EWOULDBLOCK:
10691840Swh94709 		s_errcode = EWOULDBLOCK;
10701840Swh94709 		break;
10711840Swh94709 
10721840Swh94709 	case H_EIO:
10731840Swh94709 		s_errcode = EIO;
10741840Swh94709 		break;
10751840Swh94709 
10761840Swh94709 	default:
10771840Swh94709 		/* should not happen */
10781840Swh94709 		DPRINTF(("Unexpected device error code %ld received, "
10791840Swh94709 		    "mapped to EIO", hv_errcode));
10801840Swh94709 		s_errcode = EIO;
10811840Swh94709 		break;
10821840Swh94709 	}
10831840Swh94709 
10841840Swh94709 	return (s_errcode);
10851840Swh94709 }
1086