xref: /onnv-gate/usr/src/uts/sun4v/io/glvc/glvc.c (revision 1840:4b02d3c3fea5)
1*1840Swh94709 /*
2*1840Swh94709  * CDDL HEADER START
3*1840Swh94709  *
4*1840Swh94709  * The contents of this file are subject to the terms of the
5*1840Swh94709  * Common Development and Distribution License (the "License").
6*1840Swh94709  * You may not use this file except in compliance with the License.
7*1840Swh94709  *
8*1840Swh94709  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1840Swh94709  * or http://www.opensolaris.org/os/licensing.
10*1840Swh94709  * See the License for the specific language governing permissions
11*1840Swh94709  * and limitations under the License.
12*1840Swh94709  *
13*1840Swh94709  * When distributing Covered Code, include this CDDL HEADER in each
14*1840Swh94709  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1840Swh94709  * If applicable, add the following below this CDDL HEADER, with the
16*1840Swh94709  * fields enclosed by brackets "[]" replaced with your own identifying
17*1840Swh94709  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1840Swh94709  *
19*1840Swh94709  * CDDL HEADER END
20*1840Swh94709  */
21*1840Swh94709 /*
22*1840Swh94709  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*1840Swh94709  * Use is subject to license terms.
24*1840Swh94709  */
25*1840Swh94709 
26*1840Swh94709 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*1840Swh94709 
28*1840Swh94709 #include <sys/time.h>
29*1840Swh94709 #include <sys/errno.h>
30*1840Swh94709 #include <sys/kmem.h>
31*1840Swh94709 #include <sys/stat.h>
32*1840Swh94709 #include <sys/cmn_err.h>
33*1840Swh94709 
34*1840Swh94709 #include <sys/conf.h>
35*1840Swh94709 #include <sys/modctl.h>
36*1840Swh94709 #include <sys/devops.h>
37*1840Swh94709 #include <sys/ddi.h>
38*1840Swh94709 #include <sys/sunddi.h>
39*1840Swh94709 #include <sys/callb.h>
40*1840Swh94709 #include <sys/disp.h>
41*1840Swh94709 #include <sys/strlog.h>
42*1840Swh94709 #include <sys/file.h>
43*1840Swh94709 
44*1840Swh94709 #include <sys/uadmin.h>
45*1840Swh94709 #include <sys/machsystm.h>
46*1840Swh94709 #include <sys/hypervisor_api.h>
47*1840Swh94709 #include <sys/hsvc.h>
48*1840Swh94709 #include <sys/glvc.h>
49*1840Swh94709 
50*1840Swh94709 /*
51*1840Swh94709  * Global Variables - can be patched from Solaris
52*1840Swh94709  * ==============================================
53*1840Swh94709  */
54*1840Swh94709 
55*1840Swh94709 /* bit defination in virtual device register */
56*1840Swh94709 #define	GLVC_REG_RECV		0x0001
57*1840Swh94709 #define	GLVC_REG_RECV_ENA	0x0002
58*1840Swh94709 #define	GLVC_REG_SEND		0x0004
59*1840Swh94709 #define	GLVC_REG_SEND_ENA	0x0008
60*1840Swh94709 #define	GLVC_REG_ERR		0x8000
61*1840Swh94709 
62*1840Swh94709 /*
63*1840Swh94709  * For interrupt mode
64*1840Swh94709  */
65*1840Swh94709 #define	GLVC_MODE_NONE		0
66*1840Swh94709 #define	GLVC_POLLING_MODE	1
67*1840Swh94709 #define	GLVC_INTR_MODE		2
68*1840Swh94709 
69*1840Swh94709 /*
70*1840Swh94709  * For open
71*1840Swh94709  */
72*1840Swh94709 #define	GLVC_NO_OPEN		0
73*1840Swh94709 #define	GLVC_OPEN		1
74*1840Swh94709 #define	GLVC_EXCL_OPEN		2
75*1840Swh94709 
76*1840Swh94709 /*
77*1840Swh94709  * For timeout polling, in microsecond.
78*1840Swh94709  */
79*1840Swh94709 #define	GLVC_TIMEOUT_POLL	5000000		/* Timeout in intr mode */
80*1840Swh94709 #define	GLVC_POLLMODE_POLL	500000		/* Interval in polling mode */
81*1840Swh94709 
82*1840Swh94709 /*
83*1840Swh94709  * For debug printing
84*1840Swh94709  */
85*1840Swh94709 #define	_PRINTF			printf
86*1840Swh94709 #define	DPRINTF(args)		if (glvc_debug) _PRINTF args;
87*1840Swh94709 
88*1840Swh94709 /*
89*1840Swh94709  * Driver entry points
90*1840Swh94709  */
91*1840Swh94709 static int	glvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
92*1840Swh94709 static int	glvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
93*1840Swh94709 static int	glvc_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
94*1840Swh94709 static int	glvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
95*1840Swh94709 static int	glvc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
96*1840Swh94709     cred_t *cred_p, int *rval_p);
97*1840Swh94709 static int	glvc_read(dev_t dev, struct uio *uiop, cred_t *credp);
98*1840Swh94709 static int	glvc_write(dev_t dev, struct uio *uiop, cred_t *credp);
99*1840Swh94709 
100*1840Swh94709 static struct cb_ops glvc_cb_ops = {
101*1840Swh94709 	glvc_open,	/* open */
102*1840Swh94709 	glvc_close,	/* close */
103*1840Swh94709 	nodev,		/* strategy() */
104*1840Swh94709 	nodev,		/* print() */
105*1840Swh94709 	nodev,		/* dump() */
106*1840Swh94709 	glvc_read,	/* read() */
107*1840Swh94709 	glvc_write,	/* write() */
108*1840Swh94709 	glvc_ioctl,	/* ioctl() */
109*1840Swh94709 	nodev,		/* devmap() */
110*1840Swh94709 	nodev,		/* mmap() */
111*1840Swh94709 	ddi_segmap,	/* segmap() */
112*1840Swh94709 	nochpoll,	/* poll() */
113*1840Swh94709 	ddi_prop_op,    /* prop_op() */
114*1840Swh94709 	NULL,		/* cb_str */
115*1840Swh94709 	D_NEW | D_MP	/* cb_flag */
116*1840Swh94709 };
117*1840Swh94709 
118*1840Swh94709 
119*1840Swh94709 static struct dev_ops glvc_ops = {
120*1840Swh94709 	DEVO_REV,
121*1840Swh94709 	0,			/* ref count */
122*1840Swh94709 	ddi_getinfo_1to1,	/* getinfo() */
123*1840Swh94709 	nulldev,		/* identify() */
124*1840Swh94709 	nulldev,		/* probe() */
125*1840Swh94709 	glvc_attach,		/* attach() */
126*1840Swh94709 	glvc_detach,		/* detach */
127*1840Swh94709 	nodev,			/* reset */
128*1840Swh94709 	&glvc_cb_ops,		/* pointer to cb_ops structure */
129*1840Swh94709 	(struct bus_ops *)NULL,
130*1840Swh94709 	nulldev			/* power() */
131*1840Swh94709 };
132*1840Swh94709 
133*1840Swh94709 /*
134*1840Swh94709  * Loadable module support.
135*1840Swh94709  */
136*1840Swh94709 extern struct mod_ops mod_driverops;
137*1840Swh94709 
138*1840Swh94709 static struct modldrv modldrv = {
139*1840Swh94709 	&mod_driverops,			/* Type of module. This is a driver */
140*1840Swh94709 	"Sun4v virtual channel driver 2.0",	/* Name of the module */
141*1840Swh94709 	&glvc_ops			/* pointer to the dev_ops structure */
142*1840Swh94709 };
143*1840Swh94709 
144*1840Swh94709 static struct modlinkage modlinkage = {
145*1840Swh94709 	MODREV_1,
146*1840Swh94709 	&modldrv,
147*1840Swh94709 	NULL
148*1840Swh94709 };
149*1840Swh94709 
150*1840Swh94709 typedef struct glvc_soft_state {
151*1840Swh94709 	dev_info_t *dip;	/* dev info of myself */
152*1840Swh94709 	uint64_t s_id;		/* service id for this node */
153*1840Swh94709 	uint64_t mtu;		/* max transmit unit size */
154*1840Swh94709 	uint64_t flag;		/* flag register */
155*1840Swh94709 	kmutex_t open_mutex;	/* protect open_state flag */
156*1840Swh94709 	uint8_t open_state;	/* no-open, open or open exclusively */
157*1840Swh94709 	kmutex_t recv_mutex;
158*1840Swh94709 	kmutex_t send_complete_mutex;
159*1840Swh94709 	uint8_t send_complete_flag;	/* 1 = send completed */
160*1840Swh94709 	uint8_t intr_mode;	/* 1 = polling mode, 2 = interrupt mode */
161*1840Swh94709 	clock_t polling_interval;
162*1840Swh94709 	ddi_softintr_t poll_mode_softint_id;
163*1840Swh94709 	kcondvar_t recv_cv;
164*1840Swh94709 	kcondvar_t send_complete_cv;
165*1840Swh94709 	kmutex_t statusreg_mutex;	/* Protects status register */
166*1840Swh94709 	char *mb_recv_buf;
167*1840Swh94709 	char *mb_send_buf;
168*1840Swh94709 	uint64_t mb_recv_buf_pa;
169*1840Swh94709 	uint64_t mb_send_buf_pa;
170*1840Swh94709 } glvc_soft_state_t;
171*1840Swh94709 
172*1840Swh94709 /*
173*1840Swh94709  * Hypervisor VSC api versioning information for glvc driver.
174*1840Swh94709  */
175*1840Swh94709 static uint64_t	glvc_vsc_min_ver; /* Negotiated VSC API minor version */
176*1840Swh94709 static uint_t glvc_vsc_users = 0; /* VSC API users */
177*1840Swh94709 static kmutex_t glvc_vsc_users_mutex;	/* Mutex to protect user count */
178*1840Swh94709 
179*1840Swh94709 static hsvc_info_t glvc_hsvc = {
180*1840Swh94709 	HSVC_REV_1, NULL, HSVC_GROUP_VSC, GLVC_VSC_MAJOR_VER,
181*1840Swh94709 	GLVC_VSC_MINOR_VER, "glvc"
182*1840Swh94709 };
183*1840Swh94709 
184*1840Swh94709 /*
185*1840Swh94709  * Module Variables
186*1840Swh94709  * ================
187*1840Swh94709  */
188*1840Swh94709 
189*1840Swh94709 /*
190*1840Swh94709  * functions local to this driver.
191*1840Swh94709  */
192*1840Swh94709 static int	glvc_add_intr_handlers(dev_info_t *dip);
193*1840Swh94709 static int	glvc_remove_intr_handlers(dev_info_t *dip);
194*1840Swh94709 static uint_t	glvc_intr(caddr_t arg);
195*1840Swh94709 static int	glvc_peek(glvc_soft_state_t *softsp,
196*1840Swh94709     glvc_xport_msg_peek_t *msg_peek);
197*1840Swh94709 static uint_t	glvc_soft_intr(caddr_t arg);
198*1840Swh94709 static int	glvc_emap_h2s(uint64_t hv_errcode);
199*1840Swh94709 static int	glvc_ioctl_opt_op(glvc_soft_state_t *softsp,
200*1840Swh94709     intptr_t arg, int mode);
201*1840Swh94709 
202*1840Swh94709 /*
203*1840Swh94709  * Driver globals
204*1840Swh94709  */
205*1840Swh94709 static void *glvc_ssp; /* pointer to driver soft state */
206*1840Swh94709 
207*1840Swh94709 static uint_t glvc_debug = 0;
208*1840Swh94709 
209*1840Swh94709 int
210*1840Swh94709 _init(void)
211*1840Swh94709 {
212*1840Swh94709 	int	error = 0;
213*1840Swh94709 
214*1840Swh94709 	if ((error = ddi_soft_state_init(&glvc_ssp,
215*1840Swh94709 	    sizeof (glvc_soft_state_t), 1)) != 0)
216*1840Swh94709 		return (error);
217*1840Swh94709 
218*1840Swh94709 	/*
219*1840Swh94709 	 * Initialize the mutex for global data structure
220*1840Swh94709 	 */
221*1840Swh94709 	mutex_init(&glvc_vsc_users_mutex, NULL, MUTEX_DRIVER, NULL);
222*1840Swh94709 
223*1840Swh94709 	error = mod_install(&modlinkage);
224*1840Swh94709 
225*1840Swh94709 	return (error);
226*1840Swh94709 }
227*1840Swh94709 
228*1840Swh94709 
229*1840Swh94709 int
230*1840Swh94709 _info(struct modinfo *modinfop)
231*1840Swh94709 {
232*1840Swh94709 	return (mod_info(&modlinkage, modinfop));
233*1840Swh94709 }
234*1840Swh94709 
235*1840Swh94709 
236*1840Swh94709 int
237*1840Swh94709 _fini(void)
238*1840Swh94709 {
239*1840Swh94709 	int	error = 0;
240*1840Swh94709 
241*1840Swh94709 	error = mod_remove(&modlinkage);
242*1840Swh94709 	if (error)
243*1840Swh94709 		return (error);
244*1840Swh94709 
245*1840Swh94709 	mutex_destroy(&glvc_vsc_users_mutex);
246*1840Swh94709 
247*1840Swh94709 	ddi_soft_state_fini(&glvc_ssp);
248*1840Swh94709 	return (0);
249*1840Swh94709 }
250*1840Swh94709 
251*1840Swh94709 
252*1840Swh94709 static int
253*1840Swh94709 glvc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
254*1840Swh94709 {
255*1840Swh94709 	int			instance;
256*1840Swh94709 	int			err;
257*1840Swh94709 	glvc_soft_state_t 	*softsp;
258*1840Swh94709 
259*1840Swh94709 	switch (cmd) {
260*1840Swh94709 	case DDI_ATTACH:
261*1840Swh94709 		instance = ddi_get_instance(dip);
262*1840Swh94709 
263*1840Swh94709 		/*
264*1840Swh94709 		 * Negotiate the API version for VSC hypervisor services.
265*1840Swh94709 		 */
266*1840Swh94709 		mutex_enter(&glvc_vsc_users_mutex);
267*1840Swh94709 		if (glvc_vsc_users == 0 &&
268*1840Swh94709 		    (err = hsvc_register(&glvc_hsvc, &glvc_vsc_min_ver))
269*1840Swh94709 		    != 0) {
270*1840Swh94709 			cmn_err(CE_WARN, "%s: cannot negotiate hypervisor "
271*1840Swh94709 			    "services group: 0x%lx major: 0x%lx minor: 0x%lx "
272*1840Swh94709 			    "errno: %d\n", glvc_hsvc.hsvc_modname,
273*1840Swh94709 			    glvc_hsvc.hsvc_group, glvc_hsvc.hsvc_major,
274*1840Swh94709 			    glvc_hsvc.hsvc_minor, err);
275*1840Swh94709 
276*1840Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
277*1840Swh94709 			return (DDI_FAILURE);
278*1840Swh94709 		} else {
279*1840Swh94709 			glvc_vsc_users++;
280*1840Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
281*1840Swh94709 		}
282*1840Swh94709 
283*1840Swh94709 		DPRINTF(("Glvc instance %d negotiated VSC API version, "
284*1840Swh94709 		    " major 0x%lx minor 0x%lx\n",
285*1840Swh94709 		    instance, glvc_hsvc.hsvc_major, glvc_vsc_min_ver));
286*1840Swh94709 
287*1840Swh94709 		if (ddi_soft_state_zalloc(glvc_ssp, instance)
288*1840Swh94709 		    != DDI_SUCCESS) {
289*1840Swh94709 			mutex_enter(&glvc_vsc_users_mutex);
290*1840Swh94709 			if (--glvc_vsc_users == 0)
291*1840Swh94709 				(void) hsvc_unregister(&glvc_hsvc);
292*1840Swh94709 			mutex_exit(&glvc_vsc_users_mutex);
293*1840Swh94709 			return (DDI_FAILURE);
294*1840Swh94709 		}
295*1840Swh94709 
296*1840Swh94709 		softsp = ddi_get_soft_state(glvc_ssp, instance);
297*1840Swh94709 
298*1840Swh94709 		/* Set the dip in the soft state */
299*1840Swh94709 		softsp->dip = dip;
300*1840Swh94709 
301*1840Swh94709 		softsp->open_state = GLVC_NO_OPEN;
302*1840Swh94709 		softsp->send_complete_flag = 1;
303*1840Swh94709 
304*1840Swh94709 		glvc_debug = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
305*1840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "glvc_debug", glvc_debug);
306*1840Swh94709 
307*1840Swh94709 		if ((softsp->s_id = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
308*1840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "channel#", -1))
309*1840Swh94709 		    == -1) {
310*1840Swh94709 			cmn_err(CE_WARN, "Failed to get channel#");
311*1840Swh94709 			goto bad;
312*1840Swh94709 		}
313*1840Swh94709 
314*1840Swh94709 		if ((softsp->mtu = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
315*1840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "mtu", -1))
316*1840Swh94709 		    <= 0) {
317*1840Swh94709 			cmn_err(CE_WARN, "Failed to get mtu");
318*1840Swh94709 			goto bad;
319*1840Swh94709 		}
320*1840Swh94709 
321*1840Swh94709 		softsp->mb_recv_buf =
322*1840Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
323*1840Swh94709 		if (softsp->mb_recv_buf == NULL) {
324*1840Swh94709 			cmn_err(CE_WARN, "Failed to alloc mem for recv buf");
325*1840Swh94709 			goto bad;
326*1840Swh94709 		}
327*1840Swh94709 		softsp->mb_recv_buf_pa =
328*1840Swh94709 		    va_to_pa((caddr_t)softsp->mb_recv_buf);
329*1840Swh94709 
330*1840Swh94709 		softsp->mb_send_buf =
331*1840Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
332*1840Swh94709 		if (softsp->mb_send_buf == NULL) {
333*1840Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
334*1840Swh94709 			cmn_err(CE_WARN, "Failed to alloc mem for send buf");
335*1840Swh94709 			goto bad;
336*1840Swh94709 		}
337*1840Swh94709 		softsp->mb_send_buf_pa =
338*1840Swh94709 		    va_to_pa((caddr_t)softsp->mb_send_buf);
339*1840Swh94709 
340*1840Swh94709 		err = ddi_create_minor_node(dip, "glvc", S_IFCHR,
341*1840Swh94709 			instance, DDI_PSEUDO, NULL);
342*1840Swh94709 		if (err != DDI_SUCCESS) {
343*1840Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
344*1840Swh94709 			kmem_free(softsp->mb_send_buf, softsp->mtu);
345*1840Swh94709 			cmn_err(CE_WARN, "Failed to create minor node");
346*1840Swh94709 			goto bad;
347*1840Swh94709 		}
348*1840Swh94709 
349*1840Swh94709 		mutex_init(&(softsp->open_mutex), NULL, MUTEX_DRIVER, NULL);
350*1840Swh94709 		mutex_init(&(softsp->recv_mutex), NULL, MUTEX_DRIVER, NULL);
351*1840Swh94709 		mutex_init(&(softsp->send_complete_mutex), NULL,
352*1840Swh94709 		    MUTEX_DRIVER, NULL);
353*1840Swh94709 		mutex_init(&(softsp->statusreg_mutex), NULL,
354*1840Swh94709 		    MUTEX_DRIVER, NULL);
355*1840Swh94709 		cv_init(&(softsp->recv_cv), NULL, CV_DRIVER, NULL);
356*1840Swh94709 		cv_init(&(softsp->send_complete_cv), NULL, CV_DRIVER, NULL);
357*1840Swh94709 
358*1840Swh94709 		/*
359*1840Swh94709 		 * Add the handlers which watch for unsolicited messages
360*1840Swh94709 		 * and post event to Sysevent Framework.
361*1840Swh94709 		 */
362*1840Swh94709 		err = glvc_add_intr_handlers(dip);
363*1840Swh94709 		if (err != DDI_SUCCESS) {
364*1840Swh94709 			cmn_err(CE_WARN, "Failed to add intr handler");
365*1840Swh94709 			kmem_free(softsp->mb_recv_buf, softsp->mtu);
366*1840Swh94709 			kmem_free(softsp->mb_send_buf, softsp->mtu);
367*1840Swh94709 			ddi_remove_minor_node(dip, NULL);
368*1840Swh94709 			goto bad1;
369*1840Swh94709 		}
370*1840Swh94709 
371*1840Swh94709 		/*
372*1840Swh94709 		 * Trigger soft interrupt to start polling device if
373*1840Swh94709 		 * we are in the polling mode
374*1840Swh94709 		 */
375*1840Swh94709 		if (softsp->intr_mode == GLVC_POLLING_MODE)
376*1840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
377*1840Swh94709 
378*1840Swh94709 		ddi_report_dev(dip);
379*1840Swh94709 
380*1840Swh94709 		DPRINTF(("glvc instance %d, s_id %lu,"
381*1840Swh94709 		    "mtu %lu attached\n", instance, softsp->s_id,
382*1840Swh94709 		    softsp->mtu));
383*1840Swh94709 
384*1840Swh94709 		return (DDI_SUCCESS);
385*1840Swh94709 	case DDI_RESUME:
386*1840Swh94709 		return (DDI_SUCCESS);
387*1840Swh94709 	default:
388*1840Swh94709 		return (DDI_FAILURE);
389*1840Swh94709 	}
390*1840Swh94709 
391*1840Swh94709 bad1:
392*1840Swh94709 	cv_destroy(&(softsp->send_complete_cv));
393*1840Swh94709 	cv_destroy(&(softsp->recv_cv));
394*1840Swh94709 	mutex_destroy(&(softsp->open_mutex));
395*1840Swh94709 	mutex_destroy(&(softsp->send_complete_mutex));
396*1840Swh94709 	mutex_destroy(&(softsp->recv_mutex));
397*1840Swh94709 	mutex_destroy(&(softsp->statusreg_mutex));
398*1840Swh94709 
399*1840Swh94709 bad:
400*1840Swh94709 	mutex_enter(&glvc_vsc_users_mutex);
401*1840Swh94709 	if (--glvc_vsc_users == 0)
402*1840Swh94709 		(void) hsvc_unregister(&glvc_hsvc);
403*1840Swh94709 	mutex_exit(&glvc_vsc_users_mutex);
404*1840Swh94709 	cmn_err(CE_WARN, "glvc: attach failed for instance %d\n", instance);
405*1840Swh94709 	ddi_soft_state_free(glvc_ssp, instance);
406*1840Swh94709 	return (DDI_FAILURE);
407*1840Swh94709 }
408*1840Swh94709 
409*1840Swh94709 
410*1840Swh94709 static int
411*1840Swh94709 glvc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
412*1840Swh94709 {
413*1840Swh94709 	int	instance;
414*1840Swh94709 	int	err;
415*1840Swh94709 	glvc_soft_state_t 	*softsp;
416*1840Swh94709 
417*1840Swh94709 
418*1840Swh94709 	switch (cmd) {
419*1840Swh94709 	case DDI_DETACH:
420*1840Swh94709 		instance = ddi_get_instance(dip);
421*1840Swh94709 
422*1840Swh94709 		softsp = ddi_get_soft_state(glvc_ssp, instance);
423*1840Swh94709 
424*1840Swh94709 		cv_destroy(&(softsp->send_complete_cv));
425*1840Swh94709 		cv_destroy(&(softsp->recv_cv));
426*1840Swh94709 		mutex_destroy(&(softsp->open_mutex));
427*1840Swh94709 		mutex_destroy(&(softsp->statusreg_mutex));
428*1840Swh94709 		mutex_destroy(&(softsp->send_complete_mutex));
429*1840Swh94709 		mutex_destroy(&(softsp->recv_mutex));
430*1840Swh94709 
431*1840Swh94709 		kmem_free(softsp->mb_recv_buf, softsp->mtu);
432*1840Swh94709 		kmem_free(softsp->mb_send_buf, softsp->mtu);
433*1840Swh94709 
434*1840Swh94709 		err = glvc_remove_intr_handlers(dip);
435*1840Swh94709 
436*1840Swh94709 		if (err != DDI_SUCCESS) {
437*1840Swh94709 			cmn_err(CE_WARN, "Failed to remove event handlers");
438*1840Swh94709 			return (DDI_FAILURE);
439*1840Swh94709 		}
440*1840Swh94709 
441*1840Swh94709 		ddi_remove_minor_node(dip, NULL);
442*1840Swh94709 
443*1840Swh94709 		ddi_soft_state_free(glvc_ssp, instance);
444*1840Swh94709 
445*1840Swh94709 		mutex_enter(&glvc_vsc_users_mutex);
446*1840Swh94709 		if (--glvc_vsc_users == 0)
447*1840Swh94709 			(void) hsvc_unregister(&glvc_hsvc);
448*1840Swh94709 		mutex_exit(&glvc_vsc_users_mutex);
449*1840Swh94709 
450*1840Swh94709 		return (DDI_SUCCESS);
451*1840Swh94709 	case DDI_SUSPEND:
452*1840Swh94709 		return (DDI_SUCCESS);
453*1840Swh94709 	default:
454*1840Swh94709 		return (DDI_FAILURE);
455*1840Swh94709 	}
456*1840Swh94709 }
457*1840Swh94709 
458*1840Swh94709 static int
459*1840Swh94709 glvc_add_intr_handlers(dev_info_t *dip)
460*1840Swh94709 {
461*1840Swh94709 	int	instance;
462*1840Swh94709 	glvc_soft_state_t	*softsp;
463*1840Swh94709 	int	err = DDI_FAILURE;
464*1840Swh94709 	uint64_t polling_interval;
465*1840Swh94709 
466*1840Swh94709 	instance = ddi_get_instance(dip);
467*1840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
468*1840Swh94709 
469*1840Swh94709 	if ((uint64_t)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
470*1840Swh94709 	    DDI_PROP_DONTPASS, "flags", -1) != -1) {
471*1840Swh94709 		err = ddi_add_intr(dip, 0, NULL, NULL, glvc_intr,
472*1840Swh94709 		    (caddr_t)softsp);
473*1840Swh94709 		if (err != DDI_SUCCESS)
474*1840Swh94709 			cmn_err(CE_NOTE, "glvc, instance %d"
475*1840Swh94709 			    " ddi_add_intr() failed, using"
476*1840Swh94709 			    " polling mode", instance);
477*1840Swh94709 	}
478*1840Swh94709 
479*1840Swh94709 	if (err == DDI_SUCCESS) {
480*1840Swh94709 		softsp->intr_mode = GLVC_INTR_MODE;
481*1840Swh94709 		polling_interval = (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
482*1840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "intrmode_poll",
483*1840Swh94709 		    GLVC_TIMEOUT_POLL);
484*1840Swh94709 		DPRINTF(("glvc instance %d polling_interval = %lu\n",
485*1840Swh94709 		    instance, polling_interval));
486*1840Swh94709 		softsp->polling_interval = drv_usectohz(polling_interval);
487*1840Swh94709 	} else {
488*1840Swh94709 		DPRINTF(("glvc, instance %d  intr support not found, "
489*1840Swh94709 		    "err = %d , use polling mode", instance, err));
490*1840Swh94709 		softsp->intr_mode = GLVC_POLLING_MODE;
491*1840Swh94709 		polling_interval =
492*1840Swh94709 		    (uint64_t)ddi_getprop(DDI_DEV_T_ANY,
493*1840Swh94709 		    softsp->dip, DDI_PROP_DONTPASS, "pollmode_poll",
494*1840Swh94709 		    GLVC_POLLMODE_POLL);
495*1840Swh94709 		DPRINTF(("glvc instance %d polling_interval = %lu\n",
496*1840Swh94709 		    instance, polling_interval));
497*1840Swh94709 		softsp->polling_interval =
498*1840Swh94709 		    drv_usectohz(polling_interval);
499*1840Swh94709 	}
500*1840Swh94709 
501*1840Swh94709 	/* Now enable interrupt bits in the status register */
502*1840Swh94709 	if (softsp->intr_mode == GLVC_INTR_MODE) {
503*1840Swh94709 		err = hv_service_setstatus(softsp->s_id,
504*1840Swh94709 		    GLVC_REG_RECV_ENA|GLVC_REG_SEND_ENA);
505*1840Swh94709 		if (err != H_EOK) {
506*1840Swh94709 			cmn_err(CE_NOTE, "glvc instance %d"
507*1840Swh94709 			    " cannot enable receive interrupt\n",
508*1840Swh94709 			    instance);
509*1840Swh94709 			return (DDI_FAILURE);
510*1840Swh94709 		}
511*1840Swh94709 	}
512*1840Swh94709 
513*1840Swh94709 
514*1840Swh94709 	err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
515*1840Swh94709 	    &softsp->poll_mode_softint_id, NULL, NULL,
516*1840Swh94709 	    glvc_soft_intr, (caddr_t)softsp);
517*1840Swh94709 
518*1840Swh94709 	return (err);
519*1840Swh94709 }
520*1840Swh94709 
521*1840Swh94709 static int
522*1840Swh94709 glvc_remove_intr_handlers(dev_info_t *dip)
523*1840Swh94709 {
524*1840Swh94709 	int	instance;
525*1840Swh94709 	glvc_soft_state_t	*softsp;
526*1840Swh94709 
527*1840Swh94709 	instance = ddi_get_instance(dip);
528*1840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
529*1840Swh94709 
530*1840Swh94709 	if (softsp->intr_mode ==  GLVC_INTR_MODE)
531*1840Swh94709 		ddi_remove_intr(dip, 0, NULL);
532*1840Swh94709 
533*1840Swh94709 	ddi_remove_softintr(softsp->poll_mode_softint_id);
534*1840Swh94709 
535*1840Swh94709 	softsp->intr_mode = GLVC_MODE_NONE;
536*1840Swh94709 	softsp->polling_interval = 0;
537*1840Swh94709 
538*1840Swh94709 	return (DDI_SUCCESS);
539*1840Swh94709 }
540*1840Swh94709 
541*1840Swh94709 static uint_t
542*1840Swh94709 glvc_soft_intr(caddr_t arg)
543*1840Swh94709 {
544*1840Swh94709 	/*
545*1840Swh94709 	 * Call the interrupt handle routine to check the register
546*1840Swh94709 	 * status.
547*1840Swh94709 	 */
548*1840Swh94709 	(uint_t)glvc_intr(arg);
549*1840Swh94709 
550*1840Swh94709 	return (DDI_INTR_CLAIMED);
551*1840Swh94709 }
552*1840Swh94709 
553*1840Swh94709 /*ARGSUSED*/
554*1840Swh94709 static int
555*1840Swh94709 glvc_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
556*1840Swh94709 {
557*1840Swh94709 	int error = 0;
558*1840Swh94709 	int instance;
559*1840Swh94709 	glvc_soft_state_t *softsp;
560*1840Swh94709 
561*1840Swh94709 	instance = getminor(*dev_p);
562*1840Swh94709 
563*1840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
564*1840Swh94709 
565*1840Swh94709 	mutex_enter(&softsp->open_mutex);
566*1840Swh94709 
567*1840Swh94709 	switch (softsp->open_state) {
568*1840Swh94709 	case GLVC_NO_OPEN:
569*1840Swh94709 		if (flag & FEXCL)
570*1840Swh94709 			softsp->open_state = GLVC_EXCL_OPEN;
571*1840Swh94709 		else
572*1840Swh94709 			softsp->open_state = GLVC_OPEN;
573*1840Swh94709 		break;
574*1840Swh94709 
575*1840Swh94709 	case GLVC_OPEN:
576*1840Swh94709 		if (flag & FEXCL)
577*1840Swh94709 			error = EBUSY;
578*1840Swh94709 		break;
579*1840Swh94709 
580*1840Swh94709 	case GLVC_EXCL_OPEN:
581*1840Swh94709 		error = EBUSY;
582*1840Swh94709 		break;
583*1840Swh94709 
584*1840Swh94709 	default:
585*1840Swh94709 		/* Should not happen */
586*1840Swh94709 		cmn_err(CE_WARN, "glvc_open: bad open state %d.",
587*1840Swh94709 		    softsp->open_state);
588*1840Swh94709 		error = ENXIO;
589*1840Swh94709 		break;
590*1840Swh94709 	}
591*1840Swh94709 
592*1840Swh94709 	mutex_exit(&softsp->open_mutex);
593*1840Swh94709 
594*1840Swh94709 	return (error);
595*1840Swh94709 }
596*1840Swh94709 
597*1840Swh94709 /*ARGSUSED*/
598*1840Swh94709 static int
599*1840Swh94709 glvc_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
600*1840Swh94709 {
601*1840Swh94709 	glvc_soft_state_t *softsp;
602*1840Swh94709 	int instance;
603*1840Swh94709 	int error = 0;
604*1840Swh94709 
605*1840Swh94709 	instance = getminor(dev);
606*1840Swh94709 
607*1840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
608*1840Swh94709 
609*1840Swh94709 	mutex_enter(&softsp->open_mutex);
610*1840Swh94709 	if (softsp->open_state == GLVC_NO_OPEN) {
611*1840Swh94709 		cmn_err(CE_WARN,
612*1840Swh94709 		    "glvc_close: device already closed");
613*1840Swh94709 		error = ENXIO;
614*1840Swh94709 	} else {
615*1840Swh94709 		softsp->open_state = GLVC_NO_OPEN;
616*1840Swh94709 	}
617*1840Swh94709 	mutex_exit(&softsp->open_mutex);
618*1840Swh94709 
619*1840Swh94709 	return (error);
620*1840Swh94709 }
621*1840Swh94709 
622*1840Swh94709 /*ARGSUSED*/
623*1840Swh94709 static int
624*1840Swh94709 glvc_read(dev_t dev, struct uio *uiop, cred_t *credp)
625*1840Swh94709 {
626*1840Swh94709 	glvc_soft_state_t *softsp;
627*1840Swh94709 	int instance;
628*1840Swh94709 	int rv, error = DDI_SUCCESS;
629*1840Swh94709 	uint64_t hverr, recv_count = 0;
630*1840Swh94709 	uint64_t status_reg;
631*1840Swh94709 	clock_t	tick;
632*1840Swh94709 
633*1840Swh94709 	instance = getminor(dev);
634*1840Swh94709 
635*1840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
636*1840Swh94709 
637*1840Swh94709 	mutex_enter(&softsp->recv_mutex);
638*1840Swh94709 
639*1840Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
640*1840Swh94709 	DPRINTF(("glvc_read: err = %ld, getstatus = 0x%lx",
641*1840Swh94709 	    hverr, status_reg));
642*1840Swh94709 
643*1840Swh94709 
644*1840Swh94709 	/*
645*1840Swh94709 	 * If no data available, we wait till we get some.
646*1840Swh94709 	 * Notice we still holding the recv_mutex lock at this
647*1840Swh94709 	 * point.
648*1840Swh94709 	 */
649*1840Swh94709 	while (hverr == H_EOK && (status_reg & GLVC_REG_RECV) !=
650*1840Swh94709 	    GLVC_REG_RECV) {
651*1840Swh94709 		tick = ddi_get_lbolt() + softsp->polling_interval;
652*1840Swh94709 		rv =  cv_timedwait_sig(&softsp->recv_cv,
653*1840Swh94709 		    &softsp->recv_mutex, tick);
654*1840Swh94709 		if (rv == 0) {
655*1840Swh94709 			/*
656*1840Swh94709 			 * We got interrupted.
657*1840Swh94709 			 */
658*1840Swh94709 			mutex_exit(&softsp->recv_mutex);
659*1840Swh94709 			return (EINTR);
660*1840Swh94709 		}
661*1840Swh94709 		if (rv == -1) {
662*1840Swh94709 			/*
663*1840Swh94709 			 * Timeout wait, trigger a soft intr in case
664*1840Swh94709 			 * we miss an interrupt or in polling mode.
665*1840Swh94709 			 */
666*1840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
667*1840Swh94709 		}
668*1840Swh94709 		hverr = hv_service_getstatus(softsp->s_id, &status_reg);
669*1840Swh94709 		DPRINTF(("glvc_read: err = %ld, getstatus = 0x%lx",
670*1840Swh94709 		    hverr, status_reg));
671*1840Swh94709 	}
672*1840Swh94709 
673*1840Swh94709 	/* Read data into kernel buffer */
674*1840Swh94709 	hverr = hv_service_recv(softsp->s_id, softsp->mb_recv_buf_pa,
675*1840Swh94709 	    softsp->mtu, &recv_count);
676*1840Swh94709 
677*1840Swh94709 	DPRINTF(("Instance %d glvc_read returns error = %ld, recv_count = %lu",
678*1840Swh94709 	    instance, hverr, recv_count));
679*1840Swh94709 
680*1840Swh94709 	if (hverr == H_EOK) {
681*1840Swh94709 		if (uiop->uio_resid < recv_count) {
682*1840Swh94709 			DPRINTF(("Instance %d, glvc_read user buffer "
683*1840Swh94709 			    "size(%lu) smaller than number of bytes "
684*1840Swh94709 			    "received(%lu).", instance, uiop->uio_resid,
685*1840Swh94709 			    recv_count));
686*1840Swh94709 			mutex_exit(&softsp->recv_mutex);
687*1840Swh94709 			return (EINVAL);
688*1840Swh94709 		}
689*1840Swh94709 		/* move data from kernel to user space */
690*1840Swh94709 		error = uiomove(softsp->mb_recv_buf, recv_count,
691*1840Swh94709 		    UIO_READ, uiop);
692*1840Swh94709 	} else {
693*1840Swh94709 		error = glvc_emap_h2s(hverr);
694*1840Swh94709 	}
695*1840Swh94709 
696*1840Swh94709 	/* Clear the RECV data interrupt bit on device register */
697*1840Swh94709 	if (hv_service_clrstatus(softsp->s_id, GLVC_REG_RECV) != H_EOK) {
698*1840Swh94709 		cmn_err(CE_WARN, "glvc_read clear status reg failed");
699*1840Swh94709 	}
700*1840Swh94709 
701*1840Swh94709 	/* Set RECV interrupt enable bit so we can receive interrupt */
702*1840Swh94709 	if (softsp->intr_mode == GLVC_INTR_MODE)
703*1840Swh94709 		if (hv_service_setstatus(softsp->s_id, GLVC_REG_RECV_ENA)
704*1840Swh94709 		    != H_EOK) {
705*1840Swh94709 			cmn_err(CE_WARN, "glvc_read set status reg failed");
706*1840Swh94709 		}
707*1840Swh94709 
708*1840Swh94709 	mutex_exit(&softsp->recv_mutex);
709*1840Swh94709 
710*1840Swh94709 	return (error);
711*1840Swh94709 }
712*1840Swh94709 
713*1840Swh94709 /*ARGSUSED*/
714*1840Swh94709 static int
715*1840Swh94709 glvc_write(dev_t dev, struct uio *uiop, cred_t *credp)
716*1840Swh94709 {
717*1840Swh94709 	glvc_soft_state_t *softsp;
718*1840Swh94709 	int instance;
719*1840Swh94709 	int rv, error = DDI_SUCCESS;
720*1840Swh94709 	uint64_t hverr, send_count = 0;
721*1840Swh94709 	clock_t tick;
722*1840Swh94709 
723*1840Swh94709 	instance = getminor(dev);
724*1840Swh94709 
725*1840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
726*1840Swh94709 
727*1840Swh94709 	if (uiop->uio_resid > softsp->mtu)
728*1840Swh94709 		return (EINVAL);
729*1840Swh94709 
730*1840Swh94709 	send_count = uiop->uio_resid;
731*1840Swh94709 	DPRINTF(("instance %d glvc_write: request to send %lu bytes",
732*1840Swh94709 	    instance, send_count));
733*1840Swh94709 
734*1840Swh94709 	mutex_enter(&softsp->send_complete_mutex);
735*1840Swh94709 	while (softsp->send_complete_flag == 0) {
736*1840Swh94709 		tick = ddi_get_lbolt() + softsp->polling_interval;
737*1840Swh94709 		rv = cv_timedwait_sig(&softsp->send_complete_cv,
738*1840Swh94709 		    &softsp->send_complete_mutex, tick);
739*1840Swh94709 		if (rv == 0) {
740*1840Swh94709 			/*
741*1840Swh94709 			 * We got interrupted.
742*1840Swh94709 			 */
743*1840Swh94709 			mutex_exit(&softsp->send_complete_mutex);
744*1840Swh94709 			return (EINTR);
745*1840Swh94709 		}
746*1840Swh94709 		if (rv == -1) {
747*1840Swh94709 			/*
748*1840Swh94709 			 * Timeout wait, trigger a soft intr in case
749*1840Swh94709 			 * we miss an interrupt or in polling mode.
750*1840Swh94709 			 */
751*1840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
752*1840Swh94709 		}
753*1840Swh94709 	}
754*1840Swh94709 
755*1840Swh94709 	/* move data from to user to kernel space */
756*1840Swh94709 	error = uiomove(softsp->mb_send_buf, send_count,
757*1840Swh94709 	    UIO_WRITE, uiop);
758*1840Swh94709 
759*1840Swh94709 	if (error == 0) {
760*1840Swh94709 		hverr = hv_service_send(softsp->s_id,
761*1840Swh94709 		    softsp->mb_send_buf_pa, send_count, &send_count);
762*1840Swh94709 		error = glvc_emap_h2s(hverr);
763*1840Swh94709 	}
764*1840Swh94709 
765*1840Swh94709 	DPRINTF(("instance %d glvc_write write check error = %d,"
766*1840Swh94709 	    " send_count = %lu", instance, error, send_count));
767*1840Swh94709 
768*1840Swh94709 	softsp->send_complete_flag = 0;
769*1840Swh94709 
770*1840Swh94709 	mutex_exit(&softsp->send_complete_mutex);
771*1840Swh94709 
772*1840Swh94709 	return (error);
773*1840Swh94709 }
774*1840Swh94709 
775*1840Swh94709 /*
776*1840Swh94709  * Interrupt handler
777*1840Swh94709  */
778*1840Swh94709 static uint_t
779*1840Swh94709 glvc_intr(caddr_t arg)
780*1840Swh94709 {
781*1840Swh94709 	glvc_soft_state_t *softsp = (glvc_soft_state_t *)arg;
782*1840Swh94709 	uint64_t status_reg;
783*1840Swh94709 	int error = DDI_INTR_UNCLAIMED;
784*1840Swh94709 	uint64_t hverr = H_EOK;
785*1840Swh94709 	uint64_t clr_bits = 0;
786*1840Swh94709 
787*1840Swh94709 	mutex_enter(&softsp->recv_mutex);
788*1840Swh94709 	mutex_enter(&softsp->send_complete_mutex);
789*1840Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
790*1840Swh94709 	DPRINTF(("glvc_intr: err = %ld, getstatus = 0x%lx",
791*1840Swh94709 	    hverr, status_reg));
792*1840Swh94709 
793*1840Swh94709 	/*
794*1840Swh94709 	 * Clear SEND_COMPLETE bit and disable RECV interrupt
795*1840Swh94709 	 */
796*1840Swh94709 	if (status_reg & GLVC_REG_SEND)
797*1840Swh94709 		clr_bits |= GLVC_REG_SEND;
798*1840Swh94709 	if ((softsp->intr_mode == GLVC_INTR_MODE) &&
799*1840Swh94709 	    (status_reg & GLVC_REG_RECV))
800*1840Swh94709 		clr_bits |= GLVC_REG_RECV_ENA;
801*1840Swh94709 
802*1840Swh94709 	if ((hverr = hv_service_clrstatus(softsp->s_id, clr_bits))
803*1840Swh94709 	    != H_EOK) {
804*1840Swh94709 		cmn_err(CE_WARN, "glvc_intr clear status reg failed"
805*1840Swh94709 		    "error = %ld", hverr);
806*1840Swh94709 		mutex_exit(&softsp->send_complete_mutex);
807*1840Swh94709 		mutex_exit(&softsp->recv_mutex);
808*1840Swh94709 		return (DDI_INTR_UNCLAIMED);
809*1840Swh94709 	}
810*1840Swh94709 
811*1840Swh94709 	if (status_reg & GLVC_REG_RECV) {
812*1840Swh94709 		cv_broadcast(&softsp->recv_cv);
813*1840Swh94709 		error = DDI_INTR_CLAIMED;
814*1840Swh94709 	}
815*1840Swh94709 
816*1840Swh94709 	if (status_reg & GLVC_REG_SEND) {
817*1840Swh94709 		softsp->send_complete_flag = 1;
818*1840Swh94709 		cv_broadcast(&softsp->send_complete_cv);
819*1840Swh94709 		error = DDI_INTR_CLAIMED;
820*1840Swh94709 	}
821*1840Swh94709 
822*1840Swh94709 	mutex_exit(&softsp->send_complete_mutex);
823*1840Swh94709 	mutex_exit(&softsp->recv_mutex);
824*1840Swh94709 
825*1840Swh94709 	return (error);
826*1840Swh94709 }
827*1840Swh94709 
828*1840Swh94709 /*
829*1840Swh94709  * Peek to see if there is data received. If no data available,
830*1840Swh94709  * we sleep wait. If there is data, read from hypervisor and copy
831*1840Swh94709  * to ioctl buffer. We don't clear the receive data interrupt bit.
832*1840Swh94709  */
833*1840Swh94709 static int
834*1840Swh94709 glvc_peek(glvc_soft_state_t *softsp, glvc_xport_msg_peek_t *msg_peek)
835*1840Swh94709 {
836*1840Swh94709 	int rv, error = 0;
837*1840Swh94709 	uint64_t hverr = H_EOK;
838*1840Swh94709 	uint64_t recv_count = 0;
839*1840Swh94709 	uint64_t status_reg;
840*1840Swh94709 	clock_t tick;
841*1840Swh94709 
842*1840Swh94709 	mutex_enter(&softsp->recv_mutex);
843*1840Swh94709 
844*1840Swh94709 	hverr = hv_service_getstatus(softsp->s_id, &status_reg);
845*1840Swh94709 	DPRINTF(("glvc_peek: err = %ld, getstatus = 0x%lx",
846*1840Swh94709 	    hverr, status_reg));
847*1840Swh94709 
848*1840Swh94709 	/*
849*1840Swh94709 	 * If no data available, we wait till we get some.
850*1840Swh94709 	 * Notice we still holding the recv_mutex lock at
851*1840Swh94709 	 * this point.
852*1840Swh94709 	 */
853*1840Swh94709 	while (hverr == H_EOK && (status_reg & GLVC_REG_RECV) !=
854*1840Swh94709 	    GLVC_REG_RECV) {
855*1840Swh94709 		tick = ddi_get_lbolt() + softsp->polling_interval;
856*1840Swh94709 		rv = cv_timedwait_sig(&softsp->recv_cv,
857*1840Swh94709 		    &softsp->recv_mutex, tick);
858*1840Swh94709 		if (rv == 0) {
859*1840Swh94709 			/*
860*1840Swh94709 			 * We got interrupted.
861*1840Swh94709 			 */
862*1840Swh94709 			mutex_exit(&softsp->recv_mutex);
863*1840Swh94709 			return (EINTR);
864*1840Swh94709 		}
865*1840Swh94709 		if (rv == -1) {
866*1840Swh94709 			/*
867*1840Swh94709 			 * Timeout wait, trigger a soft intr in case
868*1840Swh94709 			 * we miss an interrupt or in polling mode.
869*1840Swh94709 			 */
870*1840Swh94709 			ddi_trigger_softintr(softsp->poll_mode_softint_id);
871*1840Swh94709 		}
872*1840Swh94709 		hverr = hv_service_getstatus(softsp->s_id, &status_reg);
873*1840Swh94709 		DPRINTF(("glvc_peek: err = %ld, getstatus = 0x%lx",
874*1840Swh94709 		    hverr, status_reg));
875*1840Swh94709 	}
876*1840Swh94709 
877*1840Swh94709 	/* Read data into kernel buffer */
878*1840Swh94709 	hverr = hv_service_recv(softsp->s_id, softsp->mb_recv_buf_pa,
879*1840Swh94709 	    softsp->mtu, &recv_count);
880*1840Swh94709 	DPRINTF(("glvc_peek recv data, error = %ld, recv_count = %lu",
881*1840Swh94709 	    hverr, recv_count));
882*1840Swh94709 
883*1840Swh94709 	if (hverr == H_EOK && recv_count > 0) {
884*1840Swh94709 		(void *) memcpy(msg_peek->buf,
885*1840Swh94709 		    softsp->mb_recv_buf, recv_count);
886*1840Swh94709 		msg_peek->buflen = recv_count;
887*1840Swh94709 	} else {
888*1840Swh94709 		error = glvc_emap_h2s(hverr);
889*1840Swh94709 	}
890*1840Swh94709 
891*1840Swh94709 	mutex_exit(&softsp->recv_mutex);
892*1840Swh94709 
893*1840Swh94709 	return (error);
894*1840Swh94709 }
895*1840Swh94709 
896*1840Swh94709 static int
897*1840Swh94709 glvc_ioctl_opt_op(glvc_soft_state_t *softsp, intptr_t arg, int mode)
898*1840Swh94709 {
899*1840Swh94709 	glvc_xport_opt_op_t glvc_xport_cmd;
900*1840Swh94709 	uint64_t status_reg;
901*1840Swh94709 	int retval = 0;
902*1840Swh94709 	uint64_t hverr;
903*1840Swh94709 
904*1840Swh94709 	if (ddi_copyin((caddr_t)arg, (caddr_t)&glvc_xport_cmd,
905*1840Swh94709 	    sizeof (glvc_xport_opt_op_t), mode) != 0) {
906*1840Swh94709 		return (EFAULT);
907*1840Swh94709 	}
908*1840Swh94709 
909*1840Swh94709 	switch (glvc_xport_cmd.opt_sel) {
910*1840Swh94709 	case GLVC_XPORT_OPT_MTU_SZ:
911*1840Swh94709 		if (glvc_xport_cmd.op_sel == GLVC_XPORT_OPT_GET) {
912*1840Swh94709 			glvc_xport_cmd.opt_val = softsp->mtu;
913*1840Swh94709 			retval = ddi_copyout((caddr_t)&glvc_xport_cmd,
914*1840Swh94709 			    (caddr_t)arg, sizeof (glvc_xport_opt_op_t),
915*1840Swh94709 			    mode);
916*1840Swh94709 		} else
917*1840Swh94709 			retval = ENOTSUP;
918*1840Swh94709 
919*1840Swh94709 		break;
920*1840Swh94709 
921*1840Swh94709 	case GLVC_XPORT_OPT_REG_STATUS:
922*1840Swh94709 		if (glvc_xport_cmd.op_sel == GLVC_XPORT_OPT_GET) {
923*1840Swh94709 			mutex_enter(&softsp->statusreg_mutex);
924*1840Swh94709 			hverr = hv_service_getstatus(softsp->s_id, &status_reg);
925*1840Swh94709 			mutex_exit(&softsp->statusreg_mutex);
926*1840Swh94709 			if (hverr == H_EOK) {
927*1840Swh94709 				glvc_xport_cmd.opt_val = (uint32_t)status_reg;
928*1840Swh94709 				retval = ddi_copyout((caddr_t)&glvc_xport_cmd,
929*1840Swh94709 				    (caddr_t)arg, sizeof (glvc_xport_opt_op_t),
930*1840Swh94709 				    mode);
931*1840Swh94709 			} else {
932*1840Swh94709 				retval = EIO;
933*1840Swh94709 			}
934*1840Swh94709 		} else {
935*1840Swh94709 			retval = ENOTSUP;
936*1840Swh94709 		}
937*1840Swh94709 
938*1840Swh94709 		break;
939*1840Swh94709 
940*1840Swh94709 	default:
941*1840Swh94709 		retval = ENOTSUP;
942*1840Swh94709 		break;
943*1840Swh94709 	}
944*1840Swh94709 
945*1840Swh94709 	return (retval);
946*1840Swh94709 }
947*1840Swh94709 
948*1840Swh94709 
949*1840Swh94709 /*ARGSUSED*/
950*1840Swh94709 static int
951*1840Swh94709 glvc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
952*1840Swh94709     int *rval_p)
953*1840Swh94709 {
954*1840Swh94709 	glvc_soft_state_t *softsp;
955*1840Swh94709 	int instance = getminor(dev);
956*1840Swh94709 	glvc_xport_msg_peek_t glvc_peek_msg, msg_peek_cmd;
957*1840Swh94709 	glvc_xport_msg_peek32_t msg_peek_cmd32;
958*1840Swh94709 
959*1840Swh94709 	int retval = 0;
960*1840Swh94709 
961*1840Swh94709 	softsp = ddi_get_soft_state(glvc_ssp, instance);
962*1840Swh94709 
963*1840Swh94709 	switch (cmd) {
964*1840Swh94709 	case GLVC_XPORT_IOCTL_OPT_OP:
965*1840Swh94709 		retval = glvc_ioctl_opt_op(softsp, arg, mode);
966*1840Swh94709 		break;
967*1840Swh94709 
968*1840Swh94709 	case GLVC_XPORT_IOCTL_DATA_PEEK:
969*1840Swh94709 		glvc_peek_msg.buf =
970*1840Swh94709 		    (char *)kmem_zalloc(softsp->mtu, KM_NOSLEEP);
971*1840Swh94709 		if (glvc_peek_msg.buf == NULL)
972*1840Swh94709 			return (EBUSY);
973*1840Swh94709 		retval = glvc_peek(softsp, &glvc_peek_msg);
974*1840Swh94709 		if (retval == 0) {
975*1840Swh94709 			switch (ddi_model_convert_from(mode)) {
976*1840Swh94709 			case DDI_MODEL_ILP32:
977*1840Swh94709 				if (ddi_copyin((caddr_t)arg,
978*1840Swh94709 				    (caddr_t)&msg_peek_cmd32,
979*1840Swh94709 				    sizeof (glvc_xport_msg_peek32_t),
980*1840Swh94709 				    mode) == -1) {
981*1840Swh94709 					retval = EFAULT;
982*1840Swh94709 					break;
983*1840Swh94709 				}
984*1840Swh94709 
985*1840Swh94709 				if (msg_peek_cmd32.buflen32 == 0) {
986*1840Swh94709 					retval = EINVAL;
987*1840Swh94709 					break;
988*1840Swh94709 				}
989*1840Swh94709 
990*1840Swh94709 				if (msg_peek_cmd32.buflen32 >
991*1840Swh94709 				    glvc_peek_msg.buflen)
992*1840Swh94709 					msg_peek_cmd32.buflen32 =
993*1840Swh94709 					    glvc_peek_msg.buflen;
994*1840Swh94709 
995*1840Swh94709 				if (ddi_copyout((caddr_t)glvc_peek_msg.buf,
996*1840Swh94709 				    (caddr_t)(uintptr_t)msg_peek_cmd32.buf32,
997*1840Swh94709 				    msg_peek_cmd32.buflen32, mode) == -1) {
998*1840Swh94709 					retval = EFAULT;
999*1840Swh94709 					break;
1000*1840Swh94709 				}
1001*1840Swh94709 
1002*1840Swh94709 				if (ddi_copyout((caddr_t)&msg_peek_cmd32,
1003*1840Swh94709 				    (caddr_t)arg,
1004*1840Swh94709 				    sizeof (glvc_xport_msg_peek32_t), mode)
1005*1840Swh94709 				    == -1)
1006*1840Swh94709 					retval = EFAULT;
1007*1840Swh94709 				break;
1008*1840Swh94709 
1009*1840Swh94709 			case DDI_MODEL_NONE:
1010*1840Swh94709 				if (ddi_copyin((caddr_t)arg,
1011*1840Swh94709 				    (caddr_t)&msg_peek_cmd,
1012*1840Swh94709 				    sizeof (glvc_xport_msg_peek_t), mode) == -1)
1013*1840Swh94709 					retval = EFAULT;
1014*1840Swh94709 
1015*1840Swh94709 				if (msg_peek_cmd.buflen == 0) {
1016*1840Swh94709 					retval = EINVAL;
1017*1840Swh94709 					break;
1018*1840Swh94709 				}
1019*1840Swh94709 
1020*1840Swh94709 				if (msg_peek_cmd.buflen > glvc_peek_msg.buflen)
1021*1840Swh94709 					msg_peek_cmd.buflen =
1022*1840Swh94709 					    glvc_peek_msg.buflen;
1023*1840Swh94709 
1024*1840Swh94709 				if (ddi_copyout((caddr_t)glvc_peek_msg.buf,
1025*1840Swh94709 				    (caddr_t)msg_peek_cmd.buf,
1026*1840Swh94709 				    msg_peek_cmd.buflen, mode) == -1) {
1027*1840Swh94709 					retval = EFAULT;
1028*1840Swh94709 					break;
1029*1840Swh94709 				}
1030*1840Swh94709 
1031*1840Swh94709 				if (ddi_copyout((caddr_t)&msg_peek_cmd,
1032*1840Swh94709 				    (caddr_t)arg,
1033*1840Swh94709 				    sizeof (glvc_xport_msg_peek_t), mode) == -1)
1034*1840Swh94709 					retval = EFAULT;
1035*1840Swh94709 				break;
1036*1840Swh94709 
1037*1840Swh94709 			default:
1038*1840Swh94709 				retval = EFAULT;
1039*1840Swh94709 				break;
1040*1840Swh94709 			}
1041*1840Swh94709 		}
1042*1840Swh94709 		kmem_free(glvc_peek_msg.buf, softsp->mtu);
1043*1840Swh94709 		break;
1044*1840Swh94709 
1045*1840Swh94709 	default:
1046*1840Swh94709 		retval = ENOTSUP;
1047*1840Swh94709 		break;
1048*1840Swh94709 	}
1049*1840Swh94709 	return (retval);
1050*1840Swh94709 }
1051*1840Swh94709 
1052*1840Swh94709 /*
1053*1840Swh94709  * Map hypervisor error code to solaris. Only
1054*1840Swh94709  * H_EOK, H_EINVA, H_EWOULDBLOCK and H_EIO are meaningful
1055*1840Swh94709  * to this device. All other error codes are mapped to EIO.
1056*1840Swh94709  */
1057*1840Swh94709 static int
1058*1840Swh94709 glvc_emap_h2s(uint64_t hv_errcode)
1059*1840Swh94709 {
1060*1840Swh94709 	int s_errcode;
1061*1840Swh94709 
1062*1840Swh94709 	switch (hv_errcode) {
1063*1840Swh94709 	case H_EOK:
1064*1840Swh94709 		s_errcode = 0;
1065*1840Swh94709 		break;
1066*1840Swh94709 
1067*1840Swh94709 	case H_EINVAL:
1068*1840Swh94709 		s_errcode = EINVAL;
1069*1840Swh94709 		break;
1070*1840Swh94709 
1071*1840Swh94709 	case H_EWOULDBLOCK:
1072*1840Swh94709 		s_errcode = EWOULDBLOCK;
1073*1840Swh94709 		break;
1074*1840Swh94709 
1075*1840Swh94709 	case H_EIO:
1076*1840Swh94709 		s_errcode = EIO;
1077*1840Swh94709 		break;
1078*1840Swh94709 
1079*1840Swh94709 	default:
1080*1840Swh94709 		/* should not happen */
1081*1840Swh94709 		DPRINTF(("Unexpected device error code %ld received, "
1082*1840Swh94709 		    "mapped to EIO", hv_errcode));
1083*1840Swh94709 		s_errcode = EIO;
1084*1840Swh94709 		break;
1085*1840Swh94709 	}
1086*1840Swh94709 
1087*1840Swh94709 	return (s_errcode);
1088*1840Swh94709 }
1089