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