xref: /onnv-gate/usr/src/uts/common/io/devfm.c (revision 11264:99b4beb4e9f6)
17532SSean.Ye@Sun.COM /*
27532SSean.Ye@Sun.COM  * CDDL HEADER START
37532SSean.Ye@Sun.COM  *
47532SSean.Ye@Sun.COM  * The contents of this file are subject to the terms of the
57532SSean.Ye@Sun.COM  * Common Development and Distribution License (the "License").
67532SSean.Ye@Sun.COM  * You may not use this file except in compliance with the License.
77532SSean.Ye@Sun.COM  *
87532SSean.Ye@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97532SSean.Ye@Sun.COM  * or http://www.opensolaris.org/os/licensing.
107532SSean.Ye@Sun.COM  * See the License for the specific language governing permissions
117532SSean.Ye@Sun.COM  * and limitations under the License.
127532SSean.Ye@Sun.COM  *
137532SSean.Ye@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
147532SSean.Ye@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157532SSean.Ye@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
167532SSean.Ye@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
177532SSean.Ye@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
187532SSean.Ye@Sun.COM  *
197532SSean.Ye@Sun.COM  * CDDL HEADER END
207532SSean.Ye@Sun.COM  */
217532SSean.Ye@Sun.COM /*
2210942STom.Pothier@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237532SSean.Ye@Sun.COM  * Use is subject to license terms.
247532SSean.Ye@Sun.COM  */
257532SSean.Ye@Sun.COM 
267532SSean.Ye@Sun.COM #include <sys/stat.h>
277532SSean.Ye@Sun.COM #include <sys/types.h>
287532SSean.Ye@Sun.COM #include <sys/param.h>
297532SSean.Ye@Sun.COM #include <sys/cred.h>
307532SSean.Ye@Sun.COM #include <sys/policy.h>
317532SSean.Ye@Sun.COM #include <sys/file.h>
327532SSean.Ye@Sun.COM #include <sys/errno.h>
337532SSean.Ye@Sun.COM #include <sys/modctl.h>
347532SSean.Ye@Sun.COM #include <sys/ddi.h>
357532SSean.Ye@Sun.COM #include <sys/sunddi.h>
367532SSean.Ye@Sun.COM #include <sys/conf.h>
377532SSean.Ye@Sun.COM #include <sys/debug.h>
387532SSean.Ye@Sun.COM #include <sys/systeminfo.h>
397532SSean.Ye@Sun.COM 
407532SSean.Ye@Sun.COM #include <sys/fm/protocol.h>
417532SSean.Ye@Sun.COM #include <sys/devfm.h>
427532SSean.Ye@Sun.COM 
437532SSean.Ye@Sun.COM extern int fm_get_paddr(nvlist_t *, uint64_t *);
447532SSean.Ye@Sun.COM #if defined(__x86)
457532SSean.Ye@Sun.COM extern int fm_ioctl_physcpu_info(int, nvlist_t *, nvlist_t **);
467532SSean.Ye@Sun.COM extern int fm_ioctl_cpu_retire(int, nvlist_t *, nvlist_t **);
4710942STom.Pothier@Sun.COM extern int fm_ioctl_gentopo_legacy(int, nvlist_t *, nvlist_t **);
487532SSean.Ye@Sun.COM #endif /* __x86 */
497532SSean.Ye@Sun.COM 
507532SSean.Ye@Sun.COM static int fm_ioctl_versions(int, nvlist_t *, nvlist_t **);
517532SSean.Ye@Sun.COM static int fm_ioctl_page_retire(int, nvlist_t *, nvlist_t **);
527532SSean.Ye@Sun.COM 
537532SSean.Ye@Sun.COM /*
547532SSean.Ye@Sun.COM  * The driver's capabilities are strictly versioned, allowing userland patching
557532SSean.Ye@Sun.COM  * without a reboot.  The userland should start with a FM_VERSIONS ioctl to
567532SSean.Ye@Sun.COM  * query the versions of the kernel interfaces, then it's all userland's
577532SSean.Ye@Sun.COM  * responsibility to prepare arguments etc to match the current kenrel.
587532SSean.Ye@Sun.COM  * The version of FM_VERSIONS itself is FM_DRV_VERSION.
597532SSean.Ye@Sun.COM  */
607532SSean.Ye@Sun.COM typedef struct fm_version {
617532SSean.Ye@Sun.COM 	char		*interface;	/* interface name */
627532SSean.Ye@Sun.COM 	uint32_t	version;	/* interface version */
637532SSean.Ye@Sun.COM } fm_vers_t;
647532SSean.Ye@Sun.COM 
657532SSean.Ye@Sun.COM typedef struct fm_subroutine {
667532SSean.Ye@Sun.COM 	int		cmd;		/* ioctl cmd */
677532SSean.Ye@Sun.COM 	boolean_t	priv;		/* require privilege */
687532SSean.Ye@Sun.COM 	char		*version;	/* version name */
697532SSean.Ye@Sun.COM 	int		(*func)(int, nvlist_t *, nvlist_t **);	/* handler */
707532SSean.Ye@Sun.COM } fm_subr_t;
717532SSean.Ye@Sun.COM 
727532SSean.Ye@Sun.COM static const fm_vers_t fm_versions[] = {
737532SSean.Ye@Sun.COM 	{ FM_VERSIONS_VERSION, FM_DRV_VERSION },
747532SSean.Ye@Sun.COM 	{ FM_PAGE_OP_VERSION, 1 },
757532SSean.Ye@Sun.COM 	{ FM_CPU_OP_VERSION, 1 },
767532SSean.Ye@Sun.COM 	{ FM_CPU_INFO_VERSION, 1 },
7710942STom.Pothier@Sun.COM 	{ FM_TOPO_LEGACY_VERSION, 1 },
787532SSean.Ye@Sun.COM 	{ NULL, 0 }
797532SSean.Ye@Sun.COM };
807532SSean.Ye@Sun.COM 
817532SSean.Ye@Sun.COM static const fm_subr_t fm_subrs[] = {
827532SSean.Ye@Sun.COM 	{ FM_IOC_VERSIONS, B_FALSE, FM_VERSIONS_VERSION, fm_ioctl_versions },
837532SSean.Ye@Sun.COM 	{ FM_IOC_PAGE_RETIRE, B_TRUE, FM_PAGE_OP_VERSION,
847532SSean.Ye@Sun.COM 	    fm_ioctl_page_retire },
857532SSean.Ye@Sun.COM 	{ FM_IOC_PAGE_STATUS, B_FALSE, FM_PAGE_OP_VERSION,
867532SSean.Ye@Sun.COM 	    fm_ioctl_page_retire },
877532SSean.Ye@Sun.COM 	{ FM_IOC_PAGE_UNRETIRE, B_TRUE, FM_PAGE_OP_VERSION,
887532SSean.Ye@Sun.COM 	    fm_ioctl_page_retire },
897532SSean.Ye@Sun.COM #if defined(__x86)
907532SSean.Ye@Sun.COM 	{ FM_IOC_PHYSCPU_INFO, B_FALSE, FM_CPU_INFO_VERSION,
917532SSean.Ye@Sun.COM 	    fm_ioctl_physcpu_info },
927532SSean.Ye@Sun.COM 	{ FM_IOC_CPU_RETIRE, B_TRUE, FM_CPU_OP_VERSION,
937532SSean.Ye@Sun.COM 	    fm_ioctl_cpu_retire },
947532SSean.Ye@Sun.COM 	{ FM_IOC_CPU_STATUS, B_FALSE, FM_CPU_OP_VERSION,
957532SSean.Ye@Sun.COM 	    fm_ioctl_cpu_retire },
967532SSean.Ye@Sun.COM 	{ FM_IOC_CPU_UNRETIRE, B_TRUE, FM_CPU_OP_VERSION,
977532SSean.Ye@Sun.COM 	    fm_ioctl_cpu_retire },
9810942STom.Pothier@Sun.COM 	{ FM_IOC_GENTOPO_LEGACY, B_FALSE, FM_TOPO_LEGACY_VERSION,
9910942STom.Pothier@Sun.COM 	    fm_ioctl_gentopo_legacy },
1007532SSean.Ye@Sun.COM #endif	/* __x86 */
1017532SSean.Ye@Sun.COM 	{ -1, B_FALSE, NULL, NULL },
1027532SSean.Ye@Sun.COM };
1037532SSean.Ye@Sun.COM 
1047532SSean.Ye@Sun.COM static dev_info_t *fm_dip;
1057532SSean.Ye@Sun.COM static boolean_t is_i86xpv;
1067532SSean.Ye@Sun.COM static nvlist_t *fm_vers_nvl;
1077532SSean.Ye@Sun.COM 
1087532SSean.Ye@Sun.COM static int
fm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)1097532SSean.Ye@Sun.COM fm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
1107532SSean.Ye@Sun.COM {
1117532SSean.Ye@Sun.COM 	switch (cmd) {
1127532SSean.Ye@Sun.COM 	case DDI_ATTACH:
1137532SSean.Ye@Sun.COM 		if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR,
1147532SSean.Ye@Sun.COM 		    ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS) {
1157532SSean.Ye@Sun.COM 			ddi_remove_minor_node(dip, NULL);
1167532SSean.Ye@Sun.COM 			return (DDI_FAILURE);
1177532SSean.Ye@Sun.COM 		}
1187532SSean.Ye@Sun.COM 		fm_dip = dip;
1197532SSean.Ye@Sun.COM 		is_i86xpv = (strcmp(platform, "i86xpv") == 0);
1207532SSean.Ye@Sun.COM 		break;
1217532SSean.Ye@Sun.COM 	case DDI_RESUME:
1227532SSean.Ye@Sun.COM 		break;
1237532SSean.Ye@Sun.COM 	default:
1247532SSean.Ye@Sun.COM 		return (DDI_FAILURE);
1257532SSean.Ye@Sun.COM 	}
1267532SSean.Ye@Sun.COM 	return (DDI_SUCCESS);
1277532SSean.Ye@Sun.COM }
1287532SSean.Ye@Sun.COM 
1297532SSean.Ye@Sun.COM static int
fm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)1307532SSean.Ye@Sun.COM fm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
1317532SSean.Ye@Sun.COM {
1327532SSean.Ye@Sun.COM 	int ret = DDI_SUCCESS;
1337532SSean.Ye@Sun.COM 
1347532SSean.Ye@Sun.COM 	switch (cmd) {
1357532SSean.Ye@Sun.COM 	case DDI_DETACH:
1367532SSean.Ye@Sun.COM 		ddi_remove_minor_node(dip, NULL);
1377532SSean.Ye@Sun.COM 		fm_dip = NULL;
1387532SSean.Ye@Sun.COM 		break;
1397532SSean.Ye@Sun.COM 	default:
1407532SSean.Ye@Sun.COM 		ret = DDI_FAILURE;
1417532SSean.Ye@Sun.COM 	}
1427532SSean.Ye@Sun.COM 	return (ret);
1437532SSean.Ye@Sun.COM }
1447532SSean.Ye@Sun.COM 
1457532SSean.Ye@Sun.COM /*ARGSUSED*/
1467532SSean.Ye@Sun.COM static int
fm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)1477532SSean.Ye@Sun.COM fm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
1487532SSean.Ye@Sun.COM {
1497532SSean.Ye@Sun.COM 	int error;
1507532SSean.Ye@Sun.COM 
1517532SSean.Ye@Sun.COM 	switch (infocmd) {
1527532SSean.Ye@Sun.COM 	case DDI_INFO_DEVT2DEVINFO:
1537532SSean.Ye@Sun.COM 		*result = fm_dip;
1547532SSean.Ye@Sun.COM 		error = DDI_SUCCESS;
1557532SSean.Ye@Sun.COM 		break;
1567532SSean.Ye@Sun.COM 	case DDI_INFO_DEVT2INSTANCE:
1577532SSean.Ye@Sun.COM 		*result = NULL;
1587532SSean.Ye@Sun.COM 		error = DDI_SUCCESS;
1597532SSean.Ye@Sun.COM 		break;
1607532SSean.Ye@Sun.COM 	default:
1617532SSean.Ye@Sun.COM 		error = DDI_FAILURE;
1627532SSean.Ye@Sun.COM 	}
1637532SSean.Ye@Sun.COM 	return (error);
1647532SSean.Ye@Sun.COM }
1657532SSean.Ye@Sun.COM 
1667532SSean.Ye@Sun.COM /*ARGSUSED1*/
1677532SSean.Ye@Sun.COM static int
fm_open(dev_t * devp,int flag,int typ,struct cred * cred)1687532SSean.Ye@Sun.COM fm_open(dev_t *devp, int flag, int typ, struct cred *cred)
1697532SSean.Ye@Sun.COM {
1707532SSean.Ye@Sun.COM 	if (typ != OTYP_CHR)
1717532SSean.Ye@Sun.COM 		return (EINVAL);
1727532SSean.Ye@Sun.COM 	if (getminor(*devp) != 0)
1737532SSean.Ye@Sun.COM 		return (ENXIO);
1747532SSean.Ye@Sun.COM 
1757532SSean.Ye@Sun.COM 	return (0);
1767532SSean.Ye@Sun.COM }
1777532SSean.Ye@Sun.COM 
1787532SSean.Ye@Sun.COM /*ARGSUSED*/
1797532SSean.Ye@Sun.COM static int
fm_ioctl_versions(int cmd,nvlist_t * invl,nvlist_t ** onvlp)1807532SSean.Ye@Sun.COM fm_ioctl_versions(int cmd, nvlist_t *invl, nvlist_t **onvlp)
1817532SSean.Ye@Sun.COM {
1827532SSean.Ye@Sun.COM 	nvlist_t *nvl;
1837532SSean.Ye@Sun.COM 	int err;
1847532SSean.Ye@Sun.COM 
1857532SSean.Ye@Sun.COM 	if ((err = nvlist_dup(fm_vers_nvl, &nvl, KM_SLEEP)) == 0)
1867532SSean.Ye@Sun.COM 		*onvlp = nvl;
1877532SSean.Ye@Sun.COM 
1887532SSean.Ye@Sun.COM 	return (err);
1897532SSean.Ye@Sun.COM }
1907532SSean.Ye@Sun.COM 
1917532SSean.Ye@Sun.COM /*
1927532SSean.Ye@Sun.COM  * Given a mem-scheme FMRI for a page, execute the given page retire
1937532SSean.Ye@Sun.COM  * command on it.
1947532SSean.Ye@Sun.COM  */
1957532SSean.Ye@Sun.COM /*ARGSUSED*/
1967532SSean.Ye@Sun.COM static int
fm_ioctl_page_retire(int cmd,nvlist_t * invl,nvlist_t ** onvlp)1977532SSean.Ye@Sun.COM fm_ioctl_page_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp)
1987532SSean.Ye@Sun.COM {
1997532SSean.Ye@Sun.COM 	uint64_t pa;
2007532SSean.Ye@Sun.COM 	nvlist_t *fmri;
2017532SSean.Ye@Sun.COM 	int err;
2027532SSean.Ye@Sun.COM 
2037532SSean.Ye@Sun.COM 	if (is_i86xpv)
2047532SSean.Ye@Sun.COM 		return (ENOTSUP);
2057532SSean.Ye@Sun.COM 
2067532SSean.Ye@Sun.COM 	if ((err = nvlist_lookup_nvlist(invl, FM_PAGE_RETIRE_FMRI, &fmri))
2077532SSean.Ye@Sun.COM 	    != 0)
2087532SSean.Ye@Sun.COM 		return (err);
2097532SSean.Ye@Sun.COM 
2107532SSean.Ye@Sun.COM 	if ((err = fm_get_paddr(fmri, &pa)) != 0)
2117532SSean.Ye@Sun.COM 		return (err);
2127532SSean.Ye@Sun.COM 
2137532SSean.Ye@Sun.COM 	switch (cmd) {
2147532SSean.Ye@Sun.COM 	case FM_IOC_PAGE_STATUS:
2157532SSean.Ye@Sun.COM 		return (page_retire_check(pa, NULL));
2167532SSean.Ye@Sun.COM 
2177532SSean.Ye@Sun.COM 	case FM_IOC_PAGE_RETIRE:
2187532SSean.Ye@Sun.COM 		return (page_retire(pa, PR_FMA));
2197532SSean.Ye@Sun.COM 
2207532SSean.Ye@Sun.COM 	case FM_IOC_PAGE_UNRETIRE:
2217532SSean.Ye@Sun.COM 		return (page_unretire(pa));
2227532SSean.Ye@Sun.COM 	}
2237532SSean.Ye@Sun.COM 
2247532SSean.Ye@Sun.COM 	return (ENOTTY);
2257532SSean.Ye@Sun.COM }
2267532SSean.Ye@Sun.COM 
2277532SSean.Ye@Sun.COM /*ARGSUSED*/
2287532SSean.Ye@Sun.COM static int
fm_ioctl(dev_t dev,int cmd,intptr_t data,int flag,cred_t * cred,int * rvalp)2297532SSean.Ye@Sun.COM fm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred, int *rvalp)
2307532SSean.Ye@Sun.COM {
2317532SSean.Ye@Sun.COM 	char *buf;
2327532SSean.Ye@Sun.COM 	int err;
2337532SSean.Ye@Sun.COM 	uint_t model;
2347532SSean.Ye@Sun.COM 	const fm_subr_t *subr;
2357532SSean.Ye@Sun.COM 	uint32_t vers;
2367532SSean.Ye@Sun.COM 	fm_ioc_data_t fid;
2377532SSean.Ye@Sun.COM 	nvlist_t *invl = NULL, *onvl = NULL;
2387532SSean.Ye@Sun.COM #ifdef _MULTI_DATAMODEL
2397532SSean.Ye@Sun.COM 	fm_ioc_data32_t fid32;
2407532SSean.Ye@Sun.COM #endif
2417532SSean.Ye@Sun.COM 
2427532SSean.Ye@Sun.COM 	if (getminor(dev) != 0)
2437532SSean.Ye@Sun.COM 		return (ENXIO);
2447532SSean.Ye@Sun.COM 
2457532SSean.Ye@Sun.COM 	for (subr = fm_subrs; subr->cmd != cmd; subr++)
2467532SSean.Ye@Sun.COM 		if (subr->cmd == -1)
2477532SSean.Ye@Sun.COM 			return (ENOTTY);
2487532SSean.Ye@Sun.COM 
2497532SSean.Ye@Sun.COM 	if (subr->priv && (flag & FWRITE) == 0 &&
2507532SSean.Ye@Sun.COM 	    secpolicy_sys_config(CRED(), 0) != 0)
2517532SSean.Ye@Sun.COM 		return (EPERM);
2527532SSean.Ye@Sun.COM 
2537532SSean.Ye@Sun.COM 	model = ddi_model_convert_from(flag & FMODELS);
2547532SSean.Ye@Sun.COM 
2557532SSean.Ye@Sun.COM 	switch (model) {
2567532SSean.Ye@Sun.COM #ifdef _MULTI_DATAMODEL
2577532SSean.Ye@Sun.COM 	case DDI_MODEL_ILP32:
2587532SSean.Ye@Sun.COM 		if (ddi_copyin((void *)data, &fid32,
2597532SSean.Ye@Sun.COM 		    sizeof (fm_ioc_data32_t), flag) != 0)
2607532SSean.Ye@Sun.COM 			return (EFAULT);
2617532SSean.Ye@Sun.COM 		fid.fid_version = fid32.fid_version;
2627532SSean.Ye@Sun.COM 		fid.fid_insz = fid32.fid_insz;
2637532SSean.Ye@Sun.COM 		fid.fid_inbuf = (caddr_t)(uintptr_t)fid32.fid_inbuf;
2647532SSean.Ye@Sun.COM 		fid.fid_outsz = fid32.fid_outsz;
2657532SSean.Ye@Sun.COM 		fid.fid_outbuf = (caddr_t)(uintptr_t)fid32.fid_outbuf;
2667532SSean.Ye@Sun.COM 		break;
2677532SSean.Ye@Sun.COM #endif /* _MULTI_DATAMODEL */
2687532SSean.Ye@Sun.COM 	case DDI_MODEL_NONE:
2697532SSean.Ye@Sun.COM 	default:
2707532SSean.Ye@Sun.COM 		if (ddi_copyin((void *)data, &fid, sizeof (fm_ioc_data_t),
2717532SSean.Ye@Sun.COM 		    flag) != 0)
2727532SSean.Ye@Sun.COM 			return (EFAULT);
2737532SSean.Ye@Sun.COM 	}
2747532SSean.Ye@Sun.COM 
2757532SSean.Ye@Sun.COM 	if (nvlist_lookup_uint32(fm_vers_nvl, subr->version, &vers) != 0 ||
2767532SSean.Ye@Sun.COM 	    fid.fid_version != vers)
2777532SSean.Ye@Sun.COM 		return (ENOTSUP);
2787532SSean.Ye@Sun.COM 
2797532SSean.Ye@Sun.COM 	if (fid.fid_insz > FM_IOC_MAXBUFSZ)
2807532SSean.Ye@Sun.COM 		return (ENAMETOOLONG);
281*11264SAdrian.Frost@Sun.COM 	if (fid.fid_outsz > FM_IOC_OUT_MAXBUFSZ)
2827532SSean.Ye@Sun.COM 		return (EINVAL);
2837532SSean.Ye@Sun.COM 
2847532SSean.Ye@Sun.COM 	/*
2857532SSean.Ye@Sun.COM 	 * Copy in and unpack the input nvlist.
2867532SSean.Ye@Sun.COM 	 */
2877532SSean.Ye@Sun.COM 	if (fid.fid_insz != 0 && fid.fid_inbuf != (caddr_t)0) {
2887532SSean.Ye@Sun.COM 		buf = kmem_alloc(fid.fid_insz, KM_SLEEP);
2897532SSean.Ye@Sun.COM 		if (ddi_copyin(fid.fid_inbuf, buf, fid.fid_insz, flag) != 0) {
2907532SSean.Ye@Sun.COM 			kmem_free(buf, fid.fid_insz);
2917532SSean.Ye@Sun.COM 			return (EFAULT);
2927532SSean.Ye@Sun.COM 		}
2937532SSean.Ye@Sun.COM 		err = nvlist_unpack(buf, fid.fid_insz, &invl, KM_SLEEP);
2947532SSean.Ye@Sun.COM 		kmem_free(buf, fid.fid_insz);
2957532SSean.Ye@Sun.COM 		if (err != 0)
2967532SSean.Ye@Sun.COM 			return (err);
2977532SSean.Ye@Sun.COM 	}
2987532SSean.Ye@Sun.COM 
2997532SSean.Ye@Sun.COM 	err = subr->func(cmd, invl, &onvl);
3007532SSean.Ye@Sun.COM 
3017532SSean.Ye@Sun.COM 	if (invl != NULL)
3027532SSean.Ye@Sun.COM 		nvlist_free(invl);
3037532SSean.Ye@Sun.COM 
3047532SSean.Ye@Sun.COM 	if (err != 0) {
3057532SSean.Ye@Sun.COM 		if (onvl != NULL)
3067532SSean.Ye@Sun.COM 			nvlist_free(onvl);
3077532SSean.Ye@Sun.COM 		return (err);
3087532SSean.Ye@Sun.COM 	}
3097532SSean.Ye@Sun.COM 
3107532SSean.Ye@Sun.COM 	/*
3117532SSean.Ye@Sun.COM 	 * If the output nvlist contains any data, pack it and copyout.
3127532SSean.Ye@Sun.COM 	 */
3137532SSean.Ye@Sun.COM 	if (onvl != NULL) {
3147532SSean.Ye@Sun.COM 		size_t sz;
3157532SSean.Ye@Sun.COM 
3167532SSean.Ye@Sun.COM 		if ((err = nvlist_size(onvl, &sz, NV_ENCODE_NATIVE)) != 0) {
3177532SSean.Ye@Sun.COM 			nvlist_free(onvl);
3187532SSean.Ye@Sun.COM 			return (err);
3197532SSean.Ye@Sun.COM 		}
3207532SSean.Ye@Sun.COM 		if (sz > fid.fid_outsz) {
3217532SSean.Ye@Sun.COM 			nvlist_free(onvl);
3227532SSean.Ye@Sun.COM 			return (ENAMETOOLONG);
3237532SSean.Ye@Sun.COM 		}
3247532SSean.Ye@Sun.COM 
3257532SSean.Ye@Sun.COM 		buf = kmem_alloc(sz, KM_SLEEP);
3267532SSean.Ye@Sun.COM 		if ((err = nvlist_pack(onvl, &buf, &sz, NV_ENCODE_NATIVE,
3277532SSean.Ye@Sun.COM 		    KM_SLEEP)) != 0) {
3287532SSean.Ye@Sun.COM 			kmem_free(buf, sz);
3297532SSean.Ye@Sun.COM 			nvlist_free(onvl);
3307532SSean.Ye@Sun.COM 			return (err);
3317532SSean.Ye@Sun.COM 		}
3327532SSean.Ye@Sun.COM 		nvlist_free(onvl);
3337532SSean.Ye@Sun.COM 		if (ddi_copyout(buf, fid.fid_outbuf, sz, flag) != 0) {
3347532SSean.Ye@Sun.COM 			kmem_free(buf, sz);
3357532SSean.Ye@Sun.COM 			return (EFAULT);
3367532SSean.Ye@Sun.COM 		}
3377532SSean.Ye@Sun.COM 		kmem_free(buf, sz);
3387532SSean.Ye@Sun.COM 		fid.fid_outsz = sz;
3397532SSean.Ye@Sun.COM 
3407532SSean.Ye@Sun.COM 		switch (model) {
3417532SSean.Ye@Sun.COM #ifdef _MULTI_DATAMODEL
3427532SSean.Ye@Sun.COM 		case DDI_MODEL_ILP32:
3437532SSean.Ye@Sun.COM 			fid32.fid_outsz = (size32_t)fid.fid_outsz;
3447532SSean.Ye@Sun.COM 			if (ddi_copyout(&fid32, (void *)data,
3457532SSean.Ye@Sun.COM 			    sizeof (fm_ioc_data32_t), flag) != 0)
3467532SSean.Ye@Sun.COM 				return (EFAULT);
3477532SSean.Ye@Sun.COM 			break;
3487532SSean.Ye@Sun.COM #endif /* _MULTI_DATAMODEL */
3497532SSean.Ye@Sun.COM 		case DDI_MODEL_NONE:
3507532SSean.Ye@Sun.COM 		default:
3517532SSean.Ye@Sun.COM 			if (ddi_copyout(&fid, (void *)data,
3527532SSean.Ye@Sun.COM 			    sizeof (fm_ioc_data_t), flag) != 0)
3537532SSean.Ye@Sun.COM 				return (EFAULT);
3547532SSean.Ye@Sun.COM 		}
3557532SSean.Ye@Sun.COM 	}
3567532SSean.Ye@Sun.COM 
3577532SSean.Ye@Sun.COM 	return (err);
3587532SSean.Ye@Sun.COM }
3597532SSean.Ye@Sun.COM 
3607532SSean.Ye@Sun.COM static struct cb_ops fm_cb_ops = {
3617532SSean.Ye@Sun.COM 	fm_open,		/* open */
3627532SSean.Ye@Sun.COM 	nulldev,		/* close */
3637532SSean.Ye@Sun.COM 	nodev,			/* strategy */
3647532SSean.Ye@Sun.COM 	nodev,			/* print */
3657532SSean.Ye@Sun.COM 	nodev,			/* dump */
3667532SSean.Ye@Sun.COM 	nodev,			/* read */
3677532SSean.Ye@Sun.COM 	nodev,			/* write */
3687532SSean.Ye@Sun.COM 	fm_ioctl,		/* ioctl */
3697532SSean.Ye@Sun.COM 	nodev,			/* devmap */
3707532SSean.Ye@Sun.COM 	nodev,			/* mmap */
3717532SSean.Ye@Sun.COM 	nodev,			/* segmap */
3727532SSean.Ye@Sun.COM 	nochpoll,		/* poll */
3737532SSean.Ye@Sun.COM 	ddi_prop_op,		/* prop_op */
3747532SSean.Ye@Sun.COM 	NULL,			/* streamtab  */
3757532SSean.Ye@Sun.COM 	D_NEW | D_MP | D_64BIT | D_U64BIT
3767532SSean.Ye@Sun.COM };
3777532SSean.Ye@Sun.COM 
3787532SSean.Ye@Sun.COM static struct dev_ops fm_ops = {
3797532SSean.Ye@Sun.COM 	DEVO_REV,		/* devo_rev, */
3807532SSean.Ye@Sun.COM 	0,			/* refcnt  */
3817532SSean.Ye@Sun.COM 	fm_info,		/* get_dev_info */
3827532SSean.Ye@Sun.COM 	nulldev,		/* identify */
3837532SSean.Ye@Sun.COM 	nulldev,		/* probe */
3847532SSean.Ye@Sun.COM 	fm_attach,		/* attach */
3857532SSean.Ye@Sun.COM 	fm_detach,		/* detach */
3867532SSean.Ye@Sun.COM 	nodev,			/* reset */
3877532SSean.Ye@Sun.COM 	&fm_cb_ops,		/* driver operations */
3887532SSean.Ye@Sun.COM 	(struct bus_ops *)0	/* bus operations */
3897532SSean.Ye@Sun.COM };
3907532SSean.Ye@Sun.COM 
3917532SSean.Ye@Sun.COM static struct modldrv modldrv = {
3927532SSean.Ye@Sun.COM 	&mod_driverops, "fault management driver", &fm_ops,
3937532SSean.Ye@Sun.COM };
3947532SSean.Ye@Sun.COM 
3957532SSean.Ye@Sun.COM static struct modlinkage modlinkage = {
3967532SSean.Ye@Sun.COM 	MODREV_1, &modldrv, NULL
3977532SSean.Ye@Sun.COM };
3987532SSean.Ye@Sun.COM 
3997532SSean.Ye@Sun.COM int
_init(void)4007532SSean.Ye@Sun.COM _init(void)
4017532SSean.Ye@Sun.COM {
4027532SSean.Ye@Sun.COM 	const fm_vers_t *p;
4037532SSean.Ye@Sun.COM 	int ret;
4047532SSean.Ye@Sun.COM 
4057532SSean.Ye@Sun.COM 
4067532SSean.Ye@Sun.COM 	if ((ret = mod_install(&modlinkage)) == 0) {
4077532SSean.Ye@Sun.COM 		(void) nvlist_alloc(&fm_vers_nvl, NV_UNIQUE_NAME, KM_SLEEP);
4087532SSean.Ye@Sun.COM 		for (p = fm_versions; p->interface != NULL; p++)
4097532SSean.Ye@Sun.COM 			(void) nvlist_add_uint32(fm_vers_nvl, p->interface,
4107532SSean.Ye@Sun.COM 			    p->version);
4117532SSean.Ye@Sun.COM 	}
4127532SSean.Ye@Sun.COM 
4137532SSean.Ye@Sun.COM 	return (ret);
4147532SSean.Ye@Sun.COM }
4157532SSean.Ye@Sun.COM 
4167532SSean.Ye@Sun.COM int
_info(struct modinfo * modinfop)4177532SSean.Ye@Sun.COM _info(struct modinfo *modinfop)
4187532SSean.Ye@Sun.COM {
4197532SSean.Ye@Sun.COM 	return (mod_info(&modlinkage, modinfop));
4207532SSean.Ye@Sun.COM }
4217532SSean.Ye@Sun.COM 
4227532SSean.Ye@Sun.COM int
_fini(void)4237532SSean.Ye@Sun.COM _fini(void)
4247532SSean.Ye@Sun.COM {
4257532SSean.Ye@Sun.COM 	int ret;
4267532SSean.Ye@Sun.COM 
4277532SSean.Ye@Sun.COM 	if ((ret = mod_remove(&modlinkage)) == 0) {
4287532SSean.Ye@Sun.COM 		if (fm_vers_nvl != NULL)
4297532SSean.Ye@Sun.COM 			nvlist_free(fm_vers_nvl);
4307532SSean.Ye@Sun.COM 	}
4317532SSean.Ye@Sun.COM 
4327532SSean.Ye@Sun.COM 	return (ret);
4337532SSean.Ye@Sun.COM }
434