17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
5facf4a8llai * Common Development and Distribution License (the "License").
6facf4a8llai * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
217aec1d6cindi
227c478bdstevel@tonic-gate/*
23bf00242Stephen Hanson * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
24425251fSam Gwydir * Copyright (c) 2017 Joyent, Inc.
257c478bdstevel@tonic-gate */
267c478bdstevel@tonic-gate
277c478bdstevel@tonic-gate/*
287c478bdstevel@tonic-gate * modctl system call for loadable module support.
297c478bdstevel@tonic-gate */
307c478bdstevel@tonic-gate
317c478bdstevel@tonic-gate#include <sys/param.h>
327c478bdstevel@tonic-gate#include <sys/user.h>
337c478bdstevel@tonic-gate#include <sys/systm.h>
347c478bdstevel@tonic-gate#include <sys/exec.h>
357c478bdstevel@tonic-gate#include <sys/file.h>
367c478bdstevel@tonic-gate#include <sys/stat.h>
377c478bdstevel@tonic-gate#include <sys/conf.h>
387c478bdstevel@tonic-gate#include <sys/time.h>
397c478bdstevel@tonic-gate#include <sys/reboot.h>
407c478bdstevel@tonic-gate#include <sys/fs/ufs_fsdir.h>
417c478bdstevel@tonic-gate#include <sys/kmem.h>
427c478bdstevel@tonic-gate#include <sys/sysconf.h>
437c478bdstevel@tonic-gate#include <sys/cmn_err.h>
447c478bdstevel@tonic-gate#include <sys/ddi.h>
457c478bdstevel@tonic-gate#include <sys/sunddi.h>
467c478bdstevel@tonic-gate#include <sys/sunndi.h>
477c478bdstevel@tonic-gate#include <sys/ndi_impldefs.h>
487c478bdstevel@tonic-gate#include <sys/ddi_impldefs.h>
497c478bdstevel@tonic-gate#include <sys/ddi_implfuncs.h>
507c478bdstevel@tonic-gate#include <sys/bootconf.h>
517c478bdstevel@tonic-gate#include <sys/dc_ki.h>
527c478bdstevel@tonic-gate#include <sys/cladm.h>
537c478bdstevel@tonic-gate#include <sys/dtrace.h>
547c478bdstevel@tonic-gate#include <sys/kdi.h>
557c478bdstevel@tonic-gate
567c478bdstevel@tonic-gate#include <sys/devpolicy.h>
577c478bdstevel@tonic-gate#include <sys/modctl.h>
587c478bdstevel@tonic-gate#include <sys/kobj.h>
597c478bdstevel@tonic-gate#include <sys/devops.h>
607c478bdstevel@tonic-gate#include <sys/autoconf.h>
617c478bdstevel@tonic-gate#include <sys/hwconf.h>
627c478bdstevel@tonic-gate#include <sys/callb.h>
637c478bdstevel@tonic-gate#include <sys/debug.h>
647c478bdstevel@tonic-gate#include <sys/cpuvar.h>
657c478bdstevel@tonic-gate#include <sys/sysmacros.h>
667c478bdstevel@tonic-gate#include <sys/sysevent.h>
677c478bdstevel@tonic-gate#include <sys/sysevent_impl.h>
687c478bdstevel@tonic-gate#include <sys/instance.h>
697c478bdstevel@tonic-gate#include <sys/modhash.h>
707c478bdstevel@tonic-gate#include <sys/modhash_impl.h>
717c478bdstevel@tonic-gate#include <sys/dacf_impl.h>
727c478bdstevel@tonic-gate#include <sys/vfs.h>
737c478bdstevel@tonic-gate#include <sys/pathname.h>
747c478bdstevel@tonic-gate#include <sys/console.h>
757c478bdstevel@tonic-gate#include <sys/policy.h>
767c478bdstevel@tonic-gate#include <ipp/ipp_impl.h>
777c478bdstevel@tonic-gate#include <sys/fs/dv_node.h>
787c478bdstevel@tonic-gate#include <sys/strsubr.h>
793c5e027Eric Taylor#include <sys/fs/sdev_impl.h>
807c478bdstevel@tonic-gate
817c478bdstevel@tonic-gatestatic int		mod_circdep(struct modctl *);
827c478bdstevel@tonic-gatestatic int		modinfo(modid_t, struct modinfo *);
837c478bdstevel@tonic-gate
847c478bdstevel@tonic-gatestatic void		mod_uninstall_all(void);
857c478bdstevel@tonic-gatestatic int		mod_getinfo(struct modctl *, struct modinfo *);
867aec1d6cindistatic struct modctl	*allocate_modp(const char *, const char *);
877c478bdstevel@tonic-gate
887c478bdstevel@tonic-gatestatic int		mod_load(struct modctl *, int);
897c478bdstevel@tonic-gatestatic void		mod_unload(struct modctl *);
907c478bdstevel@tonic-gatestatic int		modinstall(struct modctl *);
917c478bdstevel@tonic-gatestatic int		moduninstall(struct modctl *);
927c478bdstevel@tonic-gate
937aec1d6cindistatic struct modctl	*mod_hold_by_name_common(struct modctl *, const char *);
947c478bdstevel@tonic-gatestatic struct modctl	*mod_hold_next_by_id(modid_t);
957c478bdstevel@tonic-gatestatic struct modctl	*mod_hold_loaded_mod(struct modctl *, char *, int *);
9620c794bgavinmstatic struct modctl	*mod_hold_installed_mod(char *, int, int, int *);
977c478bdstevel@tonic-gate
987c478bdstevel@tonic-gatestatic void		mod_release(struct modctl *);
997c478bdstevel@tonic-gatestatic void		mod_make_requisite(struct modctl *, struct modctl *);
1007c478bdstevel@tonic-gatestatic int		mod_install_requisites(struct modctl *);
1017c478bdstevel@tonic-gatestatic void		check_esc_sequences(char *, char *);
1027c478bdstevel@tonic-gatestatic struct modctl	*mod_hold_by_name_requisite(struct modctl *, char *);
1037c478bdstevel@tonic-gate
1047c478bdstevel@tonic-gate/*
1057c478bdstevel@tonic-gate * module loading thread control structure. Calls to kobj_load_module()() are
1067c478bdstevel@tonic-gate * handled off to a separate thead using this structure.
1077c478bdstevel@tonic-gate */
1087c478bdstevel@tonic-gatestruct loadmt {
1097c478bdstevel@tonic-gate	ksema_t		sema;
1107c478bdstevel@tonic-gate	struct modctl	*mp;
1117c478bdstevel@tonic-gate	int		usepath;
1127c478bdstevel@tonic-gate	kthread_t	*owner;
1137c478bdstevel@tonic-gate	int		retval;
1147c478bdstevel@tonic-gate};
1157c478bdstevel@tonic-gate
1167c478bdstevel@tonic-gatestatic void	modload_thread(struct loadmt *);
1177c478bdstevel@tonic-gate
1187c478bdstevel@tonic-gatekcondvar_t	mod_cv;
1197c478bdstevel@tonic-gatekcondvar_t	mod_uninstall_cv;	/* Communication between swapper */
1207c478bdstevel@tonic-gate					/* and the uninstall daemon. */
1217c478bdstevel@tonic-gatekmutex_t	mod_lock;		/* protects &modules insert linkage, */
1227c478bdstevel@tonic-gate					/* mod_busy, mod_want, and mod_ref. */
1237c478bdstevel@tonic-gate					/* blocking operations while holding */
1247c478bdstevel@tonic-gate					/* mod_lock should be avoided */
1257c478bdstevel@tonic-gatekmutex_t	mod_uninstall_lock;	/* protects mod_uninstall_cv */
1267c478bdstevel@tonic-gatekthread_id_t	mod_aul_thread;
1277c478bdstevel@tonic-gate
128a7aa4dfcthint		modunload_wait;
129a7aa4dfcthkmutex_t	modunload_wait_mutex;
130a7aa4dfcthkcondvar_t	modunload_wait_cv;
131a7aa4dfcthint		modunload_active_count;
132a7aa4dfcthint		modunload_disable_count;
133a7aa4dfcth
1347c478bdstevel@tonic-gateint	isminiroot;		/* set if running as miniroot */
1357c478bdstevel@tonic-gateint	modrootloaded;		/* set after root driver and fs are loaded */
1367c478bdstevel@tonic-gateint	moddebug = 0x0;		/* debug flags for module writers */
1377c478bdstevel@tonic-gateint	swaploaded;		/* set after swap driver and fs are loaded */
1387c478bdstevel@tonic-gateint	bop_io_quiesced = 0;	/* set when BOP I/O can no longer be used */
1397c478bdstevel@tonic-gateint	last_module_id;
1407c478bdstevel@tonic-gateclock_t	mod_uninstall_interval = 0;
14160aabb4Chris Horneint	mod_uninstall_pass_max = 6;
14260aabb4Chris Horneint	mod_uninstall_ref_zero;	/* # modules that went mod_ref == 0 */
14360aabb4Chris Horneint	mod_uninstall_pass_exc;	/* mod_uninstall_all left new stuff */
14460aabb4Chris Horne
1457c478bdstevel@tonic-gateint	ddi_modclose_unload = 1;	/* 0 -> just decrement reference */
1467c478bdstevel@tonic-gate
14751215d0Jerry Gilliamint	devcnt_incr	= 256;		/* allow for additional drivers */
14851215d0Jerry Gilliamint	devcnt_min	= 512;		/* and always at least this number */
14951215d0Jerry Gilliam
1507c478bdstevel@tonic-gatestruct devnames *devnamesp;
1517c478bdstevel@tonic-gatestruct devnames orphanlist;
1527c478bdstevel@tonic-gate
1537c478bdstevel@tonic-gatekrwlock_t	devinfo_tree_lock;	/* obsolete, to be removed */
1547c478bdstevel@tonic-gate
1557c478bdstevel@tonic-gate#define	MAJBINDFILE "/etc/name_to_major"
1567c478bdstevel@tonic-gate#define	SYSBINDFILE "/etc/name_to_sysnum"
1577c478bdstevel@tonic-gate
1587c478bdstevel@tonic-gatestatic char	majbind[] = MAJBINDFILE;
1597c478bdstevel@tonic-gatestatic char	sysbind[] = SYSBINDFILE;
1607c478bdstevel@tonic-gatestatic uint_t	mod_autounload_key;	/* for module autounload detection */
1617c478bdstevel@tonic-gate
1627c478bdstevel@tonic-gateextern int obpdebug;
1637c478bdstevel@tonic-gate
1647c478bdstevel@tonic-gate#define	DEBUGGER_PRESENT	((boothowto & RB_DEBUG) || (obpdebug != 0))
1657c478bdstevel@tonic-gate
1667c478bdstevel@tonic-gatestatic int minorperm_loaded = 0;
1677c478bdstevel@tonic-gate
1687c478bdstevel@tonic-gatevoid
1697c478bdstevel@tonic-gatemod_setup(void)
1707c478bdstevel@tonic-gate{
1717c478bdstevel@tonic-gate	struct sysent *callp;
1727c478bdstevel@tonic-gate	int callnum, exectype;
1737c478bdstevel@tonic-gate	int	num_devs;
1747c478bdstevel@tonic-gate	int	i;
1757c478bdstevel@tonic-gate
1767c478bdstevel@tonic-gate	/*
1777c478bdstevel@tonic-gate	 * Initialize the list of loaded driver dev_ops.
1787c478bdstevel@tonic-gate	 * XXX - This must be done before reading the system file so that
1797c478bdstevel@tonic-gate	 * forceloads of drivers will work.
1807c478bdstevel@tonic-gate	 */
1817c478bdstevel@tonic-gate	num_devs = read_binding_file(majbind, mb_hashtab, make_mbind);
1827c478bdstevel@tonic-gate	/*
1837c478bdstevel@tonic-gate	 * Since read_binding_file is common code, it doesn't enforce that all
1842694730Evan Yan	 * of the binding file entries have major numbers <= MAXMAJ32.	Thus,
1857c478bdstevel@tonic-gate	 * ensure that we don't allocate some massive amount of space due to a
1867c478bdstevel@tonic-gate	 * bad entry.  We can't have major numbers bigger than MAXMAJ32
1877c478bdstevel@tonic-gate	 * until file system support for larger major numbers exists.
1887c478bdstevel@tonic-gate	 */
1897c478bdstevel@tonic-gate
1907c478bdstevel@tonic-gate	/*
1917c478bdstevel@tonic-gate	 * Leave space for expansion, but not more than L_MAXMAJ32
1927c478bdstevel@tonic-gate	 */
19351215d0Jerry Gilliam	devcnt = MIN(num_devs + devcnt_incr, L_MAXMAJ32);
19451215d0Jerry Gilliam	devcnt = MAX(devcnt, devcnt_min);
1957c478bdstevel@tonic-gate	devopsp = kmem_alloc(devcnt * sizeof (struct dev_ops *), KM_SLEEP);
1967c478bdstevel@tonic-gate	for (i = 0; i < devcnt; i++)
1977c478bdstevel@tonic-gate		devopsp[i] = &mod_nodev_ops;
1987c478bdstevel@tonic-gate
1997c478bdstevel@tonic-gate	init_devnamesp(devcnt);
2007c478bdstevel@tonic-gate
2017c478bdstevel@tonic-gate	/*
2027c478bdstevel@tonic-gate	 * Sync up with the work that the stand-alone linker has already done.
2037c478bdstevel@tonic-gate	 */
2047c478bdstevel@tonic-gate	(void) kobj_sync();
2057c478bdstevel@tonic-gate
2067c478bdstevel@tonic-gate	if (boothowto & RB_DEBUG)
2077c478bdstevel@tonic-gate		kdi_dvec_modavail();
2087c478bdstevel@tonic-gate
2097c478bdstevel@tonic-gate	make_aliases(mb_hashtab);
2107c478bdstevel@tonic-gate
2117c478bdstevel@tonic-gate	/*
2127c478bdstevel@tonic-gate	 * Initialize streams device implementation structures.
2137c478bdstevel@tonic-gate	 */
2147c478bdstevel@tonic-gate	devimpl = kmem_zalloc(devcnt * sizeof (cdevsw_impl_t), KM_SLEEP);
2157c478bdstevel@tonic-gate
2167c478bdstevel@tonic-gate	/*
2177c478bdstevel@tonic-gate	 * If the cl_bootstrap module is present,
2187c478bdstevel@tonic-gate	 * we should be configured as a cluster. Loading this module
2197c478bdstevel@tonic-gate	 * will set "cluster_bootflags" to non-zero.
2207c478bdstevel@tonic-gate	 */
2217c478bdstevel@tonic-gate	(void) modload("misc", "cl_bootstrap");
2227c478bdstevel@tonic-gate
2237c478bdstevel@tonic-gate	(void) read_binding_file(sysbind, sb_hashtab, make_mbind);
2247c478bdstevel@tonic-gate	init_syscallnames(NSYSCALL);
2257c478bdstevel@tonic-gate
2267c478bdstevel@tonic-gate	/*
2277c478bdstevel@tonic-gate	 * Start up dynamic autoconfiguration framework (dacf).
2287c478bdstevel@tonic-gate	 */
2297c478bdstevel@tonic-gate	mod_hash_init();
2307c478bdstevel@tonic-gate	dacf_init();
2317c478bdstevel@tonic-gate
2327c478bdstevel@tonic-gate	/*
2337c478bdstevel@tonic-gate	 * Start up IP policy framework (ipp).
2347c478bdstevel@tonic-gate	 */
2357c478bdstevel@tonic-gate	ipp_init();
2367c478bdstevel@tonic-gate
2377c478bdstevel@tonic-gate	/*
2387c478bdstevel@tonic-gate	 * Allocate loadable native system call locks.
2397c478bdstevel@tonic-gate	 */
2407c478bdstevel@tonic-gate	for (callnum = 0, callp = sysent; callnum < NSYSCALL;
2417c478bdstevel@tonic-gate	    callnum++, callp++) {
2427c478bdstevel@tonic-gate		if (LOADABLE_SYSCALL(callp)) {
2437c478bdstevel@tonic-gate			if (mod_getsysname(callnum) != NULL) {
2447c478bdstevel@tonic-gate				callp->sy_lock =
2457c478bdstevel@tonic-gate				    kobj_zalloc(sizeof (krwlock_t), KM_SLEEP);
2467c478bdstevel@tonic-gate				rw_init(callp->sy_lock, NULL, RW_DEFAULT, NULL);
2477c478bdstevel@tonic-gate			} else {
2487c478bdstevel@tonic-gate				callp->sy_flags &= ~SE_LOADABLE;
2497c478bdstevel@tonic-gate				callp->sy_callc = nosys;
2507c478bdstevel@tonic-gate			}
2517c478bdstevel@tonic-gate#ifdef DEBUG
2527c478bdstevel@tonic-gate		} else {
2537c478bdstevel@tonic-gate			/*
2547c478bdstevel@tonic-gate			 * Do some sanity checks on the sysent table
2557c478bdstevel@tonic-gate			 */
2567c478bdstevel@tonic-gate			switch (callp->sy_flags & SE_RVAL_MASK) {
2577c478bdstevel@tonic-gate			case SE_32RVAL1:
2587c478bdstevel@tonic-gate				/* only r_val1 returned */
2597c478bdstevel@tonic-gate			case SE_32RVAL1 | SE_32RVAL2:
2607c478bdstevel@tonic-gate				/* r_val1 and r_val2 returned */
2617c478bdstevel@tonic-gate			case SE_64RVAL:
2627c478bdstevel@tonic-gate				/* 64-bit rval returned */
2637c478bdstevel@tonic-gate				break;
2647c478bdstevel@tonic-gate			default:
2657c478bdstevel@tonic-gate				cmn_err(CE_WARN, "sysent[%d]: bad flags %x",
2667c478bdstevel@tonic-gate				    callnum, callp->sy_flags);
2677c478bdstevel@tonic-gate			}
2687c478bdstevel@tonic-gate#endif
2697c478bdstevel@tonic-gate		}
2707c478bdstevel@tonic-gate	}
2717c478bdstevel@tonic-gate
2727c478bdstevel@tonic-gate#ifdef _SYSCALL32_IMPL
2737c478bdstevel@tonic-gate	/*
2747c478bdstevel@tonic-gate	 * Allocate loadable system call locks for 32-bit compat syscalls
2757c478bdstevel@tonic-gate	 */
2767c478bdstevel@tonic-gate	for (callnum = 0, callp = sysent32; callnum < NSYSCALL;
2777c478bdstevel@tonic-gate	    callnum++, callp++) {
2787c478bdstevel@tonic-gate		if (LOADABLE_SYSCALL(callp)) {
2797c478bdstevel@tonic-gate			if (mod_getsysname(callnum) != NULL) {
2807c478bdstevel@tonic-gate				callp->sy_lock =
2817c478bdstevel@tonic-gate				    kobj_zalloc(sizeof (krwlock_t), KM_SLEEP);
2827c478bdstevel@tonic-gate				rw_init(callp->sy_lock, NULL, RW_DEFAULT, NULL);
2837c478bdstevel@tonic-gate			} else {
2847c478bdstevel@tonic-gate				callp->sy_flags &= ~SE_LOADABLE;
2857c478bdstevel@tonic-gate				callp->sy_callc = nosys;
2867c478bdstevel@tonic-gate			}
2877c478bdstevel@tonic-gate#ifdef DEBUG
2887c478bdstevel@tonic-gate		} else {
2897c478bdstevel@tonic-gate			/*
2907c478bdstevel@tonic-gate			 * Do some sanity checks on the sysent table
2917c478bdstevel@tonic-gate			 */
2927c478bdstevel@tonic-gate			switch (callp->sy_flags & SE_RVAL_MASK) {
2937c478bdstevel@tonic-gate			case SE_32RVAL1:
2947c478bdstevel@tonic-gate				/* only r_val1 returned */
2957c478bdstevel@tonic-gate			case SE_32RVAL1 | SE_32RVAL2:
2967c478bdstevel@tonic-gate				/* r_val1 and r_val2 returned */
2977c478bdstevel@tonic-gate			case SE_64RVAL:
2987c478bdstevel@tonic-gate				/* 64-bit rval returned */
2997c478bdstevel@tonic-gate				break;
3007c478bdstevel@tonic-gate			default:
3017c478bdstevel@tonic-gate				cmn_err(CE_WARN, "sysent32[%d]: bad flags %x",
3027c478bdstevel@tonic-gate				    callnum, callp->sy_flags);
3037c478bdstevel@tonic-gate				goto skip;
3047c478bdstevel@tonic-gate			}
3057c478bdstevel@tonic-gate
3067c478bdstevel@tonic-gate			/*
3077c478bdstevel@tonic-gate			 * Cross-check the native and compatibility tables.
3087c478bdstevel@tonic-gate			 */
3097c478bdstevel@tonic-gate			if (callp->sy_callc == nosys ||
3107c478bdstevel@tonic-gate			    sysent[callnum].sy_callc == nosys)
3117c478bdstevel@tonic-gate				continue;
3127c478bdstevel@tonic-gate			/*
3137c478bdstevel@tonic-gate			 * If only one or the other slot is loadable, then
3147c478bdstevel@tonic-gate			 * there's an error -- they should match!
3157c478bdstevel@tonic-gate			 */
3167c478bdstevel@tonic-gate			if ((callp->sy_callc == loadable_syscall) ^
3177c478bdstevel@tonic-gate			    (sysent[callnum].sy_callc == loadable_syscall)) {
3187c478bdstevel@tonic-gate				cmn_err(CE_WARN, "sysent[%d] loadable?",
3197c478bdstevel@tonic-gate				    callnum);
3207c478bdstevel@tonic-gate			}
3217c478bdstevel@tonic-gate			/*
3227c478bdstevel@tonic-gate			 * This is more of a heuristic test -- if the
3237c478bdstevel@tonic-gate			 * system call returns two values in the 32-bit
3247c478bdstevel@tonic-gate			 * world, it should probably return two 32-bit
3257c478bdstevel@tonic-gate			 * values in the 64-bit world too.
3267c478bdstevel@tonic-gate			 */
3277c478bdstevel@tonic-gate			if (((callp->sy_flags & SE_32RVAL2) == 0) ^
3287c478bdstevel@tonic-gate			    ((sysent[callnum].sy_flags & SE_32RVAL2) == 0)) {
3297c478bdstevel@tonic-gate				cmn_err(CE_WARN, "sysent[%d] rval2 mismatch!",
3307c478bdstevel@tonic-gate				    callnum);
3317c478bdstevel@tonic-gate			}
3327c478bdstevel@tonic-gateskip:;
3337c478bdstevel@tonic-gate#endif	/* DEBUG */
3347c478bdstevel@tonic-gate		}
3357c478bdstevel@tonic-gate	}
3367c478bdstevel@tonic-gate#endif	/* _SYSCALL32_IMPL */
3377c478bdstevel@tonic-gate
3387c478bdstevel@tonic-gate	/*
3397c478bdstevel@tonic-gate	 * Allocate loadable exec locks.  (Assumes all execs are loadable)
3407c478bdstevel@tonic-gate	 */
3417c478bdstevel@tonic-gate	for (exectype = 0; exectype < nexectype; exectype++) {
3427c478bdstevel@tonic-gate		execsw[exectype].exec_lock =
3437c478bdstevel@tonic-gate		    kobj_zalloc(sizeof (krwlock_t), KM_SLEEP);
3447c478bdstevel@tonic-gate		rw_init(execsw[exectype].exec_lock, NULL, RW_DEFAULT, NULL);
3457c478bdstevel@tonic-gate	}
3467c478bdstevel@tonic-gate
3477c478bdstevel@tonic-gate	read_class_file();
3487c478bdstevel@tonic-gate
3497c478bdstevel@tonic-gate	/* init thread specific structure for mod_uninstall_all */
3507c478bdstevel@tonic-gate	tsd_create(&mod_autounload_key, NULL);
3517c478bdstevel@tonic-gate}
3527c478bdstevel@tonic-gate
3537c478bdstevel@tonic-gatestatic int
3547c478bdstevel@tonic-gatemodctl_modload(int use_path, char *filename, int *rvp)
3557c478bdstevel@tonic-gate{
3567c478bdstevel@tonic-gate	struct modctl *modp;
3577c478bdstevel@tonic-gate	int retval = 0;
3587c478bdstevel@tonic-gate	char *filenamep;
3597c478bdstevel@tonic-gate	int modid;
3607c478bdstevel@tonic-gate
3617c478bdstevel@tonic-gate	filenamep = kmem_zalloc(MOD_MAXPATH, KM_SLEEP);
3627c478bdstevel@tonic-gate
3637c478bdstevel@tonic-gate	if (copyinstr(filename, filenamep, MOD_MAXPATH, 0)) {
3647c478bdstevel@tonic-gate		retval = EFAULT;
3657c478bdstevel@tonic-gate		goto out;
3667c478bdstevel@tonic-gate	}
3677c478bdstevel@tonic-gate
3687c478bdstevel@tonic-gate	filenamep[MOD_MAXPATH - 1] = 0;
36920c794bgavinm	modp = mod_hold_installed_mod(filenamep, use_path, 0, &retval);
3707c478bdstevel@tonic-gate
3717c478bdstevel@tonic-gate	if (modp == NULL)
3727c478bdstevel@tonic-gate		goto out;
3737c478bdstevel@tonic-gate
3747c478bdstevel@tonic-gate	modp->mod_loadflags |= MOD_NOAUTOUNLOAD;
3757c478bdstevel@tonic-gate	modid = modp->mod_id;
3767c478bdstevel@tonic-gate	mod_release_mod(modp);
3770b38a8bahl	CPU_STATS_ADDQ(CPU, sys, modload, 1);
3787c478bdstevel@tonic-gate	if (rvp != NULL && copyout(&modid, rvp, sizeof (modid)) != 0)
3797c478bdstevel@tonic-gate		retval = EFAULT;
3807c478bdstevel@tonic-gateout:
3817c478bdstevel@tonic-gate	kmem_free(filenamep, MOD_MAXPATH);
3827c478bdstevel@tonic-gate
3837c478bdstevel@tonic-gate	return (retval);
3847c478bdstevel@tonic-gate}
3857c478bdstevel@tonic-gate
3867c478bdstevel@tonic-gatestatic int
3877c478bdstevel@tonic-gatemodctl_modunload(modid_t id)
3887c478bdstevel@tonic-gate{
3897c478bdstevel@tonic-gate	int rval = 0;
3907c478bdstevel@tonic-gate
3917c478bdstevel@tonic-gate	if (id == 0) {
3927c478bdstevel@tonic-gate#ifdef DEBUG
3937c478bdstevel@tonic-gate		/*
3947c478bdstevel@tonic-gate		 * Turn on mod_uninstall_daemon
3957c478bdstevel@tonic-gate		 */
3967c478bdstevel@tonic-gate		if (mod_uninstall_interval == 0) {
3977c478bdstevel@tonic-gate			mod_uninstall_interval = 60;
3987c478bdstevel@tonic-gate			modreap();
3997c478bdstevel@tonic-gate			return (rval);
4007c478bdstevel@tonic-gate		}
4017c478bdstevel@tonic-gate#endif
4027c478bdstevel@tonic-gate		mod_uninstall_all();
4037c478bdstevel@tonic-gate	} else {
4047c478bdstevel@tonic-gate		rval = modunload(id);
4057c478bdstevel@tonic-gate	}
4067c478bdstevel@tonic-gate	return (rval);
4077c478bdstevel@tonic-gate}
4087c478bdstevel@tonic-gate
4097c478bdstevel@tonic-gatestatic int
4107c478bdstevel@tonic-gatemodctl_modinfo(modid_t id, struct modinfo *umodi)
4117c478bdstevel@tonic-gate{
4127c478bdstevel@tonic-gate	int retval;
4137c478bdstevel@tonic-gate	struct modinfo modi;
4147c478bdstevel@tonic-gate#if defined(_SYSCALL32_IMPL)
4157c478bdstevel@tonic-gate	int nobase;
4167c478bdstevel@tonic-gate	struct modinfo32 modi32;
4177c478bdstevel@tonic-gate#endif
4187c478bdstevel@tonic-gate
419c6f039cToomas Soome	nobase = 0;
4207c478bdstevel@tonic-gate	if (get_udatamodel() == DATAMODEL_NATIVE) {
4217c478bdstevel@tonic-gate		if (copyin(umodi, &modi, sizeof (struct modinfo)) != 0)
4227c478bdstevel@tonic-gate			return (EFAULT);
4237c478bdstevel@tonic-gate	}
4247c478bdstevel@tonic-gate#ifdef _SYSCALL32_IMPL
4257c478bdstevel@tonic-gate	else {
4267c478bdstevel@tonic-gate		bzero(&modi, sizeof (modi));
4277c478bdstevel@tonic-gate		if (copyin(umodi, &modi32, sizeof (struct modinfo32)) != 0)
4287c478bdstevel@tonic-gate			return (EFAULT);
4297c478bdstevel@tonic-gate		modi.mi_info = modi32.mi_info;
4307c478bdstevel@tonic-gate		modi.mi_id = modi32.mi_id;
4317c478bdstevel@tonic-gate		modi.mi_nextid = modi32.mi_nextid;
4327c478bdstevel@tonic-gate		nobase = modi.mi_info & MI_INFO_NOBASE;
4337c478bdstevel@tonic-gate	}
4347c478bdstevel@tonic-gate#endif
4357c478bdstevel@tonic-gate	/*
4367c478bdstevel@tonic-gate	 * This flag is -only- for the kernels use.
4377c478bdstevel@tonic-gate	 */
4387c478bdstevel@tonic-gate	modi.mi_info &= ~MI_INFO_LINKAGE;
4397c478bdstevel@tonic-gate
4407c478bdstevel@tonic-gate	retval = modinfo(id, &modi);
4417c478bdstevel@tonic-gate	if (retval)
4427c478bdstevel@tonic-gate		return (retval);
4437c478bdstevel@tonic-gate
4447c478bdstevel@tonic-gate	if (get_udatamodel() == DATAMODEL_NATIVE) {
4457c478bdstevel@tonic-gate		if (copyout(&modi, umodi, sizeof (struct modinfo)) != 0)
4467c478bdstevel@tonic-gate			retval = EFAULT;
4477c478bdstevel@tonic-gate#ifdef _SYSCALL32_IMPL
4487c478bdstevel@tonic-gate	} else {
4497c478bdstevel@tonic-gate		int i;
4507c478bdstevel@tonic-gate
4517c478bdstevel@tonic-gate		if (!nobase && (uintptr_t)modi.mi_base > UINT32_MAX)
4527c478bdstevel@tonic-gate			return (EOVERFLOW);
4537c478bdstevel@tonic-gate
4547c478bdstevel@tonic-gate		modi32.mi_info = modi.mi_info;
4557c478bdstevel@tonic-gate		modi32.mi_state = modi.mi_state;
4567c478bdstevel@tonic-gate		modi32.mi_id = modi.mi_id;
4577c478bdstevel@tonic-gate		modi32.mi_nextid = modi.mi_nextid;
4587c478bdstevel@tonic-gate		modi32.mi_base = (caddr32_t)(uintptr_t)modi.mi_base;
4597c478bdstevel@tonic-gate		modi32.mi_size = modi.mi_size;
4607c478bdstevel@tonic-gate		modi32.mi_rev = modi.mi_rev;
4617c478bdstevel@tonic-gate		modi32.mi_loadcnt = modi.mi_loadcnt;
4627c478bdstevel@tonic-gate		bcopy(modi.mi_name, modi32.mi_name, sizeof (modi32.mi_name));
4637c478bdstevel@tonic-gate		for (i = 0; i < MODMAXLINK32; i++) {
4647c478bdstevel@tonic-gate			modi32.mi_msinfo[i].msi_p0 = modi.mi_msinfo[i].msi_p0;
4657c478bdstevel@tonic-gate			bcopy(modi.mi_msinfo[i].msi_linkinfo,
4667c478bdstevel@tonic-gate			    modi32.mi_msinfo[i].msi_linkinfo,
4677c478bdstevel@tonic-gate			    sizeof (modi32.mi_msinfo[0].msi_linkinfo));
4687c478bdstevel@tonic-gate		}
4697c478bdstevel@tonic-gate		if (copyout(&modi32, umodi, sizeof (struct modinfo32)) != 0)
4707c478bdstevel@tonic-gate			retval = EFAULT;
4717c478bdstevel@tonic-gate#endif
4727c478bdstevel@tonic-gate	}
4737c478bdstevel@tonic-gate
4747c478bdstevel@tonic-gate	return (retval);
4757c478bdstevel@tonic-gate}
4767c478bdstevel@tonic-gate
4777c478bdstevel@tonic-gate/*
4787c478bdstevel@tonic-gate * Return the last major number in the range of permissible major numbers.
4797c478bdstevel@tonic-gate */
4807c478bdstevel@tonic-gate/*ARGSUSED*/
4817c478bdstevel@tonic-gatestatic int
4827c478bdstevel@tonic-gatemodctl_modreserve(modid_t id, int *data)
4837c478bdstevel@tonic-gate{
4847c478bdstevel@tonic-gate	if (copyout(&devcnt, data, sizeof (devcnt)) != 0)
4857c478bdstevel@tonic-gate		return (EFAULT);
4867c478bdstevel@tonic-gate	return (0);
4877c478bdstevel@tonic-gate}
4887c478bdstevel@tonic-gate
4896532b96Jerry Gilliam/* Add/Remove driver and binding aliases */
4907c478bdstevel@tonic-gatestatic int
4916532b96Jerry Gilliammodctl_update_driver_aliases(int add, int *data)
4927c478bdstevel@tonic-gate{
4936532b96Jerry Gilliam	struct modconfig	mc;
4946532b96Jerry Gilliam	int			i, n, rv = 0;
4956532b96Jerry Gilliam	struct aliases		alias;
4966532b96Jerry Gilliam	struct aliases		*ap;
4976532b96Jerry Gilliam	char			name[MAXMODCONFNAME];
4986532b96Jerry Gilliam	char			cname[MAXMODCONFNAME];
4996532b96Jerry Gilliam	char			*drvname;
5006532b96Jerry Gilliam	int			resid;
5016532b96Jerry Gilliam	struct alias_info {
5026532b96Jerry Gilliam		char	*alias_name;
5036532b96Jerry Gilliam		int	alias_resid;
5046532b96Jerry Gilliam	} *aliases, *aip;
5057c478bdstevel@tonic-gate
506c6f039cToomas Soome	aliases = NULL;
5077c478bdstevel@tonic-gate	bzero(&mc, sizeof (struct modconfig));
5087c478bdstevel@tonic-gate	if (get_udatamodel() == DATAMODEL_NATIVE) {
5097c478bdstevel@tonic-gate		if (copyin(data, &mc, sizeof (struct modconfig)) != 0)
5107c478bdstevel@tonic-gate			return (EFAULT);
5117c478bdstevel@tonic-gate	}
5127c478bdstevel@tonic-gate#ifdef _SYSCALL32_IMPL
5137c478bdstevel@tonic-gate	else {
5147c478bdstevel@tonic-gate		struct modconfig32 modc32;
5157c478bdstevel@tonic-gate		if (copyin(data, &modc32, sizeof (struct modconfig32)) != 0)
5167c478bdstevel@tonic-gate			return (EFAULT);
5177c478bdstevel@tonic-gate		else {
5187c478bdstevel@tonic-gate			bcopy(modc32.drvname, mc.drvname,
5197c478bdstevel@tonic-gate			    sizeof (modc32.drvname));
5207c478bdstevel@tonic-gate			bcopy(modc32.drvclass, mc.drvclass,
5217c478bdstevel@tonic-gate			    sizeof (modc32.drvclass));
5227c478bdstevel@tonic-gate			mc.major = modc32.major;
5236532b96Jerry Gilliam			mc.flags = modc32.flags;
5247c478bdstevel@tonic-gate			mc.num_aliases = modc32.num_aliases;
5257c478bdstevel@tonic-gate			mc.ap = (struct aliases *)(uintptr_t)modc32.ap;
5267c478bdstevel@tonic-gate		}
5277c478bdstevel@tonic-gate	}
5287c478bdstevel@tonic-gate#endif
5297c478bdstevel@tonic-gate
5307c478bdstevel@tonic-gate	/*
5317c478bdstevel@tonic-gate	 * If the driver is already in the mb_hashtab, and the name given
5327c478bdstevel@tonic-gate	 * doesn't match that driver's name, fail.  Otherwise, pass, since
5337c478bdstevel@tonic-gate	 * we may be adding aliases.
5347c478bdstevel@tonic-gate	 */
5356532b96Jerry Gilliam	drvname = mod_major_to_name(mc.major);
5366532b96Jerry Gilliam	if ((drvname != NULL) && strcmp(drvname, mc.drvname) != 0)
5377c478bdstevel@tonic-gate		return (EINVAL);
5387c478bdstevel@tonic-gate
5397c478bdstevel@tonic-gate	/*
5406532b96Jerry Gilliam	 * Precede alias removal by unbinding as many devices as possible.
5416532b96Jerry Gilliam	 */
5426532b96Jerry Gilliam	if (add == 0) {
5436532b96Jerry Gilliam		(void) i_ddi_unload_drvconf(mc.major);
5446532b96Jerry Gilliam		i_ddi_unbind_devs(mc.major);
5456532b96Jerry Gilliam	}
5466532b96Jerry Gilliam
5476532b96Jerry Gilliam	/*
5486532b96Jerry Gilliam	 * Add/remove each supplied driver alias to/from mb_hashtab
5497c478bdstevel@tonic-gate	 */
5507c478bdstevel@tonic-gate	ap = mc.ap;
5516532b96Jerry Gilliam	if (mc.num_aliases > 0)
5526532b96Jerry Gilliam		aliases = kmem_zalloc(
5536532b96Jerry Gilliam		    mc.num_aliases * sizeof (struct alias_info), KM_SLEEP);
5546532b96Jerry Gilliam	aip = aliases;
5557c478bdstevel@tonic-gate	for (i = 0; i < mc.num_aliases; i++) {
5567c478bdstevel@tonic-gate		bzero(&alias, sizeof (struct aliases));
5577c478bdstevel@tonic-gate		if (get_udatamodel() == DATAMODEL_NATIVE) {
5586532b96Jerry Gilliam			if (copyin(ap, &alias, sizeof (struct aliases)) != 0) {
5596532b96Jerry Gilliam				rv = EFAULT;
5606532b96Jerry Gilliam				goto error;
5616532b96Jerry Gilliam			}
5626532b96Jerry Gilliam			if (alias.a_len > MAXMODCONFNAME) {
5636532b96Jerry Gilliam				rv = EINVAL;
5646532b96Jerry Gilliam				goto error;
5656532b96Jerry Gilliam			}
5666532b96Jerry Gilliam			if (copyin(alias.a_name, name, alias.a_len) != 0) {
5676532b96Jerry Gilliam				rv = EFAULT;
5686532b96Jerry Gilliam				goto error;
5696532b96Jerry Gilliam			}
5706532b96Jerry Gilliam			if (name[alias.a_len - 1] != '\0') {
5716532b96Jerry Gilliam				rv = EINVAL;
5726532b96Jerry Gilliam				goto error;
5736532b96Jerry Gilliam			}
5747c478bdstevel@tonic-gate		}
5757c478bdstevel@tonic-gate#ifdef _SYSCALL32_IMPL
5767c478bdstevel@tonic-gate		else {
5777c478bdstevel@tonic-gate			struct aliases32 al32;
5787c478bdstevel@tonic-gate			bzero(&al32, sizeof (struct aliases32));
5796532b96Jerry Gilliam			if (copyin(ap, &al32, sizeof (struct aliases32)) != 0) {
5806532b96Jerry Gilliam				rv = EFAULT;
5816532b96Jerry Gilliam				goto error;
5826532b96Jerry Gilliam			}
5836532b96Jerry Gilliam			if (al32.a_len > MAXMODCONFNAME) {
5846532b96Jerry Gilliam				rv = EINVAL;
5856532b96Jerry Gilliam				goto error;
5866532b96Jerry Gilliam			}
5877c478bdstevel@tonic-gate			if (copyin((void *)(uintptr_t)al32.a_name,
5886532b96Jerry Gilliam			    name, al32.a_len) != 0) {
5896532b96Jerry Gilliam				rv = EFAULT;
5906532b96Jerry Gilliam				goto error;
5916532b96Jerry Gilliam			}
5926532b96Jerry Gilliam			if (name[al32.a_len - 1] != '\0') {
5936532b96Jerry Gilliam				rv = EINVAL;
5946532b96Jerry Gilliam				goto error;
5956532b96Jerry Gilliam			}
5967c478bdstevel@tonic-gate			alias.a_next = (void *)(uintptr_t)al32.a_next;
5977c478bdstevel@tonic-gate		}
5987c478bdstevel@tonic-gate#endif
5997c478bdstevel@tonic-gate		check_esc_sequences(name, cname);
6007f0b830Edward Pilatowicz		aip->alias_name = strdup(cname);
6017c478bdstevel@tonic-gate		ap = alias.a_next;
6026532b96Jerry Gilliam		aip++;
6036532b96Jerry Gilliam	}
6046532b96Jerry Gilliam
6056532b96Jerry Gilliam	if (add == 0) {
6066532b96Jerry Gilliam		ap = mc.ap;
6076532b96Jerry Gilliam		resid = 0;
6086532b96Jerry Gilliam		aip = aliases;
6096532b96Jerry Gilliam		/* attempt to unbind all devices bound to each alias */
6106532b96Jerry Gilliam		for (i = 0; i < mc.num_aliases; i++) {
6116532b96Jerry Gilliam			n = i_ddi_unbind_devs_by_alias(
6126532b96Jerry Gilliam			    mc.major, aip->alias_name);
6136532b96Jerry Gilliam			resid += n;
6146532b96Jerry Gilliam			aip->alias_resid = n;
6156532b96Jerry Gilliam		}
6167c478bdstevel@tonic-gate
6176532b96Jerry Gilliam		/*
6186532b96Jerry Gilliam		 * If some device bound to an alias remains in use,
6196532b96Jerry Gilliam		 * and override wasn't specified, no change is made to
6206532b96Jerry Gilliam		 * the binding state and we fail the operation.
6216532b96Jerry Gilliam		 */
6226532b96Jerry Gilliam		if (resid > 0 && ((mc.flags & MOD_UNBIND_OVERRIDE) == 0)) {
6236532b96Jerry Gilliam			rv = EBUSY;
6246532b96Jerry Gilliam			goto error;
6256532b96Jerry Gilliam		}
6266532b96Jerry Gilliam
6276532b96Jerry Gilliam		/*
6286532b96Jerry Gilliam		 * No device remains bound of any of the aliases,
6296532b96Jerry Gilliam		 * or force was requested.  Mark each alias as
6306532b96Jerry Gilliam		 * inactive via delete_mbind so no future binds
6316532b96Jerry Gilliam		 * to this alias take place and that a new
6326532b96Jerry Gilliam		 * binding can be established.
6336532b96Jerry Gilliam		 */
6346532b96Jerry Gilliam		aip = aliases;
6356532b96Jerry Gilliam		for (i = 0; i < mc.num_aliases; i++) {
6366532b96Jerry Gilliam			if (moddebug & MODDEBUG_BINDING)
6376532b96Jerry Gilliam				cmn_err(CE_CONT, "Removing binding for %s "
6386532b96Jerry Gilliam				    "(%d active references)\n",
6396532b96Jerry Gilliam				    aip->alias_name, aip->alias_resid);
6406532b96Jerry Gilliam			delete_mbind(aip->alias_name, mb_hashtab);
6416532b96Jerry Gilliam			aip++;
6426532b96Jerry Gilliam		}
6436532b96Jerry Gilliam		rv = 0;
6446532b96Jerry Gilliam	} else {
6456532b96Jerry Gilliam		aip = aliases;
6466532b96Jerry Gilliam		for (i = 0; i < mc.num_aliases; i++) {
6476532b96Jerry Gilliam			if (moddebug & MODDEBUG_BINDING)
6486532b96Jerry Gilliam				cmn_err(CE_NOTE, "Adding binding for '%s'\n",
6496532b96Jerry Gilliam				    aip->alias_name);
6506532b96Jerry Gilliam			(void) make_mbind(aip->alias_name,
6516532b96Jerry Gilliam			    mc.major, NULL, mb_hashtab);
6526532b96Jerry Gilliam			aip++;
6536532b96Jerry Gilliam		}
6546532b96Jerry Gilliam		/*
6556532b96Jerry Gilliam		 * Try to establish an mbinding for mc.drvname, and add it to
6566532b96Jerry Gilliam		 * devnames. Add class if any after establishing the major
6576532b96Jerry Gilliam		 * number.
6586532b96Jerry Gilliam		 */
6596532b96Jerry Gilliam		(void) make_mbind(mc.drvname, mc.major, NULL, mb_hashtab);
660c9cc149Jerry Gilliam		if ((rv = make_devname(mc.drvname, mc.major,
661c9cc149Jerry Gilliam		    (mc.flags & MOD_ADDMAJBIND_UPDATE) ?
662c9cc149Jerry Gilliam		    DN_DRIVER_INACTIVE : 0)) != 0) {
6636532b96Jerry Gilliam			goto error;
664c9cc149Jerry Gilliam		}
6657c478bdstevel@tonic-gate
6667c478bdstevel@tonic-gate		if (mc.drvclass[0] != '\0')
6677c478bdstevel@tonic-gate			add_class(mc.drvname, mc.drvclass);
668c9cc149Jerry Gilliam		if ((mc.flags & MOD_ADDMAJBIND_UPDATE) == 0) {
669c9cc149Jerry Gilliam			(void) i_ddi_load_drvconf(mc.major);
670c9cc149Jerry Gilliam		}
6716532b96Jerry Gilliam	}
6726532b96Jerry Gilliam
6736532b96Jerry Gilliam	/*
6746532b96Jerry Gilliam	 * Ensure that all nodes are bound to the most appropriate driver
6756532b96Jerry Gilliam	 * possible, attempting demotion and rebind when a more appropriate
676c9cc149Jerry Gilliam	 * driver now exists.  But not when adding a driver update-only.
6776532b96Jerry Gilliam	 */
678c9cc149Jerry Gilliam	if ((add == 0) || ((mc.flags & MOD_ADDMAJBIND_UPDATE) == 0)) {
679c9cc149Jerry Gilliam		i_ddi_bind_devs();
680c9cc149Jerry Gilliam		i_ddi_di_cache_invalidate();
681c9cc149Jerry Gilliam	}
6826532b96Jerry Gilliam
6836532b96Jerry Gilliamerror:
6846532b96Jerry Gilliam	if (mc.num_aliases > 0) {
6856532b96Jerry Gilliam		aip = aliases;
6866532b96Jerry Gilliam		for (i = 0; i < mc.num_aliases; i++) {
6876532b96Jerry Gilliam			if (aip->alias_name != NULL)
6886532b96Jerry Gilliam				strfree(aip->alias_name);
6896532b96Jerry Gilliam			aip++;
6906532b96Jerry Gilliam		}
6916532b96Jerry Gilliam		kmem_free(aliases, mc.num_aliases * sizeof (struct alias_info));
6927c478bdstevel@tonic-gate	}
6937c478bdstevel@tonic-gate	return (rv);
6947c478bdstevel@tonic-gate}
6957c478bdstevel@tonic-gate
6967c478bdstevel@tonic-gatestatic int
6976532b96Jerry Gilliammodctl_add_driver_aliases(int *data)
6986532b96Jerry Gilliam{
6996532b96Jerry Gilliam	return (modctl_update_driver_aliases(1, data));
7006532b96Jerry Gilliam}
7016532b96Jerry Gilliam
7026532b96Jerry Gilliamstatic int
7036532b96Jerry Gilliammodctl_remove_driver_aliases(int *data)
7046532b96Jerry Gilliam{
7056532b96Jerry Gilliam	return (modctl_update_driver_aliases(0, data));
7066532b96Jerry Gilliam}
7076532b96Jerry Gilliam
7086532b96Jerry Gilliamstatic int
7097c478bdstevel@tonic-gatemodctl_rem_major(major_t major)
7107c478bdstevel@tonic-gate{
7117c478bdstevel@tonic-gate	struct devnames *dnp;
7127c478bdstevel@tonic-gate
7137c478bdstevel@tonic-gate	if (major >= devcnt)
7147c478bdstevel@tonic-gate		return (EINVAL);
7157c478bdstevel@tonic-gate
7167c478bdstevel@tonic-gate	/* mark devnames as removed */
7177c478bdstevel@tonic-gate	dnp = &devnamesp[major];
7187c478bdstevel@tonic-gate	LOCK_DEV_OPS(&dnp->dn_lock);
7197c478bdstevel@tonic-gate	if (dnp->dn_name == NULL ||
7207c478bdstevel@tonic-gate	    (dnp->dn_flags & (DN_DRIVER_REMOVED | DN_TAKEN_GETUDEV))) {
7217c478bdstevel@tonic-gate		UNLOCK_DEV_OPS(&dnp->dn_lock);
7227c478bdstevel@tonic-gate		return (EINVAL);
7237c478bdstevel@tonic-gate	}
7247c478bdstevel@tonic-gate	dnp->dn_flags |= DN_DRIVER_REMOVED;
7257c478bdstevel@tonic-gate	pm_driver_removed(major);
7267c478bdstevel@tonic-gate	UNLOCK_DEV_OPS(&dnp->dn_lock);
7277c478bdstevel@tonic-gate
7287c478bdstevel@tonic-gate	(void) i_ddi_unload_drvconf(major);
7297c478bdstevel@tonic-gate	i_ddi_unbind_devs(major);
7306532b96Jerry Gilliam	i_ddi_bind_devs();
7314c06356dh	i_ddi_di_cache_invalidate();
7326532b96Jerry Gilliam
7336532b96Jerry Gilliam	/* purge all the bindings to this driver */
7346532b96Jerry Gilliam	purge_mbind(major, mb_hashtab);
7357c478bdstevel@tonic-gate	return (0);
7367c478bdstevel@tonic-gate}
7377c478bdstevel@tonic-gate
7387c478bdstevel@tonic-gatestatic struct vfs *
7397c478bdstevel@tonic-gatepath_to_vfs(char *name)
7407c478bdstevel@tonic-gate{
7417c478bdstevel@tonic-gate	vnode_t *vp;
7427c478bdstevel@tonic-gate	struct vfs *vfsp;
7437c478bdstevel@tonic-gate
7447c478bdstevel@tonic-gate	if (lookupname(name, UIO_SYSSPACE, FOLLOW, NULLVPP, &vp))
7457c478bdstevel@tonic-gate		return (NULL);
7467c478bdstevel@tonic-gate
7477c478bdstevel@tonic-gate	vfsp = vp->v_vfsp;
7487c478bdstevel@tonic-gate	VN_RELE(vp);
7497c478bdstevel@tonic-gate	return (vfsp);
7507c478bdstevel@tonic-gate}
7517c478bdstevel@tonic-gate
7527c478bdstevel@tonic-gatestatic int
7537c478bdstevel@tonic-gatenew_vfs_in_modpath()
7547c478bdstevel@tonic-gate{
7557c478bdstevel@tonic-gate	static int n_modpath = 0;
7567c478bdstevel@tonic-gate	static char *modpath_copy;
7577c478bdstevel@tonic-gate	static struct pathvfs {
7587c478bdstevel@tonic-gate		char *path;
7597c478bdstevel@tonic-gate		struct vfs *vfsp;
7607c478bdstevel@tonic-gate	} *pathvfs;
7617c478bdstevel@tonic-gate
7627c478bdstevel@tonic-gate	int i, new_vfs = 0;
7637c478bdstevel@tonic-gate	char *tmp, *tmp1;
7647c478bdstevel@tonic-gate	struct vfs *vfsp;
7657c478bdstevel@tonic-gate
7667c478bdstevel@tonic-gate	if (n_modpath != 0) {
7677c478bdstevel@tonic-gate		for (i = 0; i < n_modpath; i++) {
7687c478bdstevel@tonic-gate			vfsp = path_to_vfs(pathvfs[i].path);
7697c478bdstevel@tonic-gate			if (vfsp != pathvfs[i].vfsp) {
7707c478bdstevel@tonic-gate				pathvfs[i].vfsp = vfsp;
7717c478bdstevel@tonic-gate				if (vfsp)
7727c478bdstevel@tonic-gate					new_vfs = 1;
7737c478bdstevel@tonic-gate			}
7747c478bdstevel@tonic-gate		}
7757c478bdstevel@tonic-gate		return (new_vfs);
7767c478bdstevel@tonic-gate	}
7777c478bdstevel@tonic-gate
7787c478bdstevel@tonic-gate	/*
7797c478bdstevel@tonic-gate	 * First call, initialize the pathvfs structure
7807c478bdstevel@tonic-gate	 */
7817c478bdstevel@tonic-gate	modpath_copy = i_ddi_strdup(default_path, KM_SLEEP);
7827c478bdstevel@tonic-gate	tmp = modpath_copy;
7837c478bdstevel@tonic-gate	n_modpath = 1;
7847c478bdstevel@tonic-gate	tmp1 = strchr(tmp, ' ');
7857c478bdstevel@tonic-gate	while (tmp1) {
7867c478bdstevel@tonic-gate		*tmp1 = '\0';
7877c478bdstevel@tonic-gate		n_modpath++;
7887c478bdstevel@tonic-gate		tmp = tmp1 + 1;
7897c478bdstevel@tonic-gate		tmp1 = strchr(tmp, ' ');
7907c478bdstevel@tonic-gate	}
7917c478bdstevel@tonic-gate
7927c478bdstevel@tonic-gate	pathvfs = kmem_zalloc(n_modpath * sizeof (struct pathvfs), KM_SLEEP);
7937c478bdstevel@tonic-gate	tmp = modpath_copy;
7947c478bdstevel@tonic-gate	for (i = 0; i < n_modpath; i++) {
7957c478bdstevel@tonic-gate		pathvfs[i].path = tmp;
7967c478bdstevel@tonic-gate		vfsp = path_to_vfs(tmp);
7977c478bdstevel@tonic-gate		pathvfs[i].vfsp = vfsp;
7987c478bdstevel@tonic-gate		tmp += strlen(tmp) + 1;
7997c478bdstevel@tonic-gate	}
8007c478bdstevel@tonic-gate	return (1);	/* always reread driver.conf the first time */
8017c478bdstevel@tonic-gate}
8027c478bdstevel@tonic-gate
803facf4a8llaistatic int
804c9cc149Jerry Gilliammodctl_load_drvconf(major_t major, int flags)
8057c478bdstevel@tonic-gate{
8067c478bdstevel@tonic-gate	int ret;
8077c478bdstevel@tonic-gate
808c9cc149Jerry Gilliam	/*
809c9cc149Jerry Gilliam	 * devfsadm -u - read all new driver.conf files
810c9cc149Jerry Gilliam	 * and bind and configure devices for new drivers.
811c9cc149Jerry Gilliam	 */
812c9cc149Jerry Gilliam	if (flags & MOD_LOADDRVCONF_RECONF) {
813c9cc149Jerry Gilliam		(void) i_ddi_load_drvconf(DDI_MAJOR_T_NONE);
814c9cc149Jerry Gilliam		i_ddi_bind_devs();
815c9cc149Jerry Gilliam		i_ddi_di_cache_invalidate();
816c9cc149Jerry Gilliam		return (0);
817c9cc149Jerry Gilliam	}
818c9cc149Jerry Gilliam
819c9cc149Jerry Gilliam	/*
820c9cc149Jerry Gilliam	 * update_drv <drv> - reload driver.conf for the specified driver
821c9cc149Jerry Gilliam	 */
822a204de7cth	if (major != DDI_MAJOR_T_NONE) {
8237c478bdstevel@tonic-gate		ret = i_ddi_load_drvconf(major);
8247c478bdstevel@tonic-gate		if (ret == 0)
8257c478bdstevel@tonic-gate			i_ddi_bind_devs();
8267c478bdstevel@tonic-gate		return (ret);
8277c478bdstevel@tonic-gate	}
8287c478bdstevel@tonic-gate
8297c478bdstevel@tonic-gate	/*
8307c478bdstevel@tonic-gate	 * We are invoked to rescan new driver.conf files. It is
8317c478bdstevel@tonic-gate	 * only necessary if a new file system was mounted in the
8327c478bdstevel@tonic-gate	 * module_path. Because rescanning driver.conf files can
8337c478bdstevel@tonic-gate	 * take some time on older platforms (sun4m), the following
8347c478bdstevel@tonic-gate	 * code skips unnecessary driver.conf rescans to optimize
8357c478bdstevel@tonic-gate	 * boot performance.
8367c478bdstevel@tonic-gate	 */
8377c478bdstevel@tonic-gate	if (new_vfs_in_modpath()) {
838a204de7cth		(void) i_ddi_load_drvconf(DDI_MAJOR_T_NONE);
8397c478bdstevel@tonic-gate		/*
8407c478bdstevel@tonic-gate		 * If we are still initializing io subsystem,
8417c478bdstevel@tonic-gate		 * load drivers with ddi-forceattach property
8427c478bdstevel@tonic-gate		 */
8437c478bdstevel@tonic-gate		if (!i_ddi_io_initialized())
8447c478bdstevel@tonic-gate			i_ddi_forceattach_drivers();
8457c478bdstevel@tonic-gate	}
8467c478bdstevel@tonic-gate	return (0);
8477c478bdstevel@tonic-gate}
8487c478bdstevel@tonic-gate
8496532b96Jerry Gilliam/*
8506532b96Jerry Gilliam * Unload driver.conf file and follow up by attempting
8516532b96Jerry Gilliam * to rebind devices to more appropriate driver.
8526532b96Jerry Gilliam */
8537c478bdstevel@tonic-gatestatic int
8547c478bdstevel@tonic-gatemodctl_unload_drvconf(major_t major)
8557c478bdstevel@tonic-gate{
8567c478bdstevel@tonic-gate	int ret;
8577c478bdstevel@tonic-gate
8587c478bdstevel@tonic-gate	if (major >= devcnt)
8597c478bdstevel@tonic-gate		return (EINVAL);
8607c478bdstevel@tonic-gate
8617c478bdstevel@tonic-gate	ret = i_ddi_unload_drvconf(major);
8627c478bdstevel@tonic-gate	if (ret != 0)
8637c478bdstevel@tonic-gate		return (ret);
8647c478bdstevel@tonic-gate	(void) i_ddi_unbind_devs(major);
8656532b96Jerry Gilliam	i_ddi_bind_devs();
8667c478bdstevel@tonic-gate
8677c478bdstevel@tonic-gate	return (0);
8687c478bdstevel@tonic-gate}
8697c478bdstevel@tonic-gate
8707c478bdstevel@tonic-gatestatic void
8717c478bdstevel@tonic-gatecheck_esc_sequences(char *str, char *cstr)
8727c478bdstevel@tonic-gate{
8737c478bdstevel@tonic-gate	int i;
8747c478bdstevel@tonic-gate	size_t len;
8757c478bdstevel@tonic-gate	char *p;
8767c478bdstevel@tonic-gate
8777c478bdstevel@tonic-gate	len = strlen(str);
8787c478bdstevel@tonic-gate	for (i = 0; i < len; i++, str++, cstr++) {
8797c478bdstevel@tonic-gate		if (*str != '\\') {
8807c478bdstevel@tonic-gate			*cstr = *str;
8817c478bdstevel@tonic-gate		} else {
8827c478bdstevel@tonic-gate			p = str + 1;
8837c478bdstevel@tonic-gate			/*
8847c478bdstevel@tonic-gate			 * we only handle octal escape sequences for SPACE
8857c478bdstevel@tonic-gate			 */
8867c478bdstevel@tonic-gate			if (*p++ == '0' && *p++ == '4' && *p == '0') {
8877c478bdstevel@tonic-gate				*cstr = ' ';
8887c478bdstevel@tonic-gate				str += 3;
8897c478bdstevel@tonic-gate			} else {
8907c478bdstevel@tonic-gate				*cstr = *str;
8917c478bdstevel@tonic-gate			}
8927c478bdstevel@tonic-gate		}
8937c478bdstevel@tonic-gate	}
8947c478bdstevel@tonic-gate	*cstr = 0;
8957c478bdstevel@tonic-gate}
8967c478bdstevel@tonic-gate
8977c478bdstevel@tonic-gatestatic int
8987c478bdstevel@tonic-gatemodctl_getmodpathlen(int *data)
8997c478bdstevel@tonic-gate{
9007c478bdstevel@tonic-gate	int len;
9017c478bdstevel@tonic-gate	len = strlen(default_path);
9027c478bdstevel@tonic-gate	if (copyout(&len, data, sizeof (len)) != 0)
9037c478bdstevel@tonic-gate		return (EFAULT);
9047c478bdstevel@tonic-gate	return (0);
9057c478bdstevel@tonic-gate}
9067c478bdstevel@tonic-gate
9077c478bdstevel@tonic-gatestatic int
9087c478bdstevel@tonic-gatemodctl_getmodpath(char *data)
9097c478bdstevel@tonic-gate{
9107c478bdstevel@tonic-gate	if (copyout(default_path, data, strlen(default_path) + 1) != 0)
9117c478bdstevel@tonic-gate		return (EFAULT);
9127c478bdstevel@tonic-gate	return (0);
9137c478bdstevel@tonic-gate}
9147c478bdstevel@tonic-gate
9157c478bdstevel@tonic-gatestatic int
9167c478bdstevel@tonic-gatemodctl_read_sysbinding_file(void)
9177c478bdstevel@tonic-gate{
9187c478bdstevel@tonic-gate	(void) read_binding_file(sysbind, sb_hashtab, make_mbind);
9197c478bdstevel@tonic-gate	return (0);
9207c478bdstevel@tonic-gate}
9217c478bdstevel@tonic-gate
9227c478bdstevel@tonic-gatestatic int
9237c478bdstevel@tonic-gatemodctl_getmaj(char *uname, uint_t ulen, int *umajorp)
9247c478bdstevel@tonic-gate{
9257c478bdstevel@tonic-gate	char name[256];
9267c478bdstevel@tonic-gate	int retval;
9277c478bdstevel@tonic-gate	major_t major;
9287c478bdstevel@tonic-gate
929facf4a8llai	if (ulen == 0)
930facf4a8llai		return (EINVAL);
9317c478bdstevel@tonic-gate	if ((retval = copyinstr(uname, name,
9327c478bdstevel@tonic-gate	    (ulen < 256) ? ulen : 256, 0)) != 0)
9337c478bdstevel@tonic-gate		return (retval);
934a204de7cth	if ((major = mod_name_to_major(name)) == DDI_MAJOR_T_NONE)
9357c478bdstevel@tonic-gate		return (ENODEV);
9367c478bdstevel@tonic-gate	if (copyout(&major, umajorp, sizeof (major_t)) != 0)
9377c478bdstevel@tonic-gate		return (EFAULT);
9387c478bdstevel@tonic-gate	return (0);
9397c478bdstevel@tonic-gate}
9407c478bdstevel@tonic-gate
94125e8c5avikramstatic char **
94225e8c5avikramconvert_constraint_string(char *constraints, size_t len)
94325e8c5avikram{
94425e8c5avikram	int	i;
94525e8c5avikram	int	n;
94625e8c5avikram	char	*p;
94725e8c5avikram	char	**array;
94825e8c5avikram
94925e8c5avikram	ASSERT(constraints != NULL);
95025e8c5avikram	ASSERT(len > 0);
95125e8c5avikram
9521e1ddd6cth	for (i = 0, p = constraints; strlen(p) > 0; i++, p += strlen(p) + 1)
9531e1ddd6cth		;
95425e8c5avikram
95525e8c5avikram	n = i;
95625e8c5avikram
95725e8c5avikram	if (n == 0) {
95825e8c5avikram		kmem_free(constraints, len);
95925e8c5avikram		return (NULL);
96025e8c5avikram	}
96125e8c5avikram
96225e8c5avikram	array = kmem_alloc((n + 1) * sizeof (char *), KM_SLEEP);
96325e8c5avikram
96425e8c5avikram	for (i = 0, p = constraints; i < n; i++, p += strlen(p) + 1) {
96525e8c5avikram		array[i] = i_ddi_strdup(p, KM_SLEEP);
96625e8c5avikram	}
96725e8c5avikram	array[n] = NULL;
96825e8c5avikram
96925e8c5avikram	kmem_free(constraints, len);
97025e8c5avikram
97125e8c5avikram	return (array);
97225e8c5avikram}
97325e8c5avikram/*ARGSUSED*/
97425e8c5avikramstatic int
97525e8c5avikrammodctl_retire(char *path, char *uconstraints, size_t ulen)
97625e8c5avikram{
97725e8c5avikram	char	*pathbuf;
97825e8c5avikram	char	*devpath;
97925e8c5avikram	size_t	pathsz;
98025e8c5avikram	int	retval;
98125e8c5avikram	char	*constraints;
98225e8c5avikram	char	**cons_array;
98325e8c5avikram
98425e8c5avikram	if (path == NULL)
98525e8c5avikram		return (EINVAL);
98625e8c5avikram
98725e8c5avikram	if ((uconstraints == NULL) ^ (ulen == 0))
98825e8c5avikram		return (EINVAL);
98925e8c5avikram
99025e8c5avikram	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
99125e8c5avikram	retval = copyinstr(path, pathbuf, MAXPATHLEN, &pathsz);
99225e8c5avikram	if (retval != 0) {
99325e8c5avikram		kmem_free(pathbuf, MAXPATHLEN);
99425e8c5avikram		return (retval);
99525e8c5avikram	}
99625e8c5avikram	devpath = i_ddi_strdup(pathbuf, KM_SLEEP);
99725e8c5avikram	kmem_free(pathbuf, MAXPATHLEN);
99825e8c5avikram
99925e8c5avikram	/*
100025e8c5avikram	 * First check if the device is already retired.
1001bf00242Stephen Hanson	 * If it is, then persist the retire anyway, just in case the retire
1002bf00242Stephen Hanson	 * store has got out of sync with the boot archive.
100325e8c5avikram	 */
100425e8c5avikram	if (e_ddi_device_retired(devpath)) {
100525e8c5avikram		cmn_err(CE_NOTE, "Device: already retired: %s", devpath);
1006bf00242Stephen Hanson		(void) e_ddi_retire_persist(devpath);
100725e8c5avikram		kmem_free(devpath, strlen(devpath) + 1);
100825e8c5avikram		return (0);
100925e8c5avikram	}
101025e8c5avikram
101125e8c5avikram	cons_array = NULL;
101225e8c5avikram	if (uconstraints) {
101325e8c5avikram		constraints = kmem_alloc(ulen, KM_SLEEP);
101425e8c5avikram		if (copyin(uconstraints, constraints, ulen)) {
101525e8c5avikram			kmem_free(constraints, ulen);
101625e8c5avikram			kmem_free(devpath, strlen(devpath) + 1);
101725e8c5avikram			return (EFAULT);
101825e8c5avikram		}
101925e8c5avikram		cons_array = convert_constraint_string(constraints, ulen);
102025e8c5avikram	}
102125e8c5avikram
102225e8c5avikram	/*
102325e8c5avikram	 * Try to retire the device first. The following
102425e8c5avikram	 * routine will return an error only if the device
102525e8c5avikram	 * is not retireable i.e. retire constraints forbid
102625e8c5avikram	 * a retire. A return of success from this routine
102725e8c5avikram	 * indicates that device is retireable.
102825e8c5avikram	 */
102925e8c5avikram	retval = e_ddi_retire_device(devpath, cons_array);
103025e8c5avikram	if (retval != DDI_SUCCESS) {
103125e8c5avikram		cmn_err(CE_WARN, "constraints forbid retire: %s", devpath);
103225e8c5avikram		kmem_free(devpath, strlen(devpath) + 1);
103325e8c5avikram		return (ENOTSUP);
103425e8c5avikram	}
103525e8c5avikram
103625e8c5avikram	/*
103725e8c5avikram	 * Ok, the retire succeeded. Persist the retire.
103825e8c5avikram	 * If retiring a nexus, we need to only persist the
103925e8c5avikram	 * nexus retire. Any children of a retired nexus
104025e8c5avikram	 * are automatically covered by the retire store
104125e8c5avikram	 * code.
104225e8c5avikram	 */
104325e8c5avikram	retval = e_ddi_retire_persist(devpath);
104425e8c5avikram	if (retval != 0) {
104525e8c5avikram		cmn_err(CE_WARN, "Failed to persist device retire: error %d: "
104625e8c5avikram		    "%s", retval, devpath);
104725e8c5avikram		kmem_free(devpath, strlen(devpath) + 1);
104825e8c5avikram		return (retval);
104925e8c5avikram	}
105025e8c5avikram	if (moddebug & MODDEBUG_RETIRE)
105125e8c5avikram		cmn_err(CE_NOTE, "Persisted retire of device: %s", devpath);
105225e8c5avikram
105325e8c5avikram	kmem_free(devpath, strlen(devpath) + 1);
105425e8c5avikram	return (0);
105525e8c5avikram}
105625e8c5avikram
105725e8c5avikramstatic int
105825e8c5avikrammodctl_is_retired(char *path, int *statep)
105925e8c5avikram{
106025e8c5avikram	char	*pathbuf;
106125e8c5avikram	char	*devpath;
106225e8c5avikram	size_t	pathsz;
106325e8c5avikram	int	error;
106425e8c5avikram	int	status;
106525e8c5avikram
106625e8c5avikram	if (path == NULL || statep == NULL)
106725e8c5avikram		return (EINVAL);
106825e8c5avikram
106925e8c5avikram	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
107025e8c5avikram	error = copyinstr(path, pathbuf, MAXPATHLEN, &pathsz);
107125e8c5avikram	if (error != 0) {
107225e8c5avikram		kmem_free(pathbuf, MAXPATHLEN);
107325e8c5avikram		return (error);
107425e8c5avikram	}
107525e8c5avikram	devpath = i_ddi_strdup(pathbuf, KM_SLEEP);
107625e8c5avikram	kmem_free(pathbuf, MAXPATHLEN);
107725e8c5avikram
107825e8c5avikram	if (e_ddi_device_retired(devpath))
107925e8c5avikram		status = 1;
108025e8c5avikram	else
108125e8c5avikram		status = 0;
108225e8c5avikram	kmem_free(devpath, strlen(devpath) + 1);
108325e8c5avikram
108425e8c5avikram	return (copyout(&status, statep, sizeof (status)) ? EFAULT : 0);
108525e8c5avikram}
108625e8c5avikram
108725e8c5avikramstatic int
108825e8c5avikrammodctl_unretire(char *path)
108925e8c5avikram{
109025e8c5avikram	char	*pathbuf;
109125e8c5avikram	char	*devpath;
109225e8c5avikram	size_t	pathsz;
109325e8c5avikram	int	retired;
109425e8c5avikram	int	retval;
109525e8c5avikram
109625e8c5avikram	if (path == NULL)
109725e8c5avikram		return (EINVAL);
109825e8c5avikram
109925e8c5avikram	pathbuf = kmem_alloc(MAXPATHLEN, KM_SLEEP);
110025e8c5avikram	retval = copyinstr(path, pathbuf, MAXPATHLEN, &pathsz);
110125e8c5avikram	if (retval != 0) {
110225e8c5avikram		kmem_free(pathbuf, MAXPATHLEN);
110325e8c5avikram		return (retval);
110425e8c5avikram	}
110525e8c5avikram	devpath = i_ddi_strdup(pathbuf, KM_SLEEP);
110625e8c5avikram	kmem_free(pathbuf, MAXPATHLEN);
110725e8c5avikram
110825e8c5avikram	/*
110925e8c5avikram	 * We check if a device is retired (first) before
111025e8c5avikram	 * unpersisting the retire, because we use the
111125e8c5avikram	 * retire store to determine if a device is retired.
111225e8c5avikram	 * If we unpersist first, the device will always appear
111325e8c5avikram	 * to be unretired. For the rationale behind unpersisting
111425e8c5avikram	 * a device that is not retired, see the next comment.
111525e8c5avikram	 */
111625e8c5avikram	retired = e_ddi_device_retired(devpath);
111725e8c5avikram
111825e8c5avikram	/*
111925e8c5avikram	 * We call unpersist unconditionally because the lookup
112025e8c5avikram	 * for retired devices (e_ddi_device_retired()), skips "bypassed"
112125e8c5avikram	 * devices. We still want to be able remove "bypassed" entries
112225e8c5avikram	 * from the persistent store, so we unpersist unconditionally
112325e8c5avikram	 * i.e. whether or not the entry is found on a lookup.
112425e8c5avikram	 *
112525e8c5avikram	 * e_ddi_retire_unpersist() returns 1 if it found and cleared
112625e8c5avikram	 * an entry from the retire store or 0 otherwise.
112725e8c5avikram	 */
112825e8c5avikram	if (e_ddi_retire_unpersist(devpath))
112925e8c5avikram		if (moddebug & MODDEBUG_RETIRE) {
113025e8c5avikram			cmn_err(CE_NOTE, "Unpersisted retire of device: %s",
113125e8c5avikram			    devpath);
113225e8c5avikram		}
113325e8c5avikram
113425e8c5avikram	/*
113525e8c5avikram	 * Check if the device is already unretired. If so,
113625e8c5avikram	 * the unretire becomes a NOP
113725e8c5avikram	 */
113825e8c5avikram	if (!retired) {
113925e8c5avikram		cmn_err(CE_NOTE, "Not retired: %s", devpath);
114025e8c5avikram		kmem_free(devpath, strlen(devpath) + 1);
114125e8c5avikram		return (0);
114225e8c5avikram	}
114325e8c5avikram
114425e8c5avikram	retval = e_ddi_unretire_device(devpath);
114525e8c5avikram	if (retval != 0) {
114625e8c5avikram		cmn_err(CE_WARN, "cannot unretire device: error %d, path %s\n",
114725e8c5avikram		    retval, devpath);
114825e8c5avikram	}
114925e8c5avikram
115025e8c5avikram	kmem_free(devpath, strlen(devpath) + 1);
115125e8c5avikram
115225e8c5avikram	return (retval);
115325e8c5avikram}
115425e8c5avikram
11557c478bdstevel@tonic-gatestatic int
11567c478bdstevel@tonic-gatemodctl_getname(char *uname, uint_t ulen, int *umajorp)
11577c478bdstevel@tonic-gate{
11587c478bdstevel@tonic-gate	char *name;
11597c478bdstevel@tonic-gate	major_t major;
11607c478bdstevel@tonic-gate
11617c478bdstevel@tonic-gate	if (copyin(umajorp, &major, sizeof (major)) != 0)
11627c478bdstevel@tonic-gate		return (EFAULT);
11637c478bdstevel@tonic-gate	if ((name = mod_major_to_name(major)) == NULL)
11647c478bdstevel@tonic-gate		return (ENODEV);
11657c478bdstevel@tonic-gate	if ((strlen(name) + 1) > ulen)
11667c478bdstevel@tonic-gate		return (ENOSPC);
11677c478bdstevel@tonic-gate	return (copyoutstr(name, uname, ulen, NULL));
11687c478bdstevel@tonic-gate}
11697c478bdstevel@tonic-gate
11707c478bdstevel@tonic-gatestatic int
11717c478bdstevel@tonic-gatemodctl_devt2instance(dev_t dev, int *uinstancep)
11727c478bdstevel@tonic-gate{
11737c478bdstevel@tonic-gate	int	instance;
11747c478bdstevel@tonic-gate
11757c478bdstevel@tonic-gate	if ((instance = dev_to_instance(dev)) == -1)
11767c478bdstevel@tonic-gate		return (EINVAL);
11777c478bdstevel@tonic-gate
11787c478bdstevel@tonic-gate	return (copyout(&instance, uinstancep, sizeof (int)));
11797c478bdstevel@tonic-gate}
11807c478bdstevel@tonic-gate
11817c478bdstevel@tonic-gate/*
11827c478bdstevel@tonic-gate * Return the sizeof of the device id.
11837c478bdstevel@tonic-gate */
11847c478bdstevel@tonic-gatestatic int
11857c478bdstevel@tonic-gatemodctl_sizeof_devid(dev_t dev, uint_t *len)
11867c478bdstevel@tonic-gate{
11877c478bdstevel@tonic-gate	uint_t		sz;
11887c478bdstevel@tonic-gate	ddi_devid_t	devid;
11897c478bdstevel@tonic-gate
11907c478bdstevel@tonic-gate	/* get device id */
11917c478bdstevel@tonic-gate	if (ddi_lyr_get_devid(dev, &devid) == DDI_FAILURE)
11927c478bdstevel@tonic-gate		return (EINVAL);
11937c478bdstevel@tonic-gate
11947c478bdstevel@tonic-gate	sz = ddi_devid_sizeof(devid);
11957c478bdstevel@tonic-gate	ddi_devid_free(devid);
11967c478bdstevel@tonic-gate
11977c478bdstevel@tonic-gate	/* copyout device id size */
11987c478bdstevel@tonic-gate	if (copyout(&sz, len, sizeof (sz)) != 0)
11997c478bdstevel@tonic-gate		return (EFAULT);
12007c478bdstevel@tonic-gate
12017c478bdstevel@tonic-gate	return (0);
12027c478bdstevel@tonic-gate}
12037c478bdstevel@tonic-gate
12047c478bdstevel@tonic-gate/*
12057c478bdstevel@tonic-gate * Return a copy of the device id.
12067c478bdstevel@tonic-gate */
12077c478bdstevel@tonic-gatestatic int
12087c478bdstevel@tonic-gatemodctl_get_devid(dev_t dev, uint_t len, ddi_devid_t udevid)
12097c478bdstevel@tonic-gate{
12107c478bdstevel@tonic-gate	uint_t		sz;
12117c478bdstevel@tonic-gate	ddi_devid_t	devid;
12127c478bdstevel@tonic-gate	int		err = 0;
12137c478bdstevel@tonic-gate
12147c478bdstevel@tonic-gate	/* get device id */
12157c478bdstevel@tonic-gate	if (ddi_lyr_get_devid(dev, &devid) == DDI_FAILURE)
12167c478bdstevel@tonic-gate		return (EINVAL);
12177c478bdstevel@tonic-gate
12187c478bdstevel@tonic-gate	sz = ddi_devid_sizeof(devid);
12197c478bdstevel@tonic-gate
12207c478bdstevel@tonic-gate	/* Error if device id is larger than space allocated */
12217c478bdstevel@tonic-gate	if (sz > len) {
12227c478bdstevel@tonic-gate		ddi_devid_free(devid);
12237c478bdstevel@tonic-gate		return (ENOSPC);
12247c478bdstevel@tonic-gate	}
12257c478bdstevel@tonic-gate
12267c478bdstevel@tonic-gate	/* copy out device id */
12277c478bdstevel@tonic-gate	if (copyout(devid, udevid, sz) != 0)
12287c478bdstevel@tonic-gate		err = EFAULT;
12297c478bdstevel@tonic-gate	ddi_devid_free(devid);
12307c478bdstevel@tonic-gate	return (err);
12317c478bdstevel@tonic-gate}
12327c478bdstevel@tonic-gate
12337c478bdstevel@tonic-gate/*
12347c478bdstevel@tonic-gate * return the /devices paths associated with the specified devid and
12357c478bdstevel@tonic-gate * minor name.
12367c478bdstevel@tonic-gate */
12377c478bdstevel@tonic-gate/*ARGSUSED*/
12387c478bdstevel@tonic-gatestatic int
12397c478bdstevel@tonic-gatemodctl_devid2paths(ddi_devid_t udevid, char *uminor_name, uint_t flag,
12402b987d4Alexander Eremin    size_t *ulensp, char *upaths)
12417c478bdstevel@tonic-gate{
12427c478bdstevel@tonic-gate	ddi_devid_t	devid = NULL;
12437c478bdstevel@tonic-gate	int		devid_len;
12447c478bdstevel@tonic-gate	char		*minor_name = NULL;
12457c478bdstevel@tonic-gate	dev_info_t	*dip = NULL;
1246b9ccdc5cth	int		circ;
12472694730Evan Yan	struct ddi_minor_data	*dmdp;
12487c478bdstevel@tonic-gate	char		*path = NULL;
12497c478bdstevel@tonic-gate	int		ulens;
12507c478bdstevel@tonic-gate	int		lens;
12517c478bdstevel@tonic-gate	int		len;
12527c478bdstevel@tonic-gate	dev_t		*devlist = NULL;
12537c478bdstevel@tonic-gate	int		ndevs;
12547c478bdstevel@tonic-gate	int		i;
12557c478bdstevel@tonic-gate	int		ret = 0;
12567c478bdstevel@tonic-gate
12577c478bdstevel@tonic-gate	/*
12587c478bdstevel@tonic-gate	 * If upaths is NULL then we are only computing the amount of space
12597c478bdstevel@tonic-gate	 * needed to hold the paths and returning the value in *ulensp. If we
12607c478bdstevel@tonic-gate	 * are copying out paths then we get the amount of space allocated by
12617c478bdstevel@tonic-gate	 * the caller. If the actual space needed for paths is larger, or
12627c478bdstevel@tonic-gate	 * things are changing out from under us, then we return EAGAIN.
12637c478bdstevel@tonic-gate	 */
12647c478bdstevel@tonic-gate	if (upaths) {
12657c478bdstevel@tonic-gate		if (ulensp == NULL)
12667c478bdstevel@tonic-gate			return (EINVAL);
12677c478bdstevel@tonic-gate		if (copyin(ulensp, &ulens, sizeof (ulens)) != 0)
12687c478bdstevel@tonic-gate			return (EFAULT);
12697c478bdstevel@tonic-gate	}
12707c478bdstevel@tonic-gate
12717c478bdstevel@tonic-gate	/*
12727c478bdstevel@tonic-gate	 * copyin enough of the devid to determine the length then
12737c478bdstevel@tonic-gate	 * reallocate and copy in the entire devid.
12747c478bdstevel@tonic-gate	 */
12757c478bdstevel@tonic-gate	devid_len = ddi_devid_sizeof(NULL);
12767c478bdstevel@tonic-gate	devid = kmem_alloc(devid_len, KM_SLEEP);
12777c478bdstevel@tonic-gate	if (copyin(udevid, devid, devid_len)) {
12787c478bdstevel@tonic-gate		ret = EFAULT;
12797c478bdstevel@tonic-gate		goto out;
12807c478bdstevel@tonic-gate	}
12817c478bdstevel@tonic-gate	len = devid_len;
12827c478bdstevel@tonic-gate	devid_len = ddi_devid_sizeof(devid);
12837c478bdstevel@tonic-gate	kmem_free(devid, len);
12847c478bdstevel@tonic-gate	devid = kmem_alloc(devid_len, KM_SLEEP);
12857c478bdstevel@tonic-gate	if (copyin(udevid, devid, devid_len)) {
12867c478bdstevel@tonic-gate		ret = EFAULT;
12877c478bdstevel@tonic-gate		goto out;
12887c478bdstevel@tonic-gate	}
12897c478bdstevel@tonic-gate
12907c478bdstevel@tonic-gate	/* copyin the minor name if specified. */
12917c478bdstevel@tonic-gate	minor_name = uminor_name;
12927c478bdstevel@tonic-gate	if ((minor_name != DEVID_MINOR_NAME_ALL) &&
12937c478bdstevel@tonic-gate	    (minor_name != DEVID_MINOR_NAME_ALL_CHR) &&
12947c478bdstevel@tonic-gate	    (minor_name != DEVID_MINOR_NAME_ALL_BLK)) {
12957c478bdstevel@tonic-gate		minor_name = kmem_alloc(MAXPATHLEN, KM_SLEEP);
12967c478bdstevel@tonic-gate		if (copyinstr(uminor_name, minor_name, MAXPATHLEN, 0)) {
12977c478bdstevel@tonic-gate			ret = EFAULT;
12987c478bdstevel@tonic-gate			goto out;
12997c478bdstevel@tonic-gate		}
13007c478bdstevel@tonic-gate	}
13017c478bdstevel@tonic-gate
13027c478bdstevel@tonic-gate	/*
13037c478bdstevel@tonic-gate	 * Use existing function to resolve the devid into a devlist.
13047c478bdstevel@tonic-gate	 *
13057c478bdstevel@tonic-gate	 * NOTE: there is a loss of spectype information in the current
13067c478bdstevel@tonic-gate	 * ddi_lyr_devid_to_devlist implementation. We work around this by not
13077c478bdstevel@tonic-gate	 * passing down DEVID_MINOR_NAME_ALL here, but reproducing all minor
13087c478bdstevel@tonic-gate	 * node forms in the loop processing the devlist below. It would be
13097c478bdstevel@tonic-gate	 * best if at some point the use of this interface here was replaced
13107c478bdstevel@tonic-gate	 * with a path oriented call.
13117c478bdstevel@tonic-gate	 */
13127c478bdstevel@tonic-gate	if (ddi_lyr_devid_to_devlist(devid,
13137c478bdstevel@tonic-gate	    (minor_name == DEVID_MINOR_NAME_ALL) ?
13147c478bdstevel@tonic-gate	    DEVID_MINOR_NAME_ALL_CHR : minor_name,
13157c478bdstevel@tonic-gate	    &ndevs, &devlist) != DDI_SUCCESS) {
13167c478bdstevel@tonic-gate		ret = EINVAL;
13177c478bdstevel@tonic-gate		goto out;
13187c478bdstevel@tonic-gate	}
13197c478bdstevel@tonic-gate
13207c478bdstevel@tonic-gate	/*
13217c478bdstevel@tonic-gate	 * loop over the devlist, converting each devt to a path and doing
13227c478bdstevel@tonic-gate	 * a copyout of the path and computation of the amount of space
13237c478bdstevel@tonic-gate	 * needed to hold all the paths
13247c478bdstevel@tonic-gate	 */
13257c478bdstevel@tonic-gate	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
13267c478bdstevel@tonic-gate	for (i = 0, lens = 0; i < ndevs; i++) {
13277c478bdstevel@tonic-gate
13287c478bdstevel@tonic-gate		/* find the dip associated with the dev_t */
13297c478bdstevel@tonic-gate		if ((dip = e_ddi_hold_devi_by_dev(devlist[i], 0)) == NULL)
13307c478bdstevel@tonic-gate			continue;
13317c478bdstevel@tonic-gate
13327c478bdstevel@tonic-gate		/* loop over all the minor nodes, skipping ones we don't want */
1333b9ccdc5cth		ndi_devi_enter(dip, &circ);
13347c478bdstevel@tonic-gate		for (dmdp = DEVI(dip)->devi_minor; dmdp; dmdp = dmdp->next) {
13357c478bdstevel@tonic-gate			if ((dmdp->ddm_dev != devlist[i]) ||
13367c478bdstevel@tonic-gate			    (dmdp->type != DDM_MINOR))
13377c478bdstevel@tonic-gate				continue;
13387c478bdstevel@tonic-gate
13397c478bdstevel@tonic-gate			if ((minor_name != DEVID_MINOR_NAME_ALL) &&
13407c478bdstevel@tonic-gate			    (minor_name != DEVID_MINOR_NAME_ALL_CHR) &&
13417c478bdstevel@tonic-gate			    (minor_name != DEVID_MINOR_NAME_ALL_BLK) &&
13427c478bdstevel@tonic-gate			    strcmp(minor_name, dmdp->ddm_name))
13437c478bdstevel@tonic-gate				continue;
13447c478bdstevel@tonic-gate			else {
13457c478bdstevel@tonic-gate				if ((minor_name == DEVID_MINOR_NAME_ALL_CHR) &&
13467c478bdstevel@tonic-gate				    (dmdp->ddm_spec_type != S_IFCHR))
13477c478bdstevel@tonic-gate					continue;
13487c478bdstevel@tonic-gate				if ((minor_name == DEVID_MINOR_NAME_ALL_BLK) &&
13497c478bdstevel@tonic-gate				    (dmdp->ddm_spec_type != S_IFBLK))
13507c478bdstevel@tonic-gate					continue;
13517c478bdstevel@tonic-gate			}
13527c478bdstevel@tonic-gate
1353f9722deChris Horne			(void) ddi_pathname_minor(dmdp, path);
13547c478bdstevel@tonic-gate			len = strlen(path) + 1;
13557c478bdstevel@tonic-gate			*(path + len) = '\0';	/* set double termination */
13567c478bdstevel@tonic-gate			lens += len;
13577c478bdstevel@tonic-gate
13587c478bdstevel@tonic-gate			/* copyout the path with double terminations */
13597c478bdstevel@tonic-gate			if (upaths) {
13607c478bdstevel@tonic-gate				if (lens > ulens) {
13617c478bdstevel@tonic-gate					ret = EAGAIN;
13627c478bdstevel@tonic-gate					goto out;
13637c478bdstevel@tonic-gate				}
13647c478bdstevel@tonic-gate				if (copyout(path, upaths, len + 1)) {
13657c478bdstevel@tonic-gate					ret = EFAULT;
13667c478bdstevel@tonic-gate					goto out;
13677c478bdstevel@tonic-gate				}
13687c478bdstevel@tonic-gate				upaths += len;
13697c478bdstevel@tonic-gate			}
13707c478bdstevel@tonic-gate		}
1371b9ccdc5cth		ndi_devi_exit(dip, circ);
13727c478bdstevel@tonic-gate		ddi_release_devi(dip);
13737c478bdstevel@tonic-gate		dip = NULL;
13747c478bdstevel@tonic-gate	}
13757c478bdstevel@tonic-gate	lens++;		/* add one for double termination */
13767c478bdstevel@tonic-gate
13777c478bdstevel@tonic-gate	/* copy out the amount of space needed to hold the paths */
13787c478bdstevel@tonic-gate	if (ulensp && copyout(&lens, ulensp, sizeof (lens))) {
13797c478bdstevel@tonic-gate		ret = EFAULT;
13807c478bdstevel@tonic-gate		goto out;
13817c478bdstevel@tonic-gate	}
13827c478bdstevel@tonic-gate	ret = 0;
13837c478bdstevel@tonic-gate
1384b9ccdc5cthout:	if (dip) {
1385b9ccdc5cth		ndi_devi_exit(dip, circ);
13867c478bdstevel@tonic-gate		ddi_release_devi(dip);
1387b9ccdc5cth	}
13887c478bdstevel@tonic-gate	if (path)
13897c478bdstevel@tonic-gate		kmem_free(path, MAXPATHLEN);
13907c478bdstevel@tonic-gate	if (devlist)
13917c478bdstevel@tonic-gate		ddi_lyr_free_devlist(devlist, ndevs);
13927c478bdstevel@tonic-gate	if (minor_name &&
13937c478bdstevel@tonic-gate	    (minor_name != DEVID_MINOR_NAME_ALL) &&
13947c478bdstevel@tonic-gate	    (minor_name != DEVID_MINOR_NAME_ALL_CHR) &&
13957c478bdstevel@tonic-gate	    (minor_name != DEVID_MINOR_NAME_ALL_BLK))
13967c478bdstevel@tonic-gate		kmem_free(minor_name, MAXPATHLEN);
13977c478bdstevel@tonic-gate	if (devid)
13987c478bdstevel@tonic-gate		kmem_free(devid, devid_len);
13997c478bdstevel@tonic-gate	return (ret);
14007c478bdstevel@tonic-gate}
14017c478bdstevel@tonic-gate
14027c478bdstevel@tonic-gate/*
14037c478bdstevel@tonic-gate * Return the size of the minor name.
14047c478bdstevel@tonic-gate */
14057c478bdstevel@tonic-gatestatic int
14067c478bdstevel@tonic-gatemodctl_sizeof_minorname(dev_t dev, int spectype, uint_t *len)
14077c478bdstevel@tonic-gate{
14087c478bdstevel@tonic-gate	uint_t	sz;
14097c478bdstevel@tonic-gate	char	*name;
14107c478bdstevel@tonic-gate
14117c478bdstevel@tonic-gate	/* get the minor name */
14127c478bdstevel@tonic-gate	if (ddi_lyr_get_minor_name(dev, spectype, &name) == DDI_FAILURE)
14137c478bdstevel@tonic-gate		return (EINVAL);
14147c478bdstevel@tonic-gate
14157c478bdstevel@tonic-gate	sz = strlen(name) + 1;
14167c478bdstevel@tonic-gate	kmem_free(name, sz);
14177c478bdstevel@tonic-gate
14187c478bdstevel@tonic-gate	/* copy out the size of the minor name */
14197c478bdstevel@tonic-gate	if (copyout(&sz, len, sizeof (sz)) != 0)
14207c478bdstevel@tonic-gate		return (EFAULT);
14217c478bdstevel@tonic-gate
14227c478bdstevel@tonic-gate	return (0);
14237c478bdstevel@tonic-gate}
14247c478bdstevel@tonic-gate
14257c478bdstevel@tonic-gate/*
14267c478bdstevel@tonic-gate * Return the minor name.
14277c478bdstevel@tonic-gate */
14287c478bdstevel@tonic-gatestatic int
14297c478bdstevel@tonic-gatemodctl_get_minorname(dev_t dev, int spectype, uint_t len, char *uname)
14307c478bdstevel@tonic-gate{
14317c478bdstevel@tonic-gate	uint_t	sz;
14327c478bdstevel@tonic-gate	char	*name;
14337c478bdstevel@tonic-gate	int	err = 0;
14347c478bdstevel@tonic-gate
14357c478bdstevel@tonic-gate	/* get the minor name */
14367c478bdstevel@tonic-gate	if (ddi_lyr_get_minor_name(dev, spectype, &name) == DDI_FAILURE)
14377c478bdstevel@tonic-gate		return (EINVAL);
14387c478bdstevel@tonic-gate
14397c478bdstevel@tonic-gate	sz = strlen(name) + 1;
14407c478bdstevel@tonic-gate
14417c478bdstevel@tonic-gate	/* Error if the minor name is larger than the space allocated */
14427c478bdstevel@tonic-gate	if (sz > len) {
14437c478bdstevel@tonic-gate		kmem_free(name, sz);
14447c478bdstevel@tonic-gate		return (ENOSPC);
14457c478bdstevel@tonic-gate	}
14467c478bdstevel@tonic-gate
14477c478bdstevel@tonic-gate	/* copy out the minor name */
14487c478bdstevel@tonic-gate	if (copyout(name, uname, sz) != 0)
14497c478bdstevel@tonic-gate		err = EFAULT;
14507c478bdstevel@tonic-gate	kmem_free(name, sz);
14517c478bdstevel@tonic-gate	return (err);
14527c478bdstevel@tonic-gate}
14537c478bdstevel@tonic-gate
14547c478bdstevel@tonic-gate/*
1455a08731ecth * Return the size of the (dev_t,spectype) devfspath name.
14567c478bdstevel@tonic-gate */
14577c478bdstevel@tonic-gatestatic int
14587c478bdstevel@tonic-gatemodctl_devfspath_len(dev_t dev, int spectype, uint_t *len)
14597c478bdstevel@tonic-gate{
14607c478bdstevel@tonic-gate	uint_t	sz;
14617c478bdstevel@tonic-gate	char	*name;
14627c478bdstevel@tonic-gate
14637c478bdstevel@tonic-gate	/* get the path name */
14647c478bdstevel@tonic-gate	name = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
14657c478bdstevel@tonic-gate	if (ddi_dev_pathname(dev, spectype, name) == DDI_FAILURE) {
14667c478bdstevel@tonic-gate		kmem_free(name, MAXPATHLEN);
14677c478bdstevel@tonic-gate		return (EINVAL);
14687c478bdstevel@tonic-gate	}
14697c478bdstevel@tonic-gate
14707c478bdstevel@tonic-gate	sz = strlen(name) + 1;
14717c478bdstevel@tonic-gate	kmem_free(name, MAXPATHLEN);
14727c478bdstevel@tonic-gate
14737c478bdstevel@tonic-gate	/* copy out the size of the path name */
14747c478bdstevel@tonic-gate	if (copyout(&sz, len, sizeof (sz)) != 0)
14757c478bdstevel@tonic-gate		return (EFAULT);
14767c478bdstevel@tonic-gate
14777c478bdstevel@tonic-gate	return (0);
14787c478bdstevel@tonic-gate}
14797c478bdstevel@tonic-gate
14807c478bdstevel@tonic-gate/*
1481a08731ecth * Return the (dev_t,spectype) devfspath name.
14827c478bdstevel@tonic-gate */
14837c478bdstevel@tonic-gatestatic int
14847c478bdstevel@tonic-gatemodctl_devfspath(dev_t dev, int spectype, uint_t len, char *uname)
14857c478bdstevel@tonic-gate{
14867c478bdstevel@tonic-gate	uint_t	sz;
14877c478bdstevel@tonic-gate	char	*name;
14887c478bdstevel@tonic-gate	int	err = 0;
14897c478bdstevel@tonic-gate
14907c478bdstevel@tonic-gate	/* get the path name */
14917c478bdstevel@tonic-gate	name = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
14927c478bdstevel@tonic-gate	if (ddi_dev_pathname(dev, spectype, name) == DDI_FAILURE) {
14937c478bdstevel@tonic-gate		kmem_free(name, MAXPATHLEN);
14947c478bdstevel@tonic-gate		return (EINVAL);
14957c478bdstevel@tonic-gate	}
14967c478bdstevel@tonic-gate
14977c478bdstevel@tonic-gate	sz = strlen(name) + 1;
14987c478bdstevel@tonic-gate
14997c478bdstevel@tonic-gate	/* Error if the path name is larger than the space allocated */
15007c478bdstevel@tonic-gate	if (sz > len) {
15017c478bdstevel@tonic-gate		kmem_free(name, MAXPATHLEN);
15027c478bdstevel@tonic-gate		return (ENOSPC);
15037c478bdstevel@tonic-gate	}
15047c478bdstevel@tonic-gate
15057c478bdstevel@tonic-gate	/* copy out the path name */
15067c478bdstevel@tonic-gate	if (copyout(name, uname, sz) != 0)
15077c478bdstevel@tonic-gate		err = EFAULT;
15087c478bdstevel@tonic-gate	kmem_free(name, MAXPATHLEN);
15097c478bdstevel@tonic-gate	return (err);
15107c478bdstevel@tonic-gate}
15117c478bdstevel@tonic-gate
1512a08731ecth/*
1513a08731ecth * Return the size of the (major,instance) devfspath name.
1514a08731ecth */
1515a08731ecthstatic int
1516a08731ecthmodctl_devfspath_mi_len(major_t major, int instance, uint_t *len)
1517a08731ecth{
1518a08731ecth	uint_t	sz;
1519a08731ecth	char	*name;
1520a08731ecth
1521a08731ecth	/* get the path name */
1522a08731ecth	name = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1523a08731ecth	if (e_ddi_majorinstance_to_path(major, instance, name) != DDI_SUCCESS) {
1524a08731ecth		kmem_free(name, MAXPATHLEN);
1525a08731ecth		return (EINVAL);
1526a08731ecth	}
1527a08731ecth
1528a08731ecth	sz = strlen(name) + 1;
1529a08731ecth	kmem_free(name, MAXPATHLEN);
1530a08731ecth
1531a08731ecth	/* copy out the size of the path name */
1532a08731ecth	if (copyout(&sz, len, sizeof (sz)) != 0)
1533a08731ecth		return (EFAULT);
1534a08731ecth
1535a08731ecth	return (0);
1536a08731ecth}
1537a08731ecth
1538a08731ecth/*
1539a08731ecth * Return the (major_instance) devfspath name.
1540a08731ecth * NOTE: e_ddi_majorinstance_to_path does not require the device to attach to
1541a08731ecth * return a path - it uses the instance tree.
1542a08731ecth */
1543a08731ecthstatic int
1544a08731ecthmodctl_devfspath_mi(major_t major, int instance, uint_t len, char *uname)
1545a08731ecth{
1546a08731ecth	uint_t	sz;
1547a08731ecth	char	*name;
1548a08731ecth	int	err = 0;
1549a08731ecth
1550a08731ecth	/* get the path name */
1551a08731ecth	name = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
1552a08731ecth	if (e_ddi_majorinstance_to_path(major, instance, name) != DDI_SUCCESS) {
1553a08731ecth		kmem_free(name, MAXPATHLEN);
1554a08731ecth		return (EINVAL);
1555a08731ecth	}
1556a08731ecth
1557a08731ecth	sz = strlen(name) + 1;
1558a08731ecth
1559a08731ecth	/* Error if the path name is larger than the space allocated */
1560a08731ecth	if (sz > len) {
1561a08731ecth		kmem_free(name, MAXPATHLEN);
1562a08731ecth		return (ENOSPC);
1563a08731ecth	}
1564a08731ecth
1565a08731ecth	/* copy out the path name */
1566a08731ecth	if (copyout(name, uname, sz) != 0)
1567a08731ecth		err = EFAULT;
1568a08731ecth	kmem_free(name, MAXPATHLEN);
1569a08731ecth	return (err);
1570a08731ecth}
1571a08731ecth
15727c478bdstevel@tonic-gatestatic int
15737c478bdstevel@tonic-gatemodctl_get_fbname(char *path)
15747c478bdstevel@tonic-gate{
15757c478bdstevel@tonic-gate	extern dev_t fbdev;
15767c478bdstevel@tonic-gate	char *pathname = NULL;
15777c478bdstevel@tonic-gate	int rval = 0;
15787c478bdstevel@tonic-gate
15797c478bdstevel@tonic-gate	/* make sure fbdev is set before we plunge in */
15807c478bdstevel@tonic-gate	if (fbdev == NODEV)
15817c478bdstevel@tonic-gate		return (ENODEV);
15827c478bdstevel@tonic-gate
15837c478bdstevel@tonic-gate	pathname = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
15847c478bdstevel@tonic-gate	if ((rval = ddi_dev_pathname(fbdev, S_IFCHR,
15857c478bdstevel@tonic-gate	    pathname)) == DDI_SUCCESS) {
15867c478bdstevel@tonic-gate		if (copyout(pathname, path, strlen(pathname)+1) != 0) {
15877c478bdstevel@tonic-gate			rval = EFAULT;
15887c478bdstevel@tonic-gate		}
15897c478bdstevel@tonic-gate	}
15907c478bdstevel@tonic-gate	kmem_free(pathname, MAXPATHLEN);
15917c478bdstevel@tonic-gate	return (rval);
15927c478bdstevel@tonic-gate}
15937c478bdstevel@tonic-gate
15947c478bdstevel@tonic-gate/*
15957c478bdstevel@tonic-gate * modctl_reread_dacf()
15967c478bdstevel@tonic-gate *	Reread the dacf rules database from the named binding file.
15977c478bdstevel@tonic-gate *	If NULL is specified, pass along the NULL, it means 'use the default'.
15987c478bdstevel@tonic-gate */
15997c478bdstevel@tonic-gatestatic int
16007c478bdstevel@tonic-gatemodctl_reread_dacf(char *path)
16017c478bdstevel@tonic-gate{
16027c478bdstevel@tonic-gate	int rval = 0;
16037c478bdstevel@tonic-gate	char *filename, *filenamep;
16047c478bdstevel@tonic-gate
16057c478bdstevel@tonic-gate	filename = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
16067c478bdstevel@tonic-gate
16077c478bdstevel@tonic-gate	if (path == NULL) {
16087c478bdstevel@tonic-gate		filenamep = NULL;
16097c478bdstevel@tonic-gate	} else {
16107c478bdstevel@tonic-gate		if (copyinstr(path, filename, MAXPATHLEN, 0) != 0) {
16117c478bdstevel@tonic-gate			rval = EFAULT;
16127c478bdstevel@tonic-gate			goto out;
16137c478bdstevel@tonic-gate		}
16147c478bdstevel@tonic-gate		filenamep = filename;
16157c478bdstevel@tonic-gate		filenamep[MAXPATHLEN - 1] = '\0';
16167c478bdstevel@tonic-gate	}
16177c478bdstevel@tonic-gate
16187c478bdstevel@tonic-gate	rval = read_dacf_binding_file(filenamep);
16197c478bdstevel@tonic-gateout:
16207c478bdstevel@tonic-gate	kmem_free(filename, MAXPATHLEN);
16217c478bdstevel@tonic-gate	return (rval);
16227c478bdstevel@tonic-gate}
16237c478bdstevel@tonic-gate
16247c478bdstevel@tonic-gate/*ARGSUSED*/
16257c478bdstevel@tonic-gatestatic int
16267c478bdstevel@tonic-gatemodctl_modevents(int subcmd, uintptr_t a2, uintptr_t a3, uintptr_t a4,
16277c478bdstevel@tonic-gate    uint_t flag)
16287c478bdstevel@tonic-gate{
16297c478bdstevel@tonic-gate	int error = 0;
16307c478bdstevel@tonic-gate	char *filenamep;
16317c478bdstevel@tonic-gate
16327c478bdstevel@tonic-gate	switch (subcmd) {
16337c478bdstevel@tonic-gate
16347c478bdstevel@tonic-gate	case MODEVENTS_FLUSH:
16357c478bdstevel@tonic-gate		/* flush all currently queued events */
16367c478bdstevel@tonic-gate		log_sysevent_flushq(subcmd, flag);
16377c478bdstevel@tonic-gate		break;
16387c478bdstevel@tonic-gate
16397c478bdstevel@tonic-gate	case MODEVENTS_SET_DOOR_UPCALL_FILENAME:
16407c478bdstevel@tonic-gate		/*
16417c478bdstevel@tonic-gate		 * bind door_upcall to filename
16427c478bdstevel@tonic-gate		 * this should only be done once per invocation
16437c478bdstevel@tonic-gate		 * of the event daemon.
16447c478bdstevel@tonic-gate		 */
16457c478bdstevel@tonic-gate
16467c478bdstevel@tonic-gate		filenamep = kmem_zalloc(MOD_MAXPATH, KM_SLEEP);
16477c478bdstevel@tonic-gate
16487c478bdstevel@tonic-gate		if (copyinstr((char *)a2, filenamep, MOD_MAXPATH, 0)) {
16497c478bdstevel@tonic-gate			error = EFAULT;
16507c478bdstevel@tonic-gate		} else {
16517c478bdstevel@tonic-gate			error = log_sysevent_filename(filenamep);
16527c478bdstevel@tonic-gate		}
16537c478bdstevel@tonic-gate		kmem_free(filenamep, MOD_MAXPATH);
16547c478bdstevel@tonic-gate		break;
16557c478bdstevel@tonic-gate
16567c478bdstevel@tonic-gate	case MODEVENTS_GETDATA:
16577c478bdstevel@tonic-gate		error = log_sysevent_copyout_data((sysevent_id_t *)a2,
16587c478bdstevel@tonic-gate		    (size_t)a3, (caddr_t)a4);
16597c478bdstevel@tonic-gate		break;
16607c478bdstevel@tonic-gate
16617c478bdstevel@tonic-gate	case MODEVENTS_FREEDATA:
16627c478bdstevel@tonic-gate		error = log_sysevent_free_data((sysevent_id_t *)a2);
16637c478bdstevel@tonic-gate		break;
16647c478bdstevel@tonic-gate	case MODEVENTS_POST_EVENT:
16657c478bdstevel@tonic-gate		error = log_usr_sysevent((sysevent_t *)a2, (uint32_t)a3,
16661e1ddd6cth		    (sysevent_id_t *)a4);
16677c478bdstevel@tonic-gate		break;
16687c478bdstevel@tonic-gate	case MODEVENTS_REGISTER_EVENT:
16697c478bdstevel@tonic-gate		error = log_sysevent_register((char *)a2, (char *)a3,
16707c478bdstevel@tonic-gate		    (se_pubsub_t *)a4);
16717c478bdstevel@tonic-gate		break;
16727c478bdstevel@tonic-gate	default:
16737c478bdstevel@tonic-gate		error = EINVAL;
16747c478bdstevel@tonic-gate	}
16757c478bdstevel@tonic-gate
16767c478bdstevel@tonic-gate	return (error);
16777c478bdstevel@tonic-gate}
16787c478bdstevel@tonic-gate
16797c478bdstevel@tonic-gatestatic void
16807c478bdstevel@tonic-gatefree_mperm(mperm_t *mp)
16817c478bdstevel@tonic-gate{
16827c478bdstevel@tonic-gate	int len;
16837c478bdstevel@tonic-gate
16847c478bdstevel@tonic-gate	if (mp->mp_minorname) {
16857c478bdstevel@tonic-gate		len = strlen(mp->mp_minorname) + 1;
16867c478bdstevel@tonic-gate		kmem_free(mp->mp_minorname, len);
16877c478bdstevel@tonic-gate	}
16887c478bdstevel@tonic-gate	kmem_free(mp, sizeof (mperm_t));
16897c478bdstevel@tonic-gate}
16907c478bdstevel@tonic-gate
16917c478bdstevel@tonic-gate#define	MP_NO_DRV_ERR	\
16927c478bdstevel@tonic-gate	"/etc/minor_perm: no driver for %s\n"
16937c478bdstevel@tonic-gate
16947c478bdstevel@tonic-gate#define	MP_EMPTY_MINOR	\
16957c478bdstevel@tonic-gate	"/etc/minor_perm: empty minor name for driver %s\n"
16967c478bdstevel@tonic-gate
16977c478bdstevel@tonic-gate#define	MP_NO_MINOR	\
16987c478bdstevel@tonic-gate	"/etc/minor_perm: no minor matching %s for driver %s\n"
16997c478bdstevel@tonic-gate
17007c478bdstevel@tonic-gate/*
17017c478bdstevel@tonic-gate * Remove mperm entry with matching minorname
17027c478bdstevel@tonic-gate */
17037c478bdstevel@tonic-gatestatic void
17047c478bdstevel@tonic-gaterem_minorperm(major_t major, char *drvname, mperm_t *mp, int is_clone)
17057c478bdstevel@tonic-gate{
17067c478bdstevel@tonic-gate	mperm_t **mp_head;
17077c478bdstevel@tonic-gate	mperm_t *freemp = NULL;
17087c478bdstevel@tonic-gate	struct devnames *dnp = &devnamesp[major];
17097c478bdstevel@tonic-gate	mperm_t **wildmp;
17107c478bdstevel@tonic-gate
17117c478bdstevel@tonic-gate	ASSERT(mp->mp_minorname && strlen(mp->mp_minorname) > 0);
17127c478bdstevel@tonic-gate
17137c478bdstevel@tonic-gate	LOCK_DEV_OPS(&dnp->dn_lock);
17147c478bdstevel@tonic-gate	if (strcmp(mp->mp_minorname, "*") == 0) {
17157c478bdstevel@tonic-gate		wildmp = ((is_clone == 0) ?
17161e1ddd6cth		    &dnp->dn_mperm_wild : &dnp->dn_mperm_clone);
17177c478bdstevel@tonic-gate		if (*