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