xref: /titanic_50/usr/src/uts/common/io/pseudonex.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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  * Pseudo devices are devices implemented entirely in software; pseudonex
31  * (pseudo) is the traditional nexus for pseudodevices.  Instances are
32  * typically specified via driver.conf files; e.g. a leaf device which
33  * should be attached below pseudonex will have an entry like:
34  *
35  *	name="foo" parent="/pseudo" instance=0;
36  *
37  * pseudonex also supports the devctl (see <sys/devctl.h>) interface via
38  * its :devctl minor node.  This allows priveleged userland applications to
39  * online/offline children of pseudo as needed.
40  *
41  * In general, we discourage widespread use of this tactic, as it may lead to a
42  * proliferation of nodes in /pseudo.  It is preferred that implementors update
43  * pseudo.conf, adding another 'pseudo' nexus child of /pseudo, and then use
44  * that for their collection of device nodes.  To do so, add a driver alias
45  * for the name of the nexus child and a line in pseudo.conf such as:
46  *
47  * 	name="foo" parent="/pseudo" instance=<n> valid-children="bar","baz";
48  *
49  * Setting 'valid-children' is important because we have an annoying problem;
50  * we need to prevent pseudo devices with 'parent="pseudo"' set from binding
51  * to our new pseudonex child node.  A better way might be to teach the
52  * spec-node code to understand that parent="pseudo" really means
53  * parent="/pseudo".
54  *
55  * At some point in the future, it would be desirable to extend the instance
56  * database to include nexus children of pseudo.  Then we could use devctl
57  * or devfs to online nexus children of pseudo, auto-selecting an instance #,
58  * and the instance number selected would be preserved across reboot in
59  * path_to_inst.
60  */
61 
62 #include <sys/types.h>
63 #include <sys/cmn_err.h>
64 #include <sys/conf.h>
65 #include <sys/ddi.h>
66 #include <sys/ddi_impldefs.h>
67 #include <sys/devops.h>
68 #include <sys/instance.h>
69 #include <sys/modctl.h>
70 #include <sys/open.h>
71 #include <sys/stat.h>
72 #include <sys/sunddi.h>
73 #include <sys/sunndi.h>
74 #include <sys/systm.h>
75 #include <sys/mkdev.h>
76 
77 /*
78  * Config information
79  */
80 static int pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip,
81 	    ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result);
82 
83 static int pseudonex_attach(dev_info_t *, ddi_attach_cmd_t);
84 static int pseudonex_detach(dev_info_t *, ddi_detach_cmd_t);
85 static int pseudonex_open(dev_t *, int, int, cred_t *);
86 static int pseudonex_close(dev_t, int, int, cred_t *);
87 static int pseudonex_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
88 static int pseudonex_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *,
89     void *);
90 
91 static void *pseudonex_state;
92 
93 typedef struct pseudonex_state {
94 	dev_info_t *pnx_devi;
95 } pseudonex_state_t;
96 
97 static struct bus_ops pseudonex_bus_ops = {
98 	BUSO_REV,
99 	nullbusmap,		/* bus_map */
100 	NULL,			/* bus_get_intrspec */
101 	NULL,			/* bus_add_intrspec */
102 	NULL,			/* bus_remove_intrspec */
103 	i_ddi_map_fault,	/* bus_map_fault */
104 	ddi_no_dma_map,		/* bus_dma_map */
105 	ddi_no_dma_allochdl,	/* bus_dma_allochdl */
106 	NULL,			/* bus_dma_freehdl */
107 	NULL,			/* bus_dma_bindhdl */
108 	NULL,			/* bus_dma_unbindhdl */
109 	NULL,			/* bus_dma_flush */
110 	NULL,			/* bus_dma_win */
111 	NULL,			/* bus_dma_ctl */
112 	pseudonex_ctl,		/* bus_ctl */
113 	ddi_bus_prop_op,	/* bus_prop_op */
114 	0,			/* bus_get_eventcookie */
115 	0,			/* bus_add_eventcall */
116 	0,			/* bus_remove_eventcall	*/
117 	0,			/* bus_post_event */
118 	NULL,			/* bus_intr_ctl */
119 	NULL,			/* bus_config */
120 	NULL,			/* bus_unconfig */
121 	NULL,			/* bus_fm_init */
122 	NULL,			/* bus_fm_fini */
123 	NULL,			/* bus_fm_access_enter */
124 	NULL,			/* bus_fm_access_exit */
125 	NULL,			/* bus_power */
126 	pseudonex_intr_op	/* bus_intr_op */
127 };
128 
129 static struct cb_ops pseudonex_cb_ops = {
130 	pseudonex_open,			/* open */
131 	pseudonex_close,		/* close */
132 	nodev,				/* strategy */
133 	nodev,				/* print */
134 	nodev,				/* dump */
135 	nodev,				/* read */
136 	nodev,				/* write */
137 	pseudonex_ioctl,		/* ioctl */
138 	nodev,				/* devmap */
139 	nodev,				/* mmap */
140 	nodev,				/* segmap */
141 	nochpoll,			/* poll */
142 	ddi_prop_op,			/* cb_prop_op */
143 	0,				/* streamtab  */
144 	D_MP | D_NEW | D_HOTPLUG	/* Driver compatibility flag */
145 };
146 
147 static struct dev_ops pseudo_ops = {
148 	DEVO_REV,		/* devo_rev, */
149 	0,			/* refcnt  */
150 	ddi_getinfo_1to1,	/* info */
151 	nulldev,		/* identify */
152 	nulldev,		/* probe */
153 	pseudonex_attach,	/* attach */
154 	pseudonex_detach,	/* detach */
155 	nodev,			/* reset */
156 	&pseudonex_cb_ops,	/* driver operations */
157 	&pseudonex_bus_ops,	/* bus operations */
158 	nulldev			/* power */
159 };
160 
161 /*
162  * Module linkage information for the kernel.
163  */
164 static struct modldrv modldrv = {
165 	&mod_driverops,
166 	"nexus driver for 'pseudo' %I%",
167 	&pseudo_ops,
168 };
169 
170 static struct modlinkage modlinkage = {
171 	MODREV_1, (void *)&modldrv, NULL
172 };
173 
174 int
175 _init(void)
176 {
177 	int err;
178 
179 	if ((err = ddi_soft_state_init(&pseudonex_state,
180 	    sizeof (pseudonex_state_t), 0)) != 0) {
181 		return (err);
182 	}
183 	if ((err = mod_install(&modlinkage)) != 0) {
184 		ddi_soft_state_fini(&pseudonex_state);
185 		return (err);
186 	}
187 	return (0);
188 }
189 
190 int
191 _fini(void)
192 {
193 	int err;
194 
195 	if ((err = mod_remove(&modlinkage)) != 0)
196 		return (err);
197 	ddi_soft_state_fini(&pseudonex_state);
198 	return (0);
199 }
200 
201 int
202 _info(struct modinfo *modinfop)
203 {
204 	return (mod_info(&modlinkage, modinfop));
205 }
206 
207 /*ARGSUSED*/
208 static int
209 pseudonex_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
210 {
211 	int instance;
212 	pseudonex_state_t *pnx_state;
213 
214 	switch (cmd) {
215 	case DDI_ATTACH:
216 		break;
217 	case DDI_RESUME:
218 		return (DDI_SUCCESS);
219 	default:
220 		return (DDI_FAILURE);
221 	}
222 
223 	/*
224 	 * Save the devi for this instance in the soft_state data.
225 	 */
226 	instance = ddi_get_instance(devi);
227 	if (ddi_soft_state_zalloc(pseudonex_state, instance) != DDI_SUCCESS)
228 		return (DDI_FAILURE);
229 	pnx_state = ddi_get_soft_state(pseudonex_state, instance);
230 	pnx_state->pnx_devi = devi;
231 
232 	if (ddi_create_minor_node(devi, "devctl", S_IFCHR, instance,
233 	    DDI_NT_NEXUS, 0) != DDI_SUCCESS) {
234 		ddi_remove_minor_node(devi, NULL);
235 		ddi_soft_state_free(pseudonex_state, instance);
236 		return (DDI_FAILURE);
237 	}
238 	ddi_report_dev(devi);
239 	return (DDI_SUCCESS);
240 }
241 
242 /*ARGSUSED*/
243 static int
244 pseudonex_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
245 {
246 	int instance = ddi_get_instance(devi);
247 
248 	if (cmd == DDI_SUSPEND)
249 		return (DDI_SUCCESS);
250 
251 	ddi_remove_minor_node(devi, NULL);
252 	ddi_soft_state_free(pseudonex_state, instance);
253 	return (DDI_SUCCESS);
254 }
255 
256 /*ARGSUSED*/
257 static int
258 pseudonex_open(dev_t *devp, int flags, int otyp, cred_t *credp)
259 {
260 	int instance;
261 
262 	if (otyp != OTYP_CHR)
263 		return (EINVAL);
264 
265 	instance = getminor(*devp);
266 	if (ddi_get_soft_state(pseudonex_state, instance) == NULL)
267 		return (ENXIO);
268 
269 	return (0);
270 }
271 
272 /*ARGSUSED*/
273 static int
274 pseudonex_close(dev_t dev, int flags, int otyp, cred_t *credp)
275 {
276 	int instance;
277 
278 	if (otyp != OTYP_CHR)
279 		return (EINVAL);
280 
281 	instance = getminor(dev);
282 	if (ddi_get_soft_state(pseudonex_state, instance) == NULL)
283 		return (ENXIO);
284 
285 	return (0);
286 }
287 
288 /*ARGSUSED*/
289 static int
290 pseudonex_ioctl(dev_t dev,
291     int cmd, intptr_t arg, int mode, cred_t *cred_p, int *rval_p)
292 {
293 	int instance;
294 	pseudonex_state_t *pnx_state;
295 
296 	instance = getminor(dev);
297 	if ((pnx_state = ddi_get_soft_state(pseudonex_state, instance)) == NULL)
298 		return (ENXIO);
299 	ASSERT(pnx_state->pnx_devi);
300 	return (ndi_devctl_ioctl(pnx_state->pnx_devi, cmd, arg, mode, 0));
301 }
302 
303 /*
304  * pseudonex_intr_op: pseudonex convert an interrupt number to an
305  *			interrupt. NO OP for pseudo drivers.
306  */
307 /*ARGSUSED*/
308 static int
309 pseudonex_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
310     ddi_intr_handle_impl_t *hdlp, void *result)
311 {
312 	return (DDI_FAILURE);
313 }
314 
315 static int
316 pseudonex_check_assignment(dev_info_t *child, int test_inst)
317 {
318 	dev_info_t	*tdip;
319 	kmutex_t	*dmp;
320 	const char 	*childname = ddi_driver_name(child);
321 	major_t		childmaj = ddi_name_to_major((char *)childname);
322 
323 	dmp = &devnamesp[childmaj].dn_lock;
324 	LOCK_DEV_OPS(dmp);
325 	for (tdip = devnamesp[childmaj].dn_head;
326 	    tdip != NULL; tdip = ddi_get_next(tdip)) {
327 		/* is this the current node? */
328 		if (tdip == child)
329 			continue;
330 		/* is this a duplicate instance? */
331 		if (test_inst == ddi_get_instance(tdip)) {
332 			UNLOCK_DEV_OPS(dmp);
333 			return (DDI_FAILURE);
334 		}
335 	}
336 	UNLOCK_DEV_OPS(dmp);
337 	return (DDI_SUCCESS);
338 }
339 
340 /*
341  * This is a nasty, slow hack.  But we're stuck with it until we do some
342  * major surgery on the instance assignment subsystem, to allow pseudonode
343  * instance assignment to be tracked there.
344  *
345  * To auto-assign an instance number, we exhaustively search the instance
346  * list for each possible instance number until we find one which is unused.
347  */
348 static int
349 pseudonex_auto_assign(dev_info_t *child)
350 {
351 	dev_info_t	*tdip;
352 	kmutex_t	*dmp;
353 	const char 	*childname = ddi_driver_name(child);
354 	major_t		childmaj = ddi_name_to_major((char *)childname);
355 	int inst = 0;
356 
357 	dmp = &devnamesp[childmaj].dn_lock;
358 	LOCK_DEV_OPS(dmp);
359 	for (inst = 0; inst <= MAXMIN32; inst++) {
360 		for (tdip = devnamesp[childmaj].dn_head; tdip != NULL;
361 		    tdip = ddi_get_next(tdip)) {
362 			/* is this the current node? */
363 			if (tdip == child)
364 				continue;
365 			if (inst == ddi_get_instance(tdip)) {
366 				break;
367 			}
368 		}
369 		if (tdip == NULL) {
370 			UNLOCK_DEV_OPS(dmp);
371 			return (inst);
372 		}
373 	}
374 	UNLOCK_DEV_OPS(dmp);
375 	return (-1);
376 }
377 
378 static int
379 pseudonex_ctl(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t ctlop,
380     void *arg, void *result)
381 {
382 	switch (ctlop) {
383 	case DDI_CTLOPS_REPORTDEV:
384 		if (rdip == NULL)
385 			return (DDI_FAILURE);
386 		cmn_err(CE_CONT, "?pseudo-device: %s%d\n",
387 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
388 		return (DDI_SUCCESS);
389 
390 	case DDI_CTLOPS_INITCHILD:
391 	{
392 		char name[12];	/* enough for a decimal integer */
393 		int instance = -1;
394 		dev_info_t *child = (dev_info_t *)arg;
395 		const char *childname = ddi_driver_name(child);
396 		char **childlist;
397 		uint_t nelems;
398 		int auto_assign = 0;
399 
400 		/*
401 		 * If this pseudonex node has a valid-children property,
402 		 * then that acts as an access control list for children
403 		 * allowed to attach beneath this node.  Honor it.
404 		 */
405 		if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, dip,
406 		    DDI_PROP_DONTPASS, "valid-children", &childlist,
407 		    &nelems) == DDI_PROP_SUCCESS) {
408 			int i, ok = 0;
409 			for (i = 0; i < nelems; i++) {
410 				if (strcmp(childlist[i], childname) == 0) {
411 					ok = 1;
412 					break;
413 				}
414 			}
415 			ddi_prop_free(childlist);
416 			if (!ok)
417 				return (DDI_FAILURE);
418 		}
419 
420 		/*
421 		 * Look up the "instance" property. If it does not exist,
422 		 * check to see if the "auto-assign-instance" property is set.
423 		 * If not, default to using instance 0; while not ideal, this
424 		 * is a legacy behavior we must continue to support.
425 		 */
426 		instance = ddi_prop_get_int(DDI_DEV_T_ANY, child,
427 		    DDI_PROP_DONTPASS, "instance", -1);
428 		auto_assign = ddi_prop_exists(DDI_DEV_T_ANY, child,
429 		    DDI_PROP_DONTPASS, "auto-assign-instance");
430 		NDI_CONFIG_DEBUG((CE_NOTE,
431 		    "pseudonex: DDI_CTLOPS_INITCHILD(instance=%d, "
432 		    "auto-assign=%d)", instance, auto_assign));
433 
434 		if (instance != -1 && auto_assign != 0) {
435 			NDI_CONFIG_DEBUG((CE_NOTE, "both instance and "
436 			    "auto-assign-instance properties specified. "
437 			    "Node rejected."));
438 			return (DDI_FAILURE);
439 		}
440 
441 		if (instance == -1 && auto_assign == 0) {
442 			/* default to instance 0 if not specified */
443 			NDI_CONFIG_DEBUG((CE_NOTE, "defaulting to 0"));
444 			instance = 0;
445 		}
446 
447 		/*
448 		 * If an instance has been specified, determine if this
449 		 * instance is already in use; if we need to pick an instance,
450 		 * we do it here.
451 		 */
452 		if (auto_assign) {
453 			if ((instance = pseudonex_auto_assign(child)) == -1) {
454 				NDI_CONFIG_DEBUG((CE_NOTE, "failed to "
455 				    "auto-select instance for %s", childname));
456 				return (DDI_FAILURE);
457 			}
458 			NDI_CONFIG_DEBUG((CE_NOTE,
459 			    "auto-selected instance for %s: %d",
460 			    childname, instance));
461 		} else {
462 			if (pseudonex_check_assignment(child, instance) ==
463 			    DDI_FAILURE) {
464 				NDI_CONFIG_DEBUG((CE_WARN,
465 				    "Duplicate instance %d of node \"%s\" "
466 				    "ignored.", instance, childname));
467 				return (DDI_FAILURE);
468 			}
469 			NDI_CONFIG_DEBUG((CE_NOTE,
470 			    "using fixed-assignment instance for %s: %d",
471 			    childname, instance));
472 		}
473 
474 		/*
475 		 * Attach the instance number to the node. This allows
476 		 * us to have multiple instances of the same pseudo
477 		 * device, they will be named 'device@instance'. If this
478 		 * breaks programs, we may need to special-case instance 0
479 		 * into 'device'. Ick. devlinks appears to handle the
480 		 * new names ok, so if only names in /dev are used
481 		 * this may not be necessary.
482 		 */
483 		(void) snprintf(name, sizeof (name), "%d", instance);
484 		DEVI(child)->devi_instance = instance;
485 		ddi_set_name_addr(child, name);
486 		return (DDI_SUCCESS);
487 	}
488 
489 	case DDI_CTLOPS_UNINITCHILD:
490 	{
491 		dev_info_t *child = (dev_info_t *)arg;
492 
493 		NDI_CONFIG_DEBUG((CE_NOTE,
494 		    "DDI_CTLOPS_UNINITCHILD(%s, instance=%d)",
495 		    ddi_driver_name(child), DEVI(child)->devi_instance));
496 
497 		ddi_set_name_addr(child, NULL);
498 
499 		return (DDI_SUCCESS);
500 	}
501 
502 	case DDI_CTLOPS_DMAPMAPC:
503 	case DDI_CTLOPS_REPORTINT:
504 	case DDI_CTLOPS_REGSIZE:
505 	case DDI_CTLOPS_NREGS:
506 	case DDI_CTLOPS_SIDDEV:
507 	case DDI_CTLOPS_SLAVEONLY:
508 	case DDI_CTLOPS_AFFINITY:
509 	case DDI_CTLOPS_POKE:
510 	case DDI_CTLOPS_PEEK:
511 		/*
512 		 * These ops correspond to functions that "shouldn't" be called
513 		 * by a pseudo driver.  So we whine when we're called.
514 		 */
515 		cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n",
516 		    ddi_driver_name(dip), ddi_get_instance(dip), ctlop,
517 		    ddi_driver_name(rdip), ddi_get_instance(rdip));
518 		return (DDI_FAILURE);
519 
520 	case DDI_CTLOPS_ATTACH:
521 	case DDI_CTLOPS_BTOP:
522 	case DDI_CTLOPS_BTOPR:
523 	case DDI_CTLOPS_DETACH:
524 	case DDI_CTLOPS_DVMAPAGESIZE:
525 	case DDI_CTLOPS_IOMIN:
526 	case DDI_CTLOPS_POWER:
527 	case DDI_CTLOPS_PTOB:
528 	default:
529 		/*
530 		 * The ops that we pass up (default).  We pass up memory
531 		 * allocation oriented ops that we receive - these may be
532 		 * associated with pseudo HBA drivers below us with target
533 		 * drivers below them that use ddi memory allocation
534 		 * interfaces like scsi_alloc_consistent_buf.
535 		 */
536 		return (ddi_ctlops(dip, rdip, ctlop, arg, result));
537 	}
538 }
539