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