xref: /titanic_51/usr/src/uts/common/io/dld/dld_drv.c (revision 72888e72b624f86b06ea05b1de2287dcf9f40d23)
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  * Data-Link Driver
28  */
29 
30 #include	<sys/conf.h>
31 #include	<sys/mkdev.h>
32 #include	<sys/modctl.h>
33 #include	<sys/stat.h>
34 #include	<sys/dld_impl.h>
35 #include	<sys/dls_impl.h>
36 #include	<sys/softmac.h>
37 #include	<sys/mac.h>
38 #include	<sys/mac_ether.h>
39 #include	<sys/mac_client.h>
40 #include	<sys/mac_client_impl.h>
41 #include	<sys/mac_client_priv.h>
42 #include	<inet/common.h>
43 #include	<sys/policy.h>
44 #include	<sys/priv_names.h>
45 
46 static void	drv_init(void);
47 static int	drv_fini(void);
48 
49 static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
50 static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
51 static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
52 
53 /*
54  * Secure objects declarations
55  */
56 #define	SECOBJ_WEP_HASHSZ	67
57 static krwlock_t	drv_secobj_lock;
58 static kmem_cache_t	*drv_secobj_cachep;
59 static mod_hash_t	*drv_secobj_hash;
60 static void		drv_secobj_init(void);
61 static void		drv_secobj_fini(void);
62 static int		drv_ioc_setap(datalink_id_t, struct dlautopush *);
63 static int		drv_ioc_getap(datalink_id_t, struct dlautopush *);
64 static int		drv_ioc_clrap(datalink_id_t);
65 
66 
67 /*
68  * The following entry points are private to dld and are used for control
69  * operations only. The entry points exported to mac drivers are defined
70  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
71  */
72 static int	drv_open(dev_t *, int, int, cred_t *);
73 static int	drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
74 
75 static dev_info_t	*dld_dip;	/* dev_info_t for the driver */
76 uint32_t		dld_opt = 0;	/* Global options */
77 
78 #define	NAUTOPUSH 32
79 static mod_hash_t *dld_ap_hashp;
80 static krwlock_t dld_ap_hash_lock;
81 
82 static struct cb_ops drv_cb_ops = {
83 	drv_open,		/* open */
84 	nulldev,		/* close */
85 	nulldev,		/* strategy */
86 	nulldev,		/* print */
87 	nodev,			/* dump */
88 	nodev,			/* read */
89 	nodev,			/* write */
90 	drv_ioctl,		/* ioctl */
91 	nodev,			/* devmap */
92 	nodev,			/* mmap */
93 	nodev,			/* segmap */
94 	nochpoll,		/* poll */
95 	ddi_prop_op,		/* cb_prop_op */
96 	0,			/* streamtab  */
97 	D_MP			/* Driver compatibility flag */
98 };
99 
100 static struct dev_ops drv_ops = {
101 	DEVO_REV,		/* devo_rev */
102 	0,			/* refcnt */
103 	drv_getinfo,		/* get_dev_info */
104 	nulldev,		/* identify */
105 	nulldev,		/* probe */
106 	drv_attach,		/* attach */
107 	drv_detach,		/* detach */
108 	nodev,			/* reset */
109 	&drv_cb_ops,		/* driver operations */
110 	NULL,			/* bus operations */
111 	nodev,			/* dev power */
112 	ddi_quiesce_not_supported,	/* dev quiesce */
113 };
114 
115 /*
116  * Module linkage information for the kernel.
117  */
118 static	struct modldrv		drv_modldrv = {
119 	&mod_driverops,
120 	DLD_INFO,
121 	&drv_ops
122 };
123 
124 static	struct modlinkage	drv_modlinkage = {
125 	MODREV_1,
126 	&drv_modldrv,
127 	NULL
128 };
129 
130 int
131 _init(void)
132 {
133 	return (mod_install(&drv_modlinkage));
134 }
135 
136 int
137 _fini(void)
138 {
139 	return (mod_remove(&drv_modlinkage));
140 }
141 
142 int
143 _info(struct modinfo *modinfop)
144 {
145 	return (mod_info(&drv_modlinkage, modinfop));
146 }
147 
148 /*
149  * Initialize component modules.
150  */
151 static void
152 drv_init(void)
153 {
154 	drv_secobj_init();
155 	dld_str_init();
156 
157 	/*
158 	 * Create a hash table for autopush configuration.
159 	 */
160 	dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash",
161 	    NAUTOPUSH, mod_hash_null_valdtor);
162 
163 	ASSERT(dld_ap_hashp != NULL);
164 	rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL);
165 }
166 
167 /* ARGSUSED */
168 static uint_t
169 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
170 {
171 	boolean_t *pexist = arg;
172 
173 	*pexist = B_TRUE;
174 	return (MH_WALK_TERMINATE);
175 }
176 
177 static int
178 drv_fini(void)
179 {
180 	int		err;
181 	boolean_t	exist = B_FALSE;
182 
183 	rw_enter(&dld_ap_hash_lock, RW_READER);
184 	mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist);
185 	rw_exit(&dld_ap_hash_lock);
186 	if (exist)
187 		return (EBUSY);
188 
189 	if ((err = dld_str_fini()) != 0)
190 		return (err);
191 
192 	drv_secobj_fini();
193 	mod_hash_destroy_idhash(dld_ap_hashp);
194 	rw_destroy(&dld_ap_hash_lock);
195 	return (0);
196 }
197 
198 /*
199  * devo_getinfo: getinfo(9e)
200  */
201 /*ARGSUSED*/
202 static int
203 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
204 {
205 	if (dld_dip == NULL)
206 		return (DDI_FAILURE);
207 
208 	switch (cmd) {
209 	case DDI_INFO_DEVT2INSTANCE:
210 		*resp = 0;
211 		break;
212 	case DDI_INFO_DEVT2DEVINFO:
213 		*resp = dld_dip;
214 		break;
215 	default:
216 		return (DDI_FAILURE);
217 	}
218 
219 	return (DDI_SUCCESS);
220 }
221 
222 /*
223  * Check properties to set options. (See dld.h for property definitions).
224  */
225 static void
226 drv_set_opt(dev_info_t *dip)
227 {
228 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
229 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
230 		dld_opt |= DLD_OPT_NO_FASTPATH;
231 	}
232 
233 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
234 	    DLD_PROP_NO_POLL, 0) != 0) {
235 		dld_opt |= DLD_OPT_NO_POLL;
236 	}
237 
238 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
239 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
240 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
241 	}
242 
243 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
244 	    DLD_PROP_NO_SOFTRING, 0) != 0) {
245 		dld_opt |= DLD_OPT_NO_SOFTRING;
246 	}
247 }
248 
249 /*
250  * devo_attach: attach(9e)
251  */
252 static int
253 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
254 {
255 	if (cmd != DDI_ATTACH)
256 		return (DDI_FAILURE);
257 
258 	ASSERT(ddi_get_instance(dip) == 0);
259 	drv_init();
260 	drv_set_opt(dip);
261 
262 	/*
263 	 * Create control node. DLPI provider nodes will be created on demand.
264 	 */
265 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
266 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
267 		return (DDI_FAILURE);
268 
269 	dld_dip = dip;
270 
271 	/*
272 	 * Log the fact that the driver is now attached.
273 	 */
274 	ddi_report_dev(dip);
275 	return (DDI_SUCCESS);
276 }
277 
278 /*
279  * devo_detach: detach(9e)
280  */
281 static int
282 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
283 {
284 	if (cmd != DDI_DETACH)
285 		return (DDI_FAILURE);
286 
287 	ASSERT(dld_dip == dip);
288 	if (drv_fini() != 0)
289 		return (DDI_FAILURE);
290 
291 	/*
292 	 * Remove the control node.
293 	 */
294 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
295 	dld_dip = NULL;
296 
297 	return (DDI_SUCCESS);
298 }
299 
300 /*
301  * dld control node open procedure.
302  */
303 /*ARGSUSED*/
304 static int
305 drv_open(dev_t *devp, int flag, int sflag, cred_t *credp)
306 {
307 	/*
308 	 * Only the control node can be opened.
309 	 */
310 	if (getminor(*devp) != DLD_CONTROL_MINOR)
311 		return (ENODEV);
312 	return (0);
313 }
314 
315 /*
316  * DLDIOC_ATTR
317  */
318 /* ARGSUSED */
319 static int
320 drv_ioc_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
321 {
322 	dld_ioc_attr_t		*diap = karg;
323 	dls_dl_handle_t		dlh;
324 	dls_link_t		*dlp;
325 	int			err;
326 	mac_perim_handle_t	mph;
327 
328 	if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0)
329 		return (err);
330 
331 	if ((err = mac_perim_enter_by_macname(
332 	    dls_devnet_mac(dlh), &mph)) != 0) {
333 		dls_devnet_rele_tmp(dlh);
334 		return (err);
335 	}
336 
337 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0) {
338 		mac_perim_exit(mph);
339 		dls_devnet_rele_tmp(dlh);
340 		return (err);
341 	}
342 
343 	mac_sdu_get(dlp->dl_mh, NULL, &diap->dia_max_sdu);
344 
345 	dls_link_rele(dlp);
346 	mac_perim_exit(mph);
347 	dls_devnet_rele_tmp(dlh);
348 
349 	return (0);
350 }
351 
352 /*
353  * DLDIOC_PHYS_ATTR
354  */
355 /* ARGSUSED */
356 static int
357 drv_ioc_phys_attr(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
358 {
359 	dld_ioc_phys_attr_t	*dipp = karg;
360 	int			err;
361 	dls_dl_handle_t		dlh;
362 	dls_dev_handle_t	ddh;
363 	dev_t			phydev;
364 
365 	/*
366 	 * Every physical link should have its physical dev_t kept in the
367 	 * daemon. If not, it is not a valid physical link.
368 	 */
369 	if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0)
370 		return (EINVAL);
371 
372 	/*
373 	 * Although this is a valid physical link, it might already be removed
374 	 * by DR or during system shutdown. softmac_hold_device() would return
375 	 * ENOENT in this case.
376 	 */
377 	if ((err = softmac_hold_device(phydev, &ddh)) != 0)
378 		return (err);
379 
380 	if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) {
381 		/*
382 		 * Although this is an active physical link, its link type is
383 		 * not supported by GLDv3, and therefore it does not have
384 		 * vanity naming support.
385 		 */
386 		dipp->dip_novanity = B_TRUE;
387 	} else {
388 		dipp->dip_novanity = B_FALSE;
389 		dls_devnet_rele_tmp(dlh);
390 	}
391 	/*
392 	 * Get the physical device name from the major number and the instance
393 	 * number derived from phydev.
394 	 */
395 	(void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d",
396 	    ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1);
397 
398 	softmac_rele_device(ddh);
399 	return (0);
400 }
401 
402 /* ARGSUSED */
403 static int
404 drv_ioc_hwgrpget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
405 {
406 	dld_ioc_hwgrpget_t	*hwgrpp = karg;
407 	dld_hwgrpinfo_t		hwgrp, *hip;
408 	mac_handle_t		mh = NULL;
409 	int			i, err, grpnum;
410 	uint_t			bytes_left;
411 
412 	hwgrpp->dih_n_groups = 0;
413 	err = mac_open_by_linkid(hwgrpp->dih_linkid, &mh);
414 	if (err != 0)
415 		goto done;
416 
417 	hip = (dld_hwgrpinfo_t *)
418 	    ((uchar_t *)arg + sizeof (dld_ioc_hwgrpget_t));
419 	bytes_left = hwgrpp->dih_size;
420 	grpnum = mac_hwgrp_num(mh);
421 	for (i = 0; i < grpnum; i++) {
422 		if (sizeof (dld_hwgrpinfo_t) > bytes_left) {
423 			err = ENOSPC;
424 			goto done;
425 		}
426 
427 		bzero(&hwgrp, sizeof (hwgrp));
428 		bcopy(mac_name(mh), hwgrp.dhi_link_name,
429 		    sizeof (hwgrp.dhi_link_name));
430 		mac_get_hwgrp_info(mh, i, &hwgrp.dhi_grp_num,
431 		    &hwgrp.dhi_n_rings, &hwgrp.dhi_grp_type,
432 		    &hwgrp.dhi_n_clnts, hwgrp.dhi_clnts);
433 		if (copyout(&hwgrp, hip, sizeof (hwgrp)) != 0) {
434 			err = EFAULT;
435 			goto done;
436 		}
437 
438 		hip++;
439 		bytes_left -= sizeof (dld_hwgrpinfo_t);
440 	}
441 
442 done:
443 	if (mh != NULL)
444 		dld_mac_close(mh);
445 	if (err == 0)
446 		hwgrpp->dih_n_groups = grpnum;
447 	return (err);
448 }
449 
450 /* ARGSUSED */
451 static int
452 drv_ioc_macaddrget(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
453 {
454 	dld_ioc_macaddrget_t	*magp = karg;
455 	dld_macaddrinfo_t	mai, *maip;
456 	mac_handle_t		mh = NULL;
457 	int			i, err;
458 	uint_t			bytes_left;
459 	boolean_t		is_used;
460 
461 	magp->dig_count = 0;
462 	err = mac_open_by_linkid(magp->dig_linkid, &mh);
463 	if (err != 0)
464 		goto done;
465 
466 	maip = (dld_macaddrinfo_t *)
467 	    ((uchar_t *)arg + sizeof (dld_ioc_macaddrget_t));
468 	bytes_left = magp->dig_size;
469 
470 	for (i = 0; i < mac_addr_factory_num(mh) + 1; i++) {
471 		if (sizeof (dld_macaddrinfo_t) > bytes_left) {
472 			err = ENOSPC;
473 			goto done;
474 		}
475 
476 		bzero(&mai, sizeof (mai));
477 
478 		if (i == 0) {
479 			/* primary MAC address */
480 			mac_unicast_primary_get(mh, mai.dmi_addr);
481 			mai.dmi_addrlen = mac_addr_len(mh);
482 			mac_unicast_primary_info(mh, mai.dmi_client_name,
483 			    &is_used);
484 		} else {
485 			/* factory MAC address slot */
486 			mac_addr_factory_value(mh, i, mai.dmi_addr,
487 			    &mai.dmi_addrlen, mai.dmi_client_name, &is_used);
488 		}
489 
490 		mai.dmi_slot = i;
491 		if (is_used)
492 			mai.dmi_flags |= DLDIOCMACADDR_USED;
493 
494 		if (copyout(&mai, maip, sizeof (mai)) != 0) {
495 			err = EFAULT;
496 			goto done;
497 		}
498 
499 		maip++;
500 		bytes_left -= sizeof (dld_macaddrinfo_t);
501 	}
502 
503 done:
504 	if (mh != NULL)
505 		dld_mac_close(mh);
506 	if (err == 0)
507 		magp->dig_count = mac_addr_factory_num(mh) + 1;
508 	return (err);
509 }
510 
511 /*
512  * DLDIOC_SET/GETPROP
513  */
514 static int
515 drv_ioc_prop_common(dld_ioc_macprop_t *prop, intptr_t arg, boolean_t set,
516     int mode)
517 {
518 	int			err = EINVAL;
519 	dls_dl_handle_t 	dlh = NULL;
520 	dls_link_t		*dlp = NULL;
521 	mac_perim_handle_t	mph = NULL;
522 	mac_prop_t		macprop;
523 	dld_ioc_macprop_t	*kprop;
524 	datalink_id_t		linkid;
525 	uint_t			dsize;
526 
527 
528 	/*
529 	 * We only use pr_valsize from prop, as the caller only did a
530 	 * copyin() for sizeof (dld_ioc_prop_t), which doesn't cover
531 	 * the property data.  We copyin the full dld_ioc_prop_t
532 	 * including the data into kprop down below.
533 	 */
534 	dsize = sizeof (dld_ioc_macprop_t) + prop->pr_valsize - 1;
535 	if (dsize < prop->pr_valsize)
536 		return (EINVAL);
537 
538 	/*
539 	 * The property data is variable size, so we need to allocate
540 	 * a buffer for kernel use as this data was not part of the
541 	 * prop allocation and copyin() done by the framework.
542 	 */
543 	if ((kprop = kmem_alloc(dsize, KM_NOSLEEP)) == NULL)
544 		return (ENOMEM);
545 
546 	if (ddi_copyin((void *)arg, kprop, dsize, mode) != 0) {
547 		err = EFAULT;
548 		goto done;
549 	}
550 
551 	linkid = kprop->pr_linkid;
552 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
553 		goto done;
554 
555 	if ((err = mac_perim_enter_by_macname(dls_devnet_mac(dlh),
556 	    &mph)) != 0) {
557 		goto done;
558 	}
559 
560 	switch (kprop->pr_num) {
561 	case MAC_PROP_ZONE: {
562 		if (set) {
563 			dld_ioc_zid_t	*dzp = (dld_ioc_zid_t *)kprop->pr_val;
564 
565 			err = dls_devnet_setzid(dzp->diz_link, dzp->diz_zid);
566 			goto done;
567 		} else {
568 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
569 			err = dls_devnet_getzid(linkid,
570 			    (zoneid_t *)kprop->pr_val);
571 			goto done;
572 		}
573 	}
574 	case MAC_PROP_AUTOPUSH: {
575 		struct dlautopush	*dlap =
576 		    (struct dlautopush *)kprop->pr_val;
577 
578 		if (set) {
579 			if (kprop->pr_valsize != 0) {
580 				err = drv_ioc_setap(linkid, dlap);
581 				goto done;
582 			} else {
583 				err = drv_ioc_clrap(linkid);
584 				goto done;
585 			}
586 		} else {
587 			kprop->pr_perm_flags = MAC_PROP_PERM_RW;
588 			err = drv_ioc_getap(linkid, dlap);
589 			goto done;
590 		}
591 	}
592 	default:
593 		break;
594 	}
595 
596 	if ((err = dls_link_hold(dls_devnet_mac(dlh), &dlp)) != 0)
597 		goto done;
598 
599 	macprop.mp_name = kprop->pr_name;
600 	macprop.mp_id = kprop->pr_num;
601 	macprop.mp_flags = kprop->pr_flags;
602 
603 	if (set) {
604 		err = mac_set_prop(dlp->dl_mh, &macprop, kprop->pr_val,
605 		    kprop->pr_valsize);
606 	} else {
607 		kprop->pr_perm_flags = MAC_PROP_PERM_RW;
608 		err = mac_get_prop(dlp->dl_mh, &macprop, kprop->pr_val,
609 		    kprop->pr_valsize, &kprop->pr_perm_flags);
610 	}
611 
612 done:
613 	if (!set && err == 0 &&
614 	    ddi_copyout(kprop, (void *)arg, dsize, mode) != 0)
615 		err = EFAULT;
616 
617 	if (dlp != NULL)
618 		dls_link_rele(dlp);
619 
620 	if (mph != NULL) {
621 		int32_t	cpuid;
622 		void	*mdip = NULL;
623 
624 		if (dlp != NULL && set && err == 0) {
625 			cpuid = mac_client_intr_cpu(dlp->dl_mch);
626 			mdip = mac_get_devinfo(dlp->dl_mh);
627 		}
628 
629 		mac_perim_exit(mph);
630 
631 		if (mdip != NULL)
632 			mac_client_set_intr_cpu(mdip, dlp->dl_mch, cpuid);
633 	}
634 	if (dlh != NULL)
635 		dls_devnet_rele_tmp(dlh);
636 
637 	if (kprop != NULL)
638 		kmem_free(kprop, dsize);
639 	return (err);
640 }
641 
642 /* ARGSUSED */
643 static int
644 drv_ioc_setprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
645 {
646 	return (drv_ioc_prop_common(karg, arg, B_TRUE, mode));
647 }
648 
649 /* ARGSUSED */
650 static int
651 drv_ioc_getprop(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
652 {
653 	return (drv_ioc_prop_common(karg, arg, B_FALSE, mode));
654 }
655 
656 /*
657  * DLDIOC_RENAME.
658  *
659  * This function handles two cases of link renaming. See more in comments above
660  * dls_datalink_rename().
661  */
662 /* ARGSUSED */
663 static int
664 drv_ioc_rename(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
665 {
666 	dld_ioc_rename_t	*dir = karg;
667 	mod_hash_key_t		key;
668 	mod_hash_val_t		val;
669 	int			err;
670 
671 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
672 	    dir->dir_link)) != 0)
673 		return (err);
674 
675 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
676 		return (0);
677 
678 	/*
679 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
680 	 * renaming request is to rename a valid physical link (dir_linkid1)
681 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
682 	 * or during system shutdown). In this case, the link (specified by
683 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
684 	 * and dir_linkid1 and its configuration would be lost.
685 	 *
686 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
687 	 */
688 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
689 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
690 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
691 		rw_exit(&dld_ap_hash_lock);
692 		return (0);
693 	}
694 
695 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
696 	kmem_free(val, sizeof (dld_ap_t));
697 	rw_exit(&dld_ap_hash_lock);
698 	return (0);
699 }
700 
701 static int
702 drv_ioc_setap(datalink_id_t linkid, struct dlautopush *dlap)
703 {
704 	dld_ap_t	*dap;
705 	int		i;
706 	mod_hash_key_t	key;
707 
708 	if (dlap->dap_npush == 0 || dlap->dap_npush > MAXAPUSH)
709 		return (EINVAL);
710 
711 	/*
712 	 * Validate that the specified list of modules exist.
713 	 */
714 	for (i = 0; i < dlap->dap_npush; i++) {
715 		if (fmodsw_find(dlap->dap_aplist[i], FMODSW_LOAD) == NULL)
716 			return (EINVAL);
717 	}
718 
719 
720 	key = (mod_hash_key_t)(uintptr_t)linkid;
721 
722 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
723 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
724 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
725 		if (dap == NULL) {
726 			rw_exit(&dld_ap_hash_lock);
727 			return (ENOMEM);
728 		}
729 
730 		dap->da_linkid = linkid;
731 		VERIFY(mod_hash_insert(dld_ap_hashp, key,
732 		    (mod_hash_val_t)dap) == 0);
733 	}
734 
735 	/*
736 	 * Update the configuration.
737 	 */
738 	dap->da_anchor = dlap->dap_anchor;
739 	dap->da_npush = dlap->dap_npush;
740 	for (i = 0; i < dlap->dap_npush; i++) {
741 		(void) strlcpy(dap->da_aplist[i], dlap->dap_aplist[i],
742 		    FMNAMESZ + 1);
743 	}
744 	rw_exit(&dld_ap_hash_lock);
745 
746 	return (0);
747 }
748 
749 static int
750 drv_ioc_getap(datalink_id_t linkid, struct dlautopush *dlap)
751 {
752 	dld_ap_t	*dap;
753 	int		i;
754 
755 	rw_enter(&dld_ap_hash_lock, RW_READER);
756 	if (mod_hash_find(dld_ap_hashp,
757 	    (mod_hash_key_t)(uintptr_t)linkid,
758 	    (mod_hash_val_t *)&dap) != 0) {
759 		rw_exit(&dld_ap_hash_lock);
760 		return (ENOENT);
761 	}
762 
763 	/*
764 	 * Retrieve the configuration.
765 	 */
766 	dlap->dap_anchor = dap->da_anchor;
767 	dlap->dap_npush = dap->da_npush;
768 	for (i = 0; i < dap->da_npush; i++) {
769 		(void) strlcpy(dlap->dap_aplist[i], dap->da_aplist[i],
770 		    FMNAMESZ + 1);
771 	}
772 	rw_exit(&dld_ap_hash_lock);
773 
774 	return (0);
775 }
776 
777 static int
778 drv_ioc_clrap(datalink_id_t linkid)
779 {
780 	mod_hash_val_t	val;
781 	mod_hash_key_t	key;
782 
783 	key = (mod_hash_key_t)(uintptr_t)linkid;
784 
785 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
786 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
787 		rw_exit(&dld_ap_hash_lock);
788 		return (0);
789 	}
790 
791 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
792 	kmem_free(val, sizeof (dld_ap_t));
793 	rw_exit(&dld_ap_hash_lock);
794 	return (0);
795 }
796 
797 /*
798  * DLDIOC_DOORSERVER
799  */
800 /* ARGSUSED */
801 static int
802 drv_ioc_doorserver(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
803 {
804 	dld_ioc_door_t	*did = karg;
805 
806 	return (dls_mgmt_door_set(did->did_start_door));
807 }
808 
809 /*
810  * DLDIOC_USAGELOG
811  */
812 /* ARGSUSED */
813 static int
814 drv_ioc_usagelog(void *karg, intptr_t arg, int mode, cred_t *cred,
815     int *rvalp)
816 {
817 	dld_ioc_usagelog_t	*log_info = (dld_ioc_usagelog_t *)karg;
818 
819 	if (log_info->ul_type < MAC_LOGTYPE_LINK ||
820 	    log_info->ul_type > MAC_LOGTYPE_FLOW)
821 		return (EINVAL);
822 
823 	if (log_info->ul_onoff)
824 		mac_start_logusage(log_info->ul_type, log_info->ul_interval);
825 	else
826 		mac_stop_logusage(log_info->ul_type);
827 	return (0);
828 }
829 
830 /*
831  * Process a DLDIOC_ADDFLOW request.
832  */
833 /* ARGSUSED */
834 static int
835 drv_ioc_addflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
836 {
837 	dld_ioc_addflow_t	*afp = karg;
838 
839 	return (dld_add_flow(afp->af_linkid, afp->af_name,
840 	    &afp->af_flow_desc, &afp->af_resource_props));
841 }
842 
843 /*
844  * Process a DLDIOC_REMOVEFLOW request.
845  */
846 /* ARGSUSED */
847 static int
848 drv_ioc_removeflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
849 {
850 	dld_ioc_removeflow_t	*rfp = karg;
851 
852 	return (dld_remove_flow(rfp->rf_name));
853 }
854 
855 /*
856  * Process a DLDIOC_MODIFYFLOW request.
857  */
858 /* ARGSUSED */
859 static int
860 drv_ioc_modifyflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
861 {
862 	dld_ioc_modifyflow_t	*mfp = karg;
863 
864 	return (dld_modify_flow(mfp->mf_name, &mfp->mf_resource_props));
865 }
866 
867 /*
868  * Process a DLDIOC_WALKFLOW request.
869  */
870 /* ARGSUSED */
871 static int
872 drv_ioc_walkflow(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
873 {
874 	dld_ioc_walkflow_t	*wfp = karg;
875 
876 	return (dld_walk_flow(wfp, arg));
877 }
878 
879 /*
880  * Check for GLDv3 autopush information.  There are three cases:
881  *
882  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
883  *	fill dlap in with that information and return 0.
884  *
885  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
886  *	configuration, then replace devp with the physical device (if one
887  *	exists) and return 1.  This allows stropen() to find the old-school
888  *	per-driver autopush configuration.  (For softmac, the result is that
889  *	the softmac dev_t is replaced with the legacy device's dev_t).
890  *
891  *   3. If neither of the above apply, don't touch the args and return -1.
892  */
893 int
894 dld_autopush(dev_t *devp, struct dlautopush *dlap)
895 {
896 	dld_ap_t	*dap;
897 	datalink_id_t	linkid;
898 	dev_t		phydev;
899 
900 	if (!GLDV3_DRV(getmajor(*devp)))
901 		return (-1);
902 
903 	/*
904 	 * Find the linkid by the link's dev_t.
905 	 */
906 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
907 		return (-1);
908 
909 	/*
910 	 * Find the autopush configuration associated with the linkid.
911 	 */
912 	rw_enter(&dld_ap_hash_lock, RW_READER);
913 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
914 	    (mod_hash_val_t *)&dap) == 0) {
915 		*dlap = dap->da_ap;
916 		rw_exit(&dld_ap_hash_lock);
917 		return (0);
918 	}
919 	rw_exit(&dld_ap_hash_lock);
920 
921 	if (dls_devnet_phydev(linkid, &phydev) != 0)
922 		return (-1);
923 
924 	*devp = phydev;
925 	return (1);
926 }
927 
928 /*
929  * Secure objects implementation
930  */
931 
932 /* ARGSUSED */
933 static int
934 drv_secobj_ctor(void *buf, void *arg, int kmflag)
935 {
936 	bzero(buf, sizeof (dld_secobj_t));
937 	return (0);
938 }
939 
940 static void
941 drv_secobj_init(void)
942 {
943 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
944 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
945 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
946 	    NULL, NULL, NULL, 0);
947 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
948 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
949 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
950 }
951 
952 static void
953 drv_secobj_fini(void)
954 {
955 	mod_hash_destroy_hash(drv_secobj_hash);
956 	kmem_cache_destroy(drv_secobj_cachep);
957 	rw_destroy(&drv_secobj_lock);
958 }
959 
960 /* ARGSUSED */
961 static int
962 drv_ioc_secobj_set(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
963 {
964 	dld_ioc_secobj_set_t	*ssp = karg;
965 	dld_secobj_t		*sobjp, *objp;
966 	int			err;
967 
968 	sobjp = &ssp->ss_obj;
969 
970 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
971 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
972 		return (EINVAL);
973 
974 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
975 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
976 		return (EINVAL);
977 
978 	rw_enter(&drv_secobj_lock, RW_WRITER);
979 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
980 	    (mod_hash_val_t *)&objp);
981 	if (err == 0) {
982 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
983 			rw_exit(&drv_secobj_lock);
984 			return (EEXIST);
985 		}
986 	} else {
987 		ASSERT(err == MH_ERR_NOTFOUND);
988 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
989 			rw_exit(&drv_secobj_lock);
990 			return (ENOENT);
991 		}
992 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
993 		(void) strlcpy(objp->so_name, sobjp->so_name,
994 		    DLD_SECOBJ_NAME_MAX);
995 
996 		VERIFY(mod_hash_insert(drv_secobj_hash,
997 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp) == 0);
998 	}
999 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1000 	objp->so_len = sobjp->so_len;
1001 	objp->so_class = sobjp->so_class;
1002 	rw_exit(&drv_secobj_lock);
1003 	return (0);
1004 }
1005 
1006 typedef struct dld_secobj_state {
1007 	uint_t		ss_free;
1008 	uint_t		ss_count;
1009 	int		ss_rc;
1010 	int		ss_mode;
1011 	dld_secobj_t	*ss_objp;
1012 } dld_secobj_state_t;
1013 
1014 /* ARGSUSED */
1015 static uint_t
1016 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1017 {
1018 	dld_secobj_state_t	*statep = arg;
1019 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
1020 
1021 	if (statep->ss_free < sizeof (dld_secobj_t)) {
1022 		statep->ss_rc = ENOSPC;
1023 		return (MH_WALK_TERMINATE);
1024 	}
1025 	if (ddi_copyout(sobjp, statep->ss_objp, sizeof (*sobjp),
1026 	    statep->ss_mode) != 0) {
1027 		statep->ss_rc = EFAULT;
1028 		return (MH_WALK_TERMINATE);
1029 	}
1030 	statep->ss_objp++;
1031 	statep->ss_free -= sizeof (dld_secobj_t);
1032 	statep->ss_count++;
1033 	return (MH_WALK_CONTINUE);
1034 }
1035 
1036 /* ARGSUSED */
1037 static int
1038 drv_ioc_secobj_get(void *karg, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1039 {
1040 	dld_ioc_secobj_get_t	*sgp = karg;
1041 	dld_secobj_t		*sobjp, *objp;
1042 	int			err;
1043 
1044 	sobjp = &sgp->sg_obj;
1045 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1046 		return (EINVAL);
1047 
1048 	rw_enter(&drv_secobj_lock, RW_READER);
1049 	if (sobjp->so_name[0] != '\0') {
1050 		err = mod_hash_find(drv_secobj_hash,
1051 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1052 		if (err != 0) {
1053 			ASSERT(err == MH_ERR_NOTFOUND);
1054 			rw_exit(&drv_secobj_lock);
1055 			return (ENOENT);
1056 		}
1057 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1058 		sobjp->so_len = objp->so_len;
1059 		sobjp->so_class = objp->so_class;
1060 		sgp->sg_count = 1;
1061 	} else {
1062 		dld_secobj_state_t	state;
1063 
1064 		state.ss_free = sgp->sg_size - sizeof (dld_ioc_secobj_get_t);
1065 		state.ss_count = 0;
1066 		state.ss_rc = 0;
1067 		state.ss_mode = mode;
1068 		state.ss_objp = (dld_secobj_t *)((uchar_t *)arg +
1069 		    sizeof (dld_ioc_secobj_get_t));
1070 
1071 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1072 		if (state.ss_rc != 0) {
1073 			rw_exit(&drv_secobj_lock);
1074 			return (state.ss_rc);
1075 		}
1076 		sgp->sg_count = state.ss_count;
1077 	}
1078 	rw_exit(&drv_secobj_lock);
1079 	return (0);
1080 }
1081 
1082 /* ARGSUSED */
1083 static int
1084 drv_ioc_secobj_unset(void *karg, intptr_t arg, int mode, cred_t *cred,
1085     int *rvalp)
1086 {
1087 	dld_ioc_secobj_unset_t	*sup = karg;
1088 	dld_secobj_t		*objp;
1089 	mod_hash_val_t		val;
1090 	int			err;
1091 
1092 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1093 		return (EINVAL);
1094 
1095 	rw_enter(&drv_secobj_lock, RW_WRITER);
1096 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1097 	    (mod_hash_val_t *)&objp);
1098 	if (err != 0) {
1099 		ASSERT(err == MH_ERR_NOTFOUND);
1100 		rw_exit(&drv_secobj_lock);
1101 		return (ENOENT);
1102 	}
1103 	VERIFY(mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1104 	    (mod_hash_val_t *)&val) == 0);
1105 	ASSERT(objp == (dld_secobj_t *)val);
1106 
1107 	kmem_cache_free(drv_secobj_cachep, objp);
1108 	rw_exit(&drv_secobj_lock);
1109 	return (0);
1110 }
1111 
1112 static int
1113 drv_check_policy(dld_ioc_info_t *info, cred_t *cred)
1114 {
1115 	int	i, err = 0;
1116 
1117 	for (i = 0; info->di_priv[i] != NULL && i < DLD_MAX_PRIV; i++) {
1118 		if ((err = secpolicy_dld_ioctl(cred, info->di_priv[i],
1119 		    "dld ioctl")) != 0) {
1120 			break;
1121 		}
1122 	}
1123 	if (err == 0)
1124 		return (0);
1125 
1126 	return (secpolicy_net_config(cred, B_FALSE));
1127 }
1128 
1129 static dld_ioc_info_t drv_ioc_list[] = {
1130 	{DLDIOC_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_attr_t),
1131 	    drv_ioc_attr, {NULL}},
1132 	{DLDIOC_PHYS_ATTR, DLDCOPYINOUT, sizeof (dld_ioc_phys_attr_t),
1133 	    drv_ioc_phys_attr, {NULL}},
1134 	{DLDIOC_SECOBJ_SET, DLDCOPYIN, sizeof (dld_ioc_secobj_set_t),
1135 	    drv_ioc_secobj_set, {PRIV_SYS_DL_CONFIG}},
1136 	{DLDIOC_SECOBJ_GET, DLDCOPYINOUT, sizeof (dld_ioc_secobj_get_t),
1137 	    drv_ioc_secobj_get, {PRIV_SYS_DL_CONFIG}},
1138 	{DLDIOC_SECOBJ_UNSET, DLDCOPYIN, sizeof (dld_ioc_secobj_unset_t),
1139 	    drv_ioc_secobj_unset, {PRIV_SYS_DL_CONFIG}},
1140 	{DLDIOC_DOORSERVER, DLDCOPYIN, sizeof (dld_ioc_door_t),
1141 	    drv_ioc_doorserver, {PRIV_SYS_DL_CONFIG}},
1142 	{DLDIOC_RENAME, DLDCOPYIN, sizeof (dld_ioc_rename_t),
1143 	    drv_ioc_rename, {PRIV_SYS_DL_CONFIG}},
1144 	{DLDIOC_MACADDRGET, DLDCOPYINOUT, sizeof (dld_ioc_macaddrget_t),
1145 	    drv_ioc_macaddrget, {PRIV_SYS_DL_CONFIG}},
1146 	{DLDIOC_ADDFLOW, DLDCOPYIN, sizeof (dld_ioc_addflow_t),
1147 	    drv_ioc_addflow, {PRIV_SYS_DL_CONFIG}},
1148 	{DLDIOC_REMOVEFLOW, DLDCOPYIN, sizeof (dld_ioc_removeflow_t),
1149 	    drv_ioc_removeflow, {PRIV_SYS_DL_CONFIG}},
1150 	{DLDIOC_MODIFYFLOW, DLDCOPYIN, sizeof (dld_ioc_modifyflow_t),
1151 	    drv_ioc_modifyflow, {PRIV_SYS_DL_CONFIG}},
1152 	{DLDIOC_WALKFLOW, DLDCOPYINOUT, sizeof (dld_ioc_walkflow_t),
1153 	    drv_ioc_walkflow, {NULL}},
1154 	{DLDIOC_USAGELOG, DLDCOPYIN, sizeof (dld_ioc_usagelog_t),
1155 	    drv_ioc_usagelog, {PRIV_SYS_DL_CONFIG}},
1156 	{DLDIOC_SETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1157 	    drv_ioc_setprop, {PRIV_SYS_DL_CONFIG}},
1158 	{DLDIOC_GETMACPROP, DLDCOPYIN, sizeof (dld_ioc_macprop_t),
1159 	    drv_ioc_getprop, {NULL}},
1160 	{DLDIOC_GETHWGRP, DLDCOPYINOUT, sizeof (dld_ioc_hwgrpget_t),
1161 	    drv_ioc_hwgrpget, {PRIV_SYS_DL_CONFIG}},
1162 };
1163 
1164 typedef struct dld_ioc_modentry {
1165 	uint16_t	dim_modid;	/* Top 16 bits of ioctl command */
1166 	char		*dim_modname;	/* Module to be loaded */
1167 	dld_ioc_info_t	*dim_list;	/* array of ioctl structures */
1168 	uint_t		dim_count;	/* number of elements in dim_list */
1169 } dld_ioc_modentry_t;
1170 
1171 /*
1172  * For all modules except for dld, dim_list and dim_count are assigned
1173  * when the modules register their ioctls in dld_ioc_register().  We
1174  * can statically initialize dld's ioctls in-line here; there's no
1175  * need for it to call dld_ioc_register() itself.
1176  */
1177 static dld_ioc_modentry_t dld_ioc_modtable[] = {
1178 	{DLD_IOC,	"dld",	drv_ioc_list, DLDIOCCNT(drv_ioc_list)},
1179 	{AGGR_IOC,	"aggr",	NULL, 0},
1180 	{VNIC_IOC,	"vnic",	NULL, 0}
1181 };
1182 #define	DLDIOC_CNT	\
1183 	(sizeof (dld_ioc_modtable) / sizeof (dld_ioc_modentry_t))
1184 
1185 static dld_ioc_modentry_t *
1186 dld_ioc_findmod(uint16_t modid)
1187 {
1188 	int	i;
1189 
1190 	for (i = 0; i < DLDIOC_CNT; i++) {
1191 		if (modid == dld_ioc_modtable[i].dim_modid)
1192 			return (&dld_ioc_modtable[i]);
1193 	}
1194 	return (NULL);
1195 }
1196 
1197 int
1198 dld_ioc_register(uint16_t modid, dld_ioc_info_t *list, uint_t count)
1199 {
1200 	dld_ioc_modentry_t *dim = dld_ioc_findmod(modid);
1201 
1202 	if (dim == NULL)
1203 		return (ENOENT);
1204 
1205 	dim->dim_list = list;
1206 	dim->dim_count = count;
1207 	return (0);
1208 }
1209 
1210 void
1211 dld_ioc_unregister(uint16_t modid)
1212 {
1213 	VERIFY(dld_ioc_register(modid, NULL, 0) == 0);
1214 }
1215 
1216 /*
1217  * The general design with GLDv3 ioctls is that all ioctls issued
1218  * through /dev/dld go through this drv_ioctl() function.  This
1219  * function handles all ioctls on behalf of modules listed in
1220  * dld_ioc_modtable.
1221  *
1222  * When an ioctl is received, this function looks for the associated
1223  * module-id-specific ioctl information using dld_ioc_findmod().  The
1224  * call to ddi_hold_devi_by_instance() on the associated device will
1225  * cause the kernel module responsible for the ioctl to be loaded if
1226  * it's not already loaded, which should result in that module calling
1227  * dld_ioc_register(), thereby filling in the dim_list containing the
1228  * details for the ioctl being processed.
1229  *
1230  * This function can then perform operations such as copyin() data and
1231  * do credential checks based on the registered ioctl information,
1232  * then issue the callback function di_func() registered by the
1233  * responsible module.  Upon return, the appropriate copyout()
1234  * operation can be performed and the operation completes.
1235  */
1236 /* ARGSUSED */
1237 static int
1238 drv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1239 {
1240 	dld_ioc_modentry_t *dim;
1241 	dld_ioc_info_t	*info;
1242 	dev_info_t	*dip = NULL;
1243 	void		*buf = NULL;
1244 	size_t		sz;
1245 	int		i, err;
1246 
1247 	if ((dim = dld_ioc_findmod(DLD_IOC_MODID(cmd))) == NULL)
1248 		return (ENOTSUP);
1249 
1250 	dip = ddi_hold_devi_by_instance(ddi_name_to_major(dim->dim_modname),
1251 	    0, 0);
1252 	if (dip == NULL || dim->dim_list == NULL) {
1253 		err = ENODEV;
1254 		goto done;
1255 	}
1256 
1257 	for (i = 0; i < dim->dim_count; i++) {
1258 		if (cmd == dim->dim_list[i].di_cmd)
1259 			break;
1260 	}
1261 	if (i == dim->dim_count) {
1262 		err = ENOTSUP;
1263 		goto done;
1264 	}
1265 
1266 	info = &dim->dim_list[i];
1267 	if ((err = drv_check_policy(info, cred)) != 0)
1268 		goto done;
1269 
1270 	sz = info->di_argsize;
1271 	if ((buf = kmem_zalloc(sz, KM_NOSLEEP)) == NULL) {
1272 		err = ENOMEM;
1273 		goto done;
1274 	}
1275 
1276 	if ((info->di_flags & DLDCOPYIN) &&
1277 	    ddi_copyin((void *)arg, buf, sz, mode) != 0) {
1278 		err = EFAULT;
1279 		goto done;
1280 	}
1281 
1282 	err = info->di_func(buf, arg, mode, cred, rvalp);
1283 
1284 	if ((info->di_flags & DLDCOPYOUT) &&
1285 	    ddi_copyout(buf, (void *)arg, sz, mode) != 0 && err == 0)
1286 		err = EFAULT;
1287 
1288 done:
1289 	if (buf != NULL)
1290 		kmem_free(buf, sz);
1291 	if (dip != NULL)
1292 		ddi_release_devi(dip);
1293 	return (err);
1294 }
1295