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