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