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