Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 /*
     28  * sun4v machine description driver
     29  */
     30 
     31 #include <sys/types.h>
     32 #include <sys/file.h>
     33 #include <sys/errno.h>
     34 #include <sys/open.h>
     35 #include <sys/cred.h>
     36 #include <sys/uio.h>
     37 #include <sys/stat.h>
     38 #include <sys/ksynch.h>
     39 #include <sys/modctl.h>
     40 #include <sys/conf.h>
     41 #include <sys/devops.h>
     42 #include <sys/debug.h>
     43 #include <sys/cmn_err.h>
     44 #include <sys/ddi.h>
     45 #include <sys/sunddi.h>
     46 
     47 #include <sys/mdesc.h>
     48 #include <sys/mach_descrip.h>
     49 
     50 #define	MDESC_NAME	"mdesc"
     51 
     52 /*
     53  * Operational state flags
     54  */
     55 
     56 #define	MDESC_GOT_HANDLE	0x10		/* Got mdesc handle */
     57 #define	MDESC_BUSY		0x20		/* Device is busy */
     58 
     59 static void		*mdesc_state_head;
     60 static vmem_t		*mdesc_minor;
     61 static uint16_t 	mdesc_max_opens = 256;
     62 static uint16_t		mdesc_opens = 0;
     63 static int		mdesc_attached = 0;
     64 static dev_info_t	*mdesc_devi;
     65 static kmutex_t		mdesc_lock;
     66 
     67 struct mdesc_state {
     68 	int		instance;
     69 	dev_t		dev;
     70 	kmutex_t	lock;
     71 	kcondvar_t	cv;
     72 	size_t		mdesc_len;
     73 	md_t		*mdesc;
     74 	int		flags;
     75 };
     76 
     77 typedef struct mdesc_state mdesc_state_t;
     78 
     79 static int mdesc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
     80 static int mdesc_attach(dev_info_t *, ddi_attach_cmd_t);
     81 static int mdesc_detach(dev_info_t *, ddi_detach_cmd_t);
     82 static int mdesc_open(dev_t *, int, int, cred_t *);
     83 static int mdesc_close(dev_t, int, int, cred_t *);
     84 static int mdesc_read(dev_t, struct uio *, cred_t *);
     85 static int mdesc_write(dev_t, struct uio *, cred_t *);
     86 static int mdesc_rw(dev_t, struct uio *, enum uio_rw);
     87 static int mdesc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
     88 
     89 static struct cb_ops mdesc_cb_ops = {
     90 	mdesc_open,		/* cb_open */
     91 	mdesc_close,		/* cb_close */
     92 	nodev,			/* cb_strategy */
     93 	nodev,			/* cb_print */
     94 	nodev,			/* cb_dump */
     95 	mdesc_read,		/* cb_read */
     96 	nodev,			/* cb_write */
     97 	mdesc_ioctl,		/* cb_ioctl */
     98 	nodev,			/* cb_devmap */
     99 	nodev,			/* cb_mmap */
    100 	nodev,			/* cb_segmap */
    101 	nochpoll,		/* cb_chpoll */
    102 	ddi_prop_op,		/* cb_prop_op */
    103 	(struct streamtab *)NULL, /* cb_str */
    104 	D_MP | D_64BIT,		/* cb_flag */
    105 	CB_REV,			/* cb_rev */
    106 	nodev,			/* cb_aread */
    107 	nodev			/* cb_awrite */
    108 };
    109 
    110 static struct dev_ops mdesc_dev_ops = {
    111 	DEVO_REV,		/* devo_rev */
    112 	0,			/* devo_refcnt */
    113 	mdesc_getinfo,		/* devo_getinfo */
    114 	nulldev,		/* devo_identify */
    115 	nulldev,		/* devo_probe */
    116 	mdesc_attach,		/* devo_attach */
    117 	mdesc_detach,		/* devo_detach */
    118 	nodev,			/* devo_reset */
    119 	&mdesc_cb_ops,		/* devo_cb_ops */
    120 	(struct bus_ops *)NULL,	/* devo_bus_ops */
    121 	nulldev,		/* devo_power */
    122 	ddi_quiesce_not_needed,		/* quiesce */
    123 };
    124 
    125 static struct modldrv modldrv = {
    126 	&mod_driverops,
    127 	"Machine Description Driver",
    128 	&mdesc_dev_ops};
    129 
    130 static struct modlinkage modlinkage = {
    131 	MODREV_1,
    132 	(void *)&modldrv,
    133 	NULL
    134 };
    135 
    136 
    137 int
    138 _init(void)
    139 {
    140 	int retval;
    141 
    142 	if ((retval = ddi_soft_state_init(&mdesc_state_head,
    143 	    sizeof (struct mdesc_state), mdesc_max_opens)) != 0)
    144 		return (retval);
    145 	if ((retval = mod_install(&modlinkage)) != 0) {
    146 		ddi_soft_state_fini(&mdesc_state_head);
    147 		return (retval);
    148 	}
    149 
    150 	return (retval);
    151 }
    152 
    153 
    154 
    155 
    156 int
    157 _info(struct modinfo *modinfop)
    158 {
    159 	return (mod_info(&modlinkage, modinfop));
    160 }
    161 
    162 
    163 
    164 
    165 int
    166 _fini(void)
    167 {
    168 	int retval;
    169 
    170 	if ((retval = mod_remove(&modlinkage)) != 0)
    171 		return (retval);
    172 	ddi_soft_state_fini(&mdesc_state_head);
    173 
    174 	return (retval);
    175 }
    176 
    177 
    178 
    179 
    180 /*ARGSUSED*/
    181 static int
    182 mdesc_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
    183 {
    184 	struct mdesc_state *mdsp;
    185 	int retval = DDI_FAILURE;
    186 
    187 	ASSERT(resultp != NULL);
    188 
    189 	switch (cmd) {
    190 	case DDI_INFO_DEVT2DEVINFO:
    191 		mdsp = ddi_get_soft_state(mdesc_state_head,
    192 		    getminor((dev_t)arg));
    193 		if (mdsp != NULL) {
    194 			*resultp = mdesc_devi;
    195 			retval = DDI_SUCCESS;
    196 		} else
    197 			*resultp = NULL;
    198 		break;
    199 	case DDI_INFO_DEVT2INSTANCE:
    200 		*resultp = (void *)(uintptr_t)getminor((dev_t)arg);
    201 		retval = DDI_SUCCESS;
    202 		break;
    203 	}
    204 
    205 	return (retval);
    206 }
    207 
    208 
    209 
    210 
    211 static int
    212 mdesc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
    213 {
    214 	int instance = ddi_get_instance(dip);
    215 
    216 	switch (cmd) {
    217 	case DDI_ATTACH:
    218 
    219 		if (ddi_create_minor_node(dip, MDESC_NAME, S_IFCHR, instance,
    220 		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
    221 			cmn_err(CE_WARN, "%s@%d: Unable to create minor node",
    222 			    MDESC_NAME, instance);
    223 			return (DDI_FAILURE);
    224 		}
    225 		ddi_report_dev(dip);
    226 		mdesc_devi = dip;
    227 		mdesc_minor = vmem_create("mdesc_minor", (void *) 1,
    228 		    mdesc_max_opens, 1, NULL, NULL, NULL, 0,
    229 		    VM_SLEEP | VMC_IDENTIFIER);
    230 		mutex_init(&mdesc_lock, NULL, MUTEX_DRIVER, NULL);
    231 		mdesc_attached = 1;
    232 		return (DDI_SUCCESS);
    233 	case DDI_RESUME:
    234 		return (DDI_SUCCESS);
    235 	default:
    236 		return (DDI_FAILURE);
    237 	}
    238 }
    239 
    240 /*ARGSUSED*/
    241 static int
    242 mdesc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
    243 {
    244 	switch (cmd) {
    245 	case DDI_DETACH:
    246 		mutex_destroy(&mdesc_lock);
    247 		vmem_destroy(mdesc_minor);
    248 		ddi_remove_minor_node(mdesc_devi, NULL);
    249 		mdesc_attached = 0;
    250 		return (DDI_SUCCESS);
    251 
    252 	case DDI_SUSPEND:
    253 		return (DDI_SUCCESS);
    254 
    255 	default:
    256 		return (DDI_FAILURE);
    257 	}
    258 }
    259 
    260 static void
    261 mdesc_destroy_state(mdesc_state_t *mdsp)
    262 {
    263 	minor_t minor = getminor(mdsp->dev);
    264 
    265 	if (mdsp->flags & MDESC_GOT_HANDLE)
    266 		(void) md_fini_handle(mdsp->mdesc);
    267 
    268 	cv_destroy(&mdsp->cv);
    269 	mutex_destroy(&mdsp->lock);
    270 	ddi_soft_state_free(mdesc_state_head, minor);
    271 	vmem_free(mdesc_minor, (void *)(uintptr_t)minor, 1);
    272 }
    273 
    274 static mdesc_state_t *
    275 mdesc_create_state(dev_t *devp)
    276 {
    277 	major_t	major;
    278 	minor_t	minor;
    279 	mdesc_state_t *mdsp;
    280 
    281 	minor = (minor_t)(uintptr_t)vmem_alloc(mdesc_minor, 1,
    282 	    VM_BESTFIT | VM_SLEEP);
    283 
    284 	if (ddi_soft_state_zalloc(mdesc_state_head, minor) !=
    285 	    DDI_SUCCESS) {
    286 		cmn_err(CE_WARN, "%s@%d: Unable to allocate state",
    287 		    MDESC_NAME, minor);
    288 		vmem_free(mdesc_minor, (void *)(uintptr_t)minor, 1);
    289 		return (NULL);
    290 	}
    291 
    292 	mdsp = ddi_get_soft_state(mdesc_state_head, minor);
    293 
    294 	if (devp != NULL) {
    295 		major = getemajor(*devp);
    296 	} else {
    297 		major = ddi_driver_major(mdesc_devi);
    298 	}
    299 
    300 	mdsp->dev = makedevice(major, minor);
    301 
    302 	if (devp != NULL)
    303 		*devp = mdsp->dev;
    304 
    305 	mdsp->instance = minor;
    306 
    307 	mutex_init(&mdsp->lock, NULL, MUTEX_DRIVER, NULL);
    308 
    309 	cv_init(&mdsp->cv, NULL, CV_DRIVER, NULL);
    310 
    311 	mdsp->mdesc = md_get_handle();
    312 
    313 	if (mdsp->mdesc == NULL) {
    314 		mdesc_destroy_state(mdsp);
    315 		return (NULL);
    316 	}
    317 	mdsp->flags |= MDESC_GOT_HANDLE;
    318 
    319 	mdsp->mdesc_len = md_get_bin_size(mdsp->mdesc);
    320 
    321 	if (mdsp->mdesc_len == 0) {
    322 		mdesc_destroy_state(mdsp);
    323 		mdsp = NULL;
    324 	}
    325 
    326 	return (mdsp);
    327 }
    328 
    329 
    330 /*ARGSUSED*/
    331 static int
    332 mdesc_open(dev_t *devp, int flag, int otyp, cred_t *credp)
    333 {
    334 	struct mdesc_state *mdsp;
    335 
    336 	if (otyp != OTYP_CHR)
    337 		return (EINVAL);
    338 	if (!mdesc_attached)
    339 		return (ENXIO);
    340 
    341 	mutex_enter(&mdesc_lock);
    342 
    343 	if (mdesc_opens >= mdesc_max_opens) {
    344 		mutex_exit(&mdesc_lock);
    345 		return (ENXIO);
    346 	}
    347 
    348 	mdsp = mdesc_create_state(devp);
    349 
    350 	if (mdsp == NULL) {
    351 		mutex_exit(&mdesc_lock);
    352 		return (ENXIO);
    353 	}
    354 
    355 	mdesc_opens++;
    356 
    357 	mutex_exit(&mdesc_lock);
    358 
    359 	return (0);
    360 }
    361 
    362 /*ARGSUSED*/
    363 static int
    364 mdesc_close(dev_t dev, int flag, int otyp, cred_t *credp)
    365 {
    366 	struct mdesc_state *mdsp;
    367 	int instance = getminor(dev);
    368 
    369 	if (otyp != OTYP_CHR)
    370 		return (EINVAL);
    371 
    372 	mutex_enter(&mdesc_lock);
    373 	if (mdesc_opens == 0) {
    374 		mutex_exit(&mdesc_lock);
    375 		return (0);
    376 	}
    377 	mutex_exit(&mdesc_lock);
    378 
    379 	if ((mdsp = ddi_get_soft_state(mdesc_state_head, instance)) == NULL)
    380 		return (ENXIO);
    381 
    382 	ASSERT(mdsp->instance == instance);
    383 
    384 	mdesc_destroy_state(mdsp);
    385 	mutex_enter(&mdesc_lock);
    386 	mdesc_opens--;
    387 	mutex_exit(&mdesc_lock);
    388 
    389 	return (0);
    390 }
    391 
    392 
    393 
    394 
    395 /*ARGSUSED*/
    396 static int
    397 mdesc_read(dev_t dev, struct uio *uiop, cred_t *credp)
    398 {
    399 	return (mdesc_rw(dev, uiop, UIO_READ));
    400 }
    401 
    402 
    403 
    404 
    405 /*ARGSUSED*/
    406 static int
    407 mdesc_write(dev_t dev, struct uio *uiop, cred_t *credp)
    408 {
    409 	return (ENXIO);	/* This driver version does not allow updates */
    410 }
    411 
    412 
    413 
    414 
    415 static int
    416 mdesc_rw(dev_t dev, struct uio *uiop, enum uio_rw rw)
    417 {
    418 	struct mdesc_state *mdsp;
    419 	int instance = getminor(dev);
    420 	size_t len;
    421 	int retval;
    422 	caddr_t buf;
    423 
    424 	len = uiop->uio_resid;
    425 
    426 	if ((mdsp = ddi_get_soft_state(mdesc_state_head, instance)) == NULL)
    427 		return (ENXIO);
    428 
    429 	ASSERT(mdsp->instance == instance);
    430 
    431 	if (len == 0)
    432 		return (0);
    433 
    434 	mutex_enter(&mdsp->lock);
    435 
    436 	while (mdsp->flags & MDESC_BUSY) {
    437 		if (cv_wait_sig(&mdsp->cv, &mdsp->lock) == 0) {
    438 			mutex_exit(&mdsp->lock);
    439 			return (EINTR);
    440 		}
    441 	}
    442 
    443 	if (uiop->uio_offset < 0 || uiop->uio_offset > mdsp->mdesc_len) {
    444 		mutex_exit(&mdsp->lock);
    445 		return (EINVAL);
    446 	}
    447 
    448 	if (len > (mdsp->mdesc_len - uiop->uio_offset))
    449 		len = mdsp->mdesc_len - uiop->uio_offset;
    450 
    451 		/* already checked that offset<mdesc_len above */
    452 	if (len == 0) {
    453 		mutex_exit(&mdsp->lock);
    454 		return (rw == UIO_WRITE ? ENOSPC : 0);
    455 	}
    456 
    457 	mdsp->flags |= MDESC_BUSY;
    458 	mutex_exit(&mdsp->lock);
    459 
    460 	buf = md_get_md_raw(mdsp->mdesc);
    461 	if (buf == NULL)
    462 		return (ENXIO);
    463 
    464 	retval = uiomove((void *)(buf + uiop->uio_offset),
    465 	    len, rw, uiop);
    466 
    467 	mutex_enter(&mdsp->lock);
    468 	mdsp->flags &= ~MDESC_BUSY;
    469 	cv_broadcast(&mdsp->cv);
    470 	mutex_exit(&mdsp->lock);
    471 
    472 	return (retval);
    473 }
    474 
    475 
    476 
    477 
    478 /*ARGSUSED*/
    479 static int
    480 mdesc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
    481     int *rvalp)
    482 {
    483 	struct mdesc_state *mdsp;
    484 	int instance = getminor(dev);
    485 
    486 	if ((mdsp = ddi_get_soft_state(mdesc_state_head, instance)) == NULL)
    487 		return (ENXIO);
    488 
    489 	ASSERT(mdsp->instance == instance);
    490 
    491 	switch (cmd) {
    492 	case MDESCIOCGSZ: {
    493 		/*
    494 		 * We are not guaranteed that ddi_copyout(9F) will read
    495 		 * atomically anything larger than a byte.  Therefore we
    496 		 * must duplicate the size before copying it out to the user.
    497 		 */
    498 		size_t sz = mdsp->mdesc_len;
    499 
    500 		if (!(mode & FREAD))
    501 			return (EACCES);
    502 
    503 #ifdef _MULTI_DATAMODEL
    504 		switch (ddi_model_convert_from(mode & FMODELS)) {
    505 		case DDI_MODEL_ILP32: {
    506 			size32_t sz32 = (size32_t)sz;
    507 			if (ddi_copyout(&sz32, (void *)arg, sizeof (size32_t),
    508 			    mode) != 0)
    509 				return (EFAULT);
    510 			return (0);
    511 		}
    512 		case DDI_MODEL_NONE:
    513 			if (ddi_copyout(&sz, (void *)arg, sizeof (size_t),
    514 			    mode) != 0)
    515 				return (EFAULT);
    516 			return (0);
    517 		default:
    518 			cmn_err(CE_WARN,
    519 			    "mdesc: Invalid data model %d in ioctl\n",
    520 			    ddi_model_convert_from(mode & FMODELS));
    521 			return (ENOTSUP);
    522 		}
    523 #else /* ! _MULTI_DATAMODEL */
    524 		if (ddi_copyout(&sz, (void *)arg, sizeof (size_t), mode) != 0)
    525 			return (EFAULT);
    526 		return (0);
    527 #endif /* _MULTI_DATAMODEL */
    528 	}
    529 
    530 	default:
    531 		return (ENOTTY);
    532 	}
    533 }
    534