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 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 2304ab777b1Swh94709 _info(struct modinfo *modinfop) 2314ab777b1Swh94709 { 2324ab777b1Swh94709 return (mod_info(&modlinkage, modinfop)); 2334ab777b1Swh94709 } 2344ab777b1Swh94709 2354ab777b1Swh94709 2364ab777b1Swh94709 int 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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