xref: /illumos-gate/usr/src/uts/common/io/sad.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 2004 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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
30 /*	  All Rights Reserved  	*/
31 
32 
33 /*
34  * STREAMS Administrative Driver
35  *
36  * Currently only handles autopush and module name verification.
37  */
38 
39 #include <sys/types.h>
40 #include <sys/param.h>
41 #include <sys/errno.h>
42 #include <sys/stream.h>
43 #include <sys/stropts.h>
44 #include <sys/strsubr.h>
45 #include <sys/strsun.h>
46 #include <sys/conf.h>
47 #include <sys/sad.h>
48 #include <sys/cred.h>
49 #include <sys/debug.h>
50 #include <sys/ddi.h>
51 #include <sys/sunddi.h>
52 #include <sys/stat.h>
53 #include <sys/cmn_err.h>
54 #include <sys/systm.h>
55 #include <sys/modctl.h>
56 #include <sys/priv_names.h>
57 
58 static int sadopen(queue_t *, dev_t *, int, int, cred_t *);
59 static int sadclose(queue_t *, int, cred_t *);
60 static int sadwput(queue_t *qp, mblk_t *mp);
61 
62 static int sad_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
63 static int sad_attach(dev_info_t *, ddi_attach_cmd_t);
64 
65 static struct autopush *ap_alloc(), *ap_hfind();
66 static void ap_hadd(), ap_hrmv();
67 static void apush_ioctl(), apush_iocdata();
68 static void vml_ioctl(), vml_iocdata();
69 static int valid_major(major_t);
70 
71 extern kmutex_t sad_lock;
72 static dev_info_t *sad_dip;		/* private copy of devinfo pointer */
73 static struct autopush *strpfreep;	/* autopush freelist */
74 
75 static struct module_info sad_minfo = {
76 	0x7361, "sad", 0, INFPSZ, 0, 0
77 };
78 
79 static struct qinit sad_rinit = {
80 	NULL, NULL, sadopen, sadclose, NULL, &sad_minfo, NULL
81 };
82 
83 static struct qinit sad_winit = {
84 	sadwput, NULL, NULL, NULL, NULL, &sad_minfo, NULL
85 };
86 
87 struct streamtab sadinfo = {
88 	&sad_rinit, &sad_winit, NULL, NULL
89 };
90 
91 DDI_DEFINE_STREAM_OPS(sad_ops, nulldev, nulldev, sad_attach,
92     nodev, nodev, sad_info, D_NEW | D_MTPERQ | D_MP, &sadinfo);
93 
94 /*
95  * Module linkage information for the kernel.
96  */
97 
98 static struct modldrv modldrv = {
99 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
100 	"STREAMS Administrative Driver 'sad' %I%",
101 	&sad_ops,	/* driver ops */
102 };
103 
104 static struct modlinkage modlinkage = {
105 	MODREV_1, &modldrv, NULL
106 };
107 
108 int
109 _init(void)
110 {
111 	return (mod_install(&modlinkage));
112 }
113 
114 int
115 _fini(void)
116 {
117 	return (mod_remove(&modlinkage));
118 }
119 
120 int
121 _info(struct modinfo *modinfop)
122 {
123 	return (mod_info(&modlinkage, modinfop));
124 }
125 
126 static int
127 sad_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
128 {
129 	if (cmd != DDI_ATTACH)
130 		return (DDI_FAILURE);
131 
132 	if (ddi_create_minor_node(devi, "user", S_IFCHR,
133 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
134 		ddi_remove_minor_node(devi, NULL);
135 		return (DDI_FAILURE);
136 	}
137 	if (ddi_create_priv_minor_node(devi, "admin", S_IFCHR,
138 	    1, DDI_PSEUDO, PRIVONLY_DEV, PRIV_SYS_CONFIG,
139 	    PRIV_SYS_CONFIG, 0666) == DDI_FAILURE) {
140 		ddi_remove_minor_node(devi, NULL);
141 		return (DDI_FAILURE);
142 	}
143 	sad_dip = devi;
144 	return (DDI_SUCCESS);
145 }
146 
147 /* ARGSUSED */
148 static int
149 sad_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
150 {
151 	int error;
152 
153 	switch (infocmd) {
154 	case DDI_INFO_DEVT2DEVINFO:
155 		if (sad_dip == NULL) {
156 			error = DDI_FAILURE;
157 		} else {
158 			*result = sad_dip;
159 			error = DDI_SUCCESS;
160 		}
161 		break;
162 	case DDI_INFO_DEVT2INSTANCE:
163 		*result = (void *)0;
164 		error = DDI_SUCCESS;
165 		break;
166 	default:
167 		error = DDI_FAILURE;
168 	}
169 	return (error);
170 }
171 
172 
173 /*
174  * sadinit() -
175  * Initialize autopush freelist.
176  */
177 void
178 sadinit()
179 {
180 	struct autopush *ap;
181 	int i;
182 
183 	/*
184 	 * build the autopush freelist.
185 	 */
186 	strpfreep = autopush;
187 	ap = autopush;
188 	for (i = 1; i < nautopush; i++) {
189 		ap->ap_nextp = &autopush[i];
190 		ap->ap_flags = APFREE;
191 		ap = ap->ap_nextp;
192 	}
193 	ap->ap_nextp = NULL;
194 	ap->ap_flags = APFREE;
195 }
196 
197 /*
198  * sadopen() -
199  * Allocate a sad device.  Only one
200  * open at a time allowed per device.
201  */
202 /* ARGSUSED */
203 static int
204 sadopen(
205 	queue_t *qp,	/* pointer to read queue */
206 	dev_t *devp,	/* major/minor device of stream */
207 	int flag,	/* file open flags */
208 	int sflag,	/* stream open flags */
209 	cred_t *credp)	/* user credentials */
210 {
211 	int i;
212 
213 	if (sflag)		/* no longer called from clone driver */
214 		return (EINVAL);
215 
216 	/*
217 	 * Both USRMIN and ADMMIN are clone interfaces.
218 	 */
219 	for (i = 0; i < sadcnt; i++)
220 		if (saddev[i].sa_qp == NULL)
221 			break;
222 	if (i >= sadcnt)		/* no such device */
223 		return (ENXIO);
224 
225 	switch (getminor(*devp)) {
226 	case USRMIN:			/* mere mortal */
227 		saddev[i].sa_flags = 0;
228 		break;
229 
230 	case ADMMIN:			/* privileged user */
231 		saddev[i].sa_flags = SADPRIV;
232 		break;
233 
234 	default:
235 		return (EINVAL);
236 	}
237 
238 	saddev[i].sa_qp = qp;
239 	qp->q_ptr = (caddr_t)&saddev[i];
240 	WR(qp)->q_ptr = (caddr_t)&saddev[i];
241 
242 	/*
243 	 * NOTE: should the ADMMIN or USRMIN minors change
244 	 * then so should the offset of 2 below
245 	 * Both USRMIN and ADMMIN are clone interfaces and
246 	 * therefore their minor numbers (0 and 1) are reserved.
247 	 */
248 	*devp = makedevice(getemajor(*devp), i + 2);
249 	qprocson(qp);
250 	return (0);
251 }
252 
253 /*
254  * sadclose() -
255  * Clean up the data structures.
256  */
257 /* ARGSUSED */
258 static int
259 sadclose(
260 	queue_t *qp,	/* pointer to read queue */
261 	int flag,	/* file open flags */
262 	cred_t *credp)	/* user credentials */
263 {
264 	struct saddev *sadp;
265 
266 	qprocsoff(qp);
267 	sadp = (struct saddev *)qp->q_ptr;
268 	sadp->sa_qp = NULL;
269 	sadp->sa_addr = NULL;
270 	qp->q_ptr = NULL;
271 	WR(qp)->q_ptr = NULL;
272 	return (0);
273 }
274 
275 /*
276  * sadwput() -
277  * Write side put procedure.
278  */
279 static int
280 sadwput(
281 	queue_t *qp,	/* pointer to write queue */
282 	mblk_t *mp)	/* message pointer */
283 {
284 	struct iocblk *iocp;
285 
286 	switch (mp->b_datap->db_type) {
287 	case M_FLUSH:
288 		if (*mp->b_rptr & FLUSHR) {
289 			*mp->b_rptr &= ~FLUSHW;
290 			qreply(qp, mp);
291 		} else
292 			freemsg(mp);
293 		break;
294 
295 	case M_IOCTL:
296 		iocp = (struct iocblk *)mp->b_rptr;
297 		switch (SAD_CMD(iocp->ioc_cmd)) {
298 		case SAD_CMD(SAD_SAP):
299 		case SAD_CMD(SAD_GAP):
300 			apush_ioctl(qp, mp);
301 			break;
302 
303 		case SAD_VML:
304 			vml_ioctl(qp, mp);
305 			break;
306 
307 		default:
308 			miocnak(qp, mp, 0, EINVAL);
309 			break;
310 		}
311 		break;
312 
313 	case M_IOCDATA:
314 		iocp = (struct iocblk *)mp->b_rptr;
315 		switch (SAD_CMD(iocp->ioc_cmd)) {
316 		case SAD_CMD(SAD_SAP):
317 		case SAD_CMD(SAD_GAP):
318 			apush_iocdata(qp, mp);
319 			break;
320 
321 		case SAD_VML:
322 			vml_iocdata(qp, mp);
323 			break;
324 
325 		default:
326 			cmn_err(CE_WARN,
327 			    "sadwput: invalid ioc_cmd in case M_IOCDATA: %d",
328 			    iocp->ioc_cmd);
329 			freemsg(mp);
330 			break;
331 		}
332 		break;
333 
334 	default:
335 		freemsg(mp);
336 		break;
337 	} /* switch (db_type) */
338 	return (0);
339 }
340 
341 /*
342  * apush_ioctl() -
343  * Handle the M_IOCTL messages associated with
344  * the autopush feature.
345  */
346 static void
347 apush_ioctl(
348 	queue_t *qp,	/* pointer to write queue */
349 	mblk_t *mp)	/* message pointer */
350 {
351 	struct iocblk	*iocp;
352 	struct saddev	*sadp;
353 	uint_t		size;
354 
355 	iocp = (struct iocblk *)mp->b_rptr;
356 	if (iocp->ioc_count != TRANSPARENT) {
357 		miocnak(qp, mp, 0, EINVAL);
358 		return;
359 	}
360 	if (SAD_VER(iocp->ioc_cmd) > AP_VERSION) {
361 		miocnak(qp, mp, 0, EINVAL);
362 		return;
363 	}
364 
365 	sadp = (struct saddev *)qp->q_ptr;
366 	switch (SAD_CMD(iocp->ioc_cmd)) {
367 	case SAD_CMD(SAD_SAP):
368 		if (!(sadp->sa_flags & SADPRIV)) {
369 			miocnak(qp, mp, 0, EPERM);
370 			break;
371 		}
372 		/* FALLTHRU */
373 
374 	case SAD_CMD(SAD_GAP):
375 		sadp->sa_addr = (caddr_t)*(uintptr_t *)mp->b_cont->b_rptr;
376 		if (SAD_VER(iocp->ioc_cmd) == 1)
377 			size = STRAPUSH_V1_LEN;
378 		else
379 			size = STRAPUSH_V0_LEN;
380 		mcopyin(mp, (void *)GETSTRUCT, size, NULL);
381 		qreply(qp, mp);
382 		break;
383 
384 	default:
385 		ASSERT(0);
386 		miocnak(qp, mp, 0, EINVAL);
387 		break;
388 	} /* switch (ioc_cmd) */
389 }
390 
391 /*
392  * apush_iocdata() -
393  * Handle the M_IOCDATA messages associated with
394  * the autopush feature.
395  */
396 static void
397 apush_iocdata(
398 	queue_t *qp,	/* pointer to write queue */
399 	mblk_t *mp)	/* message pointer */
400 {
401 	int i, ret;
402 	struct copyresp *csp;
403 	struct strapush *sap;
404 	struct autopush *ap;
405 	struct saddev *sadp;
406 	uint_t size;
407 
408 	csp = (struct copyresp *)mp->b_rptr;
409 	if (csp->cp_rval) {	/* if there was an error */
410 		freemsg(mp);
411 		return;
412 	}
413 	if (mp->b_cont)
414 		/* sap needed only if mp->b_cont is set */
415 		sap = (struct strapush *)mp->b_cont->b_rptr;
416 	switch (SAD_CMD(csp->cp_cmd)) {
417 	case SAD_CMD(SAD_SAP):
418 		switch ((long)csp->cp_private) {
419 		case GETSTRUCT:
420 			switch (sap->sap_cmd) {
421 			case SAP_ONE:
422 			case SAP_RANGE:
423 			case SAP_ALL:
424 				if ((sap->sap_npush == 0) ||
425 				    (sap->sap_npush > MAXAPUSH) ||
426 				    (sap->sap_npush > nstrpush)) {
427 
428 					/* invalid number of modules to push */
429 
430 					miocnak(qp, mp, 0, EINVAL);
431 					break;
432 				}
433 				if (ret = valid_major(sap->sap_major)) {
434 					miocnak(qp, mp, 0, ret);
435 					break;
436 				}
437 				if ((sap->sap_cmd == SAP_RANGE) &&
438 				    (sap->sap_lastminor <= sap->sap_minor)) {
439 
440 					/* bad range */
441 
442 					miocnak(qp, mp, 0, ERANGE);
443 					break;
444 				}
445 
446 				/*
447 				 * Validate that the specified list of
448 				 * modules exist.
449 				 */
450 				for (i = 0; i < sap->sap_npush; i++) {
451 					sap->sap_list[i][FMNAMESZ] = '\0';
452 					if (fmodsw_find(sap->sap_list[i],
453 					    FMODSW_LOAD) == NULL) {
454 						miocnak(qp, mp, 0, EINVAL);
455 						return;
456 					}
457 				}
458 
459 				mutex_enter(&sad_lock);
460 				if (ap_hfind(sap->sap_major, sap->sap_minor,
461 				    sap->sap_lastminor, sap->sap_cmd)) {
462 					mutex_exit(&sad_lock);
463 
464 					/* already configured */
465 
466 					miocnak(qp, mp, 0, EEXIST);
467 					break;
468 				}
469 				if ((ap = ap_alloc()) == NULL) {
470 					mutex_exit(&sad_lock);
471 
472 					/* no autopush structures */
473 
474 					miocnak(qp, mp, 0, ENOSR);
475 					break;
476 				}
477 				ap->ap_cnt++;
478 				ap->ap_common = sap->sap_common;
479 				if (SAD_VER(csp->cp_cmd) > 0)
480 					ap->ap_anchor = sap->sap_anchor;
481 				else
482 					ap->ap_anchor = 0;
483 				for (i = 0; i < ap->ap_npush; i++)
484 					(void) strcpy(ap->ap_list[i],
485 					    sap->sap_list[i]);
486 				ap_hadd(ap);
487 				mutex_exit(&sad_lock);
488 				miocack(qp, mp, 0, 0);
489 				break;
490 
491 			case SAP_CLEAR:
492 				if (ret = valid_major(sap->sap_major)) {
493 					miocnak(qp, mp, 0, ret);
494 					break;
495 				}
496 				mutex_enter(&sad_lock);
497 				if ((ap = ap_hfind(sap->sap_major,
498 				    sap->sap_minor, sap->sap_lastminor,
499 				    sap->sap_cmd)) == NULL) {
500 					mutex_exit(&sad_lock);
501 
502 					/* not configured */
503 
504 					miocnak(qp, mp, 0, ENODEV);
505 					break;
506 				}
507 				if ((ap->ap_type == SAP_RANGE) &&
508 				    (sap->sap_minor != ap->ap_minor)) {
509 					mutex_exit(&sad_lock);
510 
511 					/* starting minors do not match */
512 
513 					miocnak(qp, mp, 0, ERANGE);
514 					break;
515 				}
516 				if ((ap->ap_type == SAP_ALL) &&
517 				    (sap->sap_minor != 0)) {
518 					mutex_exit(&sad_lock);
519 
520 					/* SAP_ALL must have minor == 0 */
521 
522 					miocnak(qp, mp, 0, EINVAL);
523 					break;
524 				}
525 				ap_hrmv(ap);
526 				if (--(ap->ap_cnt) <= 0)
527 					ap_free(ap);
528 				mutex_exit(&sad_lock);
529 				miocack(qp, mp, 0, 0);
530 				break;
531 
532 			default:
533 				miocnak(qp, mp, 0, EINVAL);
534 				break;
535 			} /* switch (sap_cmd) */
536 			break;
537 
538 		default:
539 			cmn_err(CE_WARN,
540 			    "apush_iocdata: cp_private bad in SAD_SAP: %p",
541 			    (void *)csp->cp_private);
542 			freemsg(mp);
543 			break;
544 		} /* switch (cp_private) */
545 		break;
546 
547 	case SAD_CMD(SAD_GAP):
548 		switch ((long)csp->cp_private) {
549 
550 		case GETSTRUCT: {
551 			if (ret = valid_major(sap->sap_major)) {
552 				miocnak(qp, mp, 0, ret);
553 				break;
554 			}
555 			mutex_enter(&sad_lock);
556 			if ((ap = ap_hfind(sap->sap_major, sap->sap_minor,
557 			    sap->sap_lastminor, SAP_ONE)) == NULL) {
558 				mutex_exit(&sad_lock);
559 
560 				/* not configured */
561 
562 				miocnak(qp, mp, 0, ENODEV);
563 				break;
564 			}
565 
566 			sap->sap_common = ap->ap_common;
567 			if (SAD_VER(csp->cp_cmd) > 0)
568 				sap->sap_anchor = ap->ap_anchor;
569 			for (i = 0; i < ap->ap_npush; i++)
570 				(void) strcpy(sap->sap_list[i], ap->ap_list[i]);
571 			for (; i < MAXAPUSH; i++)
572 				bzero(sap->sap_list[i], FMNAMESZ + 1);
573 			mutex_exit(&sad_lock);
574 
575 			if (SAD_VER(csp->cp_cmd) == 1)
576 				size = STRAPUSH_V1_LEN;
577 			else
578 				size = STRAPUSH_V0_LEN;
579 
580 			sadp = (struct saddev *)qp->q_ptr;
581 			mcopyout(mp, (void *)GETRESULT, size, sadp->sa_addr,
582 			    NULL);
583 			qreply(qp, mp);
584 			break;
585 			}
586 		case GETRESULT:
587 			miocack(qp, mp, 0, 0);
588 			break;
589 
590 		default:
591 			cmn_err(CE_WARN,
592 			    "apush_iocdata: cp_private bad case SAD_GAP: %p",
593 			    (void *)csp->cp_private);
594 			freemsg(mp);
595 			break;
596 		} /* switch (cp_private) */
597 		break;
598 
599 	default:	/* can't happen */
600 		ASSERT(0);
601 		freemsg(mp);
602 		break;
603 	} /* switch (cp_cmd) */
604 }
605 
606 /*
607  * ap_alloc() -
608  * Allocate an autopush structure.
609  */
610 static struct autopush *
611 ap_alloc(void)
612 {
613 	struct autopush *ap;
614 
615 	ASSERT(MUTEX_HELD(&sad_lock));
616 	if (strpfreep == NULL)
617 		return (NULL);
618 	ap = strpfreep;
619 	if (ap->ap_flags != APFREE)
620 		cmn_err(CE_PANIC, "ap_alloc: autopush struct not free: %d",
621 		    ap->ap_flags);
622 	strpfreep = strpfreep->ap_nextp;
623 	ap->ap_nextp = NULL;
624 	ap->ap_flags = APUSED;
625 	return (ap);
626 }
627 
628 /*
629  * ap_free() -
630  * Give an autopush structure back to the freelist.
631  */
632 void
633 ap_free(struct autopush *ap)
634 {
635 	ASSERT(MUTEX_HELD(&sad_lock));
636 	if (!(ap->ap_flags & APUSED))
637 		cmn_err(CE_PANIC, "ap_free: autopush struct not used: %d",
638 		    ap->ap_flags);
639 	if (ap->ap_flags & APHASH)
640 		cmn_err(CE_PANIC, "ap_free: autopush struct not hashed: %d",
641 		    ap->ap_flags);
642 	ap->ap_flags = APFREE;
643 	ap->ap_nextp = strpfreep;
644 	strpfreep = ap;
645 }
646 
647 /*
648  * ap_hadd() -
649  * Add an autopush structure to the hash list.
650  */
651 static void
652 ap_hadd(struct autopush *ap)
653 {
654 	ASSERT(MUTEX_HELD(&sad_lock));
655 	if (!(ap->ap_flags & APUSED))
656 		cmn_err(CE_PANIC, "ap_hadd: autopush struct not used: %d",
657 		    ap->ap_flags);
658 	if (ap->ap_flags & APHASH)
659 		cmn_err(CE_PANIC, "ap_hadd: autopush struct not hashed: %d",
660 		    ap->ap_flags);
661 	ap->ap_nextp = strphash(ap->ap_major);
662 	strphash(ap->ap_major) = ap;
663 	ap->ap_flags |= APHASH;
664 }
665 
666 /*
667  * ap_hrmv() -
668  * Remove an autopush structure from the hash list.
669  */
670 static void
671 ap_hrmv(struct autopush *ap)
672 {
673 	struct autopush *hap;
674 	struct autopush *prevp = NULL;
675 
676 	ASSERT(MUTEX_HELD(&sad_lock));
677 	if (!(ap->ap_flags & APUSED))
678 		cmn_err(CE_PANIC, "ap_hrmv: autopush struct not used: %d",
679 		    ap->ap_flags);
680 	if (!(ap->ap_flags & APHASH))
681 		cmn_err(CE_PANIC, "ap_hrmv: autopush struct not hashed: %d",
682 		    ap->ap_flags);
683 
684 	hap = strphash(ap->ap_major);
685 	while (hap) {
686 		if (ap == hap) {
687 			hap->ap_flags &= ~APHASH;
688 			if (prevp)
689 				prevp->ap_nextp = hap->ap_nextp;
690 			else
691 				strphash(ap->ap_major) = hap->ap_nextp;
692 			return;
693 		} /* if */
694 		prevp = hap;
695 		hap = hap->ap_nextp;
696 	} /* while */
697 }
698 
699 /*
700  * ap_hfind() -
701  * Look for an autopush structure in the hash list
702  * based on major, minor, lastminor, and command.
703  */
704 static struct autopush *
705 ap_hfind(
706 	major_t maj,	/* major device number */
707 	minor_t minor,	/* minor device number */
708 	minor_t last,	/* last minor device number (SAP_RANGE only) */
709 	uint_t cmd)	/* who is asking */
710 {
711 	struct autopush *ap;
712 
713 	ASSERT(MUTEX_HELD(&sad_lock));
714 	ap = strphash(maj);
715 	while (ap) {
716 		if (ap->ap_major == maj) {
717 			if (cmd == SAP_ALL)
718 				break;
719 			switch (ap->ap_type) {
720 			case SAP_ALL:
721 				break;
722 
723 			case SAP_ONE:
724 				if (ap->ap_minor == minor)
725 					break;
726 				if ((cmd == SAP_RANGE) &&
727 				    (ap->ap_minor >= minor) &&
728 				    (ap->ap_minor <= last))
729 					break;
730 				ap = ap->ap_nextp;
731 				continue;
732 
733 			case SAP_RANGE:
734 				if ((cmd == SAP_RANGE) &&
735 				    (((minor >= ap->ap_minor) &&
736 				    (minor <= ap->ap_lastminor)) ||
737 				    ((ap->ap_minor >= minor) &&
738 				    (ap->ap_minor <= last))))
739 					break;
740 				if ((minor >= ap->ap_minor) &&
741 				    (minor <= ap->ap_lastminor))
742 					break;
743 				ap = ap->ap_nextp;
744 				continue;
745 
746 			default:
747 				ASSERT(0);
748 				break;
749 			}
750 			break;
751 		}
752 		ap = ap->ap_nextp;
753 	}
754 	return (ap);
755 }
756 
757 /*
758  * vml_ioctl() -
759  * Handle the M_IOCTL message associated with a request
760  * to validate a module list.
761  */
762 static void
763 vml_ioctl(
764 	queue_t *qp,	/* pointer to write queue */
765 	mblk_t *mp)	/* message pointer */
766 {
767 	struct iocblk *iocp;
768 
769 	iocp = (struct iocblk *)mp->b_rptr;
770 	if (iocp->ioc_count != TRANSPARENT) {
771 		miocnak(qp, mp, 0, EINVAL);
772 		return;
773 	}
774 	ASSERT(iocp->ioc_cmd == SAD_VML);
775 	mcopyin(mp, (void *)GETSTRUCT,
776 	    SIZEOF_STRUCT(str_list, iocp->ioc_flag), NULL);
777 	qreply(qp, mp);
778 }
779 
780 /*
781  * vml_iocdata() -
782  * Handle the M_IOCDATA messages associated with
783  * a request to validate a module list.
784  */
785 static void
786 vml_iocdata(
787 	queue_t *qp,	/* pointer to write queue */
788 	mblk_t *mp)	/* message pointer */
789 {
790 	long i;
791 	int	nmods;
792 	struct copyresp *csp;
793 	struct str_mlist *lp;
794 	STRUCT_HANDLE(str_list, slp);
795 	struct saddev *sadp;
796 
797 	csp = (struct copyresp *)mp->b_rptr;
798 	if (csp->cp_rval) {	/* if there was an error */
799 		freemsg(mp);
800 		return;
801 	}
802 
803 	ASSERT(csp->cp_cmd == SAD_VML);
804 	sadp = (struct saddev *)qp->q_ptr;
805 	switch ((long)csp->cp_private) {
806 	case GETSTRUCT:
807 		STRUCT_SET_HANDLE(slp, csp->cp_flag,
808 		    (struct str_list *)mp->b_cont->b_rptr);
809 		nmods = STRUCT_FGET(slp, sl_nmods);
810 		if (nmods <= 0) {
811 			miocnak(qp, mp, 0, EINVAL);
812 			break;
813 		}
814 		sadp->sa_addr = (caddr_t)(uintptr_t)nmods;
815 
816 		mcopyin(mp, (void *)GETLIST, nmods * sizeof (struct str_mlist),
817 		    STRUCT_FGETP(slp, sl_modlist));
818 		qreply(qp, mp);
819 		break;
820 
821 	case GETLIST:
822 		lp = (struct str_mlist *)mp->b_cont->b_rptr;
823 		for (i = 0; i < (long)sadp->sa_addr; i++, lp++) {
824 			lp->l_name[FMNAMESZ] = '\0';
825 			if (fmodsw_find(lp->l_name, FMODSW_LOAD) == NULL) {
826 				miocack(qp, mp, 0, 1);
827 				return;
828 			}
829 		}
830 		miocack(qp, mp, 0, 0);
831 		break;
832 
833 	default:
834 		cmn_err(CE_WARN, "vml_iocdata: invalid cp_private value: %p",
835 		    (void *)csp->cp_private);
836 		freemsg(mp);
837 		break;
838 	} /* switch (cp_private) */
839 }
840 
841 /*
842  * Validate a major number and also verify if
843  * it is a STREAMS device.
844  * Return values: 0 if a valid STREAMS dev
845  *		  error code otherwise
846  */
847 static int
848 valid_major(major_t major)
849 {
850 	int ret = 0;
851 
852 	if (etoimajor(major) == -1)
853 		return (EINVAL);
854 
855 	/*
856 	 * attempt to load the driver 'major' and verify that
857 	 * it is a STREAMS driver.
858 	 */
859 	if (ddi_hold_driver(major) == NULL)
860 		return (EINVAL);
861 
862 	if (!STREAMSTAB(major))
863 		ret = ENOSTR;
864 
865 	ddi_rele_driver(major);
866 
867 	return (ret);
868 }
869