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
_init(void)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
_info(struct modinfo * modinfop)157 _info(struct modinfo *modinfop)
158 {
159 return (mod_info(&modlinkage, modinfop));
160 }
161
162
163
164
165 int
_fini(void)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
mdesc_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** resultp)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
mdesc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
mdesc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
mdesc_destroy_state(mdesc_state_t * mdsp)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 *
mdesc_create_state(dev_t * devp)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
mdesc_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
mdesc_close(dev_t dev,int flag,int otyp,cred_t * credp)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
mdesc_read(dev_t dev,struct uio * uiop,cred_t * credp)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
mdesc_write(dev_t dev,struct uio * uiop,cred_t * credp)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
mdesc_rw(dev_t dev,struct uio * uiop,enum uio_rw rw)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
mdesc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)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