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