xref: /titanic_44/usr/src/uts/common/os/modctl.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * modctl system call for loadable module support.
31*7c478bd9Sstevel@tonic-gate  */
32*7c478bd9Sstevel@tonic-gate 
33*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/user.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/exec.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/file.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/conf.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/reboot.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/fs/ufs_fsdir.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
44*7c478bd9Sstevel@tonic-gate #include <sys/sysconf.h>
45*7c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
46*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
47*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
48*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
49*7c478bd9Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
50*7c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
51*7c478bd9Sstevel@tonic-gate #include <sys/ddi_implfuncs.h>
52*7c478bd9Sstevel@tonic-gate #include <sys/bootconf.h>
53*7c478bd9Sstevel@tonic-gate #include <sys/dc_ki.h>
54*7c478bd9Sstevel@tonic-gate #include <sys/cladm.h>
55*7c478bd9Sstevel@tonic-gate #include <sys/dtrace.h>
56*7c478bd9Sstevel@tonic-gate #include <sys/kdi.h>
57*7c478bd9Sstevel@tonic-gate 
58*7c478bd9Sstevel@tonic-gate #include <sys/devpolicy.h>
59*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
60*7c478bd9Sstevel@tonic-gate #include <sys/kobj.h>
61*7c478bd9Sstevel@tonic-gate #include <sys/devops.h>
62*7c478bd9Sstevel@tonic-gate #include <sys/autoconf.h>
63*7c478bd9Sstevel@tonic-gate #include <sys/hwconf.h>
64*7c478bd9Sstevel@tonic-gate #include <sys/callb.h>
65*7c478bd9Sstevel@tonic-gate #include <sys/debug.h>
66*7c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
67*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
68*7c478bd9Sstevel@tonic-gate #include <sys/sysevent.h>
69*7c478bd9Sstevel@tonic-gate #include <sys/sysevent_impl.h>
70*7c478bd9Sstevel@tonic-gate #include <sys/instance.h>
71*7c478bd9Sstevel@tonic-gate #include <sys/modhash.h>
72*7c478bd9Sstevel@tonic-gate #include <sys/modhash_impl.h>
73*7c478bd9Sstevel@tonic-gate #include <sys/dacf_impl.h>
74*7c478bd9Sstevel@tonic-gate #include <sys/vfs.h>
75*7c478bd9Sstevel@tonic-gate #include <sys/pathname.h>
76*7c478bd9Sstevel@tonic-gate #include <sys/console.h>
77*7c478bd9Sstevel@tonic-gate #include <sys/policy.h>
78*7c478bd9Sstevel@tonic-gate #include <ipp/ipp_impl.h>
79*7c478bd9Sstevel@tonic-gate #include <sys/fs/dv_node.h>
80*7c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
81*7c478bd9Sstevel@tonic-gate 
82*7c478bd9Sstevel@tonic-gate static int		mod_circdep(struct modctl *);
83*7c478bd9Sstevel@tonic-gate static int		modinfo(modid_t, struct modinfo *);
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate static void		mod_uninstall_all(void);
86*7c478bd9Sstevel@tonic-gate static int		mod_getinfo(struct modctl *, struct modinfo *);
87*7c478bd9Sstevel@tonic-gate static struct modctl	*allocate_modp(char *, char *);
88*7c478bd9Sstevel@tonic-gate 
89*7c478bd9Sstevel@tonic-gate static int		mod_load(struct modctl *, int);
90*7c478bd9Sstevel@tonic-gate static void		mod_unload(struct modctl *);
91*7c478bd9Sstevel@tonic-gate static int		modinstall(struct modctl *);
92*7c478bd9Sstevel@tonic-gate static int		moduninstall(struct modctl *);
93*7c478bd9Sstevel@tonic-gate 
94*7c478bd9Sstevel@tonic-gate static struct modctl	*mod_hold_by_name_common(struct modctl *, char *);
95*7c478bd9Sstevel@tonic-gate static struct modctl	*mod_hold_by_id(modid_t);
96*7c478bd9Sstevel@tonic-gate static struct modctl	*mod_hold_next_by_id(modid_t);
97*7c478bd9Sstevel@tonic-gate static struct modctl	*mod_hold_loaded_mod(struct modctl *, char *, int *);
98*7c478bd9Sstevel@tonic-gate static struct modctl	*mod_hold_installed_mod(char *, int, int *);
99*7c478bd9Sstevel@tonic-gate 
100*7c478bd9Sstevel@tonic-gate static void		mod_release(struct modctl *);
101*7c478bd9Sstevel@tonic-gate static void		mod_make_requisite(struct modctl *, struct modctl *);
102*7c478bd9Sstevel@tonic-gate static int		mod_install_requisites(struct modctl *);
103*7c478bd9Sstevel@tonic-gate static void		check_esc_sequences(char *, char *);
104*7c478bd9Sstevel@tonic-gate static struct modctl	*mod_hold_by_name_requisite(struct modctl *, char *);
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate /*
107*7c478bd9Sstevel@tonic-gate  * module loading thread control structure. Calls to kobj_load_module()() are
108*7c478bd9Sstevel@tonic-gate  * handled off to a separate thead using this structure.
109*7c478bd9Sstevel@tonic-gate  */
110*7c478bd9Sstevel@tonic-gate struct loadmt {
111*7c478bd9Sstevel@tonic-gate 	ksema_t		sema;
112*7c478bd9Sstevel@tonic-gate 	struct modctl	*mp;
113*7c478bd9Sstevel@tonic-gate 	int		usepath;
114*7c478bd9Sstevel@tonic-gate 	kthread_t	*owner;
115*7c478bd9Sstevel@tonic-gate 	int		retval;
116*7c478bd9Sstevel@tonic-gate };
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate static void	modload_thread(struct loadmt *);
119*7c478bd9Sstevel@tonic-gate 
120*7c478bd9Sstevel@tonic-gate kcondvar_t	mod_cv;
121*7c478bd9Sstevel@tonic-gate kcondvar_t	mod_uninstall_cv;	/* Communication between swapper */
122*7c478bd9Sstevel@tonic-gate 					/* and the uninstall daemon. */
123*7c478bd9Sstevel@tonic-gate kmutex_t	mod_lock;		/* protects &modules insert linkage, */
124*7c478bd9Sstevel@tonic-gate 					/* mod_busy, mod_want, and mod_ref. */
125*7c478bd9Sstevel@tonic-gate 					/* blocking operations while holding */
126*7c478bd9Sstevel@tonic-gate 					/* mod_lock should be avoided */
127*7c478bd9Sstevel@tonic-gate kmutex_t	mod_uninstall_lock;	/* protects mod_uninstall_cv */
128*7c478bd9Sstevel@tonic-gate kthread_id_t	mod_aul_thread;
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate int	isminiroot;		/* set if running as miniroot */
131*7c478bd9Sstevel@tonic-gate int	modrootloaded;		/* set after root driver and fs are loaded */
132*7c478bd9Sstevel@tonic-gate int	moddebug = 0x0;		/* debug flags for module writers */
133*7c478bd9Sstevel@tonic-gate int	swaploaded;		/* set after swap driver and fs are loaded */
134*7c478bd9Sstevel@tonic-gate int	bop_io_quiesced = 0;	/* set when BOP I/O can no longer be used */
135*7c478bd9Sstevel@tonic-gate int	last_module_id;
136*7c478bd9Sstevel@tonic-gate clock_t	mod_uninstall_interval = 0;
137*7c478bd9Sstevel@tonic-gate int	ddi_modclose_unload = 1;	/* 0 -> just decrement reference */
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate struct devnames *devnamesp;
140*7c478bd9Sstevel@tonic-gate struct devnames orphanlist;
141*7c478bd9Sstevel@tonic-gate 
142*7c478bd9Sstevel@tonic-gate krwlock_t	devinfo_tree_lock;	/* obsolete, to be removed */
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate #define	MAJBINDFILE "/etc/name_to_major"
145*7c478bd9Sstevel@tonic-gate #define	SYSBINDFILE "/etc/name_to_sysnum"
146*7c478bd9Sstevel@tonic-gate 
147*7c478bd9Sstevel@tonic-gate static char	majbind[] = MAJBINDFILE;
148*7c478bd9Sstevel@tonic-gate static char	sysbind[] = SYSBINDFILE;
149*7c478bd9Sstevel@tonic-gate static uint_t	mod_autounload_key;	/* for module autounload detection */
150*7c478bd9Sstevel@tonic-gate 
151*7c478bd9Sstevel@tonic-gate extern int obpdebug;
152*7c478bd9Sstevel@tonic-gate extern int make_mbind(char *, int, char *, struct bind **);
153*7c478bd9Sstevel@tonic-gate 
154*7c478bd9Sstevel@tonic-gate #define	DEBUGGER_PRESENT	((boothowto & RB_DEBUG) || (obpdebug != 0))
155*7c478bd9Sstevel@tonic-gate 
156*7c478bd9Sstevel@tonic-gate static int minorperm_loaded = 0;
157*7c478bd9Sstevel@tonic-gate 
158*7c478bd9Sstevel@tonic-gate 
159*7c478bd9Sstevel@tonic-gate 
160*7c478bd9Sstevel@tonic-gate void
161*7c478bd9Sstevel@tonic-gate mod_setup(void)
162*7c478bd9Sstevel@tonic-gate {
163*7c478bd9Sstevel@tonic-gate 	struct sysent *callp;
164*7c478bd9Sstevel@tonic-gate 	int callnum, exectype;
165*7c478bd9Sstevel@tonic-gate 	int	num_devs;
166*7c478bd9Sstevel@tonic-gate 	int	i;
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate 	/*
169*7c478bd9Sstevel@tonic-gate 	 * Initialize the list of loaded driver dev_ops.
170*7c478bd9Sstevel@tonic-gate 	 * XXX - This must be done before reading the system file so that
171*7c478bd9Sstevel@tonic-gate 	 * forceloads of drivers will work.
172*7c478bd9Sstevel@tonic-gate 	 */
173*7c478bd9Sstevel@tonic-gate 	num_devs = read_binding_file(majbind, mb_hashtab, make_mbind);
174*7c478bd9Sstevel@tonic-gate 	/*
175*7c478bd9Sstevel@tonic-gate 	 * Since read_binding_file is common code, it doesn't enforce that all
176*7c478bd9Sstevel@tonic-gate 	 * of the binding file entries have major numbers <= MAXMAJ32.  Thus,
177*7c478bd9Sstevel@tonic-gate 	 * ensure that we don't allocate some massive amount of space due to a
178*7c478bd9Sstevel@tonic-gate 	 * bad entry.  We can't have major numbers bigger than MAXMAJ32
179*7c478bd9Sstevel@tonic-gate 	 * until file system support for larger major numbers exists.
180*7c478bd9Sstevel@tonic-gate 	 */
181*7c478bd9Sstevel@tonic-gate 
182*7c478bd9Sstevel@tonic-gate 	/*
183*7c478bd9Sstevel@tonic-gate 	 * Leave space for expansion, but not more than L_MAXMAJ32
184*7c478bd9Sstevel@tonic-gate 	 */
185*7c478bd9Sstevel@tonic-gate 	devcnt = MIN(num_devs + 30, L_MAXMAJ32);
186*7c478bd9Sstevel@tonic-gate 	devopsp = kmem_alloc(devcnt * sizeof (struct dev_ops *), KM_SLEEP);
187*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < devcnt; i++)
188*7c478bd9Sstevel@tonic-gate 		devopsp[i] = &mod_nodev_ops;
189*7c478bd9Sstevel@tonic-gate 
190*7c478bd9Sstevel@tonic-gate 	init_devnamesp(devcnt);
191*7c478bd9Sstevel@tonic-gate 
192*7c478bd9Sstevel@tonic-gate 	/*
193*7c478bd9Sstevel@tonic-gate 	 * Sync up with the work that the stand-alone linker has already done.
194*7c478bd9Sstevel@tonic-gate 	 */
195*7c478bd9Sstevel@tonic-gate 	(void) kobj_sync();
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 	if (boothowto & RB_DEBUG)
198*7c478bd9Sstevel@tonic-gate 		kdi_dvec_modavail();
199*7c478bd9Sstevel@tonic-gate 
200*7c478bd9Sstevel@tonic-gate 	make_aliases(mb_hashtab);
201*7c478bd9Sstevel@tonic-gate 
202*7c478bd9Sstevel@tonic-gate 	/*
203*7c478bd9Sstevel@tonic-gate 	 * Initialize streams device implementation structures.
204*7c478bd9Sstevel@tonic-gate 	 */
205*7c478bd9Sstevel@tonic-gate 	devimpl = kmem_zalloc(devcnt * sizeof (cdevsw_impl_t), KM_SLEEP);
206*7c478bd9Sstevel@tonic-gate 
207*7c478bd9Sstevel@tonic-gate 	/*
208*7c478bd9Sstevel@tonic-gate 	 * If the cl_bootstrap module is present,
209*7c478bd9Sstevel@tonic-gate 	 * we should be configured as a cluster. Loading this module
210*7c478bd9Sstevel@tonic-gate 	 * will set "cluster_bootflags" to non-zero.
211*7c478bd9Sstevel@tonic-gate 	 */
212*7c478bd9Sstevel@tonic-gate 	(void) modload("misc", "cl_bootstrap");
213*7c478bd9Sstevel@tonic-gate 
214*7c478bd9Sstevel@tonic-gate 	(void) read_binding_file(sysbind, sb_hashtab, make_mbind);
215*7c478bd9Sstevel@tonic-gate 	init_syscallnames(NSYSCALL);
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate 	/*
218*7c478bd9Sstevel@tonic-gate 	 * Start up dynamic autoconfiguration framework (dacf).
219*7c478bd9Sstevel@tonic-gate 	 */
220*7c478bd9Sstevel@tonic-gate 	mod_hash_init();
221*7c478bd9Sstevel@tonic-gate 	dacf_init();
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate 	/*
224*7c478bd9Sstevel@tonic-gate 	 * Start up IP policy framework (ipp).
225*7c478bd9Sstevel@tonic-gate 	 */
226*7c478bd9Sstevel@tonic-gate 	ipp_init();
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate 	/*
229*7c478bd9Sstevel@tonic-gate 	 * Allocate loadable native system call locks.
230*7c478bd9Sstevel@tonic-gate 	 */
231*7c478bd9Sstevel@tonic-gate 	for (callnum = 0, callp = sysent; callnum < NSYSCALL;
232*7c478bd9Sstevel@tonic-gate 	    callnum++, callp++) {
233*7c478bd9Sstevel@tonic-gate 		if (LOADABLE_SYSCALL(callp)) {
234*7c478bd9Sstevel@tonic-gate 			if (mod_getsysname(callnum) != NULL) {
235*7c478bd9Sstevel@tonic-gate 				callp->sy_lock =
236*7c478bd9Sstevel@tonic-gate 				    kobj_zalloc(sizeof (krwlock_t), KM_SLEEP);
237*7c478bd9Sstevel@tonic-gate 				rw_init(callp->sy_lock, NULL, RW_DEFAULT, NULL);
238*7c478bd9Sstevel@tonic-gate 			} else {
239*7c478bd9Sstevel@tonic-gate 				callp->sy_flags &= ~SE_LOADABLE;
240*7c478bd9Sstevel@tonic-gate 				callp->sy_callc = nosys;
241*7c478bd9Sstevel@tonic-gate 			}
242*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
243*7c478bd9Sstevel@tonic-gate 		} else {
244*7c478bd9Sstevel@tonic-gate 			/*
245*7c478bd9Sstevel@tonic-gate 			 * Do some sanity checks on the sysent table
246*7c478bd9Sstevel@tonic-gate 			 */
247*7c478bd9Sstevel@tonic-gate 			switch (callp->sy_flags & SE_RVAL_MASK) {
248*7c478bd9Sstevel@tonic-gate 			case SE_32RVAL1:
249*7c478bd9Sstevel@tonic-gate 				/* only r_val1 returned */
250*7c478bd9Sstevel@tonic-gate 			case SE_32RVAL1 | SE_32RVAL2:
251*7c478bd9Sstevel@tonic-gate 				/* r_val1 and r_val2 returned */
252*7c478bd9Sstevel@tonic-gate 			case SE_64RVAL:
253*7c478bd9Sstevel@tonic-gate 				/* 64-bit rval returned */
254*7c478bd9Sstevel@tonic-gate 				break;
255*7c478bd9Sstevel@tonic-gate 			default:
256*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "sysent[%d]: bad flags %x",
257*7c478bd9Sstevel@tonic-gate 				    callnum, callp->sy_flags);
258*7c478bd9Sstevel@tonic-gate 			}
259*7c478bd9Sstevel@tonic-gate #endif
260*7c478bd9Sstevel@tonic-gate 		}
261*7c478bd9Sstevel@tonic-gate 	}
262*7c478bd9Sstevel@tonic-gate 
263*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
264*7c478bd9Sstevel@tonic-gate 	/*
265*7c478bd9Sstevel@tonic-gate 	 * Allocate loadable system call locks for 32-bit compat syscalls
266*7c478bd9Sstevel@tonic-gate 	 */
267*7c478bd9Sstevel@tonic-gate 	for (callnum = 0, callp = sysent32; callnum < NSYSCALL;
268*7c478bd9Sstevel@tonic-gate 	    callnum++, callp++) {
269*7c478bd9Sstevel@tonic-gate 		if (LOADABLE_SYSCALL(callp)) {
270*7c478bd9Sstevel@tonic-gate 			if (mod_getsysname(callnum) != NULL) {
271*7c478bd9Sstevel@tonic-gate 				callp->sy_lock =
272*7c478bd9Sstevel@tonic-gate 				    kobj_zalloc(sizeof (krwlock_t), KM_SLEEP);
273*7c478bd9Sstevel@tonic-gate 				rw_init(callp->sy_lock, NULL, RW_DEFAULT, NULL);
274*7c478bd9Sstevel@tonic-gate 			} else {
275*7c478bd9Sstevel@tonic-gate 				callp->sy_flags &= ~SE_LOADABLE;
276*7c478bd9Sstevel@tonic-gate 				callp->sy_callc = nosys;
277*7c478bd9Sstevel@tonic-gate 			}
278*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
279*7c478bd9Sstevel@tonic-gate 		} else {
280*7c478bd9Sstevel@tonic-gate 			/*
281*7c478bd9Sstevel@tonic-gate 			 * Do some sanity checks on the sysent table
282*7c478bd9Sstevel@tonic-gate 			 */
283*7c478bd9Sstevel@tonic-gate 			switch (callp->sy_flags & SE_RVAL_MASK) {
284*7c478bd9Sstevel@tonic-gate 			case SE_32RVAL1:
285*7c478bd9Sstevel@tonic-gate 				/* only r_val1 returned */
286*7c478bd9Sstevel@tonic-gate 			case SE_32RVAL1 | SE_32RVAL2:
287*7c478bd9Sstevel@tonic-gate 				/* r_val1 and r_val2 returned */
288*7c478bd9Sstevel@tonic-gate 			case SE_64RVAL:
289*7c478bd9Sstevel@tonic-gate 				/* 64-bit rval returned */
290*7c478bd9Sstevel@tonic-gate 				break;
291*7c478bd9Sstevel@tonic-gate 			default:
292*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "sysent32[%d]: bad flags %x",
293*7c478bd9Sstevel@tonic-gate 				    callnum, callp->sy_flags);
294*7c478bd9Sstevel@tonic-gate 				goto skip;
295*7c478bd9Sstevel@tonic-gate 			}
296*7c478bd9Sstevel@tonic-gate 
297*7c478bd9Sstevel@tonic-gate 			/*
298*7c478bd9Sstevel@tonic-gate 			 * Cross-check the native and compatibility tables.
299*7c478bd9Sstevel@tonic-gate 			 */
300*7c478bd9Sstevel@tonic-gate 			if (callp->sy_callc == nosys ||
301*7c478bd9Sstevel@tonic-gate 			    sysent[callnum].sy_callc == nosys)
302*7c478bd9Sstevel@tonic-gate 				continue;
303*7c478bd9Sstevel@tonic-gate 			/*
304*7c478bd9Sstevel@tonic-gate 			 * If only one or the other slot is loadable, then
305*7c478bd9Sstevel@tonic-gate 			 * there's an error -- they should match!
306*7c478bd9Sstevel@tonic-gate 			 */
307*7c478bd9Sstevel@tonic-gate 			if ((callp->sy_callc == loadable_syscall) ^
308*7c478bd9Sstevel@tonic-gate 			    (sysent[callnum].sy_callc == loadable_syscall)) {
309*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "sysent[%d] loadable?",
310*7c478bd9Sstevel@tonic-gate 				    callnum);
311*7c478bd9Sstevel@tonic-gate 			}
312*7c478bd9Sstevel@tonic-gate 			/*
313*7c478bd9Sstevel@tonic-gate 			 * This is more of a heuristic test -- if the
314*7c478bd9Sstevel@tonic-gate 			 * system call returns two values in the 32-bit
315*7c478bd9Sstevel@tonic-gate 			 * world, it should probably return two 32-bit
316*7c478bd9Sstevel@tonic-gate 			 * values in the 64-bit world too.
317*7c478bd9Sstevel@tonic-gate 			 */
318*7c478bd9Sstevel@tonic-gate 			if (((callp->sy_flags & SE_32RVAL2) == 0) ^
319*7c478bd9Sstevel@tonic-gate 			    ((sysent[callnum].sy_flags & SE_32RVAL2) == 0)) {
320*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_WARN, "sysent[%d] rval2 mismatch!",
321*7c478bd9Sstevel@tonic-gate 				    callnum);
322*7c478bd9Sstevel@tonic-gate 			}
323*7c478bd9Sstevel@tonic-gate skip:;
324*7c478bd9Sstevel@tonic-gate #endif	/* DEBUG */
325*7c478bd9Sstevel@tonic-gate 		}
326*7c478bd9Sstevel@tonic-gate 	}
327*7c478bd9Sstevel@tonic-gate #endif	/* _SYSCALL32_IMPL */
328*7c478bd9Sstevel@tonic-gate 
329*7c478bd9Sstevel@tonic-gate 	/*
330*7c478bd9Sstevel@tonic-gate 	 * Allocate loadable exec locks.  (Assumes all execs are loadable)
331*7c478bd9Sstevel@tonic-gate 	 */
332*7c478bd9Sstevel@tonic-gate 	for (exectype = 0; exectype < nexectype; exectype++) {
333*7c478bd9Sstevel@tonic-gate 		execsw[exectype].exec_lock =
334*7c478bd9Sstevel@tonic-gate 		    kobj_zalloc(sizeof (krwlock_t), KM_SLEEP);
335*7c478bd9Sstevel@tonic-gate 		rw_init(execsw[exectype].exec_lock, NULL, RW_DEFAULT, NULL);
336*7c478bd9Sstevel@tonic-gate 	}
337*7c478bd9Sstevel@tonic-gate 
338*7c478bd9Sstevel@tonic-gate 	read_class_file();
339*7c478bd9Sstevel@tonic-gate 
340*7c478bd9Sstevel@tonic-gate 	/* init thread specific structure for mod_uninstall_all */
341*7c478bd9Sstevel@tonic-gate 	tsd_create(&mod_autounload_key, NULL);
342*7c478bd9Sstevel@tonic-gate }
343*7c478bd9Sstevel@tonic-gate 
344*7c478bd9Sstevel@tonic-gate static int
345*7c478bd9Sstevel@tonic-gate modctl_modload(int use_path, char *filename, int *rvp)
346*7c478bd9Sstevel@tonic-gate {
347*7c478bd9Sstevel@tonic-gate 	struct modctl *modp;
348*7c478bd9Sstevel@tonic-gate 	int retval = 0;
349*7c478bd9Sstevel@tonic-gate 	char *filenamep;
350*7c478bd9Sstevel@tonic-gate 	int modid;
351*7c478bd9Sstevel@tonic-gate 
352*7c478bd9Sstevel@tonic-gate 	filenamep = kmem_zalloc(MOD_MAXPATH, KM_SLEEP);
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate 	if (copyinstr(filename, filenamep, MOD_MAXPATH, 0)) {
355*7c478bd9Sstevel@tonic-gate 		retval = EFAULT;
356*7c478bd9Sstevel@tonic-gate 		goto out;
357*7c478bd9Sstevel@tonic-gate 	}
358*7c478bd9Sstevel@tonic-gate 
359*7c478bd9Sstevel@tonic-gate 	filenamep[MOD_MAXPATH - 1] = 0;
360*7c478bd9Sstevel@tonic-gate 	modp = mod_hold_installed_mod(filenamep, use_path, &retval);
361*7c478bd9Sstevel@tonic-gate 
362*7c478bd9Sstevel@tonic-gate 	if (modp == NULL)
363*7c478bd9Sstevel@tonic-gate 		goto out;
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 	modp->mod_loadflags |= MOD_NOAUTOUNLOAD;
366*7c478bd9Sstevel@tonic-gate 	modid = modp->mod_id;
367*7c478bd9Sstevel@tonic-gate 	mod_release_mod(modp);
368*7c478bd9Sstevel@tonic-gate 	if (rvp != NULL && copyout(&modid, rvp, sizeof (modid)) != 0)
369*7c478bd9Sstevel@tonic-gate 		retval = EFAULT;
370*7c478bd9Sstevel@tonic-gate out:
371*7c478bd9Sstevel@tonic-gate 	kmem_free(filenamep, MOD_MAXPATH);
372*7c478bd9Sstevel@tonic-gate 
373*7c478bd9Sstevel@tonic-gate 	return (retval);
374*7c478bd9Sstevel@tonic-gate }
375*7c478bd9Sstevel@tonic-gate 
376*7c478bd9Sstevel@tonic-gate static int
377*7c478bd9Sstevel@tonic-gate modctl_modunload(modid_t id)
378*7c478bd9Sstevel@tonic-gate {
379*7c478bd9Sstevel@tonic-gate 	int rval = 0;
380*7c478bd9Sstevel@tonic-gate 
381*7c478bd9Sstevel@tonic-gate 	if (id == 0) {
382*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
383*7c478bd9Sstevel@tonic-gate 		/*
384*7c478bd9Sstevel@tonic-gate 		 * Turn on mod_uninstall_daemon
385*7c478bd9Sstevel@tonic-gate 		 */
386*7c478bd9Sstevel@tonic-gate 		if (mod_uninstall_interval == 0) {
387*7c478bd9Sstevel@tonic-gate 			mod_uninstall_interval = 60;
388*7c478bd9Sstevel@tonic-gate 			modreap();
389*7c478bd9Sstevel@tonic-gate 			return (rval);
390*7c478bd9Sstevel@tonic-gate 		}
391*7c478bd9Sstevel@tonic-gate #endif
392*7c478bd9Sstevel@tonic-gate 		mod_uninstall_all();
393*7c478bd9Sstevel@tonic-gate 	} else {
394*7c478bd9Sstevel@tonic-gate 		(void) devfs_clean(ddi_root_node(), NULL, 0);
395*7c478bd9Sstevel@tonic-gate 		rval = modunload(id);
396*7c478bd9Sstevel@tonic-gate 	}
397*7c478bd9Sstevel@tonic-gate 	return (rval);
398*7c478bd9Sstevel@tonic-gate }
399*7c478bd9Sstevel@tonic-gate 
400*7c478bd9Sstevel@tonic-gate static int
401*7c478bd9Sstevel@tonic-gate modctl_modinfo(modid_t id, struct modinfo *umodi)
402*7c478bd9Sstevel@tonic-gate {
403*7c478bd9Sstevel@tonic-gate 	int retval;
404*7c478bd9Sstevel@tonic-gate 	struct modinfo modi;
405*7c478bd9Sstevel@tonic-gate #if defined(_SYSCALL32_IMPL)
406*7c478bd9Sstevel@tonic-gate 	int nobase;
407*7c478bd9Sstevel@tonic-gate 	struct modinfo32 modi32;
408*7c478bd9Sstevel@tonic-gate #endif
409*7c478bd9Sstevel@tonic-gate 
410*7c478bd9Sstevel@tonic-gate 	if (get_udatamodel() == DATAMODEL_NATIVE) {
411*7c478bd9Sstevel@tonic-gate 		if (copyin(umodi, &modi, sizeof (struct modinfo)) != 0)
412*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
413*7c478bd9Sstevel@tonic-gate 	}
414*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
415*7c478bd9Sstevel@tonic-gate 	else {
416*7c478bd9Sstevel@tonic-gate 		bzero(&modi, sizeof (modi));
417*7c478bd9Sstevel@tonic-gate 		if (copyin(umodi, &modi32, sizeof (struct modinfo32)) != 0)
418*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
419*7c478bd9Sstevel@tonic-gate 		modi.mi_info = modi32.mi_info;
420*7c478bd9Sstevel@tonic-gate 		modi.mi_id = modi32.mi_id;
421*7c478bd9Sstevel@tonic-gate 		modi.mi_nextid = modi32.mi_nextid;
422*7c478bd9Sstevel@tonic-gate 		nobase = modi.mi_info & MI_INFO_NOBASE;
423*7c478bd9Sstevel@tonic-gate 	}
424*7c478bd9Sstevel@tonic-gate #endif
425*7c478bd9Sstevel@tonic-gate 	/*
426*7c478bd9Sstevel@tonic-gate 	 * This flag is -only- for the kernels use.
427*7c478bd9Sstevel@tonic-gate 	 */
428*7c478bd9Sstevel@tonic-gate 	modi.mi_info &= ~MI_INFO_LINKAGE;
429*7c478bd9Sstevel@tonic-gate 
430*7c478bd9Sstevel@tonic-gate 	retval = modinfo(id, &modi);
431*7c478bd9Sstevel@tonic-gate 	if (retval)
432*7c478bd9Sstevel@tonic-gate 		return (retval);
433*7c478bd9Sstevel@tonic-gate 
434*7c478bd9Sstevel@tonic-gate 	if (get_udatamodel() == DATAMODEL_NATIVE) {
435*7c478bd9Sstevel@tonic-gate 		if (copyout(&modi, umodi, sizeof (struct modinfo)) != 0)
436*7c478bd9Sstevel@tonic-gate 			retval = EFAULT;
437*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
438*7c478bd9Sstevel@tonic-gate 	} else {
439*7c478bd9Sstevel@tonic-gate 		int i;
440*7c478bd9Sstevel@tonic-gate 
441*7c478bd9Sstevel@tonic-gate 		if (!nobase && (uintptr_t)modi.mi_base > UINT32_MAX)
442*7c478bd9Sstevel@tonic-gate 			return (EOVERFLOW);
443*7c478bd9Sstevel@tonic-gate 
444*7c478bd9Sstevel@tonic-gate 		modi32.mi_info = modi.mi_info;
445*7c478bd9Sstevel@tonic-gate 		modi32.mi_state = modi.mi_state;
446*7c478bd9Sstevel@tonic-gate 		modi32.mi_id = modi.mi_id;
447*7c478bd9Sstevel@tonic-gate 		modi32.mi_nextid = modi.mi_nextid;
448*7c478bd9Sstevel@tonic-gate 		modi32.mi_base = (caddr32_t)(uintptr_t)modi.mi_base;
449*7c478bd9Sstevel@tonic-gate 		modi32.mi_size = modi.mi_size;
450*7c478bd9Sstevel@tonic-gate 		modi32.mi_rev = modi.mi_rev;
451*7c478bd9Sstevel@tonic-gate 		modi32.mi_loadcnt = modi.mi_loadcnt;
452*7c478bd9Sstevel@tonic-gate 		bcopy(modi.mi_name, modi32.mi_name, sizeof (modi32.mi_name));
453*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < MODMAXLINK32; i++) {
454*7c478bd9Sstevel@tonic-gate 			modi32.mi_msinfo[i].msi_p0 = modi.mi_msinfo[i].msi_p0;
455*7c478bd9Sstevel@tonic-gate 			bcopy(modi.mi_msinfo[i].msi_linkinfo,
456*7c478bd9Sstevel@tonic-gate 			    modi32.mi_msinfo[i].msi_linkinfo,
457*7c478bd9Sstevel@tonic-gate 			    sizeof (modi32.mi_msinfo[0].msi_linkinfo));
458*7c478bd9Sstevel@tonic-gate 		}
459*7c478bd9Sstevel@tonic-gate 		if (copyout(&modi32, umodi, sizeof (struct modinfo32)) != 0)
460*7c478bd9Sstevel@tonic-gate 			retval = EFAULT;
461*7c478bd9Sstevel@tonic-gate #endif
462*7c478bd9Sstevel@tonic-gate 	}
463*7c478bd9Sstevel@tonic-gate 
464*7c478bd9Sstevel@tonic-gate 	return (retval);
465*7c478bd9Sstevel@tonic-gate }
466*7c478bd9Sstevel@tonic-gate 
467*7c478bd9Sstevel@tonic-gate /*
468*7c478bd9Sstevel@tonic-gate  * Return the last major number in the range of permissible major numbers.
469*7c478bd9Sstevel@tonic-gate  */
470*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
471*7c478bd9Sstevel@tonic-gate static int
472*7c478bd9Sstevel@tonic-gate modctl_modreserve(modid_t id, int *data)
473*7c478bd9Sstevel@tonic-gate {
474*7c478bd9Sstevel@tonic-gate 	if (copyout(&devcnt, data, sizeof (devcnt)) != 0)
475*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
476*7c478bd9Sstevel@tonic-gate 	return (0);
477*7c478bd9Sstevel@tonic-gate }
478*7c478bd9Sstevel@tonic-gate 
479*7c478bd9Sstevel@tonic-gate static int
480*7c478bd9Sstevel@tonic-gate modctl_add_major(int *data)
481*7c478bd9Sstevel@tonic-gate {
482*7c478bd9Sstevel@tonic-gate 	struct modconfig mc;
483*7c478bd9Sstevel@tonic-gate 	int i, rv;
484*7c478bd9Sstevel@tonic-gate 	struct aliases alias;
485*7c478bd9Sstevel@tonic-gate 	struct aliases *ap;
486*7c478bd9Sstevel@tonic-gate 	char name[MAXMODCONFNAME];
487*7c478bd9Sstevel@tonic-gate 	char cname[MAXMODCONFNAME];
488*7c478bd9Sstevel@tonic-gate 	char *drvname;
489*7c478bd9Sstevel@tonic-gate 
490*7c478bd9Sstevel@tonic-gate 	bzero(&mc, sizeof (struct modconfig));
491*7c478bd9Sstevel@tonic-gate 	if (get_udatamodel() == DATAMODEL_NATIVE) {
492*7c478bd9Sstevel@tonic-gate 		if (copyin(data, &mc, sizeof (struct modconfig)) != 0)
493*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
494*7c478bd9Sstevel@tonic-gate 	}
495*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
496*7c478bd9Sstevel@tonic-gate 	else {
497*7c478bd9Sstevel@tonic-gate 		struct modconfig32 modc32;
498*7c478bd9Sstevel@tonic-gate 
499*7c478bd9Sstevel@tonic-gate 		if (copyin(data, &modc32, sizeof (struct modconfig32)) != 0)
500*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
501*7c478bd9Sstevel@tonic-gate 		else {
502*7c478bd9Sstevel@tonic-gate 			bcopy(modc32.drvname, mc.drvname,
503*7c478bd9Sstevel@tonic-gate 			    sizeof (modc32.drvname));
504*7c478bd9Sstevel@tonic-gate 			bcopy(modc32.drvclass, mc.drvclass,
505*7c478bd9Sstevel@tonic-gate 			    sizeof (modc32.drvclass));
506*7c478bd9Sstevel@tonic-gate 			mc.major = modc32.major;
507*7c478bd9Sstevel@tonic-gate 			mc.num_aliases = modc32.num_aliases;
508*7c478bd9Sstevel@tonic-gate 			mc.ap = (struct aliases *)(uintptr_t)modc32.ap;
509*7c478bd9Sstevel@tonic-gate 		}
510*7c478bd9Sstevel@tonic-gate 	}
511*7c478bd9Sstevel@tonic-gate #endif
512*7c478bd9Sstevel@tonic-gate 
513*7c478bd9Sstevel@tonic-gate 	/*
514*7c478bd9Sstevel@tonic-gate 	 * If the driver is already in the mb_hashtab, and the name given
515*7c478bd9Sstevel@tonic-gate 	 * doesn't match that driver's name, fail.  Otherwise, pass, since
516*7c478bd9Sstevel@tonic-gate 	 * we may be adding aliases.
517*7c478bd9Sstevel@tonic-gate 	 */
518*7c478bd9Sstevel@tonic-gate 	if ((drvname = mod_major_to_name(mc.major)) != NULL &&
519*7c478bd9Sstevel@tonic-gate 	    strcmp(drvname, mc.drvname) != 0)
520*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
521*7c478bd9Sstevel@tonic-gate 
522*7c478bd9Sstevel@tonic-gate 	/*
523*7c478bd9Sstevel@tonic-gate 	 * Add each supplied driver alias to mb_hashtab
524*7c478bd9Sstevel@tonic-gate 	 */
525*7c478bd9Sstevel@tonic-gate 	ap = mc.ap;
526*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < mc.num_aliases; i++) {
527*7c478bd9Sstevel@tonic-gate 		bzero(&alias, sizeof (struct aliases));
528*7c478bd9Sstevel@tonic-gate 
529*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
530*7c478bd9Sstevel@tonic-gate 			if (copyin(ap, &alias, sizeof (struct aliases)) != 0)
531*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
532*7c478bd9Sstevel@tonic-gate 
533*7c478bd9Sstevel@tonic-gate 			if (alias.a_len > MAXMODCONFNAME)
534*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
535*7c478bd9Sstevel@tonic-gate 
536*7c478bd9Sstevel@tonic-gate 			if (copyin(alias.a_name, name, alias.a_len) != 0)
537*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
538*7c478bd9Sstevel@tonic-gate 
539*7c478bd9Sstevel@tonic-gate 			if (name[alias.a_len - 1] != '\0')
540*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
541*7c478bd9Sstevel@tonic-gate 		}
542*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
543*7c478bd9Sstevel@tonic-gate 		else {
544*7c478bd9Sstevel@tonic-gate 			struct aliases32 al32;
545*7c478bd9Sstevel@tonic-gate 
546*7c478bd9Sstevel@tonic-gate 			bzero(&al32, sizeof (struct aliases32));
547*7c478bd9Sstevel@tonic-gate 			if (copyin(ap, &al32, sizeof (struct aliases32)) != 0)
548*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
549*7c478bd9Sstevel@tonic-gate 
550*7c478bd9Sstevel@tonic-gate 			if (al32.a_len > MAXMODCONFNAME)
551*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
552*7c478bd9Sstevel@tonic-gate 
553*7c478bd9Sstevel@tonic-gate 			if (copyin((void *)(uintptr_t)al32.a_name,
554*7c478bd9Sstevel@tonic-gate 			    name, al32.a_len) != 0)
555*7c478bd9Sstevel@tonic-gate 				return (EFAULT);
556*7c478bd9Sstevel@tonic-gate 
557*7c478bd9Sstevel@tonic-gate 			if (name[al32.a_len - 1] != '\0')
558*7c478bd9Sstevel@tonic-gate 				return (EINVAL);
559*7c478bd9Sstevel@tonic-gate 
560*7c478bd9Sstevel@tonic-gate 			alias.a_next = (void *)(uintptr_t)al32.a_next;
561*7c478bd9Sstevel@tonic-gate 		}
562*7c478bd9Sstevel@tonic-gate #endif
563*7c478bd9Sstevel@tonic-gate 		check_esc_sequences(name, cname);
564*7c478bd9Sstevel@tonic-gate 		(void) make_mbind(cname, mc.major, NULL, mb_hashtab);
565*7c478bd9Sstevel@tonic-gate 		ap = alias.a_next;
566*7c478bd9Sstevel@tonic-gate 	}
567*7c478bd9Sstevel@tonic-gate 
568*7c478bd9Sstevel@tonic-gate 	/*
569*7c478bd9Sstevel@tonic-gate 	 * Try to establish an mbinding for mc.drvname, and add it to devnames.
570*7c478bd9Sstevel@tonic-gate 	 * Add class if any after establishing the major number
571*7c478bd9Sstevel@tonic-gate 	 */
572*7c478bd9Sstevel@tonic-gate 	(void) make_mbind(mc.drvname, mc.major, NULL, mb_hashtab);
573*7c478bd9Sstevel@tonic-gate 	rv = make_devname(mc.drvname, mc.major);
574*7c478bd9Sstevel@tonic-gate 
575*7c478bd9Sstevel@tonic-gate 	if (rv == 0) {
576*7c478bd9Sstevel@tonic-gate 		if (mc.drvclass[0] != '\0')
577*7c478bd9Sstevel@tonic-gate 			add_class(mc.drvname, mc.drvclass);
578*7c478bd9Sstevel@tonic-gate 		(void) i_ddi_load_drvconf(mc.major);
579*7c478bd9Sstevel@tonic-gate 		i_ddi_bind_devs();
580*7c478bd9Sstevel@tonic-gate 		i_ddi_di_cache_invalidate(KM_SLEEP);
581*7c478bd9Sstevel@tonic-gate 	}
582*7c478bd9Sstevel@tonic-gate 	return (rv);
583*7c478bd9Sstevel@tonic-gate }
584*7c478bd9Sstevel@tonic-gate 
585*7c478bd9Sstevel@tonic-gate static int
586*7c478bd9Sstevel@tonic-gate modctl_rem_major(major_t major)
587*7c478bd9Sstevel@tonic-gate {
588*7c478bd9Sstevel@tonic-gate 	struct devnames *dnp;
589*7c478bd9Sstevel@tonic-gate 
590*7c478bd9Sstevel@tonic-gate 	if (major >= devcnt)
591*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
592*7c478bd9Sstevel@tonic-gate 
593*7c478bd9Sstevel@tonic-gate 	/* mark devnames as removed */
594*7c478bd9Sstevel@tonic-gate 	dnp = &devnamesp[major];
595*7c478bd9Sstevel@tonic-gate 	LOCK_DEV_OPS(&dnp->dn_lock);
596*7c478bd9Sstevel@tonic-gate 	if (dnp->dn_name == NULL ||
597*7c478bd9Sstevel@tonic-gate 	    (dnp->dn_flags & (DN_DRIVER_REMOVED | DN_TAKEN_GETUDEV))) {
598*7c478bd9Sstevel@tonic-gate 		UNLOCK_DEV_OPS(&dnp->dn_lock);
599*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
600*7c478bd9Sstevel@tonic-gate 	}
601*7c478bd9Sstevel@tonic-gate 	dnp->dn_flags |= DN_DRIVER_REMOVED;
602*7c478bd9Sstevel@tonic-gate 	pm_driver_removed(major);
603*7c478bd9Sstevel@tonic-gate 	UNLOCK_DEV_OPS(&dnp->dn_lock);
604*7c478bd9Sstevel@tonic-gate 
605*7c478bd9Sstevel@tonic-gate 	(void) i_ddi_unload_drvconf(major);
606*7c478bd9Sstevel@tonic-gate 	i_ddi_unbind_devs(major);
607*7c478bd9Sstevel@tonic-gate 	i_ddi_di_cache_invalidate(KM_SLEEP);
608*7c478bd9Sstevel@tonic-gate 	return (0);
609*7c478bd9Sstevel@tonic-gate }
610*7c478bd9Sstevel@tonic-gate 
611*7c478bd9Sstevel@tonic-gate static struct vfs *
612*7c478bd9Sstevel@tonic-gate path_to_vfs(char *name)
613*7c478bd9Sstevel@tonic-gate {
614*7c478bd9Sstevel@tonic-gate 	vnode_t *vp;
615*7c478bd9Sstevel@tonic-gate 	struct vfs *vfsp;
616*7c478bd9Sstevel@tonic-gate 
617*7c478bd9Sstevel@tonic-gate 	if (lookupname(name, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp))
618*7c478bd9Sstevel@tonic-gate 		return (NULL);
619*7c478bd9Sstevel@tonic-gate 
620*7c478bd9Sstevel@tonic-gate 	vfsp = vp->v_vfsp;
621*7c478bd9Sstevel@tonic-gate 	VN_RELE(vp);
622*7c478bd9Sstevel@tonic-gate 	return (vfsp);
623*7c478bd9Sstevel@tonic-gate }
624*7c478bd9Sstevel@tonic-gate 
625*7c478bd9Sstevel@tonic-gate static int
626*7c478bd9Sstevel@tonic-gate new_vfs_in_modpath()
627*7c478bd9Sstevel@tonic-gate {
628*7c478bd9Sstevel@tonic-gate 	static int n_modpath = 0;
629*7c478bd9Sstevel@tonic-gate 	static char *modpath_copy;
630*7c478bd9Sstevel@tonic-gate 	static struct pathvfs {
631*7c478bd9Sstevel@tonic-gate 		char *path;
632*7c478bd9Sstevel@tonic-gate 		struct vfs *vfsp;
633*7c478bd9Sstevel@tonic-gate 	} *pathvfs;
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate 	int i, new_vfs = 0;
636*7c478bd9Sstevel@tonic-gate 	char *tmp, *tmp1;
637*7c478bd9Sstevel@tonic-gate 	struct vfs *vfsp;
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate 	if (n_modpath != 0) {
640*7c478bd9Sstevel@tonic-gate 		for (i = 0; i < n_modpath; i++) {
641*7c478bd9Sstevel@tonic-gate 			vfsp = path_to_vfs(pathvfs[i].path);
642*7c478bd9Sstevel@tonic-gate 			if (vfsp != pathvfs[i].vfsp) {
643*7c478bd9Sstevel@tonic-gate 				pathvfs[i].vfsp = vfsp;
644*7c478bd9Sstevel@tonic-gate 				if (vfsp)
645*7c478bd9Sstevel@tonic-gate 					new_vfs = 1;
646*7c478bd9Sstevel@tonic-gate 			}
647*7c478bd9Sstevel@tonic-gate 		}
648*7c478bd9Sstevel@tonic-gate 		return (new_vfs);
649*7c478bd9Sstevel@tonic-gate 	}
650*7c478bd9Sstevel@tonic-gate 
651*7c478bd9Sstevel@tonic-gate 	/*
652*7c478bd9Sstevel@tonic-gate 	 * First call, initialize the pathvfs structure
653*7c478bd9Sstevel@tonic-gate 	 */
654*7c478bd9Sstevel@tonic-gate 	modpath_copy = i_ddi_strdup(default_path, KM_SLEEP);
655*7c478bd9Sstevel@tonic-gate 	tmp = modpath_copy;
656*7c478bd9Sstevel@tonic-gate 	n_modpath = 1;
657*7c478bd9Sstevel@tonic-gate 	tmp1 = strchr(tmp, ' ');
658*7c478bd9Sstevel@tonic-gate 	while (tmp1) {
659*7c478bd9Sstevel@tonic-gate 		*tmp1 = '\0';
660*7c478bd9Sstevel@tonic-gate 		n_modpath++;
661*7c478bd9Sstevel@tonic-gate 		tmp = tmp1 + 1;
662*7c478bd9Sstevel@tonic-gate 		tmp1 = strchr(tmp, ' ');
663*7c478bd9Sstevel@tonic-gate 	}
664*7c478bd9Sstevel@tonic-gate 
665*7c478bd9Sstevel@tonic-gate 	pathvfs = kmem_zalloc(n_modpath * sizeof (struct pathvfs), KM_SLEEP);
666*7c478bd9Sstevel@tonic-gate 	tmp = modpath_copy;
667*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < n_modpath; i++) {
668*7c478bd9Sstevel@tonic-gate 		pathvfs[i].path = tmp;
669*7c478bd9Sstevel@tonic-gate 		vfsp = path_to_vfs(tmp);
670*7c478bd9Sstevel@tonic-gate 		pathvfs[i].vfsp = vfsp;
671*7c478bd9Sstevel@tonic-gate 		tmp += strlen(tmp) + 1;
672*7c478bd9Sstevel@tonic-gate 	}
673*7c478bd9Sstevel@tonic-gate 	return (1);	/* always reread driver.conf the first time */
674*7c478bd9Sstevel@tonic-gate }
675*7c478bd9Sstevel@tonic-gate 
676*7c478bd9Sstevel@tonic-gate static int modctl_load_drvconf(major_t major)
677*7c478bd9Sstevel@tonic-gate {
678*7c478bd9Sstevel@tonic-gate 	int ret;
679*7c478bd9Sstevel@tonic-gate 
680*7c478bd9Sstevel@tonic-gate 	if (major != (major_t)-1) {
681*7c478bd9Sstevel@tonic-gate 		ret = i_ddi_load_drvconf(major);
682*7c478bd9Sstevel@tonic-gate 		if (ret == 0)
683*7c478bd9Sstevel@tonic-gate 			i_ddi_bind_devs();
684*7c478bd9Sstevel@tonic-gate 		return (ret);
685*7c478bd9Sstevel@tonic-gate 	}
686*7c478bd9Sstevel@tonic-gate 
687*7c478bd9Sstevel@tonic-gate 	/*
688*7c478bd9Sstevel@tonic-gate 	 * We are invoked to rescan new driver.conf files. It is
689*7c478bd9Sstevel@tonic-gate 	 * only necessary if a new file system was mounted in the
690*7c478bd9Sstevel@tonic-gate 	 * module_path. Because rescanning driver.conf files can
691*7c478bd9Sstevel@tonic-gate 	 * take some time on older platforms (sun4m), the following
692*7c478bd9Sstevel@tonic-gate 	 * code skips unnecessary driver.conf rescans to optimize
693*7c478bd9Sstevel@tonic-gate 	 * boot performance.
694*7c478bd9Sstevel@tonic-gate 	 */
695*7c478bd9Sstevel@tonic-gate 	if (new_vfs_in_modpath()) {
696*7c478bd9Sstevel@tonic-gate 		(void) i_ddi_load_drvconf((major_t)-1);
697*7c478bd9Sstevel@tonic-gate 		/*
698*7c478bd9Sstevel@tonic-gate 		 * If we are still initializing io subsystem,
699*7c478bd9Sstevel@tonic-gate 		 * load drivers with ddi-forceattach property
700*7c478bd9Sstevel@tonic-gate 		 */
701*7c478bd9Sstevel@tonic-gate 		if (!i_ddi_io_initialized())
702*7c478bd9Sstevel@tonic-gate 			i_ddi_forceattach_drivers();
703*7c478bd9Sstevel@tonic-gate 	}
704*7c478bd9Sstevel@tonic-gate 	return (0);
705*7c478bd9Sstevel@tonic-gate }
706*7c478bd9Sstevel@tonic-gate 
707*7c478bd9Sstevel@tonic-gate static int
708*7c478bd9Sstevel@tonic-gate modctl_unload_drvconf(major_t major)
709*7c478bd9Sstevel@tonic-gate {
710*7c478bd9Sstevel@tonic-gate 	int ret;
711*7c478bd9Sstevel@tonic-gate 
712*7c478bd9Sstevel@tonic-gate 	if (major >= devcnt)
713*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
714*7c478bd9Sstevel@tonic-gate 
715*7c478bd9Sstevel@tonic-gate 	ret = i_ddi_unload_drvconf(major);
716*7c478bd9Sstevel@tonic-gate 	if (ret != 0)
717*7c478bd9Sstevel@tonic-gate 		return (ret);
718*7c478bd9Sstevel@tonic-gate 	(void) i_ddi_unbind_devs(major);
719*7c478bd9Sstevel@tonic-gate 
720*7c478bd9Sstevel@tonic-gate 	return (0);
721*7c478bd9Sstevel@tonic-gate }
722*7c478bd9Sstevel@tonic-gate 
723*7c478bd9Sstevel@tonic-gate static void
724*7c478bd9Sstevel@tonic-gate check_esc_sequences(char *str, char *cstr)
725*7c478bd9Sstevel@tonic-gate {
726*7c478bd9Sstevel@tonic-gate 	int i;
727*7c478bd9Sstevel@tonic-gate 	size_t len;
728*7c478bd9Sstevel@tonic-gate 	char *p;
729*7c478bd9Sstevel@tonic-gate 
730*7c478bd9Sstevel@tonic-gate 	len = strlen(str);
731*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < len; i++, str++, cstr++) {
732*7c478bd9Sstevel@tonic-gate 		if (*str != '\\') {
733*7c478bd9Sstevel@tonic-gate 			*cstr = *str;
734*7c478bd9Sstevel@tonic-gate 		} else {
735*7c478bd9Sstevel@tonic-gate 			p = str + 1;
736*7c478bd9Sstevel@tonic-gate 			/*
737*7c478bd9Sstevel@tonic-gate 			 * we only handle octal escape sequences for SPACE
738*7c478bd9Sstevel@tonic-gate 			 */
739*7c478bd9Sstevel@tonic-gate 			if (*p++ == '0' && *p++ == '4' && *p == '0') {
740*7c478bd9Sstevel@tonic-gate 				*cstr = ' ';
741*7c478bd9Sstevel@tonic-gate 				str += 3;
742*7c478bd9Sstevel@tonic-gate 			} else {
743*7c478bd9Sstevel@tonic-gate 				*cstr = *str;
744*7c478bd9Sstevel@tonic-gate 			}
745*7c478bd9Sstevel@tonic-gate 		}
746*7c478bd9Sstevel@tonic-gate 	}
747*7c478bd9Sstevel@tonic-gate 	*cstr = 0;
748*7c478bd9Sstevel@tonic-gate }
749*7c478bd9Sstevel@tonic-gate 
750*7c478bd9Sstevel@tonic-gate static int
751*7c478bd9Sstevel@tonic-gate modctl_getmodpathlen(int *data)
752*7c478bd9Sstevel@tonic-gate {
753*7c478bd9Sstevel@tonic-gate 	int len;
754*7c478bd9Sstevel@tonic-gate 	len = strlen(default_path);
755*7c478bd9Sstevel@tonic-gate 	if (copyout(&len, data, sizeof (len)) != 0)
756*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
757*7c478bd9Sstevel@tonic-gate 	return (0);
758*7c478bd9Sstevel@tonic-gate }
759*7c478bd9Sstevel@tonic-gate 
760*7c478bd9Sstevel@tonic-gate static int
761*7c478bd9Sstevel@tonic-gate modctl_getmodpath(char *data)
762*7c478bd9Sstevel@tonic-gate {
763*7c478bd9Sstevel@tonic-gate 	if (copyout(default_path, data, strlen(default_path) + 1) != 0)
764*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
765*7c478bd9Sstevel@tonic-gate 	return (0);
766*7c478bd9Sstevel@tonic-gate }
767*7c478bd9Sstevel@tonic-gate 
768*7c478bd9Sstevel@tonic-gate static int
769*7c478bd9Sstevel@tonic-gate modctl_read_sysbinding_file(void)
770*7c478bd9Sstevel@tonic-gate {
771*7c478bd9Sstevel@tonic-gate 	(void) read_binding_file(sysbind, sb_hashtab, make_mbind);
772*7c478bd9Sstevel@tonic-gate 	return (0);
773*7c478bd9Sstevel@tonic-gate }
774*7c478bd9Sstevel@tonic-gate 
775*7c478bd9Sstevel@tonic-gate static int
776*7c478bd9Sstevel@tonic-gate modctl_getmaj(char *uname, uint_t ulen, int *umajorp)
777*7c478bd9Sstevel@tonic-gate {
778*7c478bd9Sstevel@tonic-gate 	char name[256];
779*7c478bd9Sstevel@tonic-gate 	int retval;
780*7c478bd9Sstevel@tonic-gate 	major_t major;
781*7c478bd9Sstevel@tonic-gate 
782*7c478bd9Sstevel@tonic-gate 	if ((retval = copyinstr(uname, name,
783*7c478bd9Sstevel@tonic-gate 	    (ulen < 256) ? ulen : 256, 0)) != 0)
784*7c478bd9Sstevel@tonic-gate 		return (retval);
785*7c478bd9Sstevel@tonic-gate 	if ((major = mod_name_to_major(name)) == (major_t)-1)
786*7c478bd9Sstevel@tonic-gate 		return (ENODEV);
787*7c478bd9Sstevel@tonic-gate 	if (copyout(&major, umajorp, sizeof (major_t)) != 0)
788*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
789*7c478bd9Sstevel@tonic-gate 	return (0);
790*7c478bd9Sstevel@tonic-gate }
791*7c478bd9Sstevel@tonic-gate 
792*7c478bd9Sstevel@tonic-gate static int
793*7c478bd9Sstevel@tonic-gate modctl_getname(char *uname, uint_t ulen, int *umajorp)
794*7c478bd9Sstevel@tonic-gate {
795*7c478bd9Sstevel@tonic-gate 	char *name;
796*7c478bd9Sstevel@tonic-gate 	major_t major;
797*7c478bd9Sstevel@tonic-gate 
798*7c478bd9Sstevel@tonic-gate 	if (copyin(umajorp, &major, sizeof (major)) != 0)
799*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
800*7c478bd9Sstevel@tonic-gate 	if ((name = mod_major_to_name(major)) == NULL)
801*7c478bd9Sstevel@tonic-gate 		return (ENODEV);
802*7c478bd9Sstevel@tonic-gate 	if ((strlen(name) + 1) > ulen)
803*7c478bd9Sstevel@tonic-gate 		return (ENOSPC);
804*7c478bd9Sstevel@tonic-gate 	return (copyoutstr(name, uname, ulen, NULL));
805*7c478bd9Sstevel@tonic-gate }
806*7c478bd9Sstevel@tonic-gate 
807*7c478bd9Sstevel@tonic-gate static int
808*7c478bd9Sstevel@tonic-gate modctl_devt2instance(dev_t dev, int *uinstancep)
809*7c478bd9Sstevel@tonic-gate {
810*7c478bd9Sstevel@tonic-gate 	int	instance;
811*7c478bd9Sstevel@tonic-gate 
812*7c478bd9Sstevel@tonic-gate 	if ((instance = dev_to_instance(dev)) == -1)
813*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
814*7c478bd9Sstevel@tonic-gate 
815*7c478bd9Sstevel@tonic-gate 	return (copyout(&instance, uinstancep, sizeof (int)));
816*7c478bd9Sstevel@tonic-gate }
817*7c478bd9Sstevel@tonic-gate 
818*7c478bd9Sstevel@tonic-gate /*
819*7c478bd9Sstevel@tonic-gate  * Return the sizeof of the device id.
820*7c478bd9Sstevel@tonic-gate  */
821*7c478bd9Sstevel@tonic-gate static int
822*7c478bd9Sstevel@tonic-gate modctl_sizeof_devid(dev_t dev, uint_t *len)
823*7c478bd9Sstevel@tonic-gate {
824*7c478bd9Sstevel@tonic-gate 	uint_t		sz;
825*7c478bd9Sstevel@tonic-gate 	ddi_devid_t	devid;
826*7c478bd9Sstevel@tonic-gate 
827*7c478bd9Sstevel@tonic-gate 	/* get device id */
828*7c478bd9Sstevel@tonic-gate 	if (ddi_lyr_get_devid(dev, &devid) == DDI_FAILURE)
829*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
830*7c478bd9Sstevel@tonic-gate 
831*7c478bd9Sstevel@tonic-gate 	sz = ddi_devid_sizeof(devid);
832*7c478bd9Sstevel@tonic-gate 	ddi_devid_free(devid);
833*7c478bd9Sstevel@tonic-gate 
834*7c478bd9Sstevel@tonic-gate 	/* copyout device id size */
835*7c478bd9Sstevel@tonic-gate 	if (copyout(&sz, len, sizeof (sz)) != 0)
836*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
837*7c478bd9Sstevel@tonic-gate 
838*7c478bd9Sstevel@tonic-gate 	return (0);
839*7c478bd9Sstevel@tonic-gate }
840*7c478bd9Sstevel@tonic-gate 
841*7c478bd9Sstevel@tonic-gate /*
842*7c478bd9Sstevel@tonic-gate  * Return a copy of the device id.
843*7c478bd9Sstevel@tonic-gate  */
844*7c478bd9Sstevel@tonic-gate static int
845*7c478bd9Sstevel@tonic-gate modctl_get_devid(dev_t dev, uint_t len, ddi_devid_t udevid)
846*7c478bd9Sstevel@tonic-gate {
847*7c478bd9Sstevel@tonic-gate 	uint_t		sz;
848*7c478bd9Sstevel@tonic-gate 	ddi_devid_t	devid;
849*7c478bd9Sstevel@tonic-gate 	int		err = 0;
850*7c478bd9Sstevel@tonic-gate 
851*7c478bd9Sstevel@tonic-gate 	/* get device id */
852*7c478bd9Sstevel@tonic-gate 	if (ddi_lyr_get_devid(dev, &devid) == DDI_FAILURE)
853*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
854*7c478bd9Sstevel@tonic-gate 
855*7c478bd9Sstevel@tonic-gate 	sz = ddi_devid_sizeof(devid);
856*7c478bd9Sstevel@tonic-gate 
857*7c478bd9Sstevel@tonic-gate 	/* Error if device id is larger than space allocated */
858*7c478bd9Sstevel@tonic-gate 	if (sz > len) {
859*7c478bd9Sstevel@tonic-gate 		ddi_devid_free(devid);
860*7c478bd9Sstevel@tonic-gate 		return (ENOSPC);
861*7c478bd9Sstevel@tonic-gate 	}
862*7c478bd9Sstevel@tonic-gate 
863*7c478bd9Sstevel@tonic-gate 	/* copy out device id */
864*7c478bd9Sstevel@tonic-gate 	if (copyout(devid, udevid, sz) != 0)
865*7c478bd9Sstevel@tonic-gate 		err = EFAULT;
866*7c478bd9Sstevel@tonic-gate 	ddi_devid_free(devid);
867*7c478bd9Sstevel@tonic-gate 	return (err);
868*7c478bd9Sstevel@tonic-gate }
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate /*
871*7c478bd9Sstevel@tonic-gate  * return the /devices paths associated with the specified devid and
872*7c478bd9Sstevel@tonic-gate  * minor name.
873*7c478bd9Sstevel@tonic-gate  */
874*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
875*7c478bd9Sstevel@tonic-gate static int
876*7c478bd9Sstevel@tonic-gate modctl_devid2paths(ddi_devid_t udevid, char *uminor_name, uint_t flag,
877*7c478bd9Sstevel@tonic-gate 	size_t *ulensp, char *upaths)
878*7c478bd9Sstevel@tonic-gate {
879*7c478bd9Sstevel@tonic-gate 	ddi_devid_t	devid = NULL;
880*7c478bd9Sstevel@tonic-gate 	int		devid_len;
881*7c478bd9Sstevel@tonic-gate 	char		*minor_name = NULL;
882*7c478bd9Sstevel@tonic-gate 	dev_info_t	*dip = NULL;
883*7c478bd9Sstevel@tonic-gate 	struct ddi_minor_data   *dmdp;
884*7c478bd9Sstevel@tonic-gate 	char		*path = NULL;
885*7c478bd9Sstevel@tonic-gate 	int		ulens;
886*7c478bd9Sstevel@tonic-gate 	int		lens;
887*7c478bd9Sstevel@tonic-gate 	int		len;
888*7c478bd9Sstevel@tonic-gate 	dev_t		*devlist = NULL;
889*7c478bd9Sstevel@tonic-gate 	int		ndevs;
890*7c478bd9Sstevel@tonic-gate 	int		i;
891*7c478bd9Sstevel@tonic-gate 	int		ret = 0;
892*7c478bd9Sstevel@tonic-gate 
893*7c478bd9Sstevel@tonic-gate 	/*
894*7c478bd9Sstevel@tonic-gate 	 * If upaths is NULL then we are only computing the amount of space
895*7c478bd9Sstevel@tonic-gate 	 * needed to hold the paths and returning the value in *ulensp. If we
896*7c478bd9Sstevel@tonic-gate 	 * are copying out paths then we get the amount of space allocated by
897*7c478bd9Sstevel@tonic-gate 	 * the caller. If the actual space needed for paths is larger, or
898*7c478bd9Sstevel@tonic-gate 	 * things are changing out from under us, then we return EAGAIN.
899*7c478bd9Sstevel@tonic-gate 	 */
900*7c478bd9Sstevel@tonic-gate 	if (upaths) {
901*7c478bd9Sstevel@tonic-gate 		if (ulensp == NULL)
902*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
903*7c478bd9Sstevel@tonic-gate 		if (copyin(ulensp, &ulens, sizeof (ulens)) != 0)
904*7c478bd9Sstevel@tonic-gate 			return (EFAULT);
905*7c478bd9Sstevel@tonic-gate 	}
906*7c478bd9Sstevel@tonic-gate 
907*7c478bd9Sstevel@tonic-gate 	/*
908*7c478bd9Sstevel@tonic-gate 	 * copyin enough of the devid to determine the length then
909*7c478bd9Sstevel@tonic-gate 	 * reallocate and copy in the entire devid.
910*7c478bd9Sstevel@tonic-gate 	 */
911*7c478bd9Sstevel@tonic-gate 	devid_len = ddi_devid_sizeof(NULL);
912*7c478bd9Sstevel@tonic-gate 	devid = kmem_alloc(devid_len, KM_SLEEP);
913*7c478bd9Sstevel@tonic-gate 	if (copyin(udevid, devid, devid_len)) {
914*7c478bd9Sstevel@tonic-gate 		ret = EFAULT;
915*7c478bd9Sstevel@tonic-gate 		goto out;
916*7c478bd9Sstevel@tonic-gate 	}
917*7c478bd9Sstevel@tonic-gate 	len = devid_len;
918*7c478bd9Sstevel@tonic-gate 	devid_len = ddi_devid_sizeof(devid);
919*7c478bd9Sstevel@tonic-gate 	kmem_free(devid, len);
920*7c478bd9Sstevel@tonic-gate 	devid = kmem_alloc(devid_len, KM_SLEEP);
921*7c478bd9Sstevel@tonic-gate 	if (copyin(udevid, devid, devid_len)) {
922*7c478bd9Sstevel@tonic-gate 		ret = EFAULT;
923*7c478bd9Sstevel@tonic-gate 		goto out;
924*7c478bd9Sstevel@tonic-gate 	}
925*7c478bd9Sstevel@tonic-gate 
926*7c478bd9Sstevel@tonic-gate 	/* copyin the minor name if specified. */
927*7c478bd9Sstevel@tonic-gate 	minor_name = uminor_name;
928*7c478bd9Sstevel@tonic-gate 	if ((minor_name != DEVID_MINOR_NAME_ALL) &&
929*7c478bd9Sstevel@tonic-gate 	    (minor_name != DEVID_MINOR_NAME_ALL_CHR) &&
930*7c478bd9Sstevel@tonic-gate 	    (minor_name != DEVID_MINOR_NAME_ALL_BLK)) {
931*7c478bd9Sstevel@tonic-gate 		minor_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
932*7c478bd9Sstevel@tonic-gate 		if (copyinstr(uminor_name, minor_name, MAXPATHLEN, 0)) {
933*7c478bd9Sstevel@tonic-gate 			ret = EFAULT;
934*7c478bd9Sstevel@tonic-gate 			goto out;
935*7c478bd9Sstevel@tonic-gate 		}
936*7c478bd9Sstevel@tonic-gate 	}
937*7c478bd9Sstevel@tonic-gate 
938*7c478bd9Sstevel@tonic-gate 	/*
939*7c478bd9Sstevel@tonic-gate 	 * Use existing function to resolve the devid into a devlist.
940*7c478bd9Sstevel@tonic-gate 	 *
941*7c478bd9Sstevel@tonic-gate 	 * NOTE: there is a loss of spectype information in the current
942*7c478bd9Sstevel@tonic-gate 	 * ddi_lyr_devid_to_devlist implementation. We work around this by not
943*7c478bd9Sstevel@tonic-gate 	 * passing down DEVID_MINOR_NAME_ALL here, but reproducing all minor
944*7c478bd9Sstevel@tonic-gate 	 * node forms in the loop processing the devlist below. It would be
945*7c478bd9Sstevel@tonic-gate 	 * best if at some point the use of this interface here was replaced
946*7c478bd9Sstevel@tonic-gate 	 * with a path oriented call.
947*7c478bd9Sstevel@tonic-gate 	 */
948*7c478bd9Sstevel@tonic-gate 	if (ddi_lyr_devid_to_devlist(devid,
949*7c478bd9Sstevel@tonic-gate 	    (minor_name == DEVID_MINOR_NAME_ALL) ?
950*7c478bd9Sstevel@tonic-gate 	    DEVID_MINOR_NAME_ALL_CHR : minor_name,
951*7c478bd9Sstevel@tonic-gate 	    &ndevs, &devlist) != DDI_SUCCESS) {
952*7c478bd9Sstevel@tonic-gate 		ret = EINVAL;
953*7c478bd9Sstevel@tonic-gate 		goto out;
954*7c478bd9Sstevel@tonic-gate 	}
955*7c478bd9Sstevel@tonic-gate 
956*7c478bd9Sstevel@tonic-gate 	/*
957*7c478bd9Sstevel@tonic-gate 	 * loop over the devlist, converting each devt to a path and doing
958*7c478bd9Sstevel@tonic-gate 	 * a copyout of the path and computation of the amount of space
959*7c478bd9Sstevel@tonic-gate 	 * needed to hold all the paths
960*7c478bd9Sstevel@tonic-gate 	 */
961*7c478bd9Sstevel@tonic-gate 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
962*7c478bd9Sstevel@tonic-gate 	for (i = 0, lens = 0; i < ndevs; i++) {
963*7c478bd9Sstevel@tonic-gate 
964*7c478bd9Sstevel@tonic-gate 		/* find the dip associated with the dev_t */
965*7c478bd9Sstevel@tonic-gate 		if ((dip = e_ddi_hold_devi_by_dev(devlist[i], 0)) == NULL)
966*7c478bd9Sstevel@tonic-gate 			continue;
967*7c478bd9Sstevel@tonic-gate 
968*7c478bd9Sstevel@tonic-gate 		/* loop over all the minor nodes, skipping ones we don't want */
969*7c478bd9Sstevel@tonic-gate 		for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) {
970*7c478bd9Sstevel@tonic-gate 			if ((dmdp->ddm_dev != devlist[i]) ||
971*7c478bd9Sstevel@tonic-gate 			    (dmdp->type != DDM_MINOR))
972*7c478bd9Sstevel@tonic-gate 				continue;
973*7c478bd9Sstevel@tonic-gate 
974*7c478bd9Sstevel@tonic-gate 			if ((minor_name != DEVID_MINOR_NAME_ALL) &&
975*7c478bd9Sstevel@tonic-gate 			    (minor_name != DEVID_MINOR_NAME_ALL_CHR) &&
976*7c478bd9Sstevel@tonic-gate 			    (minor_name != DEVID_MINOR_NAME_ALL_BLK) &&
977*7c478bd9Sstevel@tonic-gate 			    strcmp(minor_name, dmdp->ddm_name))
978*7c478bd9Sstevel@tonic-gate 				continue;
979*7c478bd9Sstevel@tonic-gate 			else {
980*7c478bd9Sstevel@tonic-gate 				if ((minor_name == DEVID_MINOR_NAME_ALL_CHR) &&
981*7c478bd9Sstevel@tonic-gate 				    (dmdp->ddm_spec_type != S_IFCHR))
982*7c478bd9Sstevel@tonic-gate 					continue;
983*7c478bd9Sstevel@tonic-gate 				if ((minor_name == DEVID_MINOR_NAME_ALL_BLK) &&
984*7c478bd9Sstevel@tonic-gate 				    (dmdp->ddm_spec_type != S_IFBLK))
985*7c478bd9Sstevel@tonic-gate 					continue;
986*7c478bd9Sstevel@tonic-gate 			}
987*7c478bd9Sstevel@tonic-gate 
988*7c478bd9Sstevel@tonic-gate 			/* XXX need ddi_pathname_minor(dmdp, path); interface */
989*7c478bd9Sstevel@tonic-gate 			if (ddi_dev_pathname(dmdp->ddm_dev, dmdp->ddm_spec_type,
990*7c478bd9Sstevel@tonic-gate 			    path) != DDI_SUCCESS) {
991*7c478bd9Sstevel@tonic-gate 				ret = EAGAIN;
992*7c478bd9Sstevel@tonic-gate 				goto out;
993*7c478bd9Sstevel@tonic-gate 			}
994*7c478bd9Sstevel@tonic-gate 			len = strlen(path) + 1;
995*7c478bd9Sstevel@tonic-gate 			*(path + len) = '\0';	/* set double termination */
996*7c478bd9Sstevel@tonic-gate 			lens += len;
997*7c478bd9Sstevel@tonic-gate 
998*7c478bd9Sstevel@tonic-gate 			/* copyout the path with double terminations */
999*7c478bd9Sstevel@tonic-gate 			if (upaths) {
1000*7c478bd9Sstevel@tonic-gate 				if (lens > ulens) {
1001*7c478bd9Sstevel@tonic-gate 					ret = EAGAIN;
1002*7c478bd9Sstevel@tonic-gate 					goto out;
1003*7c478bd9Sstevel@tonic-gate 				}
1004*7c478bd9Sstevel@tonic-gate 				if (copyout(path, upaths, len + 1)) {
1005*7c478bd9Sstevel@tonic-gate 					ret = EFAULT;
1006*7c478bd9Sstevel@tonic-gate 					goto out;
1007*7c478bd9Sstevel@tonic-gate 				}
1008*7c478bd9Sstevel@tonic-gate 				upaths += len;
1009*7c478bd9Sstevel@tonic-gate 			}
1010*7c478bd9Sstevel@tonic-gate 		}
1011*7c478bd9Sstevel@tonic-gate 		ddi_release_devi(dip);
1012*7c478bd9Sstevel@tonic-gate 		dip = NULL;
1013*7c478bd9Sstevel@tonic-gate 	}
1014*7c478bd9Sstevel@tonic-gate 	lens++;		/* add one for double termination */
1015*7c478bd9Sstevel@tonic-gate 
1016*7c478bd9Sstevel@tonic-gate 	/* copy out the amount of space needed to hold the paths */
1017*7c478bd9Sstevel@tonic-gate 	if (ulensp && copyout(&lens, ulensp, sizeof (lens))) {
1018*7c478bd9Sstevel@tonic-gate 		ret = EFAULT;
1019*7c478bd9Sstevel@tonic-gate 		goto out;
1020*7c478bd9Sstevel@tonic-gate 	}
1021*7c478bd9Sstevel@tonic-gate 	ret = 0;
1022*7c478bd9Sstevel@tonic-gate 
1023*7c478bd9Sstevel@tonic-gate out:	if (dip)
1024*7c478bd9Sstevel@tonic-gate 		ddi_release_devi(dip);
1025*7c478bd9Sstevel@tonic-gate 	if (path)
1026*7c478bd9Sstevel@tonic-gate 		kmem_free(path, MAXPATHLEN);
1027*7c478bd9Sstevel@tonic-gate 	if (devlist)
1028*7c478bd9Sstevel@tonic-gate 		ddi_lyr_free_devlist(devlist, ndevs);
1029*7c478bd9Sstevel@tonic-gate 	if (minor_name &&
1030*7c478bd9Sstevel@tonic-gate 	    (minor_name != DEVID_MINOR_NAME_ALL) &&
1031*7c478bd9Sstevel@tonic-gate 	    (minor_name != DEVID_MINOR_NAME_ALL_CHR) &&
1032*7c478bd9Sstevel@tonic-gate 	    (minor_name != DEVID_MINOR_NAME_ALL_BLK))
1033*7c478bd9Sstevel@tonic-gate 		kmem_free(minor_name, MAXPATHLEN);
1034*7c478bd9Sstevel@tonic-gate 	if (devid)
1035*7c478bd9Sstevel@tonic-gate 		kmem_free(devid, devid_len);
1036*7c478bd9Sstevel@tonic-gate 	return (ret);
1037*7c478bd9Sstevel@tonic-gate }
1038*7c478bd9Sstevel@tonic-gate 
1039*7c478bd9Sstevel@tonic-gate /*
1040*7c478bd9Sstevel@tonic-gate  * Return the size of the minor name.
1041*7c478bd9Sstevel@tonic-gate  */
1042*7c478bd9Sstevel@tonic-gate static int
1043*7c478bd9Sstevel@tonic-gate modctl_sizeof_minorname(dev_t dev, int spectype, uint_t *len)
1044*7c478bd9Sstevel@tonic-gate {
1045*7c478bd9Sstevel@tonic-gate 	uint_t	sz;
1046*7c478bd9Sstevel@tonic-gate 	char	*name;
1047*7c478bd9Sstevel@tonic-gate 
1048*7c478bd9Sstevel@tonic-gate 	/* get the minor name */
1049*7c478bd9Sstevel@tonic-gate 	if (ddi_lyr_get_minor_name(dev, spectype, &name) == DDI_FAILURE)
1050*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1051*7c478bd9Sstevel@tonic-gate 
1052*7c478bd9Sstevel@tonic-gate 	sz = strlen(name) + 1;
1053*7c478bd9Sstevel@tonic-gate 	kmem_free(name, sz);
1054*7c478bd9Sstevel@tonic-gate 
1055*7c478bd9Sstevel@tonic-gate 	/* copy out the size of the minor name */
1056*7c478bd9Sstevel@tonic-gate 	if (copyout(&sz, len, sizeof (sz)) != 0)
1057*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
1058*7c478bd9Sstevel@tonic-gate 
1059*7c478bd9Sstevel@tonic-gate 	return (0);
1060*7c478bd9Sstevel@tonic-gate }
1061*7c478bd9Sstevel@tonic-gate 
1062*7c478bd9Sstevel@tonic-gate /*
1063*7c478bd9Sstevel@tonic-gate  * Return the minor name.
1064*7c478bd9Sstevel@tonic-gate  */
1065*7c478bd9Sstevel@tonic-gate static int
1066*7c478bd9Sstevel@tonic-gate modctl_get_minorname(dev_t dev, int spectype, uint_t len, char *uname)
1067*7c478bd9Sstevel@tonic-gate {
1068*7c478bd9Sstevel@tonic-gate 	uint_t	sz;
1069*7c478bd9Sstevel@tonic-gate 	char	*name;
1070*7c478bd9Sstevel@tonic-gate 	int	err = 0;
1071*7c478bd9Sstevel@tonic-gate 
1072*7c478bd9Sstevel@tonic-gate 	/* get the minor name */
1073*7c478bd9Sstevel@tonic-gate 	if (ddi_lyr_get_minor_name(dev, spectype, &name) == DDI_FAILURE)
1074*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1075*7c478bd9Sstevel@tonic-gate 
1076*7c478bd9Sstevel@tonic-gate 	sz = strlen(name) + 1;
1077*7c478bd9Sstevel@tonic-gate 
1078*7c478bd9Sstevel@tonic-gate 	/* Error if the minor name is larger than the space allocated */
1079*7c478bd9Sstevel@tonic-gate 	if (sz > len) {
1080*7c478bd9Sstevel@tonic-gate 		kmem_free(name, sz);
1081*7c478bd9Sstevel@tonic-gate 		return (ENOSPC);
1082*7c478bd9Sstevel@tonic-gate 	}
1083*7c478bd9Sstevel@tonic-gate 
1084*7c478bd9Sstevel@tonic-gate 	/* copy out the minor name */
1085*7c478bd9Sstevel@tonic-gate 	if (copyout(name, uname, sz) != 0)
1086*7c478bd9Sstevel@tonic-gate 		err = EFAULT;
1087*7c478bd9Sstevel@tonic-gate 	kmem_free(name, sz);
1088*7c478bd9Sstevel@tonic-gate 	return (err);
1089*7c478bd9Sstevel@tonic-gate }
1090*7c478bd9Sstevel@tonic-gate 
1091*7c478bd9Sstevel@tonic-gate /*
1092*7c478bd9Sstevel@tonic-gate  * Return the size of the devfspath name.
1093*7c478bd9Sstevel@tonic-gate  */
1094*7c478bd9Sstevel@tonic-gate static int
1095*7c478bd9Sstevel@tonic-gate modctl_devfspath_len(dev_t dev, int spectype, uint_t *len)
1096*7c478bd9Sstevel@tonic-gate {
1097*7c478bd9Sstevel@tonic-gate 	uint_t	sz;
1098*7c478bd9Sstevel@tonic-gate 	char	*name;
1099*7c478bd9Sstevel@tonic-gate 
1100*7c478bd9Sstevel@tonic-gate 	/* get the path name */
1101*7c478bd9Sstevel@tonic-gate 	name = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1102*7c478bd9Sstevel@tonic-gate 	if (ddi_dev_pathname(dev, spectype, name) == DDI_FAILURE) {
1103*7c478bd9Sstevel@tonic-gate 		kmem_free(name, MAXPATHLEN);
1104*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1105*7c478bd9Sstevel@tonic-gate 	}
1106*7c478bd9Sstevel@tonic-gate 
1107*7c478bd9Sstevel@tonic-gate 	sz = strlen(name) + 1;
1108*7c478bd9Sstevel@tonic-gate 	kmem_free(name, MAXPATHLEN);
1109*7c478bd9Sstevel@tonic-gate 
1110*7c478bd9Sstevel@tonic-gate 	/* copy out the size of the path name */
1111*7c478bd9Sstevel@tonic-gate 	if (copyout(&sz, len, sizeof (sz)) != 0)
1112*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
1113*7c478bd9Sstevel@tonic-gate 
1114*7c478bd9Sstevel@tonic-gate 	return (0);
1115*7c478bd9Sstevel@tonic-gate }
1116*7c478bd9Sstevel@tonic-gate 
1117*7c478bd9Sstevel@tonic-gate /*
1118*7c478bd9Sstevel@tonic-gate  * Return the devfspath name.
1119*7c478bd9Sstevel@tonic-gate  */
1120*7c478bd9Sstevel@tonic-gate static int
1121*7c478bd9Sstevel@tonic-gate modctl_devfspath(dev_t dev, int spectype, uint_t len, char *uname)
1122*7c478bd9Sstevel@tonic-gate {
1123*7c478bd9Sstevel@tonic-gate 	uint_t	sz;
1124*7c478bd9Sstevel@tonic-gate 	char	*name;
1125*7c478bd9Sstevel@tonic-gate 	int	err = 0;
1126*7c478bd9Sstevel@tonic-gate 
1127*7c478bd9Sstevel@tonic-gate 	/* get the path name */
1128*7c478bd9Sstevel@tonic-gate 	name = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1129*7c478bd9Sstevel@tonic-gate 	if (ddi_dev_pathname(dev, spectype, name) == DDI_FAILURE) {
1130*7c478bd9Sstevel@tonic-gate 		kmem_free(name, MAXPATHLEN);
1131*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
1132*7c478bd9Sstevel@tonic-gate 	}
1133*7c478bd9Sstevel@tonic-gate 
1134*7c478bd9Sstevel@tonic-gate 	sz = strlen(name) + 1;
1135*7c478bd9Sstevel@tonic-gate 
1136*7c478bd9Sstevel@tonic-gate 	/* Error if the path name is larger than the space allocated */
1137*7c478bd9Sstevel@tonic-gate 	if (sz > len) {
1138*7c478bd9Sstevel@tonic-gate 		kmem_free(name, MAXPATHLEN);
1139*7c478bd9Sstevel@tonic-gate 		return (ENOSPC);
1140*7c478bd9Sstevel@tonic-gate 	}
1141*7c478bd9Sstevel@tonic-gate 
1142*7c478bd9Sstevel@tonic-gate 	/* copy out the path name */
1143*7c478bd9Sstevel@tonic-gate 	if (copyout(name, uname, sz) != 0)
1144*7c478bd9Sstevel@tonic-gate 		err = EFAULT;
1145*7c478bd9Sstevel@tonic-gate 	kmem_free(name, MAXPATHLEN);
1146*7c478bd9Sstevel@tonic-gate 	return (err);
1147*7c478bd9Sstevel@tonic-gate }
1148*7c478bd9Sstevel@tonic-gate 
1149*7c478bd9Sstevel@tonic-gate static int
1150*7c478bd9Sstevel@tonic-gate modctl_get_fbname(char *path)
1151*7c478bd9Sstevel@tonic-gate {
1152*7c478bd9Sstevel@tonic-gate 	extern dev_t fbdev;
1153*7c478bd9Sstevel@tonic-gate 	char *pathname = NULL;
1154*7c478bd9Sstevel@tonic-gate 	int rval = 0;
1155*7c478bd9Sstevel@tonic-gate 
1156*7c478bd9Sstevel@tonic-gate 	/* make sure fbdev is set before we plunge in */
1157*7c478bd9Sstevel@tonic-gate 	if (fbdev == NODEV)
1158*7c478bd9Sstevel@tonic-gate 		return (ENODEV);
1159*7c478bd9Sstevel@tonic-gate 
1160*7c478bd9Sstevel@tonic-gate 	pathname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1161*7c478bd9Sstevel@tonic-gate 	if ((rval = ddi_dev_pathname(fbdev, S_IFCHR,
1162*7c478bd9Sstevel@tonic-gate 	    pathname)) == DDI_SUCCESS) {
1163*7c478bd9Sstevel@tonic-gate 		if (copyout(pathname, path, strlen(pathname)+1) != 0) {
1164*7c478bd9Sstevel@tonic-gate 			rval = EFAULT;
1165*7c478bd9Sstevel@tonic-gate 		}
1166*7c478bd9Sstevel@tonic-gate 	}
1167*7c478bd9Sstevel@tonic-gate 	kmem_free(pathname, MAXPATHLEN);
1168*7c478bd9Sstevel@tonic-gate 	return (rval);
1169*7c478bd9Sstevel@tonic-gate }
1170*7c478bd9Sstevel@tonic-gate 
1171*7c478bd9Sstevel@tonic-gate /*
1172*7c478bd9Sstevel@tonic-gate  * modctl_reread_dacf()
1173*7c478bd9Sstevel@tonic-gate  *	Reread the dacf rules database from the named binding file.
1174*7c478bd9Sstevel@tonic-gate  *	If NULL is specified, pass along the NULL, it means 'use the default'.
1175*7c478bd9Sstevel@tonic-gate  */
1176*7c478bd9Sstevel@tonic-gate static int
1177*7c478bd9Sstevel@tonic-gate modctl_reread_dacf(char *path)
1178*7c478bd9Sstevel@tonic-gate {
1179*7c478bd9Sstevel@tonic-gate 	int rval = 0;
1180*7c478bd9Sstevel@tonic-gate 	char *filename, *filenamep;
1181*7c478bd9Sstevel@tonic-gate 
1182*7c478bd9Sstevel@tonic-gate 	filename = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1183*7c478bd9Sstevel@tonic-gate 
1184*7c478bd9Sstevel@tonic-gate 	if (path == NULL) {
1185*7c478bd9Sstevel@tonic-gate 		filenamep = NULL;
1186*7c478bd9Sstevel@tonic-gate 	} else {
1187*7c478bd9Sstevel@tonic-gate 		if (copyinstr(path, filename, MAXPATHLEN, 0) != 0) {
1188*7c478bd9Sstevel@tonic-gate 			rval = EFAULT;
1189*7c478bd9Sstevel@tonic-gate 			goto out;
1190*7c478bd9Sstevel@tonic-gate 		}
1191*7c478bd9Sstevel@tonic-gate 		filenamep = filename;
1192*7c478bd9Sstevel@tonic-gate 		filenamep[MAXPATHLEN - 1] = '\0';
1193*7c478bd9Sstevel@tonic-gate 	}
1194*7c478bd9Sstevel@tonic-gate 
1195*7c478bd9Sstevel@tonic-gate 	rval = read_dacf_binding_file(filenamep);
1196*7c478bd9Sstevel@tonic-gate out:
1197*7c478bd9Sstevel@tonic-gate 	kmem_free(filename, MAXPATHLEN);
1198*7c478bd9Sstevel@tonic-gate 	return (rval);
1199*7c478bd9Sstevel@tonic-gate }
1200*7c478bd9Sstevel@tonic-gate 
1201*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1202*7c478bd9Sstevel@tonic-gate static int
1203*7c478bd9Sstevel@tonic-gate modctl_modevents(int subcmd, uintptr_t a2, uintptr_t a3, uintptr_t a4,
1204*7c478bd9Sstevel@tonic-gate     uint_t flag)
1205*7c478bd9Sstevel@tonic-gate {
1206*7c478bd9Sstevel@tonic-gate 	int error = 0;
1207*7c478bd9Sstevel@tonic-gate 	char *filenamep;
1208*7c478bd9Sstevel@tonic-gate 
1209*7c478bd9Sstevel@tonic-gate 	switch (subcmd) {
1210*7c478bd9Sstevel@tonic-gate 
1211*7c478bd9Sstevel@tonic-gate 	case MODEVENTS_FLUSH:
1212*7c478bd9Sstevel@tonic-gate 		/* flush all currently queued events */
1213*7c478bd9Sstevel@tonic-gate 		log_sysevent_flushq(subcmd, flag);
1214*7c478bd9Sstevel@tonic-gate 		break;
1215*7c478bd9Sstevel@tonic-gate 
1216*7c478bd9Sstevel@tonic-gate 	case MODEVENTS_SET_DOOR_UPCALL_FILENAME:
1217*7c478bd9Sstevel@tonic-gate 		/*
1218*7c478bd9Sstevel@tonic-gate 		 * bind door_upcall to filename
1219*7c478bd9Sstevel@tonic-gate 		 * this should only be done once per invocation
1220*7c478bd9Sstevel@tonic-gate 		 * of the event daemon.
1221*7c478bd9Sstevel@tonic-gate 		 */
1222*7c478bd9Sstevel@tonic-gate 
1223*7c478bd9Sstevel@tonic-gate 		filenamep = kmem_zalloc(MOD_MAXPATH, KM_SLEEP);
1224*7c478bd9Sstevel@tonic-gate 
1225*7c478bd9Sstevel@tonic-gate 		if (copyinstr((char *)a2, filenamep, MOD_MAXPATH, 0)) {
1226*7c478bd9Sstevel@tonic-gate 			error = EFAULT;
1227*7c478bd9Sstevel@tonic-gate 		} else {
1228*7c478bd9Sstevel@tonic-gate 			error = log_sysevent_filename(filenamep);
1229*7c478bd9Sstevel@tonic-gate 		}
1230*7c478bd9Sstevel@tonic-gate 		kmem_free(filenamep, MOD_MAXPATH);
1231*7c478bd9Sstevel@tonic-gate 		break;
1232*7c478bd9Sstevel@tonic-gate 
1233*7c478bd9Sstevel@tonic-gate 	case MODEVENTS_GETDATA:
1234*7c478bd9Sstevel@tonic-gate 		error = log_sysevent_copyout_data((sysevent_id_t *)a2,
1235*7c478bd9Sstevel@tonic-gate 		    (size_t)a3, (caddr_t)a4);
1236*7c478bd9Sstevel@tonic-gate 		break;
1237*7c478bd9Sstevel@tonic-gate 
1238*7c478bd9Sstevel@tonic-gate 	case MODEVENTS_FREEDATA:
1239*7c478bd9Sstevel@tonic-gate 		error = log_sysevent_free_data((sysevent_id_t *)a2);
1240*7c478bd9Sstevel@tonic-gate 		break;
1241*7c478bd9Sstevel@tonic-gate 	case MODEVENTS_POST_EVENT:
1242*7c478bd9Sstevel@tonic-gate 		error = log_usr_sysevent((sysevent_t *)a2, (uint32_t)a3,
1243*7c478bd9Sstevel@tonic-gate 			(sysevent_id_t *)a4);
1244*7c478bd9Sstevel@tonic-gate 		break;
1245*7c478bd9Sstevel@tonic-gate 	case MODEVENTS_REGISTER_EVENT:
1246*7c478bd9Sstevel@tonic-gate 		error = log_sysevent_register((char *)a2, (char *)a3,
1247*7c478bd9Sstevel@tonic-gate 		    (se_pubsub_t *)a4);
1248*7c478bd9Sstevel@tonic-gate 		break;
1249*7c478bd9Sstevel@tonic-gate 	default:
1250*7c478bd9Sstevel@tonic-gate 		error = EINVAL;
1251*7c478bd9Sstevel@tonic-gate 	}
1252*7c478bd9Sstevel@tonic-gate 
1253*7c478bd9Sstevel@tonic-gate 	return (error);
1254*7c478bd9Sstevel@tonic-gate }
1255*7c478bd9Sstevel@tonic-gate 
1256*7c478bd9Sstevel@tonic-gate static void
1257*7c478bd9Sstevel@tonic-gate free_mperm(mperm_t *mp)
1258*7c478bd9Sstevel@tonic-gate {
1259*7c478bd9Sstevel@tonic-gate 	int len;
1260*7c478bd9Sstevel@tonic-gate 
1261*7c478bd9Sstevel@tonic-gate 	if (mp->mp_minorname) {
1262*7c478bd9Sstevel@tonic-gate 		len = strlen(mp->mp_minorname) + 1;
1263*7c478bd9Sstevel@tonic-gate 		kmem_free(mp->mp_minorname, len);
1264*7c478bd9Sstevel@tonic-gate 	}
1265*7c478bd9Sstevel@tonic-gate 	kmem_free(mp, sizeof (mperm_t));
1266*7c478bd9Sstevel@tonic-gate }
1267*7c478bd9Sstevel@tonic-gate 
1268*7c478bd9Sstevel@tonic-gate #define	MP_NO_DRV_ERR	\
1269*7c478bd9Sstevel@tonic-gate 	"/etc/minor_perm: no driver for %s\n"
1270*7c478bd9Sstevel@tonic-gate 
1271*7c478bd9Sstevel@tonic-gate #define	MP_EMPTY_MINOR	\
1272*7c478bd9Sstevel@tonic-gate 	"/etc/minor_perm: empty minor name for driver %s\n"
1273*7c478bd9Sstevel@tonic-gate 
1274*7c478bd9Sstevel@tonic-gate #define	MP_NO_MINOR	\
1275*7c478bd9Sstevel@tonic-gate 	"/etc/minor_perm: no minor matching %s for driver %s\n"
1276*7c478bd9Sstevel@tonic-gate 
1277*7c478bd9Sstevel@tonic-gate /*
1278*7c478bd9Sstevel@tonic-gate  * Remove mperm entry with matching minorname
1279*7c478bd9Sstevel@tonic-gate  */
1280*7c478bd9Sstevel@tonic-gate static void
1281*7c478bd9Sstevel@tonic-gate rem_minorperm(major_t major, char *drvname, mperm_t *mp, int is_clone)
1282*7c478bd9Sstevel@tonic-gate {
1283*7c478bd9Sstevel@tonic-gate 	mperm_t **mp_head;
1284*7c478bd9Sstevel@tonic-gate 	mperm_t *freemp = NULL;
1285*7c478bd9Sstevel@tonic-gate 	struct devnames *dnp = &devnamesp[major];
1286*7c478bd9Sstevel@tonic-gate 	mperm_t **wildmp;
1287*7c478bd9Sstevel@tonic-gate 
1288*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mp_minorname && strlen(mp->mp_minorname) > 0);
1289*7c478bd9Sstevel@tonic-gate 
1290*7c478bd9Sstevel@tonic-gate 	LOCK_DEV_OPS(&dnp->dn_lock);
1291*7c478bd9Sstevel@tonic-gate 	if (strcmp(mp->mp_minorname, "*") == 0) {
1292*7c478bd9Sstevel@tonic-gate 		wildmp = ((is_clone == 0) ?
1293*7c478bd9Sstevel@tonic-gate 			&dnp->dn_mperm_wild : &dnp->dn_mperm_clone);
1294*7c478bd9Sstevel@tonic-gate 		if (*wildmp)
1295*7c478bd9Sstevel@tonic-gate 			freemp = *wildmp;
1296*7c478bd9Sstevel@tonic-gate 		*wildmp = NULL;
1297*7c478bd9Sstevel@tonic-gate 	} else {
1298*7c478bd9Sstevel@tonic-gate 		mp_head = &dnp->dn_mperm;
1299*7c478bd9Sstevel@tonic-gate 		while (*mp_head) {
1300*7c478bd9Sstevel@tonic-gate 			if (strcmp((*mp_head)->mp_minorname,
1301*7c478bd9Sstevel@tonic-gate 			    mp->mp_minorname) != 0) {
1302*7c478bd9Sstevel@tonic-gate 				mp_head = &(*mp_head)->mp_next;
1303*7c478bd9Sstevel@tonic-gate 				continue;
1304*7c478bd9Sstevel@tonic-gate 			}
1305*7c478bd9Sstevel@tonic-gate 			/* remove the entry */
1306*7c478bd9Sstevel@tonic-gate 			freemp = *mp_head;
1307*7c478bd9Sstevel@tonic-gate 			*mp_head = freemp->mp_next;
1308*7c478bd9Sstevel@tonic-gate 			break;
1309*7c478bd9Sstevel@tonic-gate 		}
1310*7c478bd9Sstevel@tonic-gate 	}
1311*7c478bd9Sstevel@tonic-gate 	if (freemp) {
1312*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_MINORPERM) {
1313*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT, "< %s %s 0%o %d %d\n",
1314*7c478bd9Sstevel@tonic-gate 			    drvname, freemp->mp_minorname,
1315*7c478bd9Sstevel@tonic-gate 			    freemp->mp_mode & 0777,
1316*7c478bd9Sstevel@tonic-gate 			    freemp->mp_uid, freemp->mp_gid);
1317*7c478bd9Sstevel@tonic-gate 		}
1318*7c478bd9Sstevel@tonic-gate 		free_mperm(freemp);
1319*7c478bd9Sstevel@tonic-gate 	} else {
1320*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_MINORPERM) {
1321*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT, MP_NO_MINOR,
1322*7c478bd9Sstevel@tonic-gate 				drvname, mp->mp_minorname);
1323*7c478bd9Sstevel@tonic-gate 		}
1324*7c478bd9Sstevel@tonic-gate 	}
1325*7c478bd9Sstevel@tonic-gate 
1326*7c478bd9Sstevel@tonic-gate 	UNLOCK_DEV_OPS(&dnp->dn_lock);
1327*7c478bd9Sstevel@tonic-gate }
1328*7c478bd9Sstevel@tonic-gate 
1329*7c478bd9Sstevel@tonic-gate /*
1330*7c478bd9Sstevel@tonic-gate  * Add minor perm entry
1331*7c478bd9Sstevel@tonic-gate  */
1332*7c478bd9Sstevel@tonic-gate static void
1333*7c478bd9Sstevel@tonic-gate add_minorperm(major_t major, char *drvname, mperm_t *mp, int is_clone)
1334*7c478bd9Sstevel@tonic-gate {
1335*7c478bd9Sstevel@tonic-gate 	mperm_t **mp_head;
1336*7c478bd9Sstevel@tonic-gate 	mperm_t *freemp = NULL;
1337*7c478bd9Sstevel@tonic-gate 	struct devnames *dnp = &devnamesp[major];
1338*7c478bd9Sstevel@tonic-gate 	mperm_t **wildmp;
1339*7c478bd9Sstevel@tonic-gate 
1340*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mp_minorname && strlen(mp->mp_minorname) > 0);
1341*7c478bd9Sstevel@tonic-gate 
1342*7c478bd9Sstevel@tonic-gate 	/*
1343*7c478bd9Sstevel@tonic-gate 	 * Note that update_drv replace semantics require
1344*7c478bd9Sstevel@tonic-gate 	 * replacing matching entries with the new permissions.
1345*7c478bd9Sstevel@tonic-gate 	 */
1346*7c478bd9Sstevel@tonic-gate 	LOCK_DEV_OPS(&dnp->dn_lock);
1347*7c478bd9Sstevel@tonic-gate 	if (strcmp(mp->mp_minorname, "*") == 0) {
1348*7c478bd9Sstevel@tonic-gate 		wildmp = ((is_clone == 0) ?
1349*7c478bd9Sstevel@tonic-gate 			&dnp->dn_mperm_wild : &dnp->dn_mperm_clone);
1350*7c478bd9Sstevel@tonic-gate 		if (*wildmp)
1351*7c478bd9Sstevel@tonic-gate 			freemp = *wildmp;
1352*7c478bd9Sstevel@tonic-gate 		*wildmp = mp;
1353*7c478bd9Sstevel@tonic-gate 	} else {
1354*7c478bd9Sstevel@tonic-gate 		mperm_t *p, *v = NULL;
1355*7c478bd9Sstevel@tonic-gate 		for (p = dnp->dn_mperm; p; v = p, p = p->mp_next) {
1356*7c478bd9Sstevel@tonic-gate 			if (strcmp(p->mp_minorname, mp->mp_minorname) == 0) {
1357*7c478bd9Sstevel@tonic-gate 				if (v == NULL)
1358*7c478bd9Sstevel@tonic-gate 					dnp->dn_mperm = mp;
1359*7c478bd9Sstevel@tonic-gate 				else
1360*7c478bd9Sstevel@tonic-gate 					v->mp_next = mp;
1361*7c478bd9Sstevel@tonic-gate 				mp->mp_next = p->mp_next;
1362*7c478bd9Sstevel@tonic-gate 				freemp = p;
1363*7c478bd9Sstevel@tonic-gate 				goto replaced;
1364*7c478bd9Sstevel@tonic-gate 			}
1365*7c478bd9Sstevel@tonic-gate 		}
1366*7c478bd9Sstevel@tonic-gate 		if (p == NULL) {
1367*7c478bd9Sstevel@tonic-gate 			mp_head = &dnp->dn_mperm;
1368*7c478bd9Sstevel@tonic-gate 			if (*mp_head == NULL) {
1369*7c478bd9Sstevel@tonic-gate 				*mp_head = mp;
1370*7c478bd9Sstevel@tonic-gate 			} else {
1371*7c478bd9Sstevel@tonic-gate 				mp->mp_next = *mp_head;
1372*7c478bd9Sstevel@tonic-gate 				*mp_head = mp;
1373*7c478bd9Sstevel@tonic-gate 			}
1374*7c478bd9Sstevel@tonic-gate 		}
1375*7c478bd9Sstevel@tonic-gate 	}
1376*7c478bd9Sstevel@tonic-gate replaced:
1377*7c478bd9Sstevel@tonic-gate 	if (freemp) {
1378*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_MINORPERM) {
1379*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT, "< %s %s 0%o %d %d\n",
1380*7c478bd9Sstevel@tonic-gate 			    drvname, freemp->mp_minorname,
1381*7c478bd9Sstevel@tonic-gate 			    freemp->mp_mode & 0777,
1382*7c478bd9Sstevel@tonic-gate 			    freemp->mp_uid, freemp->mp_gid);
1383*7c478bd9Sstevel@tonic-gate 		}
1384*7c478bd9Sstevel@tonic-gate 		free_mperm(freemp);
1385*7c478bd9Sstevel@tonic-gate 	}
1386*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_MINORPERM) {
1387*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_CONT, "> %s %s 0%o %d %d\n",
1388*7c478bd9Sstevel@tonic-gate 		    drvname, mp->mp_minorname, mp->mp_mode & 0777,
1389*7c478bd9Sstevel@tonic-gate 		    mp->mp_uid, mp->mp_gid);
1390*7c478bd9Sstevel@tonic-gate 	}
1391*7c478bd9Sstevel@tonic-gate 	UNLOCK_DEV_OPS(&dnp->dn_lock);
1392*7c478bd9Sstevel@tonic-gate }
1393*7c478bd9Sstevel@tonic-gate 
1394*7c478bd9Sstevel@tonic-gate 
1395*7c478bd9Sstevel@tonic-gate static int
1396*7c478bd9Sstevel@tonic-gate process_minorperm(int cmd, nvlist_t *nvl)
1397*7c478bd9Sstevel@tonic-gate {
1398*7c478bd9Sstevel@tonic-gate 	char *minor;
1399*7c478bd9Sstevel@tonic-gate 	major_t major;
1400*7c478bd9Sstevel@tonic-gate 	mperm_t *mp;
1401*7c478bd9Sstevel@tonic-gate 	nvpair_t *nvp;
1402*7c478bd9Sstevel@tonic-gate 	char *name;
1403*7c478bd9Sstevel@tonic-gate 	int is_clone;
1404*7c478bd9Sstevel@tonic-gate 	major_t minmaj;
1405*7c478bd9Sstevel@tonic-gate 
1406*7c478bd9Sstevel@tonic-gate 	ASSERT(cmd == MODLOADMINORPERM ||
1407*7c478bd9Sstevel@tonic-gate 	    cmd == MODADDMINORPERM || cmd == MODREMMINORPERM);
1408*7c478bd9Sstevel@tonic-gate 
1409*7c478bd9Sstevel@tonic-gate 	nvp = NULL;
1410*7c478bd9Sstevel@tonic-gate 	while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
1411*7c478bd9Sstevel@tonic-gate 		name = nvpair_name(nvp);
1412*7c478bd9Sstevel@tonic-gate 
1413*7c478bd9Sstevel@tonic-gate 		is_clone = 0;
1414*7c478bd9Sstevel@tonic-gate 		(void) nvpair_value_string(nvp, &minor);
1415*7c478bd9Sstevel@tonic-gate 		major = ddi_name_to_major(name);
1416*7c478bd9Sstevel@tonic-gate 		if (major != (major_t)-1) {
1417*7c478bd9Sstevel@tonic-gate 			mp = kmem_zalloc(sizeof (*mp), KM_SLEEP);
1418*7c478bd9Sstevel@tonic-gate 			if (minor == NULL || strlen(minor) == 0) {
1419*7c478bd9Sstevel@tonic-gate 				if (moddebug & MODDEBUG_MINORPERM) {
1420*7c478bd9Sstevel@tonic-gate 					cmn_err(CE_CONT, MP_EMPTY_MINOR, name);
1421*7c478bd9Sstevel@tonic-gate 				}
1422*7c478bd9Sstevel@tonic-gate 				minor = "*";
1423*7c478bd9Sstevel@tonic-gate 			}
1424*7c478bd9Sstevel@tonic-gate 
1425*7c478bd9Sstevel@tonic-gate 			/*
1426*7c478bd9Sstevel@tonic-gate 			 * The minor name of a node using the clone
1427*7c478bd9Sstevel@tonic-gate 			 * driver must be the driver name.  To avoid
1428*7c478bd9Sstevel@tonic-gate 			 * multiple searches, we map entries in the form
1429*7c478bd9Sstevel@tonic-gate 			 * clone:<driver> to <driver>:*.  This also allows us
1430*7c478bd9Sstevel@tonic-gate 			 * to filter out some of the litter in /etc/minor_perm.
1431*7c478bd9Sstevel@tonic-gate 			 * Minor perm alias entries where the name is not
1432*7c478bd9Sstevel@tonic-gate 			 * the driver kept on the clone list itself.
1433*7c478bd9Sstevel@tonic-gate 			 * This all seems very fragile as a driver could
1434*7c478bd9Sstevel@tonic-gate 			 * be introduced with an existing alias name.
1435*7c478bd9Sstevel@tonic-gate 			 */
1436*7c478bd9Sstevel@tonic-gate 			if (strcmp(name, "clone") == 0) {
1437*7c478bd9Sstevel@tonic-gate 				minmaj = ddi_name_to_major(minor);
1438*7c478bd9Sstevel@tonic-gate 				if (minmaj != (major_t)-1) {
1439*7c478bd9Sstevel@tonic-gate 					if (moddebug & MODDEBUG_MINORPERM) {
1440*7c478bd9Sstevel@tonic-gate 						cmn_err(CE_CONT,
1441*7c478bd9Sstevel@tonic-gate 						    "mapping %s:%s to %s:*\n",
1442*7c478bd9Sstevel@tonic-gate 						    name, minor, minor);
1443*7c478bd9Sstevel@tonic-gate 					}
1444*7c478bd9Sstevel@tonic-gate 					major = minmaj;
1445*7c478bd9Sstevel@tonic-gate 					name = minor;
1446*7c478bd9Sstevel@tonic-gate 					minor = "*";
1447*7c478bd9Sstevel@tonic-gate 					is_clone = 1;
1448*7c478bd9Sstevel@tonic-gate 				}
1449*7c478bd9Sstevel@tonic-gate 			}
1450*7c478bd9Sstevel@tonic-gate 
1451*7c478bd9Sstevel@tonic-gate 			if (mp) {
1452*7c478bd9Sstevel@tonic-gate 				mp->mp_minorname =
1453*7c478bd9Sstevel@tonic-gate 				    i_ddi_strdup(minor, KM_SLEEP);
1454*7c478bd9Sstevel@tonic-gate 			}
1455*7c478bd9Sstevel@tonic-gate 		} else {
1456*7c478bd9Sstevel@tonic-gate 			mp = NULL;
1457*7c478bd9Sstevel@tonic-gate 			if (moddebug & MODDEBUG_MINORPERM) {
1458*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT, MP_NO_DRV_ERR, name);
1459*7c478bd9Sstevel@tonic-gate 			}
1460*7c478bd9Sstevel@tonic-gate 		}
1461*7c478bd9Sstevel@tonic-gate 
1462*7c478bd9Sstevel@tonic-gate 		/* mode */
1463*7c478bd9Sstevel@tonic-gate 		nvp = nvlist_next_nvpair(nvl, nvp);
1464*7c478bd9Sstevel@tonic-gate 		ASSERT(strcmp(nvpair_name(nvp), "mode") == 0);
1465*7c478bd9Sstevel@tonic-gate 		if (mp)
1466*7c478bd9Sstevel@tonic-gate 			(void) nvpair_value_int32(nvp, (int *)&mp->mp_mode);
1467*7c478bd9Sstevel@tonic-gate 		/* uid */
1468*7c478bd9Sstevel@tonic-gate 		nvp = nvlist_next_nvpair(nvl, nvp);
1469*7c478bd9Sstevel@tonic-gate 		ASSERT(strcmp(nvpair_name(nvp), "uid") == 0);
1470*7c478bd9Sstevel@tonic-gate 		if (mp)
1471*7c478bd9Sstevel@tonic-gate 			(void) nvpair_value_int32(nvp, &mp->mp_uid);
1472*7c478bd9Sstevel@tonic-gate 		/* gid */
1473*7c478bd9Sstevel@tonic-gate 		nvp = nvlist_next_nvpair(nvl, nvp);
1474*7c478bd9Sstevel@tonic-gate 		ASSERT(strcmp(nvpair_name(nvp), "gid") == 0);
1475*7c478bd9Sstevel@tonic-gate 		if (mp) {
1476*7c478bd9Sstevel@tonic-gate 			(void) nvpair_value_int32(nvp, &mp->mp_gid);
1477*7c478bd9Sstevel@tonic-gate 
1478*7c478bd9Sstevel@tonic-gate 			if (cmd == MODREMMINORPERM) {
1479*7c478bd9Sstevel@tonic-gate 				rem_minorperm(major, name, mp, is_clone);
1480*7c478bd9Sstevel@tonic-gate 				free_mperm(mp);
1481*7c478bd9Sstevel@tonic-gate 			} else {
1482*7c478bd9Sstevel@tonic-gate 				add_minorperm(major, name, mp, is_clone);
1483*7c478bd9Sstevel@tonic-gate 			}
1484*7c478bd9Sstevel@tonic-gate 		}
1485*7c478bd9Sstevel@tonic-gate 	}
1486*7c478bd9Sstevel@tonic-gate 
1487*7c478bd9Sstevel@tonic-gate 	if (cmd == MODLOADMINORPERM)
1488*7c478bd9Sstevel@tonic-gate 		minorperm_loaded = 1;
1489*7c478bd9Sstevel@tonic-gate 
1490*7c478bd9Sstevel@tonic-gate 	/*
1491*7c478bd9Sstevel@tonic-gate 	 * Reset permissions of cached dv_nodes
1492*7c478bd9Sstevel@tonic-gate 	 */
1493*7c478bd9Sstevel@tonic-gate 	(void) devfs_reset_perm(DV_RESET_PERM);
1494*7c478bd9Sstevel@tonic-gate 
1495*7c478bd9Sstevel@tonic-gate 	return (0);
1496*7c478bd9Sstevel@tonic-gate }
1497*7c478bd9Sstevel@tonic-gate 
1498*7c478bd9Sstevel@tonic-gate static int
1499*7c478bd9Sstevel@tonic-gate modctl_minorperm(int cmd, char *usrbuf, size_t buflen)
1500*7c478bd9Sstevel@tonic-gate {
1501*7c478bd9Sstevel@tonic-gate 	int error;
1502*7c478bd9Sstevel@tonic-gate 	nvlist_t *nvl;
1503*7c478bd9Sstevel@tonic-gate 	char *buf = kmem_alloc(buflen, KM_SLEEP);
1504*7c478bd9Sstevel@tonic-gate 
1505*7c478bd9Sstevel@tonic-gate 	if ((error = ddi_copyin(usrbuf, buf, buflen, 0)) != 0) {
1506*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, buflen);
1507*7c478bd9Sstevel@tonic-gate 		return (error);
1508*7c478bd9Sstevel@tonic-gate 	}
1509*7c478bd9Sstevel@tonic-gate 
1510*7c478bd9Sstevel@tonic-gate 	error = nvlist_unpack(buf, buflen, &nvl, KM_SLEEP);
1511*7c478bd9Sstevel@tonic-gate 	kmem_free(buf, buflen);
1512*7c478bd9Sstevel@tonic-gate 	if (error)
1513*7c478bd9Sstevel@tonic-gate 		return (error);
1514*7c478bd9Sstevel@tonic-gate 
1515*7c478bd9Sstevel@tonic-gate 	error = process_minorperm(cmd, nvl);
1516*7c478bd9Sstevel@tonic-gate 	nvlist_free(nvl);
1517*7c478bd9Sstevel@tonic-gate 	return (error);
1518*7c478bd9Sstevel@tonic-gate }
1519*7c478bd9Sstevel@tonic-gate 
1520*7c478bd9Sstevel@tonic-gate struct walk_args {
1521*7c478bd9Sstevel@tonic-gate 	char		*wa_drvname;
1522*7c478bd9Sstevel@tonic-gate 	list_t		wa_pathlist;
1523*7c478bd9Sstevel@tonic-gate };
1524*7c478bd9Sstevel@tonic-gate 
1525*7c478bd9Sstevel@tonic-gate struct path_elem {
1526*7c478bd9Sstevel@tonic-gate 	char		*pe_dir;
1527*7c478bd9Sstevel@tonic-gate 	char		*pe_nodename;
1528*7c478bd9Sstevel@tonic-gate 	list_node_t	pe_node;
1529*7c478bd9Sstevel@tonic-gate 	int		pe_dirlen;
1530*7c478bd9Sstevel@tonic-gate };
1531*7c478bd9Sstevel@tonic-gate 
1532*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
1533*7c478bd9Sstevel@tonic-gate static int
1534*7c478bd9Sstevel@tonic-gate modctl_inst_walker(const char *path, in_node_t *np, in_drv_t *dp, void *arg)
1535*7c478bd9Sstevel@tonic-gate {
1536*7c478bd9Sstevel@tonic-gate 	struct walk_args *wargs = (struct walk_args *)arg;
1537*7c478bd9Sstevel@tonic-gate 	struct path_elem *pe;
1538*7c478bd9Sstevel@tonic-gate 	char *nodename;
1539*7c478bd9Sstevel@tonic-gate 
1540*7c478bd9Sstevel@tonic-gate 	if (strcmp(dp->ind_driver_name, wargs->wa_drvname) != 0)
1541*7c478bd9Sstevel@tonic-gate 		return (INST_WALK_CONTINUE);
1542*7c478bd9Sstevel@tonic-gate 
1543*7c478bd9Sstevel@tonic-gate 	pe = kmem_zalloc(sizeof (*pe), KM_SLEEP);
1544*7c478bd9Sstevel@tonic-gate 	pe->pe_dir = i_ddi_strdup((char *)path, KM_SLEEP);
1545*7c478bd9Sstevel@tonic-gate 	pe->pe_dirlen = strlen(pe->pe_dir) + 1;
1546*7c478bd9Sstevel@tonic-gate 	ASSERT(strrchr(pe->pe_dir, '/') != NULL);
1547*7c478bd9Sstevel@tonic-gate 	nodename = strrchr(pe->pe_dir, '/');
1548*7c478bd9Sstevel@tonic-gate 	*nodename++ = 0;
1549*7c478bd9Sstevel@tonic-gate 	pe->pe_nodename = nodename;
1550*7c478bd9Sstevel@tonic-gate 	list_insert_tail(&wargs->wa_pathlist, pe);
1551*7c478bd9Sstevel@tonic-gate 
1552*7c478bd9Sstevel@tonic-gate 	return (INST_WALK_CONTINUE);
1553*7c478bd9Sstevel@tonic-gate }
1554*7c478bd9Sstevel@tonic-gate 
1555*7c478bd9Sstevel@tonic-gate static int
1556*7c478bd9Sstevel@tonic-gate modctl_remdrv_cleanup(const char *u_drvname)
1557*7c478bd9Sstevel@tonic-gate {
1558*7c478bd9Sstevel@tonic-gate 	struct walk_args *wargs;
1559*7c478bd9Sstevel@tonic-gate 	struct path_elem *pe;
1560*7c478bd9Sstevel@tonic-gate 	char *drvname;
1561*7c478bd9Sstevel@tonic-gate 	int err, rval = 0;
1562*7c478bd9Sstevel@tonic-gate 
1563*7c478bd9Sstevel@tonic-gate 	drvname = kmem_alloc(MAXMODCONFNAME, KM_SLEEP);
1564*7c478bd9Sstevel@tonic-gate 	if ((err = copyinstr(u_drvname, drvname, MAXMODCONFNAME, 0))) {
1565*7c478bd9Sstevel@tonic-gate 		kmem_free(drvname, MAXMODCONFNAME);
1566*7c478bd9Sstevel@tonic-gate 		return (err);
1567*7c478bd9Sstevel@tonic-gate 	}
1568*7c478bd9Sstevel@tonic-gate 
1569*7c478bd9Sstevel@tonic-gate 	/*
1570*7c478bd9Sstevel@tonic-gate 	 * First go through the instance database.  For each
1571*7c478bd9Sstevel@tonic-gate 	 * instance of a device bound to the driver being
1572*7c478bd9Sstevel@tonic-gate 	 * removed, remove any underlying devfs attribute nodes.
1573*7c478bd9Sstevel@tonic-gate 	 *
1574*7c478bd9Sstevel@tonic-gate 	 * This is a two-step process.  First we go through
1575*7c478bd9Sstevel@tonic-gate 	 * the instance data itself, constructing a list of
1576*7c478bd9Sstevel@tonic-gate 	 * the nodes discovered.  The second step is then
1577*7c478bd9Sstevel@tonic-gate 	 * to find and remove any devfs attribute nodes
1578*7c478bd9Sstevel@tonic-gate 	 * for the instances discovered in the first step.
1579*7c478bd9Sstevel@tonic-gate 	 * The two-step process avoids any difficulties
1580*7c478bd9Sstevel@tonic-gate 	 * which could arise by holding the instance data
1581*7c478bd9Sstevel@tonic-gate 	 * lock with simultaneous devfs operations.
1582*7c478bd9Sstevel@tonic-gate 	 */
1583*7c478bd9Sstevel@tonic-gate 	wargs = kmem_zalloc(sizeof (*wargs), KM_SLEEP);
1584*7c478bd9Sstevel@tonic-gate 
1585*7c478bd9Sstevel@tonic-gate 	wargs->wa_drvname = drvname;
1586*7c478bd9Sstevel@tonic-gate 	list_create(&wargs->wa_pathlist,
1587*7c478bd9Sstevel@tonic-gate 	    sizeof (struct path_elem), offsetof(struct path_elem, pe_node));
1588*7c478bd9Sstevel@tonic-gate 
1589*7c478bd9Sstevel@tonic-gate 	(void) e_ddi_walk_instances(modctl_inst_walker, (void *)wargs);
1590*7c478bd9Sstevel@tonic-gate 
1591*7c478bd9Sstevel@tonic-gate 	for (pe = list_head(&wargs->wa_pathlist); pe != NULL;
1592*7c478bd9Sstevel@tonic-gate 	    pe = list_next(&wargs->wa_pathlist, pe)) {
1593*7c478bd9Sstevel@tonic-gate 		err = devfs_remdrv_cleanup((const char *)pe->pe_dir,
1594*7c478bd9Sstevel@tonic-gate 			(const char *)pe->pe_nodename);
1595*7c478bd9Sstevel@tonic-gate 		if (rval == 0)
1596*7c478bd9Sstevel@tonic-gate 			rval = err;
1597*7c478bd9Sstevel@tonic-gate 	}
1598*7c478bd9Sstevel@tonic-gate 
1599*7c478bd9Sstevel@tonic-gate 	while ((pe = list_head(&wargs->wa_pathlist)) != NULL) {
1600*7c478bd9Sstevel@tonic-gate 		list_remove(&wargs->wa_pathlist, pe);
1601*7c478bd9Sstevel@tonic-gate 		kmem_free(pe->pe_dir, pe->pe_dirlen);
1602*7c478bd9Sstevel@tonic-gate 		kmem_free(pe, sizeof (*pe));
1603*7c478bd9Sstevel@tonic-gate 	}
1604*7c478bd9Sstevel@tonic-gate 	kmem_free(wargs, sizeof (*wargs));
1605*7c478bd9Sstevel@tonic-gate 
1606*7c478bd9Sstevel@tonic-gate 	/*
1607*7c478bd9Sstevel@tonic-gate 	 * Pseudo nodes aren't recorded in the instance database
1608*7c478bd9Sstevel@tonic-gate 	 * so any such nodes need to be handled separately.
1609*7c478bd9Sstevel@tonic-gate 	 */
1610*7c478bd9Sstevel@tonic-gate 	err = devfs_remdrv_cleanup("pseudo", (const char *)drvname);
1611*7c478bd9Sstevel@tonic-gate 	if (rval == 0)
1612*7c478bd9Sstevel@tonic-gate 		rval = err;
1613*7c478bd9Sstevel@tonic-gate 
1614*7c478bd9Sstevel@tonic-gate 	kmem_free(drvname, MAXMODCONFNAME);
1615*7c478bd9Sstevel@tonic-gate 	return (rval);
1616*7c478bd9Sstevel@tonic-gate }
1617*7c478bd9Sstevel@tonic-gate 
1618*7c478bd9Sstevel@tonic-gate static int
1619*7c478bd9Sstevel@tonic-gate modctl_allocpriv(const char *name)
1620*7c478bd9Sstevel@tonic-gate {
1621*7c478bd9Sstevel@tonic-gate 	char *pstr = kmem_alloc(PRIVNAME_MAX, KM_SLEEP);
1622*7c478bd9Sstevel@tonic-gate 	int error;
1623*7c478bd9Sstevel@tonic-gate 
1624*7c478bd9Sstevel@tonic-gate 	if ((error = copyinstr(name, pstr, PRIVNAME_MAX, 0))) {
1625*7c478bd9Sstevel@tonic-gate 		kmem_free(pstr, PRIVNAME_MAX);
1626*7c478bd9Sstevel@tonic-gate 		return (error);
1627*7c478bd9Sstevel@tonic-gate 	}
1628*7c478bd9Sstevel@tonic-gate 	error = priv_getbyname(pstr, PRIV_ALLOC);
1629*7c478bd9Sstevel@tonic-gate 	if (error < 0)
1630*7c478bd9Sstevel@tonic-gate 		error = -error;
1631*7c478bd9Sstevel@tonic-gate 	else
1632*7c478bd9Sstevel@tonic-gate 		error = 0;
1633*7c478bd9Sstevel@tonic-gate 	kmem_free(pstr, PRIVNAME_MAX);
1634*7c478bd9Sstevel@tonic-gate 	return (error);
1635*7c478bd9Sstevel@tonic-gate }
1636*7c478bd9Sstevel@tonic-gate 
1637*7c478bd9Sstevel@tonic-gate /*ARGSUSED5*/
1638*7c478bd9Sstevel@tonic-gate int
1639*7c478bd9Sstevel@tonic-gate modctl(int cmd, uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4,
1640*7c478bd9Sstevel@tonic-gate     uintptr_t a5)
1641*7c478bd9Sstevel@tonic-gate {
1642*7c478bd9Sstevel@tonic-gate 	int	error = EINVAL;
1643*7c478bd9Sstevel@tonic-gate 	dev_t	dev;
1644*7c478bd9Sstevel@tonic-gate 
1645*7c478bd9Sstevel@tonic-gate 	if (secpolicy_modctl(CRED(), cmd) != 0)
1646*7c478bd9Sstevel@tonic-gate 		return (set_errno(EPERM));
1647*7c478bd9Sstevel@tonic-gate 
1648*7c478bd9Sstevel@tonic-gate 	switch (cmd) {
1649*7c478bd9Sstevel@tonic-gate 	case MODLOAD:		/* load a module */
1650*7c478bd9Sstevel@tonic-gate 		error = modctl_modload((int)a1, (char *)a2, (int *)a3);
1651*7c478bd9Sstevel@tonic-gate 		break;
1652*7c478bd9Sstevel@tonic-gate 
1653*7c478bd9Sstevel@tonic-gate 	case MODUNLOAD:		/* unload a module */
1654*7c478bd9Sstevel@tonic-gate 		error = modctl_modunload((modid_t)a1);
1655*7c478bd9Sstevel@tonic-gate 		break;
1656*7c478bd9Sstevel@tonic-gate 
1657*7c478bd9Sstevel@tonic-gate 	case MODINFO:		/* get module status */
1658*7c478bd9Sstevel@tonic-gate 		error = modctl_modinfo((modid_t)a1, (struct modinfo *)a2);
1659*7c478bd9Sstevel@tonic-gate 		break;
1660*7c478bd9Sstevel@tonic-gate 
1661*7c478bd9Sstevel@tonic-gate 	case MODRESERVED:	/* get last major number in range */
1662*7c478bd9Sstevel@tonic-gate 		error = modctl_modreserve((modid_t)a1, (int *)a2);
1663*7c478bd9Sstevel@tonic-gate 		break;
1664*7c478bd9Sstevel@tonic-gate 
1665*7c478bd9Sstevel@tonic-gate 	case MODSETMINIROOT:	/* we are running in miniroot */
1666*7c478bd9Sstevel@tonic-gate 		isminiroot = 1;
1667*7c478bd9Sstevel@tonic-gate 		error = 0;
1668*7c478bd9Sstevel@tonic-gate 		break;
1669*7c478bd9Sstevel@tonic-gate 
1670*7c478bd9Sstevel@tonic-gate 	case MODADDMAJBIND:	/* read major binding file */
1671*7c478bd9Sstevel@tonic-gate 		error = modctl_add_major((int *)a2);
1672*7c478bd9Sstevel@tonic-gate 		break;
1673*7c478bd9Sstevel@tonic-gate 
1674*7c478bd9Sstevel@tonic-gate 	case MODGETPATHLEN:	/* get modpath length */
1675*7c478bd9Sstevel@tonic-gate 		error = modctl_getmodpathlen((int *)a2);
1676*7c478bd9Sstevel@tonic-gate 		break;
1677*7c478bd9Sstevel@tonic-gate 
1678*7c478bd9Sstevel@tonic-gate 	case MODGETPATH:	/* get modpath */
1679*7c478bd9Sstevel@tonic-gate 		error = modctl_getmodpath((char *)a2);
1680*7c478bd9Sstevel@tonic-gate 		break;
1681*7c478bd9Sstevel@tonic-gate 
1682*7c478bd9Sstevel@tonic-gate 	case MODREADSYSBIND:	/* read system call binding file */
1683*7c478bd9Sstevel@tonic-gate 		error = modctl_read_sysbinding_file();
1684*7c478bd9Sstevel@tonic-gate 		break;
1685*7c478bd9Sstevel@tonic-gate 
1686*7c478bd9Sstevel@tonic-gate 	case MODGETMAJBIND:	/* get major number for named device */
1687*7c478bd9Sstevel@tonic-gate 		error = modctl_getmaj((char *)a1, (uint_t)a2, (int *)a3);
1688*7c478bd9Sstevel@tonic-gate 		break;
1689*7c478bd9Sstevel@tonic-gate 
1690*7c478bd9Sstevel@tonic-gate 	case MODGETNAME:	/* get name of device given major number */
1691*7c478bd9Sstevel@tonic-gate 		error = modctl_getname((char *)a1, (uint_t)a2, (int *)a3);
1692*7c478bd9Sstevel@tonic-gate 		break;
1693*7c478bd9Sstevel@tonic-gate 
1694*7c478bd9Sstevel@tonic-gate 	case MODDEVT2INSTANCE:
1695*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1696*7c478bd9Sstevel@tonic-gate 			dev = (dev_t)a1;
1697*7c478bd9Sstevel@tonic-gate 		}
1698*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
1699*7c478bd9Sstevel@tonic-gate 		else {
1700*7c478bd9Sstevel@tonic-gate 			dev = expldev(a1);
1701*7c478bd9Sstevel@tonic-gate 		}
1702*7c478bd9Sstevel@tonic-gate #endif
1703*7c478bd9Sstevel@tonic-gate 		error = modctl_devt2instance(dev, (int *)a2);
1704*7c478bd9Sstevel@tonic-gate 		break;
1705*7c478bd9Sstevel@tonic-gate 
1706*7c478bd9Sstevel@tonic-gate 	case MODSIZEOF_DEVID:	/* sizeof device id of device given dev_t */
1707*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1708*7c478bd9Sstevel@tonic-gate 			dev = (dev_t)a1;
1709*7c478bd9Sstevel@tonic-gate 		}
1710*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
1711*7c478bd9Sstevel@tonic-gate 		else {
1712*7c478bd9Sstevel@tonic-gate 			dev = expldev(a1);
1713*7c478bd9Sstevel@tonic-gate 		}
1714*7c478bd9Sstevel@tonic-gate #endif
1715*7c478bd9Sstevel@tonic-gate 		error = modctl_sizeof_devid(dev, (uint_t *)a2);
1716*7c478bd9Sstevel@tonic-gate 		break;
1717*7c478bd9Sstevel@tonic-gate 
1718*7c478bd9Sstevel@tonic-gate 	case MODGETDEVID:	/* get device id of device given dev_t */
1719*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1720*7c478bd9Sstevel@tonic-gate 			dev = (dev_t)a1;
1721*7c478bd9Sstevel@tonic-gate 		}
1722*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
1723*7c478bd9Sstevel@tonic-gate 		else {
1724*7c478bd9Sstevel@tonic-gate 			dev = expldev(a1);
1725*7c478bd9Sstevel@tonic-gate 		}
1726*7c478bd9Sstevel@tonic-gate #endif
1727*7c478bd9Sstevel@tonic-gate 		error = modctl_get_devid(dev, (uint_t)a2, (ddi_devid_t)a3);
1728*7c478bd9Sstevel@tonic-gate 		break;
1729*7c478bd9Sstevel@tonic-gate 
1730*7c478bd9Sstevel@tonic-gate 	case MODSIZEOF_MINORNAME:	/* sizeof minor nm of dev_t/spectype */
1731*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1732*7c478bd9Sstevel@tonic-gate 			error = modctl_sizeof_minorname((dev_t)a1, (int)a2,
1733*7c478bd9Sstevel@tonic-gate 			    (uint_t *)a3);
1734*7c478bd9Sstevel@tonic-gate 		}
1735*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
1736*7c478bd9Sstevel@tonic-gate 		else {
1737*7c478bd9Sstevel@tonic-gate 			error = modctl_sizeof_minorname(expldev(a1), (int)a2,
1738*7c478bd9Sstevel@tonic-gate 			    (uint_t *)a3);
1739*7c478bd9Sstevel@tonic-gate 		}
1740*7c478bd9Sstevel@tonic-gate 
1741*7c478bd9Sstevel@tonic-gate #endif
1742*7c478bd9Sstevel@tonic-gate 		break;
1743*7c478bd9Sstevel@tonic-gate 
1744*7c478bd9Sstevel@tonic-gate 	case MODGETMINORNAME:   /* get minor name of dev_t and spec type */
1745*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1746*7c478bd9Sstevel@tonic-gate 			error = modctl_get_minorname((dev_t)a1, (int)a2,
1747*7c478bd9Sstevel@tonic-gate 			    (uint_t)a3, (char *)a4);
1748*7c478bd9Sstevel@tonic-gate 		}
1749*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
1750*7c478bd9Sstevel@tonic-gate 		else {
1751*7c478bd9Sstevel@tonic-gate 			error = modctl_get_minorname(expldev(a1), (int)a2,
1752*7c478bd9Sstevel@tonic-gate 			    (uint_t)a3, (char *)a4);
1753*7c478bd9Sstevel@tonic-gate 		}
1754*7c478bd9Sstevel@tonic-gate #endif
1755*7c478bd9Sstevel@tonic-gate 		break;
1756*7c478bd9Sstevel@tonic-gate 
1757*7c478bd9Sstevel@tonic-gate 	case MODGETDEVFSPATH_LEN:	/* sizeof path nm of dev_t/spectype */
1758*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1759*7c478bd9Sstevel@tonic-gate 			error = modctl_devfspath_len((dev_t)a1, (int)a2,
1760*7c478bd9Sstevel@tonic-gate 			    (uint_t *)a3);
1761*7c478bd9Sstevel@tonic-gate 		}
1762*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
1763*7c478bd9Sstevel@tonic-gate 		else {
1764*7c478bd9Sstevel@tonic-gate 			error = modctl_devfspath_len(expldev(a1), (int)a2,
1765*7c478bd9Sstevel@tonic-gate 			    (uint_t *)a3);
1766*7c478bd9Sstevel@tonic-gate 		}
1767*7c478bd9Sstevel@tonic-gate 
1768*7c478bd9Sstevel@tonic-gate #endif
1769*7c478bd9Sstevel@tonic-gate 		break;
1770*7c478bd9Sstevel@tonic-gate 
1771*7c478bd9Sstevel@tonic-gate 	case MODGETDEVFSPATH:   /* get path name of dev_t and spec type */
1772*7c478bd9Sstevel@tonic-gate 		if (get_udatamodel() == DATAMODEL_NATIVE) {
1773*7c478bd9Sstevel@tonic-gate 			error = modctl_devfspath((dev_t)a1, (int)a2,
1774*7c478bd9Sstevel@tonic-gate 			    (uint_t)a3, (char *)a4);
1775*7c478bd9Sstevel@tonic-gate 		}
1776*7c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
1777*7c478bd9Sstevel@tonic-gate 		else {
1778*7c478bd9Sstevel@tonic-gate 			error = modctl_devfspath(expldev(a1), (int)a2,
1779*7c478bd9Sstevel@tonic-gate 			    (uint_t)a3, (char *)a4);
1780*7c478bd9Sstevel@tonic-gate 		}
1781*7c478bd9Sstevel@tonic-gate #endif
1782*7c478bd9Sstevel@tonic-gate 		break;
1783*7c478bd9Sstevel@tonic-gate 
1784*7c478bd9Sstevel@tonic-gate 
1785*7c478bd9Sstevel@tonic-gate 	case MODEVENTS:
1786*7c478bd9Sstevel@tonic-gate 		error = modctl_modevents((int)a1, a2, a3, a4, (uint_t)a5);
1787*7c478bd9Sstevel@tonic-gate 		break;
1788*7c478bd9Sstevel@tonic-gate 
1789*7c478bd9Sstevel@tonic-gate 	case MODGETFBNAME:	/* get the framebuffer name */
1790*7c478bd9Sstevel@tonic-gate 		error = modctl_get_fbname((char *)a1);
1791*7c478bd9Sstevel@tonic-gate 		break;
1792*7c478bd9Sstevel@tonic-gate 
1793*7c478bd9Sstevel@tonic-gate 	case MODREREADDACF:	/* reread dacf rule database from given file */
1794*7c478bd9Sstevel@tonic-gate 		error = modctl_reread_dacf((char *)a1);
1795*7c478bd9Sstevel@tonic-gate 		break;
1796*7c478bd9Sstevel@tonic-gate 
1797*7c478bd9Sstevel@tonic-gate 	case MODLOADDRVCONF:	/* load driver.conf file for major */
1798*7c478bd9Sstevel@tonic-gate 		error = modctl_load_drvconf((major_t)a1);
1799*7c478bd9Sstevel@tonic-gate 		break;
1800*7c478bd9Sstevel@tonic-gate 
1801*7c478bd9Sstevel@tonic-gate 	case MODUNLOADDRVCONF:	/* unload driver.conf file for major */
1802*7c478bd9Sstevel@tonic-gate 		error = modctl_unload_drvconf((major_t)a1);
1803*7c478bd9Sstevel@tonic-gate 		break;
1804*7c478bd9Sstevel@tonic-gate 
1805*7c478bd9Sstevel@tonic-gate 	case MODREMMAJBIND:	/* remove a major binding */
1806*7c478bd9Sstevel@tonic-gate 		error = modctl_rem_major((major_t)a1);
1807*7c478bd9Sstevel@tonic-gate 		break;
1808*7c478bd9Sstevel@tonic-gate 
1809*7c478bd9Sstevel@tonic-gate 	case MODDEVID2PATHS:	/* get paths given devid */
1810*7c478bd9Sstevel@tonic-gate 		error = modctl_devid2paths((ddi_devid_t)a1, (char *)a2,
1811*7c478bd9Sstevel@tonic-gate 		    (uint_t)a3, (size_t *)a4, (char *)a5);
1812*7c478bd9Sstevel@tonic-gate 		break;
1813*7c478bd9Sstevel@tonic-gate 
1814*7c478bd9Sstevel@tonic-gate 	case MODSETDEVPOLICY:	/* establish device policy */
1815*7c478bd9Sstevel@tonic-gate 		error = devpolicy_load((int)a1, (size_t)a2, (devplcysys_t *)a3);
1816*7c478bd9Sstevel@tonic-gate 		break;
1817*7c478bd9Sstevel@tonic-gate 
1818*7c478bd9Sstevel@tonic-gate 	case MODGETDEVPOLICY:	/* get device policy */
1819*7c478bd9Sstevel@tonic-gate 		error = devpolicy_get((int *)a1, (size_t)a2,
1820*7c478bd9Sstevel@tonic-gate 				(devplcysys_t *)a3);
1821*7c478bd9Sstevel@tonic-gate 		break;
1822*7c478bd9Sstevel@tonic-gate 
1823*7c478bd9Sstevel@tonic-gate 	case MODALLOCPRIV:
1824*7c478bd9Sstevel@tonic-gate 		error = modctl_allocpriv((const char *)a1);
1825*7c478bd9Sstevel@tonic-gate 		break;
1826*7c478bd9Sstevel@tonic-gate 
1827*7c478bd9Sstevel@tonic-gate 	case MODGETDEVPOLICYBYNAME:
1828*7c478bd9Sstevel@tonic-gate 		error = devpolicy_getbyname((size_t)a1,
1829*7c478bd9Sstevel@tonic-gate 		    (devplcysys_t *)a2, (char *)a3);
1830*7c478bd9Sstevel@tonic-gate 		break;
1831*7c478bd9Sstevel@tonic-gate 
1832*7c478bd9Sstevel@tonic-gate 	case MODCLEANUP:
1833*7c478bd9Sstevel@tonic-gate 		e_devid_cache_cleanup();
1834*7c478bd9Sstevel@tonic-gate 		error = 0;
1835*7c478bd9Sstevel@tonic-gate 		break;
1836*7c478bd9Sstevel@tonic-gate 
1837*7c478bd9Sstevel@tonic-gate 	case MODLOADMINORPERM:
1838*7c478bd9Sstevel@tonic-gate 	case MODADDMINORPERM:
1839*7c478bd9Sstevel@tonic-gate 	case MODREMMINORPERM:
1840*7c478bd9Sstevel@tonic-gate 		error = modctl_minorperm(cmd, (char *)a1, (size_t)a2);
1841*7c478bd9Sstevel@tonic-gate 		break;
1842*7c478bd9Sstevel@tonic-gate 
1843*7c478bd9Sstevel@tonic-gate 	case MODREMDRVCLEANUP:
1844*7c478bd9Sstevel@tonic-gate 		error = modctl_remdrv_cleanup((const char *)a1);
1845*7c478bd9Sstevel@tonic-gate 		break;
1846*7c478bd9Sstevel@tonic-gate 
1847*7c478bd9Sstevel@tonic-gate 	default:
1848*7c478bd9Sstevel@tonic-gate 		error = EINVAL;
1849*7c478bd9Sstevel@tonic-gate 		break;
1850*7c478bd9Sstevel@tonic-gate 	}
1851*7c478bd9Sstevel@tonic-gate 
1852*7c478bd9Sstevel@tonic-gate 	return (error ? set_errno(error) : 0);
1853*7c478bd9Sstevel@tonic-gate }
1854*7c478bd9Sstevel@tonic-gate 
1855*7c478bd9Sstevel@tonic-gate /*
1856*7c478bd9Sstevel@tonic-gate  * Calls to kobj_load_module()() are handled off to this routine in a
1857*7c478bd9Sstevel@tonic-gate  * separate thread.
1858*7c478bd9Sstevel@tonic-gate  */
1859*7c478bd9Sstevel@tonic-gate static void
1860*7c478bd9Sstevel@tonic-gate modload_thread(struct loadmt *ltp)
1861*7c478bd9Sstevel@tonic-gate {
1862*7c478bd9Sstevel@tonic-gate 	/* load the module and signal the creator of this thread */
1863*7c478bd9Sstevel@tonic-gate 	kmutex_t	cpr_lk;
1864*7c478bd9Sstevel@tonic-gate 	callb_cpr_t	cpr_i;
1865*7c478bd9Sstevel@tonic-gate 
1866*7c478bd9Sstevel@tonic-gate 	mutex_init(&cpr_lk, NULL, MUTEX_DEFAULT, NULL);
1867*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cpr_i, &cpr_lk, callb_generic_cpr, "modload");
1868*7c478bd9Sstevel@tonic-gate 	/* borrow the devi lock from thread which invoked us */
1869*7c478bd9Sstevel@tonic-gate 	pm_borrow_lock(ltp->owner);
1870*7c478bd9Sstevel@tonic-gate 	ltp->retval = kobj_load_module(ltp->mp, ltp->usepath);
1871*7c478bd9Sstevel@tonic-gate 	pm_return_lock();
1872*7c478bd9Sstevel@tonic-gate 	sema_v(&ltp->sema);
1873*7c478bd9Sstevel@tonic-gate 	mutex_enter(&cpr_lk);
1874*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_EXIT(&cpr_i);
1875*7c478bd9Sstevel@tonic-gate 	mutex_destroy(&cpr_lk);
1876*7c478bd9Sstevel@tonic-gate 	thread_exit();
1877*7c478bd9Sstevel@tonic-gate }
1878*7c478bd9Sstevel@tonic-gate 
1879*7c478bd9Sstevel@tonic-gate /*
1880*7c478bd9Sstevel@tonic-gate  * load a module, adding a reference if caller specifies rmodp.  If rmodp
1881*7c478bd9Sstevel@tonic-gate  * is specified then an errno is returned, otherwise a module index is
1882*7c478bd9Sstevel@tonic-gate  * returned (-1 on error).
1883*7c478bd9Sstevel@tonic-gate  */
1884*7c478bd9Sstevel@tonic-gate static int
1885*7c478bd9Sstevel@tonic-gate modrload(char *subdir, char *filename, struct modctl **rmodp)
1886*7c478bd9Sstevel@tonic-gate {
1887*7c478bd9Sstevel@tonic-gate 	struct modctl *modp;
1888*7c478bd9Sstevel@tonic-gate 	size_t size;
1889*7c478bd9Sstevel@tonic-gate 	char *fullname;
1890*7c478bd9Sstevel@tonic-gate 	int retval = EINVAL;
1891*7c478bd9Sstevel@tonic-gate 	int id = -1;
1892*7c478bd9Sstevel@tonic-gate 	struct _buf *buf;
1893*7c478bd9Sstevel@tonic-gate 
1894*7c478bd9Sstevel@tonic-gate 	if (rmodp)
1895*7c478bd9Sstevel@tonic-gate 		*rmodp = NULL;			/* avoid garbage */
1896*7c478bd9Sstevel@tonic-gate 
1897*7c478bd9Sstevel@tonic-gate 	if (subdir != NULL) {
1898*7c478bd9Sstevel@tonic-gate 		/*
1899*7c478bd9Sstevel@tonic-gate 		 * refuse / in filename to prevent "../" escapes.
1900*7c478bd9Sstevel@tonic-gate 		 */
1901*7c478bd9Sstevel@tonic-gate 		if (strchr(filename, '/') != NULL)
1902*7c478bd9Sstevel@tonic-gate 			return (rmodp ? retval : id);
1903*7c478bd9Sstevel@tonic-gate 
1904*7c478bd9Sstevel@tonic-gate 		/*
1905*7c478bd9Sstevel@tonic-gate 		 * allocate enough space for <subdir>/<filename><NULL>
1906*7c478bd9Sstevel@tonic-gate 		 */
1907*7c478bd9Sstevel@tonic-gate 		size = strlen(subdir) + strlen(filename) + 2;
1908*7c478bd9Sstevel@tonic-gate 		fullname = kmem_zalloc(size, KM_SLEEP);
1909*7c478bd9Sstevel@tonic-gate 		(void) sprintf(fullname, "%s/%s", subdir, filename);
1910*7c478bd9Sstevel@tonic-gate 	} else {
1911*7c478bd9Sstevel@tonic-gate 		fullname = filename;
1912*7c478bd9Sstevel@tonic-gate 	}
1913*7c478bd9Sstevel@tonic-gate 
1914*7c478bd9Sstevel@tonic-gate 	/*
1915*7c478bd9Sstevel@tonic-gate 	 * Verify that that module in question actually exists on disk.
1916*7c478bd9Sstevel@tonic-gate 	 * Otherwise, modload_now will succeed if (for example) modload
1917*7c478bd9Sstevel@tonic-gate 	 * is requested for sched/nfs if fs/nfs is already loaded, and
1918*7c478bd9Sstevel@tonic-gate 	 * sched/nfs doesn't exist.
1919*7c478bd9Sstevel@tonic-gate 	 */
1920*7c478bd9Sstevel@tonic-gate 	if (modrootloaded && swaploaded) {
1921*7c478bd9Sstevel@tonic-gate 		if ((buf = kobj_open_path(fullname, 1, 1)) ==
1922*7c478bd9Sstevel@tonic-gate 		    (struct _buf *)-1) {
1923*7c478bd9Sstevel@tonic-gate 			retval = ENOENT;
1924*7c478bd9Sstevel@tonic-gate 			goto done;
1925*7c478bd9Sstevel@tonic-gate 		}
1926*7c478bd9Sstevel@tonic-gate 		kobj_close_file(buf);
1927*7c478bd9Sstevel@tonic-gate 	}
1928*7c478bd9Sstevel@tonic-gate 
1929*7c478bd9Sstevel@tonic-gate 	modp = mod_hold_installed_mod(fullname, 1, &retval);
1930*7c478bd9Sstevel@tonic-gate 	if (modp != NULL) {
1931*7c478bd9Sstevel@tonic-gate 		id = modp->mod_id;
1932*7c478bd9Sstevel@tonic-gate 		if (rmodp) {
1933*7c478bd9Sstevel@tonic-gate 			/* add mod_ref and return *rmodp */
1934*7c478bd9Sstevel@tonic-gate 			mutex_enter(&mod_lock);
1935*7c478bd9Sstevel@tonic-gate 			modp->mod_ref++;
1936*7c478bd9Sstevel@tonic-gate 			mutex_exit(&mod_lock);
1937*7c478bd9Sstevel@tonic-gate 			*rmodp = modp;
1938*7c478bd9Sstevel@tonic-gate 		}
1939*7c478bd9Sstevel@tonic-gate 		mod_release_mod(modp);
1940*7c478bd9Sstevel@tonic-gate 		CPU_STATS_ADDQ(CPU, sys, modload, 1);
1941*7c478bd9Sstevel@tonic-gate 	}
1942*7c478bd9Sstevel@tonic-gate 
1943*7c478bd9Sstevel@tonic-gate done:	if (subdir != NULL)
1944*7c478bd9Sstevel@tonic-gate 		kmem_free(fullname, size);
1945*7c478bd9Sstevel@tonic-gate 	return (rmodp ? retval : id);
1946*7c478bd9Sstevel@tonic-gate }
1947*7c478bd9Sstevel@tonic-gate 
1948*7c478bd9Sstevel@tonic-gate /*
1949*7c478bd9Sstevel@tonic-gate  * This is the primary kernel interface to load a module. It loads and
1950*7c478bd9Sstevel@tonic-gate  * installs the named module.  It does not hold mod_ref of the module, so
1951*7c478bd9Sstevel@tonic-gate  * a module unload attempt can occur at any time - it is up to the
1952*7c478bd9Sstevel@tonic-gate  * _fini/mod_remove implementation to determine if unload will succeed.
1953*7c478bd9Sstevel@tonic-gate  */
1954*7c478bd9Sstevel@tonic-gate int
1955*7c478bd9Sstevel@tonic-gate modload(char *subdir, char *filename)
1956*7c478bd9Sstevel@tonic-gate {
1957*7c478bd9Sstevel@tonic-gate 	return (modrload(subdir, filename, NULL));
1958*7c478bd9Sstevel@tonic-gate }
1959*7c478bd9Sstevel@tonic-gate 
1960*7c478bd9Sstevel@tonic-gate /*
1961*7c478bd9Sstevel@tonic-gate  * Load a module.
1962*7c478bd9Sstevel@tonic-gate  */
1963*7c478bd9Sstevel@tonic-gate int
1964*7c478bd9Sstevel@tonic-gate modloadonly(char *subdir, char *filename)
1965*7c478bd9Sstevel@tonic-gate {
1966*7c478bd9Sstevel@tonic-gate 	struct modctl *modp;
1967*7c478bd9Sstevel@tonic-gate 	char *fullname;
1968*7c478bd9Sstevel@tonic-gate 	size_t size;
1969*7c478bd9Sstevel@tonic-gate 	int id, retval;
1970*7c478bd9Sstevel@tonic-gate 
1971*7c478bd9Sstevel@tonic-gate 	if (subdir != NULL) {
1972*7c478bd9Sstevel@tonic-gate 		/*
1973*7c478bd9Sstevel@tonic-gate 		 * allocate enough space for <subdir>/<filename><NULL>
1974*7c478bd9Sstevel@tonic-gate 		 */
1975*7c478bd9Sstevel@tonic-gate 		size = strlen(subdir) + strlen(filename) + 2;
1976*7c478bd9Sstevel@tonic-gate 		fullname = kmem_zalloc(size, KM_SLEEP);
1977*7c478bd9Sstevel@tonic-gate 		(void) sprintf(fullname, "%s/%s", subdir, filename);
1978*7c478bd9Sstevel@tonic-gate 	} else {
1979*7c478bd9Sstevel@tonic-gate 		fullname = filename;
1980*7c478bd9Sstevel@tonic-gate 	}
1981*7c478bd9Sstevel@tonic-gate 
1982*7c478bd9Sstevel@tonic-gate 	modp = mod_hold_loaded_mod(NULL, fullname, &retval);
1983*7c478bd9Sstevel@tonic-gate 	if (modp) {
1984*7c478bd9Sstevel@tonic-gate 		id = modp->mod_id;
1985*7c478bd9Sstevel@tonic-gate 		mod_release_mod(modp);
1986*7c478bd9Sstevel@tonic-gate 	}
1987*7c478bd9Sstevel@tonic-gate 
1988*7c478bd9Sstevel@tonic-gate 	if (subdir != NULL)
1989*7c478bd9Sstevel@tonic-gate 		kmem_free(fullname, size);
1990*7c478bd9Sstevel@tonic-gate 
1991*7c478bd9Sstevel@tonic-gate 	if (retval == 0)
1992*7c478bd9Sstevel@tonic-gate 		return (id);
1993*7c478bd9Sstevel@tonic-gate 	return (-1);
1994*7c478bd9Sstevel@tonic-gate }
1995*7c478bd9Sstevel@tonic-gate 
1996*7c478bd9Sstevel@tonic-gate /*
1997*7c478bd9Sstevel@tonic-gate  * Try to uninstall and unload a module, removing a reference if caller
1998*7c478bd9Sstevel@tonic-gate  * specifies rmodp.
1999*7c478bd9Sstevel@tonic-gate  */
2000*7c478bd9Sstevel@tonic-gate static int
2001*7c478bd9Sstevel@tonic-gate modunrload(modid_t id, struct modctl **rmodp, int unload)
2002*7c478bd9Sstevel@tonic-gate {
2003*7c478bd9Sstevel@tonic-gate 	struct modctl	*modp;
2004*7c478bd9Sstevel@tonic-gate 	int		retval;
2005*7c478bd9Sstevel@tonic-gate 
2006*7c478bd9Sstevel@tonic-gate 	if (rmodp)
2007*7c478bd9Sstevel@tonic-gate 		*rmodp = NULL;			/* avoid garbage */
2008*7c478bd9Sstevel@tonic-gate 
2009*7c478bd9Sstevel@tonic-gate 	if ((modp = mod_hold_by_id((modid_t)id)) == NULL)
2010*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
2011*7c478bd9Sstevel@tonic-gate 
2012*7c478bd9Sstevel@tonic-gate 	if (rmodp) {
2013*7c478bd9Sstevel@tonic-gate 		mutex_enter(&mod_lock);
2014*7c478bd9Sstevel@tonic-gate 		modp->mod_ref--;
2015*7c478bd9Sstevel@tonic-gate 		mutex_exit(&mod_lock);
2016*7c478bd9Sstevel@tonic-gate 		*rmodp = modp;
2017*7c478bd9Sstevel@tonic-gate 	}
2018*7c478bd9Sstevel@tonic-gate 
2019*7c478bd9Sstevel@tonic-gate 	if (unload) {
2020*7c478bd9Sstevel@tonic-gate 		retval = moduninstall(modp);
2021*7c478bd9Sstevel@tonic-gate 		if (retval == 0) {
2022*7c478bd9Sstevel@tonic-gate 			mod_unload(modp);
2023*7c478bd9Sstevel@tonic-gate 			CPU_STATS_ADDQ(CPU, sys, modunload, 1);
2024*7c478bd9Sstevel@tonic-gate 		} else if (retval == EALREADY)
2025*7c478bd9Sstevel@tonic-gate 			retval = 0;	/* already unloaded, not an error */
2026*7c478bd9Sstevel@tonic-gate 	} else
2027*7c478bd9Sstevel@tonic-gate 		retval = 0;
2028*7c478bd9Sstevel@tonic-gate 
2029*7c478bd9Sstevel@tonic-gate 	mod_release_mod(modp);
2030*7c478bd9Sstevel@tonic-gate 	return (retval);
2031*7c478bd9Sstevel@tonic-gate }
2032*7c478bd9Sstevel@tonic-gate 
2033*7c478bd9Sstevel@tonic-gate /*
2034*7c478bd9Sstevel@tonic-gate  * Uninstall and unload a module.
2035*7c478bd9Sstevel@tonic-gate  */
2036*7c478bd9Sstevel@tonic-gate int
2037*7c478bd9Sstevel@tonic-gate modunload(modid_t id)
2038*7c478bd9Sstevel@tonic-gate {
2039*7c478bd9Sstevel@tonic-gate 	return (modunrload(id, NULL, 1));
2040*7c478bd9Sstevel@tonic-gate }
2041*7c478bd9Sstevel@tonic-gate 
2042*7c478bd9Sstevel@tonic-gate /*
2043*7c478bd9Sstevel@tonic-gate  * Return status of a loaded module.
2044*7c478bd9Sstevel@tonic-gate  */
2045*7c478bd9Sstevel@tonic-gate static int
2046*7c478bd9Sstevel@tonic-gate modinfo(modid_t id, struct modinfo *modinfop)
2047*7c478bd9Sstevel@tonic-gate {
2048*7c478bd9Sstevel@tonic-gate 	struct modctl	*modp;
2049*7c478bd9Sstevel@tonic-gate 	modid_t		mid;
2050*7c478bd9Sstevel@tonic-gate 	int		i;
2051*7c478bd9Sstevel@tonic-gate 
2052*7c478bd9Sstevel@tonic-gate 	mid = modinfop->mi_id;
2053*7c478bd9Sstevel@tonic-gate 	if (modinfop->mi_info & MI_INFO_ALL) {
2054*7c478bd9Sstevel@tonic-gate 		while ((modp = mod_hold_next_by_id(mid++)) != NULL) {
2055*7c478bd9Sstevel@tonic-gate 			if ((modinfop->mi_info & MI_INFO_CNT) ||
2056*7c478bd9Sstevel@tonic-gate 			    modp->mod_installed)
2057*7c478bd9Sstevel@tonic-gate 				break;
2058*7c478bd9Sstevel@tonic-gate 			mod_release_mod(modp);
2059*7c478bd9Sstevel@tonic-gate 		}
2060*7c478bd9Sstevel@tonic-gate 		if (modp == NULL)
2061*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
2062*7c478bd9Sstevel@tonic-gate 	} else {
2063*7c478bd9Sstevel@tonic-gate 		modp = mod_hold_by_id(id);
2064*7c478bd9Sstevel@tonic-gate 		if (modp == NULL)
2065*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
2066*7c478bd9Sstevel@tonic-gate 		if (!(modinfop->mi_info & MI_INFO_CNT) &&
2067*7c478bd9Sstevel@tonic-gate 		    (modp->mod_installed == 0)) {
2068*7c478bd9Sstevel@tonic-gate 			mod_release_mod(modp);
2069*7c478bd9Sstevel@tonic-gate 			return (EINVAL);
2070*7c478bd9Sstevel@tonic-gate 		}
2071*7c478bd9Sstevel@tonic-gate 	}
2072*7c478bd9Sstevel@tonic-gate 
2073*7c478bd9Sstevel@tonic-gate 	modinfop->mi_rev = 0;
2074*7c478bd9Sstevel@tonic-gate 	modinfop->mi_state = 0;
2075*7c478bd9Sstevel@tonic-gate 	for (i = 0; i < MODMAXLINK; i++) {
2076*7c478bd9Sstevel@tonic-gate 		modinfop->mi_msinfo[i].msi_p0 = -1;
2077*7c478bd9Sstevel@tonic-gate 		modinfop->mi_msinfo[i].msi_linkinfo[0] = 0;
2078*7c478bd9Sstevel@tonic-gate 	}
2079*7c478bd9Sstevel@tonic-gate 	if (modp->mod_loaded) {
2080*7c478bd9Sstevel@tonic-gate 		modinfop->mi_state = MI_LOADED;
2081*7c478bd9Sstevel@tonic-gate 		kobj_getmodinfo(modp->mod_mp, modinfop);
2082*7c478bd9Sstevel@tonic-gate 	}
2083*7c478bd9Sstevel@tonic-gate 	if (modp->mod_installed) {
2084*7c478bd9Sstevel@tonic-gate 		modinfop->mi_state |= MI_INSTALLED;
2085*7c478bd9Sstevel@tonic-gate 
2086*7c478bd9Sstevel@tonic-gate 		(void) mod_getinfo(modp, modinfop);
2087*7c478bd9Sstevel@tonic-gate 	}
2088*7c478bd9Sstevel@tonic-gate 
2089*7c478bd9Sstevel@tonic-gate 	modinfop->mi_id = modp->mod_id;
2090*7c478bd9Sstevel@tonic-gate 	modinfop->mi_loadcnt = modp->mod_loadcnt;
2091*7c478bd9Sstevel@tonic-gate 	(void) strcpy(modinfop->mi_name, modp->mod_modname);
2092*7c478bd9Sstevel@tonic-gate 
2093*7c478bd9Sstevel@tonic-gate 	mod_release_mod(modp);
2094*7c478bd9Sstevel@tonic-gate 	return (0);
2095*7c478bd9Sstevel@tonic-gate }
2096*7c478bd9Sstevel@tonic-gate 
2097*7c478bd9Sstevel@tonic-gate static char mod_stub_err[] = "mod_hold_stub: Couldn't load stub module %s";
2098*7c478bd9Sstevel@tonic-gate static char no_err[] = "No error function for weak stub %s";
2099*7c478bd9Sstevel@tonic-gate 
2100*7c478bd9Sstevel@tonic-gate /*
2101*7c478bd9Sstevel@tonic-gate  * used by the stubs themselves to load and hold a module.
2102*7c478bd9Sstevel@tonic-gate  * Returns  0 if the module is successfully held;
2103*7c478bd9Sstevel@tonic-gate  *	    the stub needs to call mod_release_stub().
2104*7c478bd9Sstevel@tonic-gate  *	    -1 if the stub should just call the err_fcn.
2105*7c478bd9Sstevel@tonic-gate  * Note that this code is stretched out so that we avoid subroutine calls
2106*7c478bd9Sstevel@tonic-gate  * and optimize for the most likely case.  That is, the case where the
2107*7c478bd9Sstevel@tonic-gate  * module is loaded and installed and not held.  In that case we just inc
2108*7c478bd9Sstevel@tonic-gate  * the mod_ref count and continue.
2109*7c478bd9Sstevel@tonic-gate  */
2110*7c478bd9Sstevel@tonic-gate int
2111*7c478bd9Sstevel@tonic-gate mod_hold_stub(struct mod_stub_info *stub)
2112*7c478bd9Sstevel@tonic-gate {
2113*7c478bd9Sstevel@tonic-gate 	struct modctl *mp;
2114*7c478bd9Sstevel@tonic-gate 	struct mod_modinfo *mip;
2115*7c478bd9Sstevel@tonic-gate 
2116*7c478bd9Sstevel@tonic-gate 	mip = stub->mods_modinfo;
2117*7c478bd9Sstevel@tonic-gate 
2118*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
2119*7c478bd9Sstevel@tonic-gate 
2120*7c478bd9Sstevel@tonic-gate 	/* we do mod_hold_by_modctl inline for speed */
2121*7c478bd9Sstevel@tonic-gate 
2122*7c478bd9Sstevel@tonic-gate mod_check_again:
2123*7c478bd9Sstevel@tonic-gate 	if ((mp = mip->mp) != NULL) {
2124*7c478bd9Sstevel@tonic-gate 		if (mp->mod_busy == 0) {
2125*7c478bd9Sstevel@tonic-gate 			if (mp->mod_installed) {
2126*7c478bd9Sstevel@tonic-gate 				/* increment the reference count */
2127*7c478bd9Sstevel@tonic-gate 				mp->mod_ref++;
2128*7c478bd9Sstevel@tonic-gate 				ASSERT(mp->mod_ref && mp->mod_installed);
2129*7c478bd9Sstevel@tonic-gate 				mutex_exit(&mod_lock);
2130*7c478bd9Sstevel@tonic-gate 				return (0);
2131*7c478bd9Sstevel@tonic-gate 			} else {
2132*7c478bd9Sstevel@tonic-gate 				mp->mod_busy = 1;
2133*7c478bd9Sstevel@tonic-gate 				mp->mod_inprogress_thread =
2134*7c478bd9Sstevel@tonic-gate 				    (curthread == NULL ?
2135*7c478bd9Sstevel@tonic-gate 				    (kthread_id_t)-1 : curthread);
2136*7c478bd9Sstevel@tonic-gate 			}
2137*7c478bd9Sstevel@tonic-gate 		} else {
2138*7c478bd9Sstevel@tonic-gate 			/*
2139*7c478bd9Sstevel@tonic-gate 			 * wait one time and then go see if someone
2140*7c478bd9Sstevel@tonic-gate 			 * else has resolved the stub (set mip->mp).
2141*7c478bd9Sstevel@tonic-gate 			 */
2142*7c478bd9Sstevel@tonic-gate 			if (mod_hold_by_modctl(mp,
2143*7c478bd9Sstevel@tonic-gate 			    MOD_WAIT_ONCE | MOD_LOCK_HELD))
2144*7c478bd9Sstevel@tonic-gate 				goto mod_check_again;
2145*7c478bd9Sstevel@tonic-gate 
2146*7c478bd9Sstevel@tonic-gate 			/*
2147*7c478bd9Sstevel@tonic-gate 			 * what we have now may have been unloaded!, in
2148*7c478bd9Sstevel@tonic-gate 			 * that case, mip->mp will be NULL, we'll hit this
2149*7c478bd9Sstevel@tonic-gate 			 * module and load again..
2150*7c478bd9Sstevel@tonic-gate 			 */
2151*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_PANIC, "mod_hold_stub should have blocked");
2152*7c478bd9Sstevel@tonic-gate 		}
2153*7c478bd9Sstevel@tonic-gate 		mutex_exit(&mod_lock);
2154*7c478bd9Sstevel@tonic-gate 	} else {
2155*7c478bd9Sstevel@tonic-gate 		/* first time we've hit this module */
2156*7c478bd9Sstevel@tonic-gate 		mutex_exit(&mod_lock);
2157*7c478bd9Sstevel@tonic-gate 		mp = mod_hold_by_name(mip->modm_module_name);
2158*7c478bd9Sstevel@tonic-gate 		mip->mp = mp;
2159*7c478bd9Sstevel@tonic-gate 	}
2160*7c478bd9Sstevel@tonic-gate 
2161*7c478bd9Sstevel@tonic-gate 	/*
2162*7c478bd9Sstevel@tonic-gate 	 * If we are here, it means that the following conditions
2163*7c478bd9Sstevel@tonic-gate 	 * are satisfied.
2164*7c478bd9Sstevel@tonic-gate 	 *
2165*7c478bd9Sstevel@tonic-gate 	 * mip->mp != NULL
2166*7c478bd9Sstevel@tonic-gate 	 * this thread has set the mp->mod_busy = 1
2167*7c478bd9Sstevel@tonic-gate 	 * mp->mod_installed = 0
2168*7c478bd9Sstevel@tonic-gate 	 *
2169*7c478bd9Sstevel@tonic-gate 	 */
2170*7c478bd9Sstevel@tonic-gate 	ASSERT(mp != NULL);
2171*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy == 1);
2172*7c478bd9Sstevel@tonic-gate 
2173*7c478bd9Sstevel@tonic-gate 	if (mp->mod_installed == 0) {
2174*7c478bd9Sstevel@tonic-gate 		/* Module not loaded, if weak stub don't load it */
2175*7c478bd9Sstevel@tonic-gate 		if (stub->mods_flag & MODS_WEAK) {
2176*7c478bd9Sstevel@tonic-gate 			if (stub->mods_errfcn == NULL) {
2177*7c478bd9Sstevel@tonic-gate 				mod_release_mod(mp);
2178*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_PANIC, no_err,
2179*7c478bd9Sstevel@tonic-gate 				    mip->modm_module_name);
2180*7c478bd9Sstevel@tonic-gate 			}
2181*7c478bd9Sstevel@tonic-gate 		} else {
2182*7c478bd9Sstevel@tonic-gate 			/* Not a weak stub so load the module */
2183*7c478bd9Sstevel@tonic-gate 
2184*7c478bd9Sstevel@tonic-gate 			if (mod_load(mp, 1) != 0 || modinstall(mp) != 0) {
2185*7c478bd9Sstevel@tonic-gate 				/*
2186*7c478bd9Sstevel@tonic-gate 				 * If mod_load() was successful
2187*7c478bd9Sstevel@tonic-gate 				 * and modinstall() failed, then
2188*7c478bd9Sstevel@tonic-gate 				 * unload the module.
2189*7c478bd9Sstevel@tonic-gate 				 */
2190*7c478bd9Sstevel@tonic-gate 				if (mp->mod_loaded)
2191*7c478bd9Sstevel@tonic-gate 					mod_unload(mp);
2192*7c478bd9Sstevel@tonic-gate 
2193*7c478bd9Sstevel@tonic-gate 				mod_release_mod(mp);
2194*7c478bd9Sstevel@tonic-gate 				if (stub->mods_errfcn == NULL) {
2195*7c478bd9Sstevel@tonic-gate 					cmn_err(CE_PANIC, mod_stub_err,
2196*7c478bd9Sstevel@tonic-gate 					    mip->modm_module_name);
2197*7c478bd9Sstevel@tonic-gate 				} else {
2198*7c478bd9Sstevel@tonic-gate 					return (-1);
2199*7c478bd9Sstevel@tonic-gate 				}
2200*7c478bd9Sstevel@tonic-gate 			}
2201*7c478bd9Sstevel@tonic-gate 		}
2202*7c478bd9Sstevel@tonic-gate 	}
2203*7c478bd9Sstevel@tonic-gate 
2204*7c478bd9Sstevel@tonic-gate 	/*
2205*7c478bd9Sstevel@tonic-gate 	 * At this point module is held and loaded. Release
2206*7c478bd9Sstevel@tonic-gate 	 * the mod_busy and mod_inprogress_thread before
2207*7c478bd9Sstevel@tonic-gate 	 * returning. We actually call mod_release() here so
2208*7c478bd9Sstevel@tonic-gate 	 * that if another stub wants to access this module,
2209*7c478bd9Sstevel@tonic-gate 	 * it can do so. mod_ref is incremented before mod_release()
2210*7c478bd9Sstevel@tonic-gate 	 * is called to prevent someone else from snatching the
2211*7c478bd9Sstevel@tonic-gate 	 * module from this thread.
2212*7c478bd9Sstevel@tonic-gate 	 */
2213*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
2214*7c478bd9Sstevel@tonic-gate 	mp->mod_ref++;
2215*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_ref &&
2216*7c478bd9Sstevel@tonic-gate 	    (mp->mod_loaded || (stub->mods_flag & MODS_WEAK)));
2217*7c478bd9Sstevel@tonic-gate 	mod_release(mp);
2218*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
2219*7c478bd9Sstevel@tonic-gate 	return (0);
2220*7c478bd9Sstevel@tonic-gate }
2221*7c478bd9Sstevel@tonic-gate 
2222*7c478bd9Sstevel@tonic-gate void
2223*7c478bd9Sstevel@tonic-gate mod_release_stub(struct mod_stub_info *stub)
2224*7c478bd9Sstevel@tonic-gate {
2225*7c478bd9Sstevel@tonic-gate 	struct modctl *mp = stub->mods_modinfo->mp;
2226*7c478bd9Sstevel@tonic-gate 
2227*7c478bd9Sstevel@tonic-gate 	/* inline mod_release_mod */
2228*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
2229*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_ref &&
2230*7c478bd9Sstevel@tonic-gate 	    (mp->mod_loaded || (stub->mods_flag & MODS_WEAK)));
2231*7c478bd9Sstevel@tonic-gate 	mp->mod_ref--;
2232*7c478bd9Sstevel@tonic-gate 	if (mp->mod_want) {
2233*7c478bd9Sstevel@tonic-gate 		mp->mod_want = 0;
2234*7c478bd9Sstevel@tonic-gate 		cv_broadcast(&mod_cv);
2235*7c478bd9Sstevel@tonic-gate 	}
2236*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
2237*7c478bd9Sstevel@tonic-gate }
2238*7c478bd9Sstevel@tonic-gate 
2239*7c478bd9Sstevel@tonic-gate static struct modctl *
2240*7c478bd9Sstevel@tonic-gate mod_hold_loaded_mod(struct modctl *dep, char *filename, int *status)
2241*7c478bd9Sstevel@tonic-gate {
2242*7c478bd9Sstevel@tonic-gate 	struct modctl *modp;
2243*7c478bd9Sstevel@tonic-gate 	int retval;
2244*7c478bd9Sstevel@tonic-gate 
2245*7c478bd9Sstevel@tonic-gate 	/*
2246*7c478bd9Sstevel@tonic-gate 	 * Hold the module.
2247*7c478bd9Sstevel@tonic-gate 	 */
2248*7c478bd9Sstevel@tonic-gate 	modp = mod_hold_by_name_requisite(dep, filename);
2249*7c478bd9Sstevel@tonic-gate 	if (modp) {
2250*7c478bd9Sstevel@tonic-gate 		retval = mod_load(modp, 1);
2251*7c478bd9Sstevel@tonic-gate 		if (retval != 0) {
2252*7c478bd9Sstevel@tonic-gate 			mod_release_mod(modp);
2253*7c478bd9Sstevel@tonic-gate 			modp = NULL;
2254*7c478bd9Sstevel@tonic-gate 		}
2255*7c478bd9Sstevel@tonic-gate 		*status = retval;
2256*7c478bd9Sstevel@tonic-gate 	} else {
2257*7c478bd9Sstevel@tonic-gate 		*status = ENOSPC;
2258*7c478bd9Sstevel@tonic-gate 	}
2259*7c478bd9Sstevel@tonic-gate 
2260*7c478bd9Sstevel@tonic-gate 	/*
2261*7c478bd9Sstevel@tonic-gate 	 * if dep is not NULL, clear the module dependency information.
2262*7c478bd9Sstevel@tonic-gate 	 * This information is set in mod_hold_by_name_common().
2263*7c478bd9Sstevel@tonic-gate 	 */
2264*7c478bd9Sstevel@tonic-gate 	if (dep != NULL && dep->mod_requisite_loading != NULL) {
2265*7c478bd9Sstevel@tonic-gate 		ASSERT(dep->mod_busy);
2266*7c478bd9Sstevel@tonic-gate 		dep->mod_requisite_loading = NULL;
2267*7c478bd9Sstevel@tonic-gate 	}
2268*7c478bd9Sstevel@tonic-gate 
2269*7c478bd9Sstevel@tonic-gate 	return (modp);
2270*7c478bd9Sstevel@tonic-gate }
2271*7c478bd9Sstevel@tonic-gate 
2272*7c478bd9Sstevel@tonic-gate /*
2273*7c478bd9Sstevel@tonic-gate  * hold, load, and install the named module
2274*7c478bd9Sstevel@tonic-gate  */
2275*7c478bd9Sstevel@tonic-gate static struct modctl *
2276*7c478bd9Sstevel@tonic-gate mod_hold_installed_mod(char *name, int usepath, int *r)
2277*7c478bd9Sstevel@tonic-gate {
2278*7c478bd9Sstevel@tonic-gate 	struct modctl *modp;
2279*7c478bd9Sstevel@tonic-gate 	int retval;
2280*7c478bd9Sstevel@tonic-gate 
2281*7c478bd9Sstevel@tonic-gate 	/*
2282*7c478bd9Sstevel@tonic-gate 	 * Hold the module.
2283*7c478bd9Sstevel@tonic-gate 	 */
2284*7c478bd9Sstevel@tonic-gate 	modp = mod_hold_by_name(name);
2285*7c478bd9Sstevel@tonic-gate 	if (modp) {
2286*7c478bd9Sstevel@tonic-gate 		retval = mod_load(modp, usepath);
2287*7c478bd9Sstevel@tonic-gate 		if (retval != 0) {
2288*7c478bd9Sstevel@tonic-gate 			mod_release_mod(modp);
2289*7c478bd9Sstevel@tonic-gate 			modp = NULL;
2290*7c478bd9Sstevel@tonic-gate 			*r = retval;
2291*7c478bd9Sstevel@tonic-gate 		} else {
2292*7c478bd9Sstevel@tonic-gate 			if ((*r = modinstall(modp)) != 0) {
2293*7c478bd9Sstevel@tonic-gate 				/*
2294*7c478bd9Sstevel@tonic-gate 				 * We loaded it, but failed to _init() it.
2295*7c478bd9Sstevel@tonic-gate 				 * Be kind to developers -- force it
2296*7c478bd9Sstevel@tonic-gate 				 * out of memory now so that the next
2297*7c478bd9Sstevel@tonic-gate 				 * attempt to use the module will cause
2298*7c478bd9Sstevel@tonic-gate 				 * a reload.  See 1093793.
2299*7c478bd9Sstevel@tonic-gate 				 */
2300*7c478bd9Sstevel@tonic-gate 				mod_unload(modp);
2301*7c478bd9Sstevel@tonic-gate 				mod_release_mod(modp);
2302*7c478bd9Sstevel@tonic-gate 				modp = NULL;
2303*7c478bd9Sstevel@tonic-gate 			}
2304*7c478bd9Sstevel@tonic-gate 		}
2305*7c478bd9Sstevel@tonic-gate 	} else {
2306*7c478bd9Sstevel@tonic-gate 		*r = ENOSPC;
2307*7c478bd9Sstevel@tonic-gate 	}
2308*7c478bd9Sstevel@tonic-gate 	return (modp);
2309*7c478bd9Sstevel@tonic-gate }
2310*7c478bd9Sstevel@tonic-gate 
2311*7c478bd9Sstevel@tonic-gate static char mod_excl_msg[] =
2312*7c478bd9Sstevel@tonic-gate 	"module %s(%s) is EXCLUDED and will not be loaded\n";
2313*7c478bd9Sstevel@tonic-gate static char mod_init_msg[] = "loadmodule:%s(%s): _init() error %d\n";
2314*7c478bd9Sstevel@tonic-gate 
2315*7c478bd9Sstevel@tonic-gate /*
2316*7c478bd9Sstevel@tonic-gate  * This routine is needed for dependencies.  Users specify dependencies
2317*7c478bd9Sstevel@tonic-gate  * by declaring a character array initialized to filenames of dependents.
2318*7c478bd9Sstevel@tonic-gate  * So the code that handles dependents deals with filenames (and not
2319*7c478bd9Sstevel@tonic-gate  * module names) because that's all it has.  We load by filename and once
2320*7c478bd9Sstevel@tonic-gate  * we've loaded a file we can get the module name.
2321*7c478bd9Sstevel@tonic-gate  * Unfortunately there isn't a single unified filename/modulename namespace.
2322*7c478bd9Sstevel@tonic-gate  * C'est la vie.
2323*7c478bd9Sstevel@tonic-gate  *
2324*7c478bd9Sstevel@tonic-gate  * We allow the name being looked up to be prepended by an optional
2325*7c478bd9Sstevel@tonic-gate  * subdirectory e.g. we can lookup (NULL, "fs/ufs") or ("fs", "ufs")
2326*7c478bd9Sstevel@tonic-gate  */
2327*7c478bd9Sstevel@tonic-gate struct modctl *
2328*7c478bd9Sstevel@tonic-gate mod_find_by_filename(char *subdir, char *filename)
2329*7c478bd9Sstevel@tonic-gate {
2330*7c478bd9Sstevel@tonic-gate 	struct modctl	*mp;
2331*7c478bd9Sstevel@tonic-gate 	size_t		sublen;
2332*7c478bd9Sstevel@tonic-gate 
2333*7c478bd9Sstevel@tonic-gate 	ASSERT(!MUTEX_HELD(&mod_lock));
2334*7c478bd9Sstevel@tonic-gate 	if (subdir != NULL)
2335*7c478bd9Sstevel@tonic-gate 		sublen = strlen(subdir);
2336*7c478bd9Sstevel@tonic-gate 	else
2337*7c478bd9Sstevel@tonic-gate 		sublen = 0;
2338*7c478bd9Sstevel@tonic-gate 
2339*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
2340*7c478bd9Sstevel@tonic-gate 	mp = &modules;
2341*7c478bd9Sstevel@tonic-gate 	do {
2342*7c478bd9Sstevel@tonic-gate 		if (sublen) {
2343*7c478bd9Sstevel@tonic-gate 			char *mod_filename = mp->mod_filename;
2344*7c478bd9Sstevel@tonic-gate 
2345*7c478bd9Sstevel@tonic-gate 			if (strncmp(subdir, mod_filename, sublen) == 0 &&
2346*7c478bd9Sstevel@tonic-gate 			    mod_filename[sublen] == '/' &&
2347*7c478bd9Sstevel@tonic-gate 			    strcmp(filename, &mod_filename[sublen + 1]) == 0) {
2348*7c478bd9Sstevel@tonic-gate 				mutex_exit(&mod_lock);
2349*7c478bd9Sstevel@tonic-gate 				return (mp);
2350*7c478bd9Sstevel@tonic-gate 			}
2351*7c478bd9Sstevel@tonic-gate 		} else if (strcmp(filename, mp->mod_filename) == 0) {
2352*7c478bd9Sstevel@tonic-gate 			mutex_exit(&mod_lock);
2353*7c478bd9Sstevel@tonic-gate 			return (mp);
2354*7c478bd9Sstevel@tonic-gate 		}
2355*7c478bd9Sstevel@tonic-gate 	} while ((mp = mp->mod_next) != &modules);
2356*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
2357*7c478bd9Sstevel@tonic-gate 	return (NULL);
2358*7c478bd9Sstevel@tonic-gate }
2359*7c478bd9Sstevel@tonic-gate 
2360*7c478bd9Sstevel@tonic-gate /*
2361*7c478bd9Sstevel@tonic-gate  * Check for circular dependencies.  This is called from do_dependents()
2362*7c478bd9Sstevel@tonic-gate  * in kobj.c.  If we are the thread already loading this module, then
2363*7c478bd9Sstevel@tonic-gate  * we're trying to load a dependent that we're already loading which
2364*7c478bd9Sstevel@tonic-gate  * means the user specified circular dependencies.
2365*7c478bd9Sstevel@tonic-gate  */
2366*7c478bd9Sstevel@tonic-gate static int
2367*7c478bd9Sstevel@tonic-gate mod_circdep(struct modctl *modp)
2368*7c478bd9Sstevel@tonic-gate {
2369*7c478bd9Sstevel@tonic-gate 	struct modctl	*rmod;
2370*7c478bd9Sstevel@tonic-gate 
2371*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mod_lock));
2372*7c478bd9Sstevel@tonic-gate 
2373*7c478bd9Sstevel@tonic-gate 	/*
2374*7c478bd9Sstevel@tonic-gate 	 * Check the mod_inprogress_thread first.
2375*7c478bd9Sstevel@tonic-gate 	 * mod_inprogress_thread is used in mod_hold_stub()
2376*7c478bd9Sstevel@tonic-gate 	 * directly to improve performance.
2377*7c478bd9Sstevel@tonic-gate 	 */
2378*7c478bd9Sstevel@tonic-gate 	if (modp->mod_inprogress_thread == curthread)
2379*7c478bd9Sstevel@tonic-gate 		return (1);
2380*7c478bd9Sstevel@tonic-gate 
2381*7c478bd9Sstevel@tonic-gate 	/*
2382*7c478bd9Sstevel@tonic-gate 	 * Check the module circular dependencies.
2383*7c478bd9Sstevel@tonic-gate 	 */
2384*7c478bd9Sstevel@tonic-gate 	for (rmod = modp; rmod != NULL; rmod = rmod->mod_requisite_loading) {
2385*7c478bd9Sstevel@tonic-gate 		/*
2386*7c478bd9Sstevel@tonic-gate 		 * Check if there is a module circular dependency.
2387*7c478bd9Sstevel@tonic-gate 		 */
2388*7c478bd9Sstevel@tonic-gate 		if (rmod->mod_requisite_loading == modp)
2389*7c478bd9Sstevel@tonic-gate 			return (1);
2390*7c478bd9Sstevel@tonic-gate 	}
2391*7c478bd9Sstevel@tonic-gate 	return (0);
2392*7c478bd9Sstevel@tonic-gate }
2393*7c478bd9Sstevel@tonic-gate 
2394*7c478bd9Sstevel@tonic-gate static int
2395*7c478bd9Sstevel@tonic-gate mod_getinfo(struct modctl *modp, struct modinfo *modinfop)
2396*7c478bd9Sstevel@tonic-gate {
2397*7c478bd9Sstevel@tonic-gate 	int (*func)(struct modinfo *);
2398*7c478bd9Sstevel@tonic-gate 	int retval;
2399*7c478bd9Sstevel@tonic-gate 
2400*7c478bd9Sstevel@tonic-gate 	ASSERT(modp->mod_busy);
2401*7c478bd9Sstevel@tonic-gate 
2402*7c478bd9Sstevel@tonic-gate 	/* primary modules don't do getinfo */
2403*7c478bd9Sstevel@tonic-gate 	if (modp->mod_prim)
2404*7c478bd9Sstevel@tonic-gate 		return (0);
2405*7c478bd9Sstevel@tonic-gate 
2406*7c478bd9Sstevel@tonic-gate 	func = (int (*)(struct modinfo *))kobj_lookup(modp->mod_mp, "_info");
2407*7c478bd9Sstevel@tonic-gate 
2408*7c478bd9Sstevel@tonic-gate 	if (kobj_addrcheck(modp->mod_mp, (caddr_t)func)) {
2409*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "_info() not defined properly in %s",
2410*7c478bd9Sstevel@tonic-gate 		    modp->mod_filename);
2411*7c478bd9Sstevel@tonic-gate 		/*
2412*7c478bd9Sstevel@tonic-gate 		 * The semantics of mod_info(9F) are that 0 is failure
2413*7c478bd9Sstevel@tonic-gate 		 * and non-zero is success.
2414*7c478bd9Sstevel@tonic-gate 		 */
2415*7c478bd9Sstevel@tonic-gate 		retval = 0;
2416*7c478bd9Sstevel@tonic-gate 	} else
2417*7c478bd9Sstevel@tonic-gate 		retval = (*func)(modinfop);	/* call _info() function */
2418*7c478bd9Sstevel@tonic-gate 
2419*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_USERDEBUG)
2420*7c478bd9Sstevel@tonic-gate 		printf("Returned from _info, retval = %x\n", retval);
2421*7c478bd9Sstevel@tonic-gate 
2422*7c478bd9Sstevel@tonic-gate 	return (retval);
2423*7c478bd9Sstevel@tonic-gate }
2424*7c478bd9Sstevel@tonic-gate 
2425*7c478bd9Sstevel@tonic-gate static void
2426*7c478bd9Sstevel@tonic-gate modadd(struct modctl *mp)
2427*7c478bd9Sstevel@tonic-gate {
2428*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mod_lock));
2429*7c478bd9Sstevel@tonic-gate 
2430*7c478bd9Sstevel@tonic-gate 	mp->mod_id = last_module_id++;
2431*7c478bd9Sstevel@tonic-gate 	mp->mod_next = &modules;
2432*7c478bd9Sstevel@tonic-gate 	mp->mod_prev = modules.mod_prev;
2433*7c478bd9Sstevel@tonic-gate 	modules.mod_prev->mod_next = mp;
2434*7c478bd9Sstevel@tonic-gate 	modules.mod_prev = mp;
2435*7c478bd9Sstevel@tonic-gate }
2436*7c478bd9Sstevel@tonic-gate 
2437*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2438*7c478bd9Sstevel@tonic-gate static struct modctl *
2439*7c478bd9Sstevel@tonic-gate allocate_modp(char *filename, char *modname)
2440*7c478bd9Sstevel@tonic-gate {
2441*7c478bd9Sstevel@tonic-gate 	struct modctl *mp;
2442*7c478bd9Sstevel@tonic-gate 
2443*7c478bd9Sstevel@tonic-gate 	mp = kobj_zalloc(sizeof (*mp), KM_SLEEP);
2444*7c478bd9Sstevel@tonic-gate 	mp->mod_modname = kobj_zalloc(strlen(modname) + 1, KM_SLEEP);
2445*7c478bd9Sstevel@tonic-gate 	(void) strcpy(mp->mod_modname, modname);
2446*7c478bd9Sstevel@tonic-gate 	return (mp);
2447*7c478bd9Sstevel@tonic-gate }
2448*7c478bd9Sstevel@tonic-gate 
2449*7c478bd9Sstevel@tonic-gate /*
2450*7c478bd9Sstevel@tonic-gate  * Get the value of a symbol.  This is a wrapper routine that
2451*7c478bd9Sstevel@tonic-gate  * calls kobj_getsymvalue().  kobj_getsymvalue() may go away but this
2452*7c478bd9Sstevel@tonic-gate  * wrapper will prevent callers from noticing.
2453*7c478bd9Sstevel@tonic-gate  */
2454*7c478bd9Sstevel@tonic-gate uintptr_t
2455*7c478bd9Sstevel@tonic-gate modgetsymvalue(char *name, int kernelonly)
2456*7c478bd9Sstevel@tonic-gate {
2457*7c478bd9Sstevel@tonic-gate 	return (kobj_getsymvalue(name, kernelonly));
2458*7c478bd9Sstevel@tonic-gate }
2459*7c478bd9Sstevel@tonic-gate 
2460*7c478bd9Sstevel@tonic-gate /*
2461*7c478bd9Sstevel@tonic-gate  * Get the symbol nearest an address.  This is a wrapper routine that
2462*7c478bd9Sstevel@tonic-gate  * calls kobj_getsymname().  kobj_getsymname() may go away but this
2463*7c478bd9Sstevel@tonic-gate  * wrapper will prevent callers from noticing.
2464*7c478bd9Sstevel@tonic-gate  */
2465*7c478bd9Sstevel@tonic-gate char *
2466*7c478bd9Sstevel@tonic-gate modgetsymname(uintptr_t value, ulong_t *offset)
2467*7c478bd9Sstevel@tonic-gate {
2468*7c478bd9Sstevel@tonic-gate 	return (kobj_getsymname(value, offset));
2469*7c478bd9Sstevel@tonic-gate }
2470*7c478bd9Sstevel@tonic-gate 
2471*7c478bd9Sstevel@tonic-gate /*
2472*7c478bd9Sstevel@tonic-gate  * Lookup a symbol in a specified module.  This is a wrapper routine that
2473*7c478bd9Sstevel@tonic-gate  * calls kobj_lookup().  kobj_lookup() may go away but this
2474*7c478bd9Sstevel@tonic-gate  * wrapper will prevent callers from noticing.
2475*7c478bd9Sstevel@tonic-gate  */
2476*7c478bd9Sstevel@tonic-gate uintptr_t
2477*7c478bd9Sstevel@tonic-gate modlookup(char *modname, char *symname)
2478*7c478bd9Sstevel@tonic-gate {
2479*7c478bd9Sstevel@tonic-gate 	struct modctl *modp;
2480*7c478bd9Sstevel@tonic-gate 	uintptr_t val;
2481*7c478bd9Sstevel@tonic-gate 
2482*7c478bd9Sstevel@tonic-gate 	if ((modp = mod_hold_by_name(modname)) == NULL)
2483*7c478bd9Sstevel@tonic-gate 		return (0);
2484*7c478bd9Sstevel@tonic-gate 	val = kobj_lookup(modp->mod_mp, symname);
2485*7c478bd9Sstevel@tonic-gate 	mod_release_mod(modp);
2486*7c478bd9Sstevel@tonic-gate 	return (val);
2487*7c478bd9Sstevel@tonic-gate }
2488*7c478bd9Sstevel@tonic-gate 
2489*7c478bd9Sstevel@tonic-gate /*
2490*7c478bd9Sstevel@tonic-gate  * Ask the user for the name of the system file and the default path
2491*7c478bd9Sstevel@tonic-gate  * for modules.
2492*7c478bd9Sstevel@tonic-gate  */
2493*7c478bd9Sstevel@tonic-gate void
2494*7c478bd9Sstevel@tonic-gate mod_askparams()
2495*7c478bd9Sstevel@tonic-gate {
2496*7c478bd9Sstevel@tonic-gate 	static char s0[64];
2497*7c478bd9Sstevel@tonic-gate 	intptr_t fd;
2498*7c478bd9Sstevel@tonic-gate 
2499*7c478bd9Sstevel@tonic-gate 	if ((fd = kobj_open(systemfile)) != -1L)
2500*7c478bd9Sstevel@tonic-gate 		kobj_close(fd);
2501*7c478bd9Sstevel@tonic-gate 	else
2502*7c478bd9Sstevel@tonic-gate 		systemfile = NULL;
2503*7c478bd9Sstevel@tonic-gate 
2504*7c478bd9Sstevel@tonic-gate 	/*CONSTANTCONDITION*/
2505*7c478bd9Sstevel@tonic-gate 	while (1) {
2506*7c478bd9Sstevel@tonic-gate 		printf("Name of system file [%s]:  ",
2507*7c478bd9Sstevel@tonic-gate 			systemfile ? systemfile : "/dev/null");
2508*7c478bd9Sstevel@tonic-gate 
2509*7c478bd9Sstevel@tonic-gate 		console_gets(s0, sizeof (s0));
2510*7c478bd9Sstevel@tonic-gate 
2511*7c478bd9Sstevel@tonic-gate 		if (s0[0] == '\0')
2512*7c478bd9Sstevel@tonic-gate 			break;
2513*7c478bd9Sstevel@tonic-gate 		else if (strcmp(s0, "/dev/null") == 0) {
2514*7c478bd9Sstevel@tonic-gate 			systemfile = NULL;
2515*7c478bd9Sstevel@tonic-gate 			break;
2516*7c478bd9Sstevel@tonic-gate 		} else {
2517*7c478bd9Sstevel@tonic-gate 			if ((fd = kobj_open(s0)) != -1L) {
2518*7c478bd9Sstevel@tonic-gate 				kobj_close(fd);
2519*7c478bd9Sstevel@tonic-gate 				systemfile = s0;
2520*7c478bd9Sstevel@tonic-gate 				break;
2521*7c478bd9Sstevel@tonic-gate 			}
2522*7c478bd9Sstevel@tonic-gate 		}
2523*7c478bd9Sstevel@tonic-gate 		printf("can't find file %s\n", s0);
2524*7c478bd9Sstevel@tonic-gate 	}
2525*7c478bd9Sstevel@tonic-gate }
2526*7c478bd9Sstevel@tonic-gate 
2527*7c478bd9Sstevel@tonic-gate static char loading_msg[] = "loading '%s' id %d\n";
2528*7c478bd9Sstevel@tonic-gate static char load_msg[] = "load '%s' id %d loaded @ 0x%p/0x%p size %d/%d\n";
2529*7c478bd9Sstevel@tonic-gate 
2530*7c478bd9Sstevel@tonic-gate /*
2531*7c478bd9Sstevel@tonic-gate  * Common code for loading a module (but not installing it).
2532*7c478bd9Sstevel@tonic-gate  * Handoff the task of module loading to a seperate thread
2533*7c478bd9Sstevel@tonic-gate  * with a large stack if possible, since this code may recurse a few times.
2534*7c478bd9Sstevel@tonic-gate  * Return zero if there are no errors or an errno value.
2535*7c478bd9Sstevel@tonic-gate  */
2536*7c478bd9Sstevel@tonic-gate static int
2537*7c478bd9Sstevel@tonic-gate mod_load(struct modctl *mp, int usepath)
2538*7c478bd9Sstevel@tonic-gate {
2539*7c478bd9Sstevel@tonic-gate 	int		retval;
2540*7c478bd9Sstevel@tonic-gate 	struct modinfo	*modinfop = NULL;
2541*7c478bd9Sstevel@tonic-gate 	struct loadmt	lt;
2542*7c478bd9Sstevel@tonic-gate 
2543*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&mod_lock));
2544*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy);
2545*7c478bd9Sstevel@tonic-gate 
2546*7c478bd9Sstevel@tonic-gate 	if (mp->mod_loaded)
2547*7c478bd9Sstevel@tonic-gate 		return (0);
2548*7c478bd9Sstevel@tonic-gate 
2549*7c478bd9Sstevel@tonic-gate 	if (mod_sysctl(SYS_CHECK_EXCLUDE, mp->mod_modname) != 0 ||
2550*7c478bd9Sstevel@tonic-gate 	    mod_sysctl(SYS_CHECK_EXCLUDE, mp->mod_filename) != 0) {
2551*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_LOADMSG) {
2552*7c478bd9Sstevel@tonic-gate 			printf(mod_excl_msg, mp->mod_filename,
2553*7c478bd9Sstevel@tonic-gate 				mp->mod_modname);
2554*7c478bd9Sstevel@tonic-gate 		}
2555*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
2556*7c478bd9Sstevel@tonic-gate 	}
2557*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_LOADMSG2)
2558*7c478bd9Sstevel@tonic-gate 		printf(loading_msg, mp->mod_filename, mp->mod_id);
2559*7c478bd9Sstevel@tonic-gate 
2560*7c478bd9Sstevel@tonic-gate 	if (curthread != &t0) {
2561*7c478bd9Sstevel@tonic-gate 		lt.mp = mp;
2562*7c478bd9Sstevel@tonic-gate 		lt.usepath = usepath;
2563*7c478bd9Sstevel@tonic-gate 		lt.owner = curthread;
2564*7c478bd9Sstevel@tonic-gate 		sema_init(&lt.sema, 0, NULL, SEMA_DEFAULT, NULL);
2565*7c478bd9Sstevel@tonic-gate 
2566*7c478bd9Sstevel@tonic-gate 		/* create thread to hand of call to */
2567*7c478bd9Sstevel@tonic-gate 		(void) thread_create(NULL, DEFAULTSTKSZ * 2,
2568*7c478bd9Sstevel@tonic-gate 		    modload_thread, &lt, 0, &p0, TS_RUN, maxclsyspri);
2569*7c478bd9Sstevel@tonic-gate 
2570*7c478bd9Sstevel@tonic-gate 		/* wait for thread to complete kobj_load_module */
2571*7c478bd9Sstevel@tonic-gate 		sema_p(&lt.sema);
2572*7c478bd9Sstevel@tonic-gate 
2573*7c478bd9Sstevel@tonic-gate 		sema_destroy(&lt.sema);
2574*7c478bd9Sstevel@tonic-gate 		retval = lt.retval;
2575*7c478bd9Sstevel@tonic-gate 	} else
2576*7c478bd9Sstevel@tonic-gate 		retval = kobj_load_module(mp, usepath);
2577*7c478bd9Sstevel@tonic-gate 
2578*7c478bd9Sstevel@tonic-gate 	if (mp->mod_mp) {
2579*7c478bd9Sstevel@tonic-gate 		ASSERT(retval == 0);
2580*7c478bd9Sstevel@tonic-gate 		mp->mod_loaded = 1;
2581*7c478bd9Sstevel@tonic-gate 		mp->mod_loadcnt++;
2582*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_LOADMSG) {
2583*7c478bd9Sstevel@tonic-gate 			printf(load_msg, mp->mod_filename, mp->mod_id,
2584*7c478bd9Sstevel@tonic-gate 				(void *)((struct module *)mp->mod_mp)->text,
2585*7c478bd9Sstevel@tonic-gate 				(void *)((struct module *)mp->mod_mp)->data,
2586*7c478bd9Sstevel@tonic-gate 				((struct module *)mp->mod_mp)->text_size,
2587*7c478bd9Sstevel@tonic-gate 				((struct module *)mp->mod_mp)->data_size);
2588*7c478bd9Sstevel@tonic-gate 		}
2589*7c478bd9Sstevel@tonic-gate 
2590*7c478bd9Sstevel@tonic-gate 		/*
2591*7c478bd9Sstevel@tonic-gate 		 * XXX - There should be a better way to get this.
2592*7c478bd9Sstevel@tonic-gate 		 */
2593*7c478bd9Sstevel@tonic-gate 		modinfop = kmem_zalloc(sizeof (struct modinfo), KM_SLEEP);
2594*7c478bd9Sstevel@tonic-gate 		modinfop->mi_info = MI_INFO_LINKAGE;
2595*7c478bd9Sstevel@tonic-gate 		if (mod_getinfo(mp, modinfop) == 0)
2596*7c478bd9Sstevel@tonic-gate 			mp->mod_linkage = NULL;
2597*7c478bd9Sstevel@tonic-gate 		else {
2598*7c478bd9Sstevel@tonic-gate 			mp->mod_linkage = (void *)modinfop->mi_base;
2599*7c478bd9Sstevel@tonic-gate 			ASSERT(mp->mod_linkage->ml_rev == MODREV_1);
2600*7c478bd9Sstevel@tonic-gate 		}
2601*7c478bd9Sstevel@tonic-gate 
2602*7c478bd9Sstevel@tonic-gate 		/*
2603*7c478bd9Sstevel@tonic-gate 		 * DCS: bootstrapping code. If the driver is loaded
2604*7c478bd9Sstevel@tonic-gate 		 * before root mount, it is assumed that the driver
2605*7c478bd9Sstevel@tonic-gate 		 * may be used before mounting root. In order to
2606*7c478bd9Sstevel@tonic-gate 		 * access mappings of global to local minor no.'s
2607*7c478bd9Sstevel@tonic-gate 		 * during installation/open of the driver, we load
2608*7c478bd9Sstevel@tonic-gate 		 * them into memory here while the BOP_interfaces
2609*7c478bd9Sstevel@tonic-gate 		 * are still up.
2610*7c478bd9Sstevel@tonic-gate 		 */
2611*7c478bd9Sstevel@tonic-gate 		if ((cluster_bootflags & CLUSTER_BOOTED) && !modrootloaded) {
2612*7c478bd9Sstevel@tonic-gate 			retval = clboot_modload(mp);
2613*7c478bd9Sstevel@tonic-gate 		}
2614*7c478bd9Sstevel@tonic-gate 
2615*7c478bd9Sstevel@tonic-gate 		kmem_free(modinfop, sizeof (struct modinfo));
2616*7c478bd9Sstevel@tonic-gate 		(void) mod_sysctl(SYS_SET_MVAR, (void *)mp);
2617*7c478bd9Sstevel@tonic-gate 		retval = install_stubs_by_name(mp, mp->mod_modname);
2618*7c478bd9Sstevel@tonic-gate 
2619*7c478bd9Sstevel@tonic-gate 		/*
2620*7c478bd9Sstevel@tonic-gate 		 * Now that the module is loaded, we need to give DTrace
2621*7c478bd9Sstevel@tonic-gate 		 * a chance to notify its providers.  This is done via
2622*7c478bd9Sstevel@tonic-gate 		 * the dtrace_modload function pointer.
2623*7c478bd9Sstevel@tonic-gate 		 */
2624*7c478bd9Sstevel@tonic-gate 		if (strcmp(mp->mod_modname, "dtrace") != 0) {
2625*7c478bd9Sstevel@tonic-gate 			struct modctl *dmp = mod_hold_by_name("dtrace");
2626*7c478bd9Sstevel@tonic-gate 
2627*7c478bd9Sstevel@tonic-gate 			if (dmp != NULL && dtrace_modload != NULL)
2628*7c478bd9Sstevel@tonic-gate 				(*dtrace_modload)(mp);
2629*7c478bd9Sstevel@tonic-gate 
2630*7c478bd9Sstevel@tonic-gate 			mod_release_mod(dmp);
2631*7c478bd9Sstevel@tonic-gate 		}
2632*7c478bd9Sstevel@tonic-gate 
2633*7c478bd9Sstevel@tonic-gate 	} else {
2634*7c478bd9Sstevel@tonic-gate 		/*
2635*7c478bd9Sstevel@tonic-gate 		 * If load failed then we need to release any requisites
2636*7c478bd9Sstevel@tonic-gate 		 * that we had established.
2637*7c478bd9Sstevel@tonic-gate 		 */
2638*7c478bd9Sstevel@tonic-gate 		ASSERT(retval);
2639*7c478bd9Sstevel@tonic-gate 		mod_release_requisites(mp);
2640*7c478bd9Sstevel@tonic-gate 
2641*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_ERRMSG)
2642*7c478bd9Sstevel@tonic-gate 			printf("error loading '%s', error %d\n",
2643*7c478bd9Sstevel@tonic-gate 			    mp->mod_filename, retval);
2644*7c478bd9Sstevel@tonic-gate 	}
2645*7c478bd9Sstevel@tonic-gate 	return (retval);
2646*7c478bd9Sstevel@tonic-gate }
2647*7c478bd9Sstevel@tonic-gate 
2648*7c478bd9Sstevel@tonic-gate static char unload_msg[] = "unloading %s, module id %d, loadcnt %d.\n";
2649*7c478bd9Sstevel@tonic-gate 
2650*7c478bd9Sstevel@tonic-gate static void
2651*7c478bd9Sstevel@tonic-gate mod_unload(struct modctl *mp)
2652*7c478bd9Sstevel@tonic-gate {
2653*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&mod_lock));
2654*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy);
2655*7c478bd9Sstevel@tonic-gate 	ASSERT((mp->mod_loaded && (mp->mod_installed == 0)) &&
2656*7c478bd9Sstevel@tonic-gate 	    ((mp->mod_prim == 0) && (mp->mod_ref >= 0)));
2657*7c478bd9Sstevel@tonic-gate 
2658*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_LOADMSG)
2659*7c478bd9Sstevel@tonic-gate 		printf(unload_msg, mp->mod_modname,
2660*7c478bd9Sstevel@tonic-gate 			mp->mod_id, mp->mod_loadcnt);
2661*7c478bd9Sstevel@tonic-gate 
2662*7c478bd9Sstevel@tonic-gate 	/*
2663*7c478bd9Sstevel@tonic-gate 	 * If mod_ref is not zero, it means some modules might still refer
2664*7c478bd9Sstevel@tonic-gate 	 * to this module. Then you can't unload this module right now.
2665*7c478bd9Sstevel@tonic-gate 	 * Instead, set 1 to mod_delay_unload to notify the system of
2666*7c478bd9Sstevel@tonic-gate 	 * unloading this module later when it's not required any more.
2667*7c478bd9Sstevel@tonic-gate 	 */
2668*7c478bd9Sstevel@tonic-gate 	if (mp->mod_ref > 0) {
2669*7c478bd9Sstevel@tonic-gate 		mp->mod_delay_unload = 1;
2670*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_LOADMSG2) {
2671*7c478bd9Sstevel@tonic-gate 			printf("module %s not unloaded,"
2672*7c478bd9Sstevel@tonic-gate 			    " non-zero reference count (%d)",
2673*7c478bd9Sstevel@tonic-gate 			    mp->mod_modname, mp->mod_ref);
2674*7c478bd9Sstevel@tonic-gate 		}
2675*7c478bd9Sstevel@tonic-gate 		return;
2676*7c478bd9Sstevel@tonic-gate 	}
2677*7c478bd9Sstevel@tonic-gate 
2678*7c478bd9Sstevel@tonic-gate 	if (((mp->mod_loaded == 0) || mp->mod_installed) ||
2679*7c478bd9Sstevel@tonic-gate 	    (mp->mod_ref || mp->mod_prim)) {
2680*7c478bd9Sstevel@tonic-gate 		/*
2681*7c478bd9Sstevel@tonic-gate 		 * A DEBUG kernel would ASSERT panic above, the code is broken
2682*7c478bd9Sstevel@tonic-gate 		 * if we get this warning.
2683*7c478bd9Sstevel@tonic-gate 		 */
2684*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "mod_unload: %s in incorrect state: %d %d %d",
2685*7c478bd9Sstevel@tonic-gate 		    mp->mod_filename, mp->mod_installed, mp->mod_loaded,
2686*7c478bd9Sstevel@tonic-gate 		    mp->mod_ref);
2687*7c478bd9Sstevel@tonic-gate 		return;
2688*7c478bd9Sstevel@tonic-gate 	}
2689*7c478bd9Sstevel@tonic-gate 
2690*7c478bd9Sstevel@tonic-gate 	/* reset stub functions to call the binder again */
2691*7c478bd9Sstevel@tonic-gate 	reset_stubs(mp);
2692*7c478bd9Sstevel@tonic-gate 
2693*7c478bd9Sstevel@tonic-gate 	/*
2694*7c478bd9Sstevel@tonic-gate 	 * mark module as unloaded before the modctl structure is freed.
2695*7c478bd9Sstevel@tonic-gate 	 * This is required not to reuse the modctl structure before
2696*7c478bd9Sstevel@tonic-gate 	 * the module is marked as unloaded.
2697*7c478bd9Sstevel@tonic-gate 	 */
2698*7c478bd9Sstevel@tonic-gate 	mp->mod_loaded = 0;
2699*7c478bd9Sstevel@tonic-gate 	mp->mod_linkage = NULL;
2700*7c478bd9Sstevel@tonic-gate 
2701*7c478bd9Sstevel@tonic-gate 	/* free the memory */
2702*7c478bd9Sstevel@tonic-gate 	kobj_unload_module(mp);
2703*7c478bd9Sstevel@tonic-gate 
2704*7c478bd9Sstevel@tonic-gate 	if (mp->mod_delay_unload) {
2705*7c478bd9Sstevel@tonic-gate 		mp->mod_delay_unload = 0;
2706*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_LOADMSG2) {
2707*7c478bd9Sstevel@tonic-gate 			printf("deferred unload of module %s"
2708*7c478bd9Sstevel@tonic-gate 			    " (id %d) successful",
2709*7c478bd9Sstevel@tonic-gate 			    mp->mod_modname, mp->mod_id);
2710*7c478bd9Sstevel@tonic-gate 		}
2711*7c478bd9Sstevel@tonic-gate 	}
2712*7c478bd9Sstevel@tonic-gate 
2713*7c478bd9Sstevel@tonic-gate 	/* release hold on requisites */
2714*7c478bd9Sstevel@tonic-gate 	mod_release_requisites(mp);
2715*7c478bd9Sstevel@tonic-gate 
2716*7c478bd9Sstevel@tonic-gate 	/*
2717*7c478bd9Sstevel@tonic-gate 	 * Now that the module is gone, we need to give DTrace a chance to
2718*7c478bd9Sstevel@tonic-gate 	 * remove any probes that it may have had in the module.  This is
2719*7c478bd9Sstevel@tonic-gate 	 * done via the dtrace_modunload function pointer.
2720*7c478bd9Sstevel@tonic-gate 	 */
2721*7c478bd9Sstevel@tonic-gate 	if (strcmp(mp->mod_modname, "dtrace") != 0) {
2722*7c478bd9Sstevel@tonic-gate 		struct modctl *dmp = mod_hold_by_name("dtrace");
2723*7c478bd9Sstevel@tonic-gate 
2724*7c478bd9Sstevel@tonic-gate 		if (dmp != NULL && dtrace_modunload != NULL)
2725*7c478bd9Sstevel@tonic-gate 			(*dtrace_modunload)(mp);
2726*7c478bd9Sstevel@tonic-gate 
2727*7c478bd9Sstevel@tonic-gate 		mod_release_mod(dmp);
2728*7c478bd9Sstevel@tonic-gate 	}
2729*7c478bd9Sstevel@tonic-gate }
2730*7c478bd9Sstevel@tonic-gate 
2731*7c478bd9Sstevel@tonic-gate static int
2732*7c478bd9Sstevel@tonic-gate modinstall(struct modctl *mp)
2733*7c478bd9Sstevel@tonic-gate {
2734*7c478bd9Sstevel@tonic-gate 	int val;
2735*7c478bd9Sstevel@tonic-gate 	int (*func)(void);
2736*7c478bd9Sstevel@tonic-gate 
2737*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&mod_lock));
2738*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy && mp->mod_loaded);
2739*7c478bd9Sstevel@tonic-gate 
2740*7c478bd9Sstevel@tonic-gate 	if (mp->mod_installed)
2741*7c478bd9Sstevel@tonic-gate 		return (0);
2742*7c478bd9Sstevel@tonic-gate 	/*
2743*7c478bd9Sstevel@tonic-gate 	 * If mod_delay_unload is on, it means the system chose the deferred
2744*7c478bd9Sstevel@tonic-gate 	 * unload for this module. Then you can't install this module until
2745*7c478bd9Sstevel@tonic-gate 	 * it's unloaded from the system.
2746*7c478bd9Sstevel@tonic-gate 	 */
2747*7c478bd9Sstevel@tonic-gate 	if (mp->mod_delay_unload)
2748*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
2749*7c478bd9Sstevel@tonic-gate 
2750*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_LOADMSG)
2751*7c478bd9Sstevel@tonic-gate 		printf("installing %s, module id %d.\n",
2752*7c478bd9Sstevel@tonic-gate 			mp->mod_modname, mp->mod_id);
2753*7c478bd9Sstevel@tonic-gate 
2754*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_mp != NULL);
2755*7c478bd9Sstevel@tonic-gate 	if (mod_install_requisites(mp) != 0) {
2756*7c478bd9Sstevel@tonic-gate 		/*
2757*7c478bd9Sstevel@tonic-gate 		 * Note that we can't call mod_unload(mp) here since
2758*7c478bd9Sstevel@tonic-gate 		 * if modinstall() was called by mod_install_requisites(),
2759*7c478bd9Sstevel@tonic-gate 		 * we won't be able to hold the dependent modules
2760*7c478bd9Sstevel@tonic-gate 		 * (otherwise there would be a deadlock).
2761*7c478bd9Sstevel@tonic-gate 		 */
2762*7c478bd9Sstevel@tonic-gate 		return (ENXIO);
2763*7c478bd9Sstevel@tonic-gate 	}
2764*7c478bd9Sstevel@tonic-gate 
2765*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_ERRMSG) {
2766*7c478bd9Sstevel@tonic-gate 		printf("init '%s' id %d loaded @ 0x%p/0x%p size %lu/%lu\n",
2767*7c478bd9Sstevel@tonic-gate 			mp->mod_filename, mp->mod_id,
2768*7c478bd9Sstevel@tonic-gate 			(void *)((struct module *)mp->mod_mp)->text,
2769*7c478bd9Sstevel@tonic-gate 			(void *)((struct module *)mp->mod_mp)->data,
2770*7c478bd9Sstevel@tonic-gate 			((struct module *)mp->mod_mp)->text_size,
2771*7c478bd9Sstevel@tonic-gate 			((struct module *)mp->mod_mp)->data_size);
2772*7c478bd9Sstevel@tonic-gate 	}
2773*7c478bd9Sstevel@tonic-gate 
2774*7c478bd9Sstevel@tonic-gate 	func = (int (*)())kobj_lookup(mp->mod_mp, "_init");
2775*7c478bd9Sstevel@tonic-gate 
2776*7c478bd9Sstevel@tonic-gate 	if (kobj_addrcheck(mp->mod_mp, (caddr_t)func)) {
2777*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "_init() not defined properly in %s",
2778*7c478bd9Sstevel@tonic-gate 		    mp->mod_filename);
2779*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
2780*7c478bd9Sstevel@tonic-gate 	}
2781*7c478bd9Sstevel@tonic-gate 
2782*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_USERDEBUG) {
2783*7c478bd9Sstevel@tonic-gate 		printf("breakpoint before calling %s:_init()\n",
2784*7c478bd9Sstevel@tonic-gate 		    mp->mod_modname);
2785*7c478bd9Sstevel@tonic-gate 		if (DEBUGGER_PRESENT)
2786*7c478bd9Sstevel@tonic-gate 			debug_enter("_init");
2787*7c478bd9Sstevel@tonic-gate 	}
2788*7c478bd9Sstevel@tonic-gate 
2789*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&mod_lock));
2790*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy && mp->mod_loaded);
2791*7c478bd9Sstevel@tonic-gate 	val = (*func)();		/* call _init */
2792*7c478bd9Sstevel@tonic-gate 
2793*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_USERDEBUG)
2794*7c478bd9Sstevel@tonic-gate 		printf("Returned from _init, val = %x\n", val);
2795*7c478bd9Sstevel@tonic-gate 
2796*7c478bd9Sstevel@tonic-gate 	if (val == 0) {
2797*7c478bd9Sstevel@tonic-gate 		/*
2798*7c478bd9Sstevel@tonic-gate 		 * Set the MODS_INSTALLED flag to enable this module
2799*7c478bd9Sstevel@tonic-gate 		 * being called now.
2800*7c478bd9Sstevel@tonic-gate 		 */
2801*7c478bd9Sstevel@tonic-gate 		install_stubs(mp);
2802*7c478bd9Sstevel@tonic-gate 		mp->mod_installed = 1;
2803*7c478bd9Sstevel@tonic-gate 	} else if (moddebug & MODDEBUG_ERRMSG)
2804*7c478bd9Sstevel@tonic-gate 		printf(mod_init_msg, mp->mod_filename, mp->mod_modname, val);
2805*7c478bd9Sstevel@tonic-gate 
2806*7c478bd9Sstevel@tonic-gate 	return (val);
2807*7c478bd9Sstevel@tonic-gate }
2808*7c478bd9Sstevel@tonic-gate 
2809*7c478bd9Sstevel@tonic-gate static int
2810*7c478bd9Sstevel@tonic-gate detach_driver(char *name)
2811*7c478bd9Sstevel@tonic-gate {
2812*7c478bd9Sstevel@tonic-gate 	major_t major;
2813*7c478bd9Sstevel@tonic-gate 	int error;
2814*7c478bd9Sstevel@tonic-gate 
2815*7c478bd9Sstevel@tonic-gate 	/*
2816*7c478bd9Sstevel@tonic-gate 	 * If being called from mod_uninstall_all() then the appropriate
2817*7c478bd9Sstevel@tonic-gate 	 * driver detaches (leaf only) have already been done.
2818*7c478bd9Sstevel@tonic-gate 	 */
2819*7c478bd9Sstevel@tonic-gate 	if (mod_in_autounload())
2820*7c478bd9Sstevel@tonic-gate 		return (0);
2821*7c478bd9Sstevel@tonic-gate 
2822*7c478bd9Sstevel@tonic-gate 	major = ddi_name_to_major(name);
2823*7c478bd9Sstevel@tonic-gate 	if (major == (major_t)-1)
2824*7c478bd9Sstevel@tonic-gate 		return (0);
2825*7c478bd9Sstevel@tonic-gate 
2826*7c478bd9Sstevel@tonic-gate 	error = ndi_devi_unconfig_driver(ddi_root_node(),
2827*7c478bd9Sstevel@tonic-gate 	    NDI_DETACH_DRIVER, major);
2828*7c478bd9Sstevel@tonic-gate 	return (error == NDI_SUCCESS ? 0 : -1);
2829*7c478bd9Sstevel@tonic-gate }
2830*7c478bd9Sstevel@tonic-gate 
2831*7c478bd9Sstevel@tonic-gate static char finiret_msg[] = "Returned from _fini for %s, status = %x\n";
2832*7c478bd9Sstevel@tonic-gate 
2833*7c478bd9Sstevel@tonic-gate static int
2834*7c478bd9Sstevel@tonic-gate moduninstall(struct modctl *mp)
2835*7c478bd9Sstevel@tonic-gate {
2836*7c478bd9Sstevel@tonic-gate 	int status = 0;
2837*7c478bd9Sstevel@tonic-gate 	int (*func)(void);
2838*7c478bd9Sstevel@tonic-gate 
2839*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&mod_lock));
2840*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy);
2841*7c478bd9Sstevel@tonic-gate 
2842*7c478bd9Sstevel@tonic-gate 	/*
2843*7c478bd9Sstevel@tonic-gate 	 * Verify that we need to do something and can uninstall the module.
2844*7c478bd9Sstevel@tonic-gate 	 *
2845*7c478bd9Sstevel@tonic-gate 	 * If we should not uninstall the module or if the module is not in
2846*7c478bd9Sstevel@tonic-gate 	 * the correct state to start an uninstall we return EBUSY to prevent
2847*7c478bd9Sstevel@tonic-gate 	 * us from progressing to mod_unload.  If the module has already been
2848*7c478bd9Sstevel@tonic-gate 	 * uninstalled and unloaded we return EALREADY.
2849*7c478bd9Sstevel@tonic-gate 	 */
2850*7c478bd9Sstevel@tonic-gate 	if (mp->mod_prim || mp->mod_ref || mp->mod_nenabled != 0)
2851*7c478bd9Sstevel@tonic-gate 		return (EBUSY);
2852*7c478bd9Sstevel@tonic-gate 	if ((mp->mod_installed == 0) || (mp->mod_loaded == 0))
2853*7c478bd9Sstevel@tonic-gate 		return (EALREADY);
2854*7c478bd9Sstevel@tonic-gate 
2855*7c478bd9Sstevel@tonic-gate 	/*
2856*7c478bd9Sstevel@tonic-gate 	 * To avoid devinfo / module deadlock we must release this module
2857*7c478bd9Sstevel@tonic-gate 	 * prior to initiating the detach_driver, otherwise the detach_driver
2858*7c478bd9Sstevel@tonic-gate 	 * might deadlock on a devinfo node held by another thread
2859*7c478bd9Sstevel@tonic-gate 	 * coming top down and involving the module we have locked.
2860*7c478bd9Sstevel@tonic-gate 	 *
2861*7c478bd9Sstevel@tonic-gate 	 * When we regrab the module we must reverify that it is OK
2862*7c478bd9Sstevel@tonic-gate 	 * to proceed with the uninstall operation.
2863*7c478bd9Sstevel@tonic-gate 	 */
2864*7c478bd9Sstevel@tonic-gate 	mod_release_mod(mp);
2865*7c478bd9Sstevel@tonic-gate 	status = detach_driver(mp->mod_modname);
2866*7c478bd9Sstevel@tonic-gate 	(void) mod_hold_by_modctl(mp, MOD_WAIT_FOREVER | MOD_LOCK_NOT_HELD);
2867*7c478bd9Sstevel@tonic-gate 
2868*7c478bd9Sstevel@tonic-gate 	/* check detach status and reverify state with lock */
2869*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
2870*7c478bd9Sstevel@tonic-gate 	if ((status != 0) || mp->mod_prim || mp->mod_ref) {
2871*7c478bd9Sstevel@tonic-gate 		mutex_exit(&mod_lock);
2872*7c478bd9Sstevel@tonic-gate 		return (EBUSY);
2873*7c478bd9Sstevel@tonic-gate 	}
2874*7c478bd9Sstevel@tonic-gate 	if ((mp->mod_installed == 0) || (mp->mod_loaded == 0)) {
2875*7c478bd9Sstevel@tonic-gate 		mutex_exit(&mod_lock);
2876*7c478bd9Sstevel@tonic-gate 		return (EALREADY);
2877*7c478bd9Sstevel@tonic-gate 	}
2878*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
2879*7c478bd9Sstevel@tonic-gate 
2880*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_LOADMSG2)
2881*7c478bd9Sstevel@tonic-gate 		printf("uninstalling %s\n", mp->mod_modname);
2882*7c478bd9Sstevel@tonic-gate 
2883*7c478bd9Sstevel@tonic-gate 	/*
2884*7c478bd9Sstevel@tonic-gate 	 * lookup _fini, return EBUSY if not defined.
2885*7c478bd9Sstevel@tonic-gate 	 *
2886*7c478bd9Sstevel@tonic-gate 	 * The MODDEBUG_FINI_EBUSY is usefull in resolving leaks in
2887*7c478bd9Sstevel@tonic-gate 	 * detach(9E) - it allows bufctl addresses to be resolved.
2888*7c478bd9Sstevel@tonic-gate 	 */
2889*7c478bd9Sstevel@tonic-gate 	func = (int (*)())kobj_lookup(mp->mod_mp, "_fini");
2890*7c478bd9Sstevel@tonic-gate 	if ((func == NULL) || (mp->mod_loadflags & MOD_NOUNLOAD) ||
2891*7c478bd9Sstevel@tonic-gate 	    (moddebug & MODDEBUG_FINI_EBUSY))
2892*7c478bd9Sstevel@tonic-gate 		return (EBUSY);
2893*7c478bd9Sstevel@tonic-gate 
2894*7c478bd9Sstevel@tonic-gate 	/* verify that _fini is in this module */
2895*7c478bd9Sstevel@tonic-gate 	if (kobj_addrcheck(mp->mod_mp, (caddr_t)func)) {
2896*7c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN, "_fini() not defined properly in %s",
2897*7c478bd9Sstevel@tonic-gate 		    mp->mod_filename);
2898*7c478bd9Sstevel@tonic-gate 		return (EFAULT);
2899*7c478bd9Sstevel@tonic-gate 	}
2900*7c478bd9Sstevel@tonic-gate 
2901*7c478bd9Sstevel@tonic-gate 	/* call _fini() */
2902*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&mod_lock));
2903*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy && mp->mod_loaded && mp->mod_installed);
2904*7c478bd9Sstevel@tonic-gate 
2905*7c478bd9Sstevel@tonic-gate 	status = (*func)();
2906*7c478bd9Sstevel@tonic-gate 
2907*7c478bd9Sstevel@tonic-gate 	if (status == 0) {
2908*7c478bd9Sstevel@tonic-gate 		/* _fini returned success, the module is no longer installed */
2909*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_LOADMSG)
2910*7c478bd9Sstevel@tonic-gate 			printf("uninstalled %s\n", mp->mod_modname);
2911*7c478bd9Sstevel@tonic-gate 
2912*7c478bd9Sstevel@tonic-gate 		/*
2913*7c478bd9Sstevel@tonic-gate 		 * Even though we only set mod_installed to zero here, a zero
2914*7c478bd9Sstevel@tonic-gate 		 * return value means we are commited to a code path were
2915*7c478bd9Sstevel@tonic-gate 		 * mod_loaded will also end up as zero - we have no other
2916*7c478bd9Sstevel@tonic-gate 		 * way to get the module data and bss back to the pre _init
2917*7c478bd9Sstevel@tonic-gate 		 * state except a reload. To ensure this, after return,
2918*7c478bd9Sstevel@tonic-gate 		 * mod_busy must stay set until mod_loaded is cleared.
2919*7c478bd9Sstevel@tonic-gate 		 */
2920*7c478bd9Sstevel@tonic-gate 		mp->mod_installed = 0;
2921*7c478bd9Sstevel@tonic-gate 
2922*7c478bd9Sstevel@tonic-gate 		/*
2923*7c478bd9Sstevel@tonic-gate 		 * Clear the MODS_INSTALLED flag not to call functions
2924*7c478bd9Sstevel@tonic-gate 		 * in the module directly from now on.
2925*7c478bd9Sstevel@tonic-gate 		 */
2926*7c478bd9Sstevel@tonic-gate 		uninstall_stubs(mp);
2927*7c478bd9Sstevel@tonic-gate 	} else {
2928*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_USERDEBUG)
2929*7c478bd9Sstevel@tonic-gate 			printf(finiret_msg, mp->mod_filename, status);
2930*7c478bd9Sstevel@tonic-gate 		/*
2931*7c478bd9Sstevel@tonic-gate 		 * By definition _fini is only allowed to return EBUSY or the
2932*7c478bd9Sstevel@tonic-gate 		 * result of mod_remove (EBUSY or EINVAL).  In the off chance
2933*7c478bd9Sstevel@tonic-gate 		 * that a driver returns EALREADY we convert this to EINVAL
2934*7c478bd9Sstevel@tonic-gate 		 * since to our caller EALREADY means module was already
2935*7c478bd9Sstevel@tonic-gate 		 * removed.
2936*7c478bd9Sstevel@tonic-gate 		 */
2937*7c478bd9Sstevel@tonic-gate 		if (status == EALREADY)
2938*7c478bd9Sstevel@tonic-gate 			status = EINVAL;
2939*7c478bd9Sstevel@tonic-gate 	}
2940*7c478bd9Sstevel@tonic-gate 
2941*7c478bd9Sstevel@tonic-gate 	return (status);
2942*7c478bd9Sstevel@tonic-gate }
2943*7c478bd9Sstevel@tonic-gate 
2944*7c478bd9Sstevel@tonic-gate /*
2945*7c478bd9Sstevel@tonic-gate  * Uninstall all modules.
2946*7c478bd9Sstevel@tonic-gate  */
2947*7c478bd9Sstevel@tonic-gate static void
2948*7c478bd9Sstevel@tonic-gate mod_uninstall_all(void)
2949*7c478bd9Sstevel@tonic-gate {
2950*7c478bd9Sstevel@tonic-gate 	struct modctl	*mp;
2951*7c478bd9Sstevel@tonic-gate 	modid_t		modid = 0;
2952*7c478bd9Sstevel@tonic-gate 
2953*7c478bd9Sstevel@tonic-gate 	/* mark this thread as doing autounloading */
2954*7c478bd9Sstevel@tonic-gate 	(void) tsd_set(mod_autounload_key, (void *)1);
2955*7c478bd9Sstevel@tonic-gate 
2956*7c478bd9Sstevel@tonic-gate 	(void) devfs_clean(ddi_root_node(), NULL, 0);
2957*7c478bd9Sstevel@tonic-gate 	(void) ndi_devi_unconfig(ddi_root_node(), NDI_AUTODETACH);
2958*7c478bd9Sstevel@tonic-gate 
2959*7c478bd9Sstevel@tonic-gate 	while ((mp = mod_hold_next_by_id(modid)) != NULL) {
2960*7c478bd9Sstevel@tonic-gate 		modid = mp->mod_id;
2961*7c478bd9Sstevel@tonic-gate 		/*
2962*7c478bd9Sstevel@tonic-gate 		 * Skip modules with the MOD_NOAUTOUNLOAD flag set
2963*7c478bd9Sstevel@tonic-gate 		 */
2964*7c478bd9Sstevel@tonic-gate 		if (mp->mod_loadflags & MOD_NOAUTOUNLOAD) {
2965*7c478bd9Sstevel@tonic-gate 			mod_release_mod(mp);
2966*7c478bd9Sstevel@tonic-gate 			continue;
2967*7c478bd9Sstevel@tonic-gate 		}
2968*7c478bd9Sstevel@tonic-gate 
2969*7c478bd9Sstevel@tonic-gate 		if (moduninstall(mp) == 0)
2970*7c478bd9Sstevel@tonic-gate 			mod_unload(mp);
2971*7c478bd9Sstevel@tonic-gate 		mod_release_mod(mp);
2972*7c478bd9Sstevel@tonic-gate 	}
2973*7c478bd9Sstevel@tonic-gate 
2974*7c478bd9Sstevel@tonic-gate 	(void) tsd_set(mod_autounload_key, NULL);
2975*7c478bd9Sstevel@tonic-gate }
2976*7c478bd9Sstevel@tonic-gate 
2977*7c478bd9Sstevel@tonic-gate static int modunload_disable_count;
2978*7c478bd9Sstevel@tonic-gate 
2979*7c478bd9Sstevel@tonic-gate void
2980*7c478bd9Sstevel@tonic-gate modunload_disable(void)
2981*7c478bd9Sstevel@tonic-gate {
2982*7c478bd9Sstevel@tonic-gate 	INCR_COUNT(&modunload_disable_count, &mod_uninstall_lock);
2983*7c478bd9Sstevel@tonic-gate }
2984*7c478bd9Sstevel@tonic-gate 
2985*7c478bd9Sstevel@tonic-gate void
2986*7c478bd9Sstevel@tonic-gate modunload_enable(void)
2987*7c478bd9Sstevel@tonic-gate {
2988*7c478bd9Sstevel@tonic-gate 	DECR_COUNT(&modunload_disable_count, &mod_uninstall_lock);
2989*7c478bd9Sstevel@tonic-gate }
2990*7c478bd9Sstevel@tonic-gate 
2991*7c478bd9Sstevel@tonic-gate void
2992*7c478bd9Sstevel@tonic-gate mod_uninstall_daemon(void)
2993*7c478bd9Sstevel@tonic-gate {
2994*7c478bd9Sstevel@tonic-gate 	callb_cpr_t	cprinfo;
2995*7c478bd9Sstevel@tonic-gate 	clock_t		ticks = 0;
2996*7c478bd9Sstevel@tonic-gate 
2997*7c478bd9Sstevel@tonic-gate 	mod_aul_thread = curthread;
2998*7c478bd9Sstevel@tonic-gate 
2999*7c478bd9Sstevel@tonic-gate 	CALLB_CPR_INIT(&cprinfo, &mod_uninstall_lock, callb_generic_cpr, "mud");
3000*7c478bd9Sstevel@tonic-gate 	for (;;) {
3001*7c478bd9Sstevel@tonic-gate 		mutex_enter(&mod_uninstall_lock);
3002*7c478bd9Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
3003*7c478bd9Sstevel@tonic-gate 		/*
3004*7c478bd9Sstevel@tonic-gate 		 * In DEBUG kernels, unheld drivers are uninstalled periodically
3005*7c478bd9Sstevel@tonic-gate 		 * every mod_uninstall_interval seconds.  Periodic uninstall can
3006*7c478bd9Sstevel@tonic-gate 		 * be disabled by setting mod_uninstall_interval to 0 which is
3007*7c478bd9Sstevel@tonic-gate 		 * the default for a non-DEBUG kernel.
3008*7c478bd9Sstevel@tonic-gate 		 */
3009*7c478bd9Sstevel@tonic-gate 		if (mod_uninstall_interval) {
3010*7c478bd9Sstevel@tonic-gate 			ticks = ddi_get_lbolt() +
3011*7c478bd9Sstevel@tonic-gate 				drv_usectohz(mod_uninstall_interval * 1000000);
3012*7c478bd9Sstevel@tonic-gate 			(void) cv_timedwait(&mod_uninstall_cv,
3013*7c478bd9Sstevel@tonic-gate 				&mod_uninstall_lock, ticks);
3014*7c478bd9Sstevel@tonic-gate 		} else {
3015*7c478bd9Sstevel@tonic-gate 			cv_wait(&mod_uninstall_cv, &mod_uninstall_lock);
3016*7c478bd9Sstevel@tonic-gate 		}
3017*7c478bd9Sstevel@tonic-gate 		/*
3018*7c478bd9Sstevel@tonic-gate 		 * The whole daemon is safe for CPR except we don't want
3019*7c478bd9Sstevel@tonic-gate 		 * the daemon to run if FREEZE is issued and this daemon
3020*7c478bd9Sstevel@tonic-gate 		 * wakes up from the cv_wait above. In this case, it'll be
3021*7c478bd9Sstevel@tonic-gate 		 * blocked in CALLB_CPR_SAFE_END until THAW is issued.
3022*7c478bd9Sstevel@tonic-gate 		 *
3023*7c478bd9Sstevel@tonic-gate 		 * The reason of calling CALLB_CPR_SAFE_BEGIN twice is that
3024*7c478bd9Sstevel@tonic-gate 		 * mod_uninstall_lock is used to protect cprinfo and
3025*7c478bd9Sstevel@tonic-gate 		 * CALLB_CPR_SAFE_BEGIN assumes that this lock is held when
3026*7c478bd9Sstevel@tonic-gate 		 * called.
3027*7c478bd9Sstevel@tonic-gate 		 */
3028*7c478bd9Sstevel@tonic-gate 		CALLB_CPR_SAFE_END(&cprinfo, &mod_uninstall_lock);
3029*7c478bd9Sstevel@tonic-gate 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
3030*7c478bd9Sstevel@tonic-gate 		mutex_exit(&mod_uninstall_lock);
3031*7c478bd9Sstevel@tonic-gate 		if ((modunload_disable_count == 0) &&
3032*7c478bd9Sstevel@tonic-gate 		    ((moddebug & MODDEBUG_NOAUTOUNLOAD) == 0)) {
3033*7c478bd9Sstevel@tonic-gate 			mod_uninstall_all();
3034*7c478bd9Sstevel@tonic-gate 		}
3035*7c478bd9Sstevel@tonic-gate 	}
3036*7c478bd9Sstevel@tonic-gate }
3037*7c478bd9Sstevel@tonic-gate 
3038*7c478bd9Sstevel@tonic-gate /*
3039*7c478bd9Sstevel@tonic-gate  * Unload all uninstalled modules.
3040*7c478bd9Sstevel@tonic-gate  */
3041*7c478bd9Sstevel@tonic-gate void
3042*7c478bd9Sstevel@tonic-gate modreap(void)
3043*7c478bd9Sstevel@tonic-gate {
3044*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_uninstall_lock);
3045*7c478bd9Sstevel@tonic-gate 	cv_broadcast(&mod_uninstall_cv);
3046*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_uninstall_lock);
3047*7c478bd9Sstevel@tonic-gate }
3048*7c478bd9Sstevel@tonic-gate 
3049*7c478bd9Sstevel@tonic-gate /*
3050*7c478bd9Sstevel@tonic-gate  * Hold the specified module. This is the module holding primitive.
3051*7c478bd9Sstevel@tonic-gate  *
3052*7c478bd9Sstevel@tonic-gate  * If MOD_LOCK_HELD then the caller already holds the mod_lock.
3053*7c478bd9Sstevel@tonic-gate  *
3054*7c478bd9Sstevel@tonic-gate  * Return values:
3055*7c478bd9Sstevel@tonic-gate  *	 0 ==> the module is held
3056*7c478bd9Sstevel@tonic-gate  *	 1 ==> the module is not held and the MOD_WAIT_ONCE caller needs
3057*7c478bd9Sstevel@tonic-gate  *		to determine how to retry.
3058*7c478bd9Sstevel@tonic-gate  */
3059*7c478bd9Sstevel@tonic-gate int
3060*7c478bd9Sstevel@tonic-gate mod_hold_by_modctl(struct modctl *mp, int f)
3061*7c478bd9Sstevel@tonic-gate {
3062*7c478bd9Sstevel@tonic-gate 	ASSERT((f & (MOD_WAIT_ONCE | MOD_WAIT_FOREVER)) &&
3063*7c478bd9Sstevel@tonic-gate 	    ((f & (MOD_WAIT_ONCE | MOD_WAIT_FOREVER)) !=
3064*7c478bd9Sstevel@tonic-gate 	    (MOD_WAIT_ONCE | MOD_WAIT_FOREVER)));
3065*7c478bd9Sstevel@tonic-gate 	ASSERT((f & (MOD_LOCK_HELD | MOD_LOCK_NOT_HELD)) &&
3066*7c478bd9Sstevel@tonic-gate 	    ((f & (MOD_LOCK_HELD | MOD_LOCK_NOT_HELD)) !=
3067*7c478bd9Sstevel@tonic-gate 	    (MOD_LOCK_HELD | MOD_LOCK_NOT_HELD)));
3068*7c478bd9Sstevel@tonic-gate 	ASSERT((f & MOD_LOCK_NOT_HELD) || MUTEX_HELD(&mod_lock));
3069*7c478bd9Sstevel@tonic-gate 
3070*7c478bd9Sstevel@tonic-gate 	if (f & MOD_LOCK_NOT_HELD)
3071*7c478bd9Sstevel@tonic-gate 		mutex_enter(&mod_lock);
3072*7c478bd9Sstevel@tonic-gate 
3073*7c478bd9Sstevel@tonic-gate 	while (mp->mod_busy) {
3074*7c478bd9Sstevel@tonic-gate 		mp->mod_want = 1;
3075*7c478bd9Sstevel@tonic-gate 		cv_wait(&mod_cv, &mod_lock);
3076*7c478bd9Sstevel@tonic-gate 		/*
3077*7c478bd9Sstevel@tonic-gate 		 * Module may be unloaded by daemon.
3078*7c478bd9Sstevel@tonic-gate 		 * Nevertheless, modctl structure is still in linked list
3079*7c478bd9Sstevel@tonic-gate 		 * (i.e., off &modules), not freed!
3080*7c478bd9Sstevel@tonic-gate 		 * Caller is not supposed to assume "mp" is valid, but there
3081*7c478bd9Sstevel@tonic-gate 		 * is no reasonable way to detect this but using
3082*7c478bd9Sstevel@tonic-gate 		 * mp->mod_modinfo->mp == NULL check (follow the back pointer)
3083*7c478bd9Sstevel@tonic-gate 		 *   (or similar check depending on calling context)
3084*7c478bd9Sstevel@tonic-gate 		 * DON'T free modctl structure, it will be very very
3085*7c478bd9Sstevel@tonic-gate 		 * problematic.
3086*7c478bd9Sstevel@tonic-gate 		 */
3087*7c478bd9Sstevel@tonic-gate 		if (f & MOD_WAIT_ONCE) {
3088*7c478bd9Sstevel@tonic-gate 			if (f & MOD_LOCK_NOT_HELD)
3089*7c478bd9Sstevel@tonic-gate 				mutex_exit(&mod_lock);
3090*7c478bd9Sstevel@tonic-gate 			return (1);	/* caller decides how to retry */
3091*7c478bd9Sstevel@tonic-gate 		}
3092*7c478bd9Sstevel@tonic-gate 	}
3093*7c478bd9Sstevel@tonic-gate 
3094*7c478bd9Sstevel@tonic-gate 	mp->mod_busy = 1;
3095*7c478bd9Sstevel@tonic-gate 	mp->mod_inprogress_thread =
3096*7c478bd9Sstevel@tonic-gate 	    (curthread == NULL ? (kthread_id_t)-1 : curthread);
3097*7c478bd9Sstevel@tonic-gate 
3098*7c478bd9Sstevel@tonic-gate 	if (f & MOD_LOCK_NOT_HELD)
3099*7c478bd9Sstevel@tonic-gate 		mutex_exit(&mod_lock);
3100*7c478bd9Sstevel@tonic-gate 	return (0);
3101*7c478bd9Sstevel@tonic-gate }
3102*7c478bd9Sstevel@tonic-gate 
3103*7c478bd9Sstevel@tonic-gate static struct modctl *
3104*7c478bd9Sstevel@tonic-gate mod_hold_by_name_common(struct modctl *dep, char *filename)
3105*7c478bd9Sstevel@tonic-gate {
3106*7c478bd9Sstevel@tonic-gate 	char		*modname;
3107*7c478bd9Sstevel@tonic-gate 	struct modctl	*mp;
3108*7c478bd9Sstevel@tonic-gate 	char		*curname, *newname;
3109*7c478bd9Sstevel@tonic-gate 	int		found = 0;
3110*7c478bd9Sstevel@tonic-gate 
3111*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
3112*7c478bd9Sstevel@tonic-gate 
3113*7c478bd9Sstevel@tonic-gate 	if ((modname = strrchr(filename, '/')) == NULL)
3114*7c478bd9Sstevel@tonic-gate 		modname = filename;
3115*7c478bd9Sstevel@tonic-gate 	else
3116*7c478bd9Sstevel@tonic-gate 		modname++;
3117*7c478bd9Sstevel@tonic-gate 
3118*7c478bd9Sstevel@tonic-gate 	mp = &modules;
3119*7c478bd9Sstevel@tonic-gate 	do {
3120*7c478bd9Sstevel@tonic-gate 		if (strcmp(modname, mp->mod_modname) == 0) {
3121*7c478bd9Sstevel@tonic-gate 			found = 1;
3122*7c478bd9Sstevel@tonic-gate 			break;
3123*7c478bd9Sstevel@tonic-gate 		}
3124*7c478bd9Sstevel@tonic-gate 	} while ((mp = mp->mod_next) != &modules);
3125*7c478bd9Sstevel@tonic-gate 
3126*7c478bd9Sstevel@tonic-gate 	if (found == 0) {
3127*7c478bd9Sstevel@tonic-gate 		mp = allocate_modp(filename, modname);
3128*7c478bd9Sstevel@tonic-gate 		modadd(mp);
3129*7c478bd9Sstevel@tonic-gate 	}
3130*7c478bd9Sstevel@tonic-gate 
3131*7c478bd9Sstevel@tonic-gate 	/*
3132*7c478bd9Sstevel@tonic-gate 	 * if dep is not NULL, set the mp in mod_requisite_loading for
3133*7c478bd9Sstevel@tonic-gate 	 * the module circular dependency check. This field is used in
3134*7c478bd9Sstevel@tonic-gate 	 * mod_circdep(), but it's cleard in mod_hold_loaded_mod().
3135*7c478bd9Sstevel@tonic-gate 	 */
3136*7c478bd9Sstevel@tonic-gate 	if (dep != NULL) {
3137*7c478bd9Sstevel@tonic-gate 		ASSERT(dep->mod_busy && dep->mod_requisite_loading == NULL);
3138*7c478bd9Sstevel@tonic-gate 		dep->mod_requisite_loading = mp;
3139*7c478bd9Sstevel@tonic-gate 	}
3140*7c478bd9Sstevel@tonic-gate 
3141*7c478bd9Sstevel@tonic-gate 	/*
3142*7c478bd9Sstevel@tonic-gate 	 * If the module was held, then it must be us who has it held.
3143*7c478bd9Sstevel@tonic-gate 	 */
3144*7c478bd9Sstevel@tonic-gate 	if (mod_circdep(mp))
3145*7c478bd9Sstevel@tonic-gate 		mp = NULL;
3146*7c478bd9Sstevel@tonic-gate 	else {
3147*7c478bd9Sstevel@tonic-gate 		(void) mod_hold_by_modctl(mp, MOD_WAIT_FOREVER | MOD_LOCK_HELD);
3148*7c478bd9Sstevel@tonic-gate 
3149*7c478bd9Sstevel@tonic-gate 		/*
3150*7c478bd9Sstevel@tonic-gate 		 * If the name hadn't been set or has changed, allocate
3151*7c478bd9Sstevel@tonic-gate 		 * space and set it.  Free space used by previous name.
3152*7c478bd9Sstevel@tonic-gate 		 *
3153*7c478bd9Sstevel@tonic-gate 		 * Do not change the name of primary modules, for primary
3154*7c478bd9Sstevel@tonic-gate 		 * modules the mod_filename was allocated in standalone mode:
3155*7c478bd9Sstevel@tonic-gate 		 * it is illegal to kobj_alloc in standalone mode and kobj_free
3156*7c478bd9Sstevel@tonic-gate 		 * in non-standalone mode.
3157*7c478bd9Sstevel@tonic-gate 		 */
3158*7c478bd9Sstevel@tonic-gate 		curname = mp->mod_filename;
3159*7c478bd9Sstevel@tonic-gate 		if (curname == NULL ||
3160*7c478bd9Sstevel@tonic-gate 		    ((mp->mod_prim == 0) &&
3161*7c478bd9Sstevel@tonic-gate 		    (curname != filename) &&
3162*7c478bd9Sstevel@tonic-gate 		    (modname != filename) &&
3163*7c478bd9Sstevel@tonic-gate 		    (strcmp(curname, filename) != 0))) {
3164*7c478bd9Sstevel@tonic-gate 			newname = kobj_zalloc(strlen(filename) + 1, KM_SLEEP);
3165*7c478bd9Sstevel@tonic-gate 			(void) strcpy(newname, filename);
3166*7c478bd9Sstevel@tonic-gate 			mp->mod_filename = newname;
3167*7c478bd9Sstevel@tonic-gate 			if (curname != NULL)
3168*7c478bd9Sstevel@tonic-gate 				kobj_free(curname, strlen(curname) + 1);
3169*7c478bd9Sstevel@tonic-gate 		}
3170*7c478bd9Sstevel@tonic-gate 	}
3171*7c478bd9Sstevel@tonic-gate 
3172*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
3173*7c478bd9Sstevel@tonic-gate 	if (mp && moddebug & MODDEBUG_LOADMSG2)
3174*7c478bd9Sstevel@tonic-gate 		printf("Holding %s\n", mp->mod_filename);
3175*7c478bd9Sstevel@tonic-gate 	if (mp == NULL && moddebug & MODDEBUG_LOADMSG2)
3176*7c478bd9Sstevel@tonic-gate 		printf("circular dependency loading %s\n", filename);
3177*7c478bd9Sstevel@tonic-gate 	return (mp);
3178*7c478bd9Sstevel@tonic-gate }
3179*7c478bd9Sstevel@tonic-gate 
3180*7c478bd9Sstevel@tonic-gate static struct modctl *
3181*7c478bd9Sstevel@tonic-gate mod_hold_by_name_requisite(struct modctl *dep, char *filename)
3182*7c478bd9Sstevel@tonic-gate {
3183*7c478bd9Sstevel@tonic-gate 	return (mod_hold_by_name_common(dep, filename));
3184*7c478bd9Sstevel@tonic-gate }
3185*7c478bd9Sstevel@tonic-gate 
3186*7c478bd9Sstevel@tonic-gate struct modctl *
3187*7c478bd9Sstevel@tonic-gate mod_hold_by_name(char *filename)
3188*7c478bd9Sstevel@tonic-gate {
3189*7c478bd9Sstevel@tonic-gate 	return (mod_hold_by_name_common(NULL, filename));
3190*7c478bd9Sstevel@tonic-gate }
3191*7c478bd9Sstevel@tonic-gate 
3192*7c478bd9Sstevel@tonic-gate static struct modctl *
3193*7c478bd9Sstevel@tonic-gate mod_hold_by_id(modid_t modid)
3194*7c478bd9Sstevel@tonic-gate {
3195*7c478bd9Sstevel@tonic-gate 	struct modctl	*mp;
3196*7c478bd9Sstevel@tonic-gate 	int		found = 0;
3197*7c478bd9Sstevel@tonic-gate 
3198*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
3199*7c478bd9Sstevel@tonic-gate 	mp = &modules;
3200*7c478bd9Sstevel@tonic-gate 	do {
3201*7c478bd9Sstevel@tonic-gate 		if (mp->mod_id == modid) {
3202*7c478bd9Sstevel@tonic-gate 			found = 1;
3203*7c478bd9Sstevel@tonic-gate 			break;
3204*7c478bd9Sstevel@tonic-gate 		}
3205*7c478bd9Sstevel@tonic-gate 	} while ((mp = mp->mod_next) != &modules);
3206*7c478bd9Sstevel@tonic-gate 
3207*7c478bd9Sstevel@tonic-gate 	if ((found == 0) || mod_circdep(mp))
3208*7c478bd9Sstevel@tonic-gate 		mp = NULL;
3209*7c478bd9Sstevel@tonic-gate 	else
3210*7c478bd9Sstevel@tonic-gate 		(void) mod_hold_by_modctl(mp, MOD_WAIT_FOREVER | MOD_LOCK_HELD);
3211*7c478bd9Sstevel@tonic-gate 
3212*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
3213*7c478bd9Sstevel@tonic-gate 	return (mp);
3214*7c478bd9Sstevel@tonic-gate }
3215*7c478bd9Sstevel@tonic-gate 
3216*7c478bd9Sstevel@tonic-gate static struct modctl *
3217*7c478bd9Sstevel@tonic-gate mod_hold_next_by_id(modid_t modid)
3218*7c478bd9Sstevel@tonic-gate {
3219*7c478bd9Sstevel@tonic-gate 	struct modctl	*mp;
3220*7c478bd9Sstevel@tonic-gate 	int		found = 0;
3221*7c478bd9Sstevel@tonic-gate 
3222*7c478bd9Sstevel@tonic-gate 	if (modid < -1)
3223*7c478bd9Sstevel@tonic-gate 		return (NULL);
3224*7c478bd9Sstevel@tonic-gate 
3225*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
3226*7c478bd9Sstevel@tonic-gate 
3227*7c478bd9Sstevel@tonic-gate 	mp = &modules;
3228*7c478bd9Sstevel@tonic-gate 	do {
3229*7c478bd9Sstevel@tonic-gate 		if (mp->mod_id > modid) {
3230*7c478bd9Sstevel@tonic-gate 			found = 1;
3231*7c478bd9Sstevel@tonic-gate 			break;
3232*7c478bd9Sstevel@tonic-gate 		}
3233*7c478bd9Sstevel@tonic-gate 	} while ((mp = mp->mod_next) != &modules);
3234*7c478bd9Sstevel@tonic-gate 
3235*7c478bd9Sstevel@tonic-gate 	if ((found == 0) || mod_circdep(mp))
3236*7c478bd9Sstevel@tonic-gate 		mp = NULL;
3237*7c478bd9Sstevel@tonic-gate 	else
3238*7c478bd9Sstevel@tonic-gate 		(void) mod_hold_by_modctl(mp, MOD_WAIT_FOREVER | MOD_LOCK_HELD);
3239*7c478bd9Sstevel@tonic-gate 
3240*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
3241*7c478bd9Sstevel@tonic-gate 	return (mp);
3242*7c478bd9Sstevel@tonic-gate }
3243*7c478bd9Sstevel@tonic-gate 
3244*7c478bd9Sstevel@tonic-gate static void
3245*7c478bd9Sstevel@tonic-gate mod_release(struct modctl *mp)
3246*7c478bd9Sstevel@tonic-gate {
3247*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&mod_lock));
3248*7c478bd9Sstevel@tonic-gate 	ASSERT(mp->mod_busy);
3249*7c478bd9Sstevel@tonic-gate 
3250*7c478bd9Sstevel@tonic-gate 	mp->mod_busy = 0;
3251*7c478bd9Sstevel@tonic-gate 	mp->mod_inprogress_thread = NULL;
3252*7c478bd9Sstevel@tonic-gate 	if (mp->mod_want) {
3253*7c478bd9Sstevel@tonic-gate 		mp->mod_want = 0;
3254*7c478bd9Sstevel@tonic-gate 		cv_broadcast(&mod_cv);
3255*7c478bd9Sstevel@tonic-gate 	}
3256*7c478bd9Sstevel@tonic-gate }
3257*7c478bd9Sstevel@tonic-gate 
3258*7c478bd9Sstevel@tonic-gate void
3259*7c478bd9Sstevel@tonic-gate mod_release_mod(struct modctl *mp)
3260*7c478bd9Sstevel@tonic-gate {
3261*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_LOADMSG2)
3262*7c478bd9Sstevel@tonic-gate 		printf("Releasing %s\n", mp->mod_filename);
3263*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
3264*7c478bd9Sstevel@tonic-gate 	mod_release(mp);
3265*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
3266*7c478bd9Sstevel@tonic-gate }
3267*7c478bd9Sstevel@tonic-gate 
3268*7c478bd9Sstevel@tonic-gate modid_t
3269*7c478bd9Sstevel@tonic-gate mod_name_to_modid(char *filename)
3270*7c478bd9Sstevel@tonic-gate {
3271*7c478bd9Sstevel@tonic-gate 	char		*modname;
3272*7c478bd9Sstevel@tonic-gate 	struct modctl	*mp;
3273*7c478bd9Sstevel@tonic-gate 
3274*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
3275*7c478bd9Sstevel@tonic-gate 
3276*7c478bd9Sstevel@tonic-gate 	if ((modname = strrchr(filename, '/')) == NULL)
3277*7c478bd9Sstevel@tonic-gate 		modname = filename;
3278*7c478bd9Sstevel@tonic-gate 	else
3279*7c478bd9Sstevel@tonic-gate 		modname++;
3280*7c478bd9Sstevel@tonic-gate 
3281*7c478bd9Sstevel@tonic-gate 	mp = &modules;
3282*7c478bd9Sstevel@tonic-gate 	do {
3283*7c478bd9Sstevel@tonic-gate 		if (strcmp(modname, mp->mod_modname) == 0) {
3284*7c478bd9Sstevel@tonic-gate 			mutex_exit(&mod_lock);
3285*7c478bd9Sstevel@tonic-gate 			return (mp->mod_id);
3286*7c478bd9Sstevel@tonic-gate 		}
3287*7c478bd9Sstevel@tonic-gate 	} while ((mp = mp->mod_next) != &modules);
3288*7c478bd9Sstevel@tonic-gate 
3289*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
3290*7c478bd9Sstevel@tonic-gate 	return (-1);
3291*7c478bd9Sstevel@tonic-gate }
3292*7c478bd9Sstevel@tonic-gate 
3293*7c478bd9Sstevel@tonic-gate 
3294*7c478bd9Sstevel@tonic-gate int
3295*7c478bd9Sstevel@tonic-gate mod_remove_by_name(char *name)
3296*7c478bd9Sstevel@tonic-gate {
3297*7c478bd9Sstevel@tonic-gate 	struct modctl *mp;
3298*7c478bd9Sstevel@tonic-gate 	int retval;
3299*7c478bd9Sstevel@tonic-gate 
3300*7c478bd9Sstevel@tonic-gate 	mp = mod_hold_by_name(name);
3301*7c478bd9Sstevel@tonic-gate 
3302*7c478bd9Sstevel@tonic-gate 	if (mp == NULL)
3303*7c478bd9Sstevel@tonic-gate 		return (EINVAL);
3304*7c478bd9Sstevel@tonic-gate 
3305*7c478bd9Sstevel@tonic-gate 	if (mp->mod_loadflags & MOD_NOAUTOUNLOAD) {
3306*7c478bd9Sstevel@tonic-gate 		/*
3307*7c478bd9Sstevel@tonic-gate 		 * Do not unload forceloaded modules
3308*7c478bd9Sstevel@tonic-gate 		 */
3309*7c478bd9Sstevel@tonic-gate 		mod_release_mod(mp);
3310*7c478bd9Sstevel@tonic-gate 		return (0);
3311*7c478bd9Sstevel@tonic-gate 	}
3312*7c478bd9Sstevel@tonic-gate 
3313*7c478bd9Sstevel@tonic-gate 	if ((retval = moduninstall(mp)) == 0)
3314*7c478bd9Sstevel@tonic-gate 		mod_unload(mp);
3315*7c478bd9Sstevel@tonic-gate 	else if (retval == EALREADY)
3316*7c478bd9Sstevel@tonic-gate 		retval = 0;		/* already unloaded, not an error */
3317*7c478bd9Sstevel@tonic-gate 	mod_release_mod(mp);
3318*7c478bd9Sstevel@tonic-gate 	return (retval);
3319*7c478bd9Sstevel@tonic-gate }
3320*7c478bd9Sstevel@tonic-gate 
3321*7c478bd9Sstevel@tonic-gate /*
3322*7c478bd9Sstevel@tonic-gate  * Record that module "dep" is dependent on module "on_mod."
3323*7c478bd9Sstevel@tonic-gate  */
3324*7c478bd9Sstevel@tonic-gate static void
3325*7c478bd9Sstevel@tonic-gate mod_make_requisite(struct modctl *dependent, struct modctl *on_mod)
3326*7c478bd9Sstevel@tonic-gate {
3327*7c478bd9Sstevel@tonic-gate 	struct modctl_list **pmlnp;	/* previous next pointer */
3328*7c478bd9Sstevel@tonic-gate 	struct modctl_list *mlp;
3329*7c478bd9Sstevel@tonic-gate 	struct modctl_list *new;
3330*7c478bd9Sstevel@tonic-gate 
3331*7c478bd9Sstevel@tonic-gate 	ASSERT(dependent->mod_busy && on_mod->mod_busy);
3332*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);
3333*7c478bd9Sstevel@tonic-gate 
3334*7c478bd9Sstevel@tonic-gate 	/*
3335*7c478bd9Sstevel@tonic-gate 	 * Search dependent's requisite list to see if on_mod is recorded.
3336*7c478bd9Sstevel@tonic-gate 	 * List is ordered by id.
3337*7c478bd9Sstevel@tonic-gate 	 */
3338*7c478bd9Sstevel@tonic-gate 	for (pmlnp = &dependent->mod_requisites, mlp = *pmlnp;
3339*7c478bd9Sstevel@tonic-gate 	    mlp; pmlnp = &mlp->modl_next, mlp = *pmlnp)
3340*7c478bd9Sstevel@tonic-gate 		if (mlp->modl_modp->mod_id >= on_mod->mod_id)
3341*7c478bd9Sstevel@tonic-gate 			break;
3342*7c478bd9Sstevel@tonic-gate 
3343*7c478bd9Sstevel@tonic-gate 	/* Create and insert if not already recorded */
3344*7c478bd9Sstevel@tonic-gate 	if ((mlp == NULL) || (mlp->modl_modp->mod_id != on_mod->mod_id)) {
3345*7c478bd9Sstevel@tonic-gate 		new = kobj_zalloc(sizeof (*new), KM_SLEEP);
3346*7c478bd9Sstevel@tonic-gate 		new->modl_modp = on_mod;
3347*7c478bd9Sstevel@tonic-gate 		new->modl_next = mlp;
3348*7c478bd9Sstevel@tonic-gate 		*pmlnp = new;
3349*7c478bd9Sstevel@tonic-gate 
3350*7c478bd9Sstevel@tonic-gate 		/*
3351*7c478bd9Sstevel@tonic-gate 		 * Increment the mod_ref count in our new requisite module.
3352*7c478bd9Sstevel@tonic-gate 		 * This is what keeps a module that has other modules
3353*7c478bd9Sstevel@tonic-gate 		 * which are dependent on it from being uninstalled and
3354*7c478bd9Sstevel@tonic-gate 		 * unloaded. "on_mod"'s mod_ref count decremented in
3355*7c478bd9Sstevel@tonic-gate 		 * mod_release_requisites when the "dependent" module
3356*7c478bd9Sstevel@tonic-gate 		 * unload is complete.  "on_mod" must be loaded, but may not
3357*7c478bd9Sstevel@tonic-gate 		 * yet be installed.
3358*7c478bd9Sstevel@tonic-gate 		 */
3359*7c478bd9Sstevel@tonic-gate 		on_mod->mod_ref++;
3360*7c478bd9Sstevel@tonic-gate 		ASSERT(on_mod->mod_ref && on_mod->mod_loaded);
3361*7c478bd9Sstevel@tonic-gate 	}
3362*7c478bd9Sstevel@tonic-gate 
3363*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
3364*7c478bd9Sstevel@tonic-gate }
3365*7c478bd9Sstevel@tonic-gate 
3366*7c478bd9Sstevel@tonic-gate /*
3367*7c478bd9Sstevel@tonic-gate  * release the hold associated with mod_make_requisite mod_ref++
3368*7c478bd9Sstevel@tonic-gate  * as part of unload.
3369*7c478bd9Sstevel@tonic-gate  */
3370*7c478bd9Sstevel@tonic-gate void
3371*7c478bd9Sstevel@tonic-gate mod_release_requisites(struct modctl *modp)
3372*7c478bd9Sstevel@tonic-gate {
3373*7c478bd9Sstevel@tonic-gate 	struct modctl_list *modl;
3374*7c478bd9Sstevel@tonic-gate 	struct modctl_list *next;
3375*7c478bd9Sstevel@tonic-gate 	struct modctl *req;
3376*7c478bd9Sstevel@tonic-gate 	struct modctl_list *start = NULL, *mod_garbage;
3377*7c478bd9Sstevel@tonic-gate 
3378*7c478bd9Sstevel@tonic-gate 	ASSERT(modp->mod_busy);
3379*7c478bd9Sstevel@tonic-gate 	ASSERT(!MUTEX_HELD(&mod_lock));
3380*7c478bd9Sstevel@tonic-gate 
3381*7c478bd9Sstevel@tonic-gate 	mutex_enter(&mod_lock);		/* needed for manipulation of req */
3382*7c478bd9Sstevel@tonic-gate 	for (modl = modp->mod_requisites; modl; modl = next) {
3383*7c478bd9Sstevel@tonic-gate 		next = modl->modl_next;
3384*7c478bd9Sstevel@tonic-gate 		req = modl->modl_modp;
3385*7c478bd9Sstevel@tonic-gate 		ASSERT(req->mod_ref >= 1 && req->mod_loaded);
3386*7c478bd9Sstevel@tonic-gate 		req->mod_ref--;
3387*7c478bd9Sstevel@tonic-gate 
3388*7c478bd9Sstevel@tonic-gate 		/*
3389*7c478bd9Sstevel@tonic-gate 		 * Check if the module has to be unloaded or not.
3390*7c478bd9Sstevel@tonic-gate 		 */
3391*7c478bd9Sstevel@tonic-gate 		if (req->mod_ref == 0 && req->mod_delay_unload) {
3392*7c478bd9Sstevel@tonic-gate 			struct modctl_list *new;
3393*7c478bd9Sstevel@tonic-gate 			/*
3394*7c478bd9Sstevel@tonic-gate 			 * Allocate the modclt_list holding the garbage
3395*7c478bd9Sstevel@tonic-gate 			 * module which should be unloaded later.
3396*7c478bd9Sstevel@tonic-gate 			 */
3397*7c478bd9Sstevel@tonic-gate 			new = kobj_zalloc(sizeof (struct modctl_list),
3398*7c478bd9Sstevel@tonic-gate 			    KM_SLEEP);
3399*7c478bd9Sstevel@tonic-gate 			new->modl_modp = req;
3400*7c478bd9Sstevel@tonic-gate 
3401*7c478bd9Sstevel@tonic-gate 			if (start == NULL)
3402*7c478bd9Sstevel@tonic-gate 				mod_garbage = start = new;
3403*7c478bd9Sstevel@tonic-gate 			else {
3404*7c478bd9Sstevel@tonic-gate 				mod_garbage->modl_next = new;
3405*7c478bd9Sstevel@tonic-gate 				mod_garbage = new;
3406*7c478bd9Sstevel@tonic-gate 			}
3407*7c478bd9Sstevel@tonic-gate 		}
3408*7c478bd9Sstevel@tonic-gate 
3409*7c478bd9Sstevel@tonic-gate 		/* free the list as we go */
3410*7c478bd9Sstevel@tonic-gate 		kobj_free(modl, sizeof (*modl));
3411*7c478bd9Sstevel@tonic-gate 	}
3412*7c478bd9Sstevel@tonic-gate 	modp->mod_requisites = NULL;
3413*7c478bd9Sstevel@tonic-gate 	mutex_exit(&mod_lock);
3414*7c478bd9Sstevel@tonic-gate 
3415*7c478bd9Sstevel@tonic-gate 	/*
3416*7c478bd9Sstevel@tonic-gate 	 * Unload the garbage modules.
3417*7c478bd9Sstevel@tonic-gate 	 */
3418*7c478bd9Sstevel@tonic-gate 	for (mod_garbage = start; mod_garbage != NULL; /* nothing */) {
3419*7c478bd9Sstevel@tonic-gate 		struct modctl_list *old = mod_garbage;
3420*7c478bd9Sstevel@tonic-gate 		struct modctl *mp = mod_garbage->modl_modp;
3421*7c478bd9Sstevel@tonic-gate 		ASSERT(mp != NULL);
3422*7c478bd9Sstevel@tonic-gate 
3423*7c478bd9Sstevel@tonic-gate 		/*
3424*7c478bd9Sstevel@tonic-gate 		 * Hold this module until it's unloaded completely.
3425*7c478bd9Sstevel@tonic-gate 		 */
3426*7c478bd9Sstevel@tonic-gate 		(void) mod_hold_by_modctl(mp,
3427*7c478bd9Sstevel@tonic-gate 		    MOD_WAIT_FOREVER | MOD_LOCK_NOT_HELD);
3428*7c478bd9Sstevel@tonic-gate 		/*
3429*7c478bd9Sstevel@tonic-gate 		 * Check if the module is not unloaded yet and nobody requires
3430*7c478bd9Sstevel@tonic-gate 		 * the module. If it's unloaded already or somebody still
3431*7c478bd9Sstevel@tonic-gate 		 * requires the module, don't unload it now.
3432*7c478bd9Sstevel@tonic-gate 		 */
3433*7c478bd9Sstevel@tonic-gate 		if (mp->mod_loaded && mp->mod_ref == 0)
3434*7c478bd9Sstevel@tonic-gate 			mod_unload(mp);
3435*7c478bd9Sstevel@tonic-gate 		ASSERT((mp->mod_loaded == 0 && mp->mod_delay_unload == 0) ||
3436*7c478bd9Sstevel@tonic-gate 		    (mp->mod_ref > 0));
3437*7c478bd9Sstevel@tonic-gate 		mod_release_mod(mp);
3438*7c478bd9Sstevel@tonic-gate 
3439*7c478bd9Sstevel@tonic-gate 		mod_garbage = mod_garbage->modl_next;
3440*7c478bd9Sstevel@tonic-gate 		kobj_free(old, sizeof (struct modctl_list));
3441*7c478bd9Sstevel@tonic-gate 	}
3442*7c478bd9Sstevel@tonic-gate }
3443*7c478bd9Sstevel@tonic-gate 
3444*7c478bd9Sstevel@tonic-gate /*
3445*7c478bd9Sstevel@tonic-gate  * Process dependency of the module represented by "dep" on the
3446*7c478bd9Sstevel@tonic-gate  * module named by "on."
3447*7c478bd9Sstevel@tonic-gate  *
3448*7c478bd9Sstevel@tonic-gate  * Called from kobj_do_dependents() to load a module "on" on which
3449*7c478bd9Sstevel@tonic-gate  * "dep" depends.
3450*7c478bd9Sstevel@tonic-gate  */
3451*7c478bd9Sstevel@tonic-gate struct modctl *
3452*7c478bd9Sstevel@tonic-gate mod_load_requisite(struct modctl *dep, char *on)
3453*7c478bd9Sstevel@tonic-gate {
3454*7c478bd9Sstevel@tonic-gate 	struct modctl *on_mod;
3455*7c478bd9Sstevel@tonic-gate 	int retval;
3456*7c478bd9Sstevel@tonic-gate 
3457*7c478bd9Sstevel@tonic-gate 	if ((on_mod = mod_hold_loaded_mod(dep, on, &retval)) != NULL) {
3458*7c478bd9Sstevel@tonic-gate 		mod_make_requisite(dep, on_mod);
3459*7c478bd9Sstevel@tonic-gate 	} else if (moddebug & MODDEBUG_ERRMSG) {
3460*7c478bd9Sstevel@tonic-gate 		printf("error processing %s on which module %s depends\n",
3461*7c478bd9Sstevel@tonic-gate 			on, dep->mod_modname);
3462*7c478bd9Sstevel@tonic-gate 	}
3463*7c478bd9Sstevel@tonic-gate 	return (on_mod);
3464*7c478bd9Sstevel@tonic-gate }
3465*7c478bd9Sstevel@tonic-gate 
3466*7c478bd9Sstevel@tonic-gate static int
3467*7c478bd9Sstevel@tonic-gate mod_install_requisites(struct modctl *modp)
3468*7c478bd9Sstevel@tonic-gate {
3469*7c478bd9Sstevel@tonic-gate 	struct modctl_list *modl;
3470*7c478bd9Sstevel@tonic-gate 	struct modctl *req;
3471*7c478bd9Sstevel@tonic-gate 	int status = 0;
3472*7c478bd9Sstevel@tonic-gate 
3473*7c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_NOT_HELD(&mod_lock));
3474*7c478bd9Sstevel@tonic-gate 	ASSERT(modp->mod_busy);
3475*7c478bd9Sstevel@tonic-gate 
3476*7c478bd9Sstevel@tonic-gate 	for (modl = modp->mod_requisites; modl; modl = modl->modl_next) {
3477*7c478bd9Sstevel@tonic-gate 		req = modl->modl_modp;
3478*7c478bd9Sstevel@tonic-gate 		(void) mod_hold_by_modctl(req,
3479*7c478bd9Sstevel@tonic-gate 		    MOD_WAIT_FOREVER | MOD_LOCK_NOT_HELD);
3480*7c478bd9Sstevel@tonic-gate 		status = modinstall(req);
3481*7c478bd9Sstevel@tonic-gate 		mod_release_mod(req);
3482*7c478bd9Sstevel@tonic-gate 
3483*7c478bd9Sstevel@tonic-gate 		if (status != 0)
3484*7c478bd9Sstevel@tonic-gate 			break;
3485*7c478bd9Sstevel@tonic-gate 	}
3486*7c478bd9Sstevel@tonic-gate 	return (status);
3487*7c478bd9Sstevel@tonic-gate }
3488*7c478bd9Sstevel@tonic-gate 
3489*7c478bd9Sstevel@tonic-gate /*
3490*7c478bd9Sstevel@tonic-gate  * returns 1 if this thread is doing autounload, 0 otherwise.
3491*7c478bd9Sstevel@tonic-gate  * see mod_uninstall_all.
3492*7c478bd9Sstevel@tonic-gate  */
3493*7c478bd9Sstevel@tonic-gate int
3494*7c478bd9Sstevel@tonic-gate mod_in_autounload()
3495*7c478bd9Sstevel@tonic-gate {
3496*7c478bd9Sstevel@tonic-gate 	return ((int)(uintptr_t)tsd_get(mod_autounload_key));
3497*7c478bd9Sstevel@tonic-gate }
3498*7c478bd9Sstevel@tonic-gate 
3499*7c478bd9Sstevel@tonic-gate /*
3500*7c478bd9Sstevel@tonic-gate  * gmatch adapted from libc, stripping the wchar stuff
3501*7c478bd9Sstevel@tonic-gate  */
3502*7c478bd9Sstevel@tonic-gate #define	popchar(p, c) \
3503*7c478bd9Sstevel@tonic-gate 	c = *p++; \
3504*7c478bd9Sstevel@tonic-gate 	if (c == 0) \
3505*7c478bd9Sstevel@tonic-gate 		return (0);
3506*7c478bd9Sstevel@tonic-gate 
3507*7c478bd9Sstevel@tonic-gate static int
3508*7c478bd9Sstevel@tonic-gate gmatch(const char *s, const char *p)
3509*7c478bd9Sstevel@tonic-gate {
3510*7c478bd9Sstevel@tonic-gate 	int c, sc;
3511*7c478bd9Sstevel@tonic-gate 	int ok, lc, notflag;
3512*7c478bd9Sstevel@tonic-gate 
3513*7c478bd9Sstevel@tonic-gate 	sc = *s++;
3514*7c478bd9Sstevel@tonic-gate 	c = *p++;
3515*7c478bd9Sstevel@tonic-gate 	if (c == 0)
3516*7c478bd9Sstevel@tonic-gate 		return (sc == c);	/* nothing matches nothing */
3517*7c478bd9Sstevel@tonic-gate 
3518*7c478bd9Sstevel@tonic-gate 	switch (c) {
3519*7c478bd9Sstevel@tonic-gate 	case '\\':
3520*7c478bd9Sstevel@tonic-gate 		/* skip to quoted character */
3521*7c478bd9Sstevel@tonic-gate 		popchar(p, c)
3522*7c478bd9Sstevel@tonic-gate 		/*FALLTHRU*/
3523*7c478bd9Sstevel@tonic-gate 
3524*7c478bd9Sstevel@tonic-gate 	default:
3525*7c478bd9Sstevel@tonic-gate 		/* straight comparison */
3526*7c478bd9Sstevel@tonic-gate 		if (c != sc)
3527*7c478bd9Sstevel@tonic-gate 			return (0);
3528*7c478bd9Sstevel@tonic-gate 		/*FALLTHRU*/
3529*7c478bd9Sstevel@tonic-gate 
3530*7c478bd9Sstevel@tonic-gate 	case '?':
3531*7c478bd9Sstevel@tonic-gate 		/* first char matches, move to remainder */
3532*7c478bd9Sstevel@tonic-gate 		return (sc != '\0' ? gmatch(s, p) : 0);
3533*7c478bd9Sstevel@tonic-gate 
3534*7c478bd9Sstevel@tonic-gate 
3535*7c478bd9Sstevel@tonic-gate 	case '*':
3536*7c478bd9Sstevel@tonic-gate 		while (*p == '*')
3537*7c478bd9Sstevel@tonic-gate 			p++;
3538*7c478bd9Sstevel@tonic-gate 
3539*7c478bd9Sstevel@tonic-gate 		/* * matches everything */
3540*7c478bd9Sstevel@tonic-gate 		if (*p == 0)
3541*7c478bd9Sstevel@tonic-gate 			return (1);
3542*7c478bd9Sstevel@tonic-gate 
3543*7c478bd9Sstevel@tonic-gate 		/* undo skip at the beginning & iterate over substrings */
3544*7c478bd9Sstevel@tonic-gate 		--s;
3545*7c478bd9Sstevel@tonic-gate 		while (*s) {
3546*7c478bd9Sstevel@tonic-gate 			if (gmatch(s, p))
3547*7c478bd9Sstevel@tonic-gate 				return (1);
3548*7c478bd9Sstevel@tonic-gate 			s++;
3549*7c478bd9Sstevel@tonic-gate 		}
3550*7c478bd9Sstevel@tonic-gate 		return (0);
3551*7c478bd9Sstevel@tonic-gate 
3552*7c478bd9Sstevel@tonic-gate 	case '[':
3553*7c478bd9Sstevel@tonic-gate 		/* match any char within [] */
3554*7c478bd9Sstevel@tonic-gate 		if (sc == 0)
3555*7c478bd9Sstevel@tonic-gate 			return (0);
3556*7c478bd9Sstevel@tonic-gate 
3557*7c478bd9Sstevel@tonic-gate 		ok = lc = notflag = 0;
3558*7c478bd9Sstevel@tonic-gate 
3559*7c478bd9Sstevel@tonic-gate 		if (*p == '!') {
3560*7c478bd9Sstevel@tonic-gate 			notflag = 1;
3561*7c478bd9Sstevel@tonic-gate 			p++;
3562*7c478bd9Sstevel@tonic-gate 		}
3563*7c478bd9Sstevel@tonic-gate 		popchar(p, c)
3564*7c478bd9Sstevel@tonic-gate 
3565*7c478bd9Sstevel@tonic-gate 		do {
3566*7c478bd9Sstevel@tonic-gate 			if (c == '-' && lc && *p != ']') {
3567*7c478bd9Sstevel@tonic-gate 				/* test sc against range [c1-c2] */
3568*7c478bd9Sstevel@tonic-gate 				popchar(p, c)
3569*7c478bd9Sstevel@tonic-gate 				if (c == '\\') {
3570*7c478bd9Sstevel@tonic-gate 					popchar(p, c)
3571*7c478bd9Sstevel@tonic-gate 				}
3572*7c478bd9Sstevel@tonic-gate 
3573*7c478bd9Sstevel@tonic-gate 				if (notflag) {
3574*7c478bd9Sstevel@tonic-gate 					/* return 0 on mismatch */
3575*7c478bd9Sstevel@tonic-gate 					if (lc <= sc && sc <= c)
3576*7c478bd9Sstevel@tonic-gate 						return (0);
3577*7c478bd9Sstevel@tonic-gate 					ok++;
3578*7c478bd9Sstevel@tonic-gate 				} else if (lc <= sc && sc <= c) {
3579*7c478bd9Sstevel@tonic-gate 					ok++;
3580*7c478bd9Sstevel@tonic-gate 				}
3581*7c478bd9Sstevel@tonic-gate 				/* keep going, may get a match next */
3582*7c478bd9Sstevel@tonic-gate 			} else if (c == '\\') {
3583*7c478bd9Sstevel@tonic-gate 				/* skip to quoted character */
3584*7c478bd9Sstevel@tonic-gate 				popchar(p, c)
3585*7c478bd9Sstevel@tonic-gate 			}
3586*7c478bd9Sstevel@tonic-gate 			lc = c;
3587*7c478bd9Sstevel@tonic-gate 			if (notflag) {
3588*7c478bd9Sstevel@tonic-gate 				if (sc == lc)
3589*7c478bd9Sstevel@tonic-gate 					return (0);
3590*7c478bd9Sstevel@tonic-gate 				ok++;
3591*7c478bd9Sstevel@tonic-gate 			} else if (sc == lc) {
3592*7c478bd9Sstevel@tonic-gate 				ok++;
3593*7c478bd9Sstevel@tonic-gate 			}
3594*7c478bd9Sstevel@tonic-gate 			popchar(p, c)
3595*7c478bd9Sstevel@tonic-gate 		} while (c != ']');
3596*7c478bd9Sstevel@tonic-gate 
3597*7c478bd9Sstevel@tonic-gate 		/* recurse on remainder of string */
3598*7c478bd9Sstevel@tonic-gate 		return (ok ? gmatch(s, p) : 0);
3599*7c478bd9Sstevel@tonic-gate 	}
3600*7c478bd9Sstevel@tonic-gate 	/*NOTREACHED*/
3601*7c478bd9Sstevel@tonic-gate }
3602*7c478bd9Sstevel@tonic-gate 
3603*7c478bd9Sstevel@tonic-gate 
3604*7c478bd9Sstevel@tonic-gate /*
3605*7c478bd9Sstevel@tonic-gate  * Get default perm for device from /etc/minor_perm. Return 0 if match found.
3606*7c478bd9Sstevel@tonic-gate  *
3607*7c478bd9Sstevel@tonic-gate  * Pure wild-carded patterns are handled separately so the ordering of
3608*7c478bd9Sstevel@tonic-gate  * these patterns doesn't matter.  We're still dependent on ordering
3609*7c478bd9Sstevel@tonic-gate  * however as the first matching entry is the one returned.
3610*7c478bd9Sstevel@tonic-gate  * Not ideal but all existing examples and usage do imply this
3611*7c478bd9Sstevel@tonic-gate  * ordering implicitly.
3612*7c478bd9Sstevel@tonic-gate  *
3613*7c478bd9Sstevel@tonic-gate  * Drivers using the clone driver are always good for some entertainment.
3614*7c478bd9Sstevel@tonic-gate  * Clone nodes under pseudo have the form clone@0:<driver>.  Some minor
3615*7c478bd9Sstevel@tonic-gate  * perm entries have the form clone:<driver>, others use <driver>:*
3616*7c478bd9Sstevel@tonic-gate  * Examples are clone:llc1 vs. llc2:*, for example.
3617*7c478bd9Sstevel@tonic-gate  *
3618*7c478bd9Sstevel@tonic-gate  * Minor perms in the clone:<driver> form are mapped to the drivers's
3619*7c478bd9Sstevel@tonic-gate  * mperm list, not the clone driver, as wildcard entries for clone
3620*7c478bd9Sstevel@tonic-gate  * reference only.  In other words, a clone wildcard will match
3621*7c478bd9Sstevel@tonic-gate  * references for clone@0:<driver> but never <driver>@<minor>.
3622*7c478bd9Sstevel@tonic-gate  *
3623*7c478bd9Sstevel@tonic-gate  * Additional minor perms in the standard form are also supported,
3624*7c478bd9Sstevel@tonic-gate  * for mixed usage, ie a node with an entry clone:<driver> could
3625*7c478bd9Sstevel@tonic-gate  * provide further entries <driver>:<minor>.
3626*7c478bd9Sstevel@tonic-gate  *
3627*7c478bd9Sstevel@tonic-gate  * Finally, some uses of clone use an alias as the minor name rather
3628*7c478bd9Sstevel@tonic-gate  * than the driver name, with the alias as the minor perm entry.
3629*7c478bd9Sstevel@tonic-gate  * This case is handled by attaching the driver to bring its
3630*7c478bd9Sstevel@tonic-gate  * minor list into existence, then discover the alias via DDI_ALIAS.
3631*7c478bd9Sstevel@tonic-gate  * The clone device's minor perm list can then be searched for
3632*7c478bd9Sstevel@tonic-gate  * that alias.
3633*7c478bd9Sstevel@tonic-gate  */
3634*7c478bd9Sstevel@tonic-gate 
3635*7c478bd9Sstevel@tonic-gate static int
3636*7c478bd9Sstevel@tonic-gate dev_alias_minorperm(dev_info_t *dip, char *minor_name, mperm_t *rmp)
3637*7c478bd9Sstevel@tonic-gate {
3638*7c478bd9Sstevel@tonic-gate 	major_t major;
3639*7c478bd9Sstevel@tonic-gate 	struct devnames *dnp;
3640*7c478bd9Sstevel@tonic-gate 	mperm_t *mp;
3641*7c478bd9Sstevel@tonic-gate 	char *alias = NULL;
3642*7c478bd9Sstevel@tonic-gate 	dev_info_t *cdevi;
3643*7c478bd9Sstevel@tonic-gate 	struct ddi_minor_data *dmd;
3644*7c478bd9Sstevel@tonic-gate 
3645*7c478bd9Sstevel@tonic-gate 	major = ddi_name_to_major(minor_name);
3646*7c478bd9Sstevel@tonic-gate 
3647*7c478bd9Sstevel@tonic-gate 	ASSERT(dip == clone_dip);
3648*7c478bd9Sstevel@tonic-gate 	ASSERT(major != (major_t)-1);
3649*7c478bd9Sstevel@tonic-gate 
3650*7c478bd9Sstevel@tonic-gate 	/*
3651*7c478bd9Sstevel@tonic-gate 	 * Attach the driver named by the minor node, then
3652*7c478bd9Sstevel@tonic-gate 	 * search its first instance's minor list for an
3653*7c478bd9Sstevel@tonic-gate 	 * alias node.
3654*7c478bd9Sstevel@tonic-gate 	 */
3655*7c478bd9Sstevel@tonic-gate 	if (ddi_hold_installed_driver(major) == NULL)
3656*7c478bd9Sstevel@tonic-gate 		return (1);
3657*7c478bd9Sstevel@tonic-gate 
3658*7c478bd9Sstevel@tonic-gate 	dnp = &devnamesp[major];
3659*7c478bd9Sstevel@tonic-gate 	LOCK_DEV_OPS(&dnp->dn_lock);
3660*7c478bd9Sstevel@tonic-gate 
3661*7c478bd9Sstevel@tonic-gate 	if ((cdevi = dnp->dn_head) != NULL) {
3662*7c478bd9Sstevel@tonic-gate 		mutex_enter(&DEVI(cdevi)->devi_lock);
3663*7c478bd9Sstevel@tonic-gate 		for (dmd = DEVI(cdevi)->devi_minor; dmd; dmd = dmd->next) {
3664*7c478bd9Sstevel@tonic-gate 			if (dmd->type == DDM_ALIAS) {
3665*7c478bd9Sstevel@tonic-gate 				alias = i_ddi_strdup(dmd->ddm_name, KM_SLEEP);
3666*7c478bd9Sstevel@tonic-gate 				break;
3667*7c478bd9Sstevel@tonic-gate 			}
3668*7c478bd9Sstevel@tonic-gate 		}
3669*7c478bd9Sstevel@tonic-gate 		mutex_exit(&DEVI(cdevi)->devi_lock);
3670*7c478bd9Sstevel@tonic-gate 	}
3671*7c478bd9Sstevel@tonic-gate 
3672*7c478bd9Sstevel@tonic-gate 	UNLOCK_DEV_OPS(&dnp->dn_lock);
3673*7c478bd9Sstevel@tonic-gate 	ddi_rele_driver(major);
3674*7c478bd9Sstevel@tonic-gate 
3675*7c478bd9Sstevel@tonic-gate 	if (alias == NULL) {
3676*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_MINORPERM)
3677*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT, "dev_minorperm: "
3678*7c478bd9Sstevel@tonic-gate 			    "no alias for %s\n", minor_name);
3679*7c478bd9Sstevel@tonic-gate 		return (1);
3680*7c478bd9Sstevel@tonic-gate 	}
3681*7c478bd9Sstevel@tonic-gate 
3682*7c478bd9Sstevel@tonic-gate 	major = ddi_driver_major(clone_dip);
3683*7c478bd9Sstevel@tonic-gate 	dnp = &devnamesp[major];
3684*7c478bd9Sstevel@tonic-gate 	LOCK_DEV_OPS(&dnp->dn_lock);
3685*7c478bd9Sstevel@tonic-gate 
3686*7c478bd9Sstevel@tonic-gate 	/*
3687*7c478bd9Sstevel@tonic-gate 	 * Go through the clone driver's mperm list looking
3688*7c478bd9Sstevel@tonic-gate 	 * for a match for the specified alias.
3689*7c478bd9Sstevel@tonic-gate 	 */
3690*7c478bd9Sstevel@tonic-gate 	for (mp = dnp->dn_mperm; mp; mp = mp->mp_next) {
3691*7c478bd9Sstevel@tonic-gate 		if (strcmp(alias, mp->mp_minorname) == 0) {
3692*7c478bd9Sstevel@tonic-gate 			break;
3693*7c478bd9Sstevel@tonic-gate 		}
3694*7c478bd9Sstevel@tonic-gate 	}
3695*7c478bd9Sstevel@tonic-gate 
3696*7c478bd9Sstevel@tonic-gate 	if (mp) {
3697*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_MP_MATCH) {
3698*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT,
3699*7c478bd9Sstevel@tonic-gate 			    "minor perm defaults: %s %s 0%o %d %d (aliased)\n",
3700*7c478bd9Sstevel@tonic-gate 			    minor_name, alias, mp->mp_mode,
3701*7c478bd9Sstevel@tonic-gate 			    mp->mp_uid, mp->mp_gid);
3702*7c478bd9Sstevel@tonic-gate 		}
3703*7c478bd9Sstevel@tonic-gate 		rmp->mp_uid = mp->mp_uid;
3704*7c478bd9Sstevel@tonic-gate 		rmp->mp_gid = mp->mp_gid;
3705*7c478bd9Sstevel@tonic-gate 		rmp->mp_mode = mp->mp_mode;
3706*7c478bd9Sstevel@tonic-gate 	}
3707*7c478bd9Sstevel@tonic-gate 	UNLOCK_DEV_OPS(&dnp->dn_lock);
3708*7c478bd9Sstevel@tonic-gate 
3709*7c478bd9Sstevel@tonic-gate 	kmem_free(alias, strlen(alias)+1);
3710*7c478bd9Sstevel@tonic-gate 
3711*7c478bd9Sstevel@tonic-gate 	return (mp == NULL);
3712*7c478bd9Sstevel@tonic-gate }
3713*7c478bd9Sstevel@tonic-gate 
3714*7c478bd9Sstevel@tonic-gate int
3715*7c478bd9Sstevel@tonic-gate dev_minorperm(dev_info_t *dip, char *name, mperm_t *rmp)
3716*7c478bd9Sstevel@tonic-gate {
3717*7c478bd9Sstevel@tonic-gate 	major_t major;
3718*7c478bd9Sstevel@tonic-gate 	char *minor_name;
3719*7c478bd9Sstevel@tonic-gate 	struct devnames *dnp;
3720*7c478bd9Sstevel@tonic-gate 	mperm_t *mp;
3721*7c478bd9Sstevel@tonic-gate 	int is_clone = 0;
3722*7c478bd9Sstevel@tonic-gate 
3723*7c478bd9Sstevel@tonic-gate 	if (!minorperm_loaded) {
3724*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_MINORPERM)
3725*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT,
3726*7c478bd9Sstevel@tonic-gate 			    "%s: minor perm not yet loaded\n", name);
3727*7c478bd9Sstevel@tonic-gate 		return (1);
3728*7c478bd9Sstevel@tonic-gate 	}
3729*7c478bd9Sstevel@tonic-gate 
3730*7c478bd9Sstevel@tonic-gate 	minor_name = strchr(name, ':');
3731*7c478bd9Sstevel@tonic-gate 	if (minor_name == NULL)
3732*7c478bd9Sstevel@tonic-gate 		return (1);
3733*7c478bd9Sstevel@tonic-gate 	minor_name++;
3734*7c478bd9Sstevel@tonic-gate 
3735*7c478bd9Sstevel@tonic-gate 	/*
3736*7c478bd9Sstevel@tonic-gate 	 * If it's the clone driver, search the driver as named
3737*7c478bd9Sstevel@tonic-gate 	 * by the minor.  All clone minor perm entries other than
3738*7c478bd9Sstevel@tonic-gate 	 * alias nodes are actually installed on the real driver's list.
3739*7c478bd9Sstevel@tonic-gate 	 */
3740*7c478bd9Sstevel@tonic-gate 	if (dip == clone_dip) {
3741*7c478bd9Sstevel@tonic-gate 		major = ddi_name_to_major(minor_name);
3742*7c478bd9Sstevel@tonic-gate 		if (major == (major_t)-1) {
3743*7c478bd9Sstevel@tonic-gate 			if (moddebug & MODDEBUG_MINORPERM)
3744*7c478bd9Sstevel@tonic-gate 				cmn_err(CE_CONT, "dev_minorperm: "
3745*7c478bd9Sstevel@tonic-gate 				    "%s: no such driver\n", minor_name);
3746*7c478bd9Sstevel@tonic-gate 			return (1);
3747*7c478bd9Sstevel@tonic-gate 		}
3748*7c478bd9Sstevel@tonic-gate 		is_clone = 1;
3749*7c478bd9Sstevel@tonic-gate 	} else {
3750*7c478bd9Sstevel@tonic-gate 		major = ddi_driver_major(dip);
3751*7c478bd9Sstevel@tonic-gate 		ASSERT(major != (major_t)-1);
3752*7c478bd9Sstevel@tonic-gate 	}
3753*7c478bd9Sstevel@tonic-gate 
3754*7c478bd9Sstevel@tonic-gate 	dnp = &devnamesp[major];
3755*7c478bd9Sstevel@tonic-gate 	LOCK_DEV_OPS(&dnp->dn_lock);
3756*7c478bd9Sstevel@tonic-gate 
3757*7c478bd9Sstevel@tonic-gate 	/*
3758*7c478bd9Sstevel@tonic-gate 	 * Go through the driver's mperm list looking for
3759*7c478bd9Sstevel@tonic-gate 	 * a match for the specified minor.  If there's
3760*7c478bd9Sstevel@tonic-gate 	 * no matching pattern, use the wild card.
3761*7c478bd9Sstevel@tonic-gate 	 * Defer to the clone wild for clone if specified,
3762*7c478bd9Sstevel@tonic-gate 	 * otherwise fall back to the normal form.
3763*7c478bd9Sstevel@tonic-gate 	 */
3764*7c478bd9Sstevel@tonic-gate 	for (mp = dnp->dn_mperm; mp; mp = mp->mp_next) {
3765*7c478bd9Sstevel@tonic-gate 		if (gmatch(minor_name, mp->mp_minorname) != 0) {
3766*7c478bd9Sstevel@tonic-gate 			break;
3767*7c478bd9Sstevel@tonic-gate 		}
3768*7c478bd9Sstevel@tonic-gate 	}
3769*7c478bd9Sstevel@tonic-gate 	if (mp == NULL) {
3770*7c478bd9Sstevel@tonic-gate 		if (is_clone)
3771*7c478bd9Sstevel@tonic-gate 			mp = dnp->dn_mperm_clone;
3772*7c478bd9Sstevel@tonic-gate 		if (mp == NULL)
3773*7c478bd9Sstevel@tonic-gate 			mp = dnp->dn_mperm_wild;
3774*7c478bd9Sstevel@tonic-gate 	}
3775*7c478bd9Sstevel@tonic-gate 
3776*7c478bd9Sstevel@tonic-gate 	if (mp) {
3777*7c478bd9Sstevel@tonic-gate 		if (moddebug & MODDEBUG_MP_MATCH) {
3778*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT,
3779*7c478bd9Sstevel@tonic-gate 			    "minor perm defaults: %s %s 0%o %d %d\n",
3780*7c478bd9Sstevel@tonic-gate 			    name, mp->mp_minorname, mp->mp_mode,
3781*7c478bd9Sstevel@tonic-gate 			    mp->mp_uid, mp->mp_gid);
3782*7c478bd9Sstevel@tonic-gate 		}
3783*7c478bd9Sstevel@tonic-gate 		rmp->mp_uid = mp->mp_uid;
3784*7c478bd9Sstevel@tonic-gate 		rmp->mp_gid = mp->mp_gid;
3785*7c478bd9Sstevel@tonic-gate 		rmp->mp_mode = mp->mp_mode;
3786*7c478bd9Sstevel@tonic-gate 	}
3787*7c478bd9Sstevel@tonic-gate 	UNLOCK_DEV_OPS(&dnp->dn_lock);
3788*7c478bd9Sstevel@tonic-gate 
3789*7c478bd9Sstevel@tonic-gate 	/*
3790*7c478bd9Sstevel@tonic-gate 	 * If no match can be found for a clone node,
3791*7c478bd9Sstevel@tonic-gate 	 * search for a possible match for an alias.
3792*7c478bd9Sstevel@tonic-gate 	 * One such example is /dev/ptmx -> /devices/pseudo/clone@0:ptm,
3793*7c478bd9Sstevel@tonic-gate 	 * with minor perm entry clone:ptmx.
3794*7c478bd9Sstevel@tonic-gate 	 */
3795*7c478bd9Sstevel@tonic-gate 	if (mp == NULL && is_clone) {
3796*7c478bd9Sstevel@tonic-gate 		return (dev_alias_minorperm(dip, minor_name, rmp));
3797*7c478bd9Sstevel@tonic-gate 	}
3798*7c478bd9Sstevel@tonic-gate 
3799*7c478bd9Sstevel@tonic-gate 	return (mp == NULL);
3800*7c478bd9Sstevel@tonic-gate }
3801*7c478bd9Sstevel@tonic-gate 
3802*7c478bd9Sstevel@tonic-gate /*
3803*7c478bd9Sstevel@tonic-gate  * dynamicaly reference load a dl module/library, returning handle
3804*7c478bd9Sstevel@tonic-gate  */
3805*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3806*7c478bd9Sstevel@tonic-gate ddi_modhandle_t
3807*7c478bd9Sstevel@tonic-gate ddi_modopen(const char *modname, int mode, int *errnop)
3808*7c478bd9Sstevel@tonic-gate {
3809*7c478bd9Sstevel@tonic-gate 	char		*subdir;
3810*7c478bd9Sstevel@tonic-gate 	char		*mod;
3811*7c478bd9Sstevel@tonic-gate 	int		subdirlen;
3812*7c478bd9Sstevel@tonic-gate 	struct modctl	*hmodp = NULL;
3813*7c478bd9Sstevel@tonic-gate 	int		retval = EINVAL;
3814*7c478bd9Sstevel@tonic-gate 
3815*7c478bd9Sstevel@tonic-gate 	ASSERT(modname && (mode == KRTLD_MODE_FIRST));
3816*7c478bd9Sstevel@tonic-gate 	if ((modname == NULL) || (mode != KRTLD_MODE_FIRST))
3817*7c478bd9Sstevel@tonic-gate 		goto out;
3818*7c478bd9Sstevel@tonic-gate 
3819*7c478bd9Sstevel@tonic-gate 	/* find optional first '/' in modname */
3820*7c478bd9Sstevel@tonic-gate 	mod = strchr(modname, '/');
3821*7c478bd9Sstevel@tonic-gate 	if (mod != strrchr(modname, '/'))
3822*7c478bd9Sstevel@tonic-gate 		goto out;		/* only one '/' is legal */
3823*7c478bd9Sstevel@tonic-gate 
3824*7c478bd9Sstevel@tonic-gate 	if (mod) {
3825*7c478bd9Sstevel@tonic-gate 		/* for subdir string without modification to argument */
3826*7c478bd9Sstevel@tonic-gate 		mod++;
3827*7c478bd9Sstevel@tonic-gate 		subdirlen = mod - modname;
3828*7c478bd9Sstevel@tonic-gate 		subdir = kmem_alloc(subdirlen, KM_SLEEP);
3829*7c478bd9Sstevel@tonic-gate 		(void) strlcpy(subdir, modname, subdirlen);
3830*7c478bd9Sstevel@tonic-gate 	} else {
3831*7c478bd9Sstevel@tonic-gate 		subdirlen = 0;
3832*7c478bd9Sstevel@tonic-gate 		subdir = "misc";
3833*7c478bd9Sstevel@tonic-gate 		mod = (char *)modname;
3834*7c478bd9Sstevel@tonic-gate 	}
3835*7c478bd9Sstevel@tonic-gate 
3836*7c478bd9Sstevel@tonic-gate 	/* reference load with errno return value */
3837*7c478bd9Sstevel@tonic-gate 	retval = modrload(subdir, mod, &hmodp);
3838*7c478bd9Sstevel@tonic-gate 
3839*7c478bd9Sstevel@tonic-gate 	if (subdirlen)
3840*7c478bd9Sstevel@tonic-gate 		kmem_free(subdir, subdirlen);
3841*7c478bd9Sstevel@tonic-gate 
3842*7c478bd9Sstevel@tonic-gate out:	if (errnop)
3843*7c478bd9Sstevel@tonic-gate 		*errnop = retval;
3844*7c478bd9Sstevel@tonic-gate 
3845*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_DDI_MOD)
3846*7c478bd9Sstevel@tonic-gate 		printf("ddi_modopen %s mode %x: %s %p %d\n",
3847*7c478bd9Sstevel@tonic-gate 		    modname ? modname : "<unknown>", mode,
3848*7c478bd9Sstevel@tonic-gate 		    hmodp ? hmodp->mod_filename : "<unknown>",
3849*7c478bd9Sstevel@tonic-gate 		    (void *)hmodp, retval);
3850*7c478bd9Sstevel@tonic-gate 
3851*7c478bd9Sstevel@tonic-gate 	return ((ddi_modhandle_t)hmodp);
3852*7c478bd9Sstevel@tonic-gate }
3853*7c478bd9Sstevel@tonic-gate 
3854*7c478bd9Sstevel@tonic-gate /* lookup "name" in open dl module/library */
3855*7c478bd9Sstevel@tonic-gate void *
3856*7c478bd9Sstevel@tonic-gate ddi_modsym(ddi_modhandle_t h, const char *name, int *errnop)
3857*7c478bd9Sstevel@tonic-gate {
3858*7c478bd9Sstevel@tonic-gate 	struct modctl	*hmodp = (struct modctl *)h;
3859*7c478bd9Sstevel@tonic-gate 	void		*f;
3860*7c478bd9Sstevel@tonic-gate 	int		retval;
3861*7c478bd9Sstevel@tonic-gate 
3862*7c478bd9Sstevel@tonic-gate 	ASSERT(hmodp && name && hmodp->mod_installed && (hmodp->mod_ref >= 1));
3863*7c478bd9Sstevel@tonic-gate 	if ((hmodp == NULL) || (name == NULL) ||
3864*7c478bd9Sstevel@tonic-gate 	    (hmodp->mod_installed == 0) || (hmodp->mod_ref < 1)) {
3865*7c478bd9Sstevel@tonic-gate 		f = NULL;
3866*7c478bd9Sstevel@tonic-gate 		retval = EINVAL;
3867*7c478bd9Sstevel@tonic-gate 	} else {
3868*7c478bd9Sstevel@tonic-gate 		f = (void *)kobj_lookup(hmodp->mod_mp, (char *)name);
3869*7c478bd9Sstevel@tonic-gate 		if (f)
3870*7c478bd9Sstevel@tonic-gate 			retval = 0;
3871*7c478bd9Sstevel@tonic-gate 		else
3872*7c478bd9Sstevel@tonic-gate 			retval = ENOTSUP;
3873*7c478bd9Sstevel@tonic-gate 	}
3874*7c478bd9Sstevel@tonic-gate 
3875*7c478bd9Sstevel@tonic-gate 	if (moddebug & MODDEBUG_DDI_MOD)
3876*7c478bd9Sstevel@tonic-gate 		printf("ddi_modsym in %s of %s: %d %p\n",
3877*7c478bd9Sstevel@tonic-gate 		    hmodp ? hmodp->mod_modname : "<unknown>",
3878*7c478bd9Sstevel@tonic-gate 		    name ? name : "<unknown>", retval, f);
3879*7c478bd9Sstevel@tonic-gate 
3880*7c478bd9Sstevel@tonic-gate 	if (errnop)
3881*7c478bd9Sstevel@tonic-gate 		*errnop = retval;
3882*7c478bd9Sstevel@tonic-gate 	return (f);
3883*7c478bd9Sstevel@tonic-gate }
3884*7c478bd9Sstevel@tonic-gate 
3885*7c478bd9Sstevel@tonic-gate /* dynamic (un)reference unload of an open dl module/library */
3886*7c478bd9Sstevel@tonic-gate int
3887*7c478bd9Sstevel@tonic-gate ddi_modclose(ddi_modhandle_t h)
3888*7c478bd9Sstevel@tonic-gate {
3889*7c478bd9Sstevel@tonic-gate 	struct modctl	*hmodp = (struct modctl *)h;
3890*7c478bd9Sstevel@tonic-gate 	struct modctl	*modp = NULL;
3891*7c478bd9Sstevel@tonic-gate 	int		retval;
3892*7c478bd9Sstevel@tonic-gate 
3893*7c478bd9Sstevel@tonic-gate 	ASSERT(hmodp && hmodp->mod_installed && (hmodp->mod_ref >= 1));
3894*7c478bd9Sstevel@tonic-gate 	if ((hmodp == NULL) ||
3895*7c478bd9Sstevel@tonic-gate 	    (hmodp->mod_installed == 0) || (hmodp->mod_ref < 1)) {
3896*7c478bd9Sstevel@tonic-gate 		retval = EINVAL;
3897*7c478bd9Sstevel@tonic-gate 		goto out;
3898*7c478bd9Sstevel@tonic-gate 	}
3899*7c478bd9Sstevel@tonic-gate 
3900*7c478bd9Sstevel@tonic-gate 	retval = modunrload(hmodp->mod_id, &modp, ddi_modclose_unload);
3901*7c478bd9Sstevel@tonic-gate 	if (retval == EBUSY)
3902*7c478bd9Sstevel@tonic-gate 		retval = 0;	/* EBUSY is not an error */
3903*7c478bd9Sstevel@tonic-gate 
3904*7c478bd9Sstevel@tonic-gate 	if (retval == 0) {
3905*7c478bd9Sstevel@tonic-gate 		ASSERT(hmodp == modp);
3906*7c478bd9Sstevel@tonic-gate 		if (hmodp != modp)
3907*7c478bd9Sstevel@tonic-gate 			retval = EINVAL;
3908*7c478bd9Sstevel@tonic-gate 	}
3909*7c478bd9Sstevel@tonic-gate 
3910*7c478bd9Sstevel@tonic-gate out:	if (moddebug & MODDEBUG_DDI_MOD)
3911*7c478bd9Sstevel@tonic-gate 		printf("ddi_modclose %s: %d\n",
3912*7c478bd9Sstevel@tonic-gate 		    hmodp ? hmodp->mod_modname : "<unknown>", retval);
3913*7c478bd9Sstevel@tonic-gate 
3914*7c478bd9Sstevel@tonic-gate 	return (retval);
3915*7c478bd9Sstevel@tonic-gate }
3916