1241 Need support for latest Emulex 10GbE
[illumos-gate.git] / usr / src / uts / common / io / sad.c
blob2affbbdc1911a4a557b5f6b462cc38a9d1660c80
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
32 * STREAMS Administrative Driver
34 * Currently only handles autopush and module name verification.
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #include <sys/errno.h>
40 #include <sys/stream.h>
41 #include <sys/stropts.h>
42 #include <sys/strsubr.h>
43 #include <sys/strsun.h>
44 #include <sys/conf.h>
45 #include <sys/sad.h>
46 #include <sys/cred.h>
47 #include <sys/debug.h>
48 #include <sys/ddi.h>
49 #include <sys/sunddi.h>
50 #include <sys/stat.h>
51 #include <sys/cmn_err.h>
52 #include <sys/systm.h>
53 #include <sys/modctl.h>
54 #include <sys/sysmacros.h>
55 #include <sys/zone.h>
56 #include <sys/policy.h>
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);
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);
65 static void apush_ioctl(), apush_iocdata();
66 static void vml_ioctl(), vml_iocdata();
67 static int valid_major(major_t);
69 static dev_info_t *sad_dip; /* private copy of devinfo pointer */
71 static struct module_info sad_minfo = {
72 0x7361, "sad", 0, INFPSZ, 0, 0
75 static struct qinit sad_rinit = {
76 NULL, NULL, sadopen, sadclose, NULL, &sad_minfo, NULL
79 static struct qinit sad_winit = {
80 sadwput, NULL, NULL, NULL, NULL, &sad_minfo, NULL
83 struct streamtab sadinfo = {
84 &sad_rinit, &sad_winit, NULL, NULL
87 DDI_DEFINE_STREAM_OPS(sad_ops, nulldev, nulldev, sad_attach,
88 nodev, nodev, sad_info,
89 D_MP | D_MTPERQ | D_MTOUTPERIM | D_MTOCEXCL, &sadinfo,
90 ddi_quiesce_not_supported);
93 * Module linkage information for the kernel.
96 static struct modldrv modldrv = {
97 &mod_driverops, /* Type of module. This one is a pseudo driver */
98 "STREAMS Administrative Driver 'sad'",
99 &sad_ops, /* driver ops */
102 static struct modlinkage modlinkage = {
103 MODREV_1, &modldrv, NULL
107 _init(void)
109 return (mod_install(&modlinkage));
113 _fini(void)
115 return (mod_remove(&modlinkage));
119 _info(struct modinfo *modinfop)
121 return (mod_info(&modlinkage, modinfop));
124 static int
125 sad_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
127 int instance = ddi_get_instance(devi);
129 if (cmd != DDI_ATTACH)
130 return (DDI_FAILURE);
132 ASSERT(instance == 0);
133 if (instance != 0)
134 return (DDI_FAILURE);
136 if (ddi_create_minor_node(devi, "user", S_IFCHR,
137 0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
138 return (DDI_FAILURE);
140 if (ddi_create_minor_node(devi, "admin", S_IFCHR,
141 1, DDI_PSEUDO, NULL) == DDI_FAILURE) {
142 ddi_remove_minor_node(devi, NULL);
143 return (DDI_FAILURE);
145 sad_dip = devi;
146 return (DDI_SUCCESS);
149 /* ARGSUSED */
150 static int
151 sad_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
153 int error;
155 switch (infocmd) {
156 case DDI_INFO_DEVT2DEVINFO:
157 if (sad_dip == NULL) {
158 error = DDI_FAILURE;
159 } else {
160 *result = sad_dip;
161 error = DDI_SUCCESS;
163 break;
164 case DDI_INFO_DEVT2INSTANCE:
165 *result = (void *)0;
166 error = DDI_SUCCESS;
167 break;
168 default:
169 error = DDI_FAILURE;
171 return (error);
176 * sadopen() -
177 * Allocate a sad device. Only one
178 * open at a time allowed per device.
180 /* ARGSUSED */
181 static int
182 sadopen(
183 queue_t *qp, /* pointer to read queue */
184 dev_t *devp, /* major/minor device of stream */
185 int flag, /* file open flags */
186 int sflag, /* stream open flags */
187 cred_t *credp) /* user credentials */
189 int i;
190 netstack_t *ns;
191 str_stack_t *ss;
193 if (sflag) /* no longer called from clone driver */
194 return (EINVAL);
196 /* Only privileged process can access ADMINDEV */
197 if (getminor(*devp) == ADMMIN) {
198 int err;
200 err = secpolicy_sadopen(credp);
202 if (err != 0)
203 return (err);
206 ns = netstack_find_by_cred(credp);
207 ASSERT(ns != NULL);
208 ss = ns->netstack_str;
209 ASSERT(ss != NULL);
212 * Both USRMIN and ADMMIN are clone interfaces.
214 for (i = 0; i < ss->ss_sadcnt; i++)
215 if (ss->ss_saddev[i].sa_qp == NULL)
216 break;
217 if (i >= ss->ss_sadcnt) { /* no such device */
218 netstack_rele(ss->ss_netstack);
219 return (ENXIO);
221 switch (getminor(*devp)) {
222 case USRMIN: /* mere mortal */
223 ss->ss_saddev[i].sa_flags = 0;
224 break;
226 case ADMMIN: /* privileged user */
227 ss->ss_saddev[i].sa_flags = SADPRIV;
228 break;
230 default:
231 netstack_rele(ss->ss_netstack);
232 return (EINVAL);
235 ss->ss_saddev[i].sa_qp = qp;
236 ss->ss_saddev[i].sa_ss = ss;
237 qp->q_ptr = (caddr_t)&ss->ss_saddev[i];
238 WR(qp)->q_ptr = (caddr_t)&ss->ss_saddev[i];
241 * NOTE: should the ADMMIN or USRMIN minors change
242 * then so should the offset of 2 below
243 * Both USRMIN and ADMMIN are clone interfaces and
244 * therefore their minor numbers (0 and 1) are reserved.
246 *devp = makedevice(getemajor(*devp), i + 2);
247 qprocson(qp);
248 return (0);
252 * sadclose() -
253 * Clean up the data structures.
255 /* ARGSUSED */
256 static int
257 sadclose(
258 queue_t *qp, /* pointer to read queue */
259 int flag, /* file open flags */
260 cred_t *credp) /* user credentials */
262 struct saddev *sadp;
264 qprocsoff(qp);
265 sadp = (struct saddev *)qp->q_ptr;
266 sadp->sa_qp = NULL;
267 sadp->sa_addr = NULL;
268 netstack_rele(sadp->sa_ss->ss_netstack);
269 sadp->sa_ss = NULL;
270 qp->q_ptr = NULL;
271 WR(qp)->q_ptr = NULL;
272 return (0);
276 * sadwput() -
277 * Write side put procedure.
279 static int
280 sadwput(
281 queue_t *qp, /* pointer to write queue */
282 mblk_t *mp) /* message pointer */
284 struct iocblk *iocp;
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;
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;
303 case SAD_VML:
304 vml_ioctl(qp, mp);
305 break;
307 default:
308 miocnak(qp, mp, 0, EINVAL);
309 break;
311 break;
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;
321 case SAD_VML:
322 vml_iocdata(qp, mp);
323 break;
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;
332 break;
334 default:
335 freemsg(mp);
336 break;
337 } /* switch (db_type) */
338 return (0);
342 * apush_ioctl() -
343 * Handle the M_IOCTL messages associated with
344 * the autopush feature.
346 static void
347 apush_ioctl(
348 queue_t *qp, /* pointer to write queue */
349 mblk_t *mp) /* message pointer */
351 struct iocblk *iocp;
352 struct saddev *sadp;
353 uint_t size;
355 iocp = (struct iocblk *)mp->b_rptr;
356 if (iocp->ioc_count != TRANSPARENT) {
357 miocnak(qp, mp, 0, EINVAL);
358 return;
360 if (SAD_VER(iocp->ioc_cmd) > AP_VERSION) {
361 miocnak(qp, mp, 0, EINVAL);
362 return;
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;
372 /* FALLTHRU */
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;
384 default:
385 ASSERT(0);
386 miocnak(qp, mp, 0, EINVAL);
387 break;
388 } /* switch (ioc_cmd) */
392 * apush_iocdata() -
393 * Handle the M_IOCDATA messages associated with
394 * the autopush feature.
396 static void
397 apush_iocdata(
398 queue_t *qp, /* pointer to write queue */
399 mblk_t *mp) /* message pointer */
401 int i, ret;
402 struct copyresp *csp;
403 struct strapush *sap = NULL;
404 struct autopush *ap, *ap_tmp;
405 struct saddev *sadp;
406 uint_t size;
407 dev_t dev;
408 str_stack_t *ss;
410 sadp = (struct saddev *)qp->q_ptr;
411 ss = sadp->sa_ss;
413 csp = (struct copyresp *)mp->b_rptr;
414 if (csp->cp_rval) { /* if there was an error */
415 freemsg(mp);
416 return;
418 if (mp->b_cont) {
420 * sap needed only if mp->b_cont is set. figure out the
421 * size of the expected sap structure and make sure
422 * enough data was supplied.
424 if (SAD_VER(csp->cp_cmd) == 1)
425 size = STRAPUSH_V1_LEN;
426 else
427 size = STRAPUSH_V0_LEN;
428 if (MBLKL(mp->b_cont) < size) {
429 miocnak(qp, mp, 0, EINVAL);
430 return;
432 sap = (struct strapush *)mp->b_cont->b_rptr;
433 dev = makedevice(sap->sap_major, sap->sap_minor);
435 switch (SAD_CMD(csp->cp_cmd)) {
436 case SAD_CMD(SAD_SAP):
438 /* currently we only support one SAD_SAP command */
439 if (((long)csp->cp_private) != GETSTRUCT) {
440 cmn_err(CE_WARN,
441 "apush_iocdata: cp_private bad in SAD_SAP: %p",
442 (void *)csp->cp_private);
443 miocnak(qp, mp, 0, EINVAL);
444 return;
447 switch (sap->sap_cmd) {
448 default:
449 miocnak(qp, mp, 0, EINVAL);
450 return;
451 case SAP_ONE:
452 case SAP_RANGE:
453 case SAP_ALL:
454 /* allocate and initialize a new config */
455 ap = sad_ap_alloc();
456 ap->ap_common = sap->sap_common;
457 if (SAD_VER(csp->cp_cmd) > 0)
458 ap->ap_anchor = sap->sap_anchor;
459 for (i = 0; i < MIN(sap->sap_npush, MAXAPUSH); i++)
460 (void) strncpy(ap->ap_list[i],
461 sap->sap_list[i], FMNAMESZ);
463 /* sanity check the request */
464 if (((ret = sad_ap_verify(ap)) != 0) ||
465 ((ret = valid_major(ap->ap_major)) != 0)) {
466 sad_ap_rele(ap, ss);
467 miocnak(qp, mp, 0, ret);
468 return;
471 /* check for overlapping configs */
472 mutex_enter(&ss->ss_sad_lock);
473 ap_tmp = sad_ap_find(&ap->ap_common, ss);
474 if (ap_tmp != NULL) {
475 /* already configured */
476 mutex_exit(&ss->ss_sad_lock);
477 sad_ap_rele(ap_tmp, ss);
478 sad_ap_rele(ap, ss);
479 miocnak(qp, mp, 0, EEXIST);
480 return;
483 /* add the new config to our hash */
484 sad_ap_insert(ap, ss);
485 mutex_exit(&ss->ss_sad_lock);
486 miocack(qp, mp, 0, 0);
487 return;
489 case SAP_CLEAR:
490 /* sanity check the request */
491 if (ret = valid_major(sap->sap_major)) {
492 miocnak(qp, mp, 0, ret);
493 return;
496 /* search for a matching config */
497 if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) {
498 /* no config found */
499 miocnak(qp, mp, 0, ENODEV);
500 return;
504 * If we matched a SAP_RANGE config
505 * the minor passed in must match the
506 * beginning of the range exactly.
508 if ((ap->ap_type == SAP_RANGE) &&
509 (ap->ap_minor != sap->sap_minor)) {
510 sad_ap_rele(ap, ss);
511 miocnak(qp, mp, 0, ERANGE);
512 return;
516 * If we matched a SAP_ALL config
517 * the minor passed in must be 0.
519 if ((ap->ap_type == SAP_ALL) &&
520 (sap->sap_minor != 0)) {
521 sad_ap_rele(ap, ss);
522 miocnak(qp, mp, 0, EINVAL);
523 return;
527 * make sure someone else hasn't already
528 * removed this config from the hash.
530 mutex_enter(&ss->ss_sad_lock);
531 ap_tmp = sad_ap_find(&ap->ap_common, ss);
532 if (ap_tmp != ap) {
533 mutex_exit(&ss->ss_sad_lock);
534 sad_ap_rele(ap_tmp, ss);
535 sad_ap_rele(ap, ss);
536 miocnak(qp, mp, 0, ENODEV);
537 return;
540 /* remove the config from the hash and return */
541 sad_ap_remove(ap, ss);
542 mutex_exit(&ss->ss_sad_lock);
545 * Release thrice, once for sad_ap_find_by_dev(),
546 * once for sad_ap_find(), and once to free.
548 sad_ap_rele(ap, ss);
549 sad_ap_rele(ap, ss);
550 sad_ap_rele(ap, ss);
551 miocack(qp, mp, 0, 0);
552 return;
553 } /* switch (sap_cmd) */
554 /*NOTREACHED*/
556 case SAD_CMD(SAD_GAP):
557 switch ((long)csp->cp_private) {
559 case GETSTRUCT:
560 /* sanity check the request */
561 if (ret = valid_major(sap->sap_major)) {
562 miocnak(qp, mp, 0, ret);
563 return;
566 /* search for a matching config */
567 if ((ap = sad_ap_find_by_dev(dev, ss)) == NULL) {
568 /* no config found */
569 miocnak(qp, mp, 0, ENODEV);
570 return;
573 /* copy out the contents of the config */
574 sap->sap_common = ap->ap_common;
575 if (SAD_VER(csp->cp_cmd) > 0)
576 sap->sap_anchor = ap->ap_anchor;
577 for (i = 0; i < ap->ap_npush; i++)
578 (void) strcpy(sap->sap_list[i], ap->ap_list[i]);
579 for (; i < MAXAPUSH; i++)
580 bzero(sap->sap_list[i], FMNAMESZ + 1);
582 /* release our hold on the config */
583 sad_ap_rele(ap, ss);
585 /* copyout the results */
586 if (SAD_VER(csp->cp_cmd) == 1)
587 size = STRAPUSH_V1_LEN;
588 else
589 size = STRAPUSH_V0_LEN;
591 mcopyout(mp, (void *)GETRESULT, size, sadp->sa_addr,
592 NULL);
593 qreply(qp, mp);
594 return;
595 case GETRESULT:
596 miocack(qp, mp, 0, 0);
597 return;
599 default:
600 cmn_err(CE_WARN,
601 "apush_iocdata: cp_private bad case SAD_GAP: %p",
602 (void *)csp->cp_private);
603 freemsg(mp);
604 return;
605 } /* switch (cp_private) */
606 /*NOTREACHED*/
607 default: /* can't happen */
608 ASSERT(0);
609 freemsg(mp);
610 return;
611 } /* switch (cp_cmd) */
615 * vml_ioctl() -
616 * Handle the M_IOCTL message associated with a request
617 * to validate a module list.
619 static void
620 vml_ioctl(
621 queue_t *qp, /* pointer to write queue */
622 mblk_t *mp) /* message pointer */
624 struct iocblk *iocp;
626 iocp = (struct iocblk *)mp->b_rptr;
627 if (iocp->ioc_count != TRANSPARENT) {
628 miocnak(qp, mp, 0, EINVAL);
629 return;
631 ASSERT(SAD_CMD(iocp->ioc_cmd) == SAD_VML);
632 mcopyin(mp, (void *)GETSTRUCT,
633 SIZEOF_STRUCT(str_list, iocp->ioc_flag), NULL);
634 qreply(qp, mp);
638 * vml_iocdata() -
639 * Handle the M_IOCDATA messages associated with
640 * a request to validate a module list.
642 static void
643 vml_iocdata(
644 queue_t *qp, /* pointer to write queue */
645 mblk_t *mp) /* message pointer */
647 long i;
648 int nmods;
649 struct copyresp *csp;
650 struct str_mlist *lp;
651 STRUCT_HANDLE(str_list, slp);
652 struct saddev *sadp;
654 csp = (struct copyresp *)mp->b_rptr;
655 if (csp->cp_rval) { /* if there was an error */
656 freemsg(mp);
657 return;
660 ASSERT(SAD_CMD(csp->cp_cmd) == SAD_VML);
661 sadp = (struct saddev *)qp->q_ptr;
662 switch ((long)csp->cp_private) {
663 case GETSTRUCT:
664 STRUCT_SET_HANDLE(slp, csp->cp_flag,
665 (struct str_list *)mp->b_cont->b_rptr);
666 nmods = STRUCT_FGET(slp, sl_nmods);
667 if (nmods <= 0) {
668 miocnak(qp, mp, 0, EINVAL);
669 break;
671 sadp->sa_addr = (caddr_t)(uintptr_t)nmods;
673 mcopyin(mp, (void *)GETLIST, nmods * sizeof (struct str_mlist),
674 STRUCT_FGETP(slp, sl_modlist));
675 qreply(qp, mp);
676 break;
678 case GETLIST:
679 lp = (struct str_mlist *)mp->b_cont->b_rptr;
680 for (i = 0; i < (long)sadp->sa_addr; i++, lp++) {
681 lp->l_name[FMNAMESZ] = '\0';
682 if (fmodsw_find(lp->l_name, FMODSW_LOAD) == NULL) {
683 miocack(qp, mp, 0, 1);
684 return;
687 miocack(qp, mp, 0, 0);
688 break;
690 default:
691 cmn_err(CE_WARN, "vml_iocdata: invalid cp_private value: %p",
692 (void *)csp->cp_private);
693 freemsg(mp);
694 break;
695 } /* switch (cp_private) */
699 * Validate a major number and also verify if
700 * it is a STREAMS device.
701 * Return values: 0 if a valid STREAMS dev
702 * error code otherwise
704 static int
705 valid_major(major_t major)
707 int ret = 0;
709 if (etoimajor(major) == -1)
710 return (EINVAL);
713 * attempt to load the driver 'major' and verify that
714 * it is a STREAMS driver.
716 if (ddi_hold_driver(major) == NULL)
717 return (EINVAL);
719 if (!STREAMSTAB(major))
720 ret = ENOSTR;
722 ddi_rele_driver(major);
724 return (ret);