xref: /illumos-gate/usr/src/uts/sun4v/io/mdesc.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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