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