xref: /titanic_52/usr/src/uts/common/io/devfm.c (revision e4b86885570d77af552e9cf94f142f4d744fb8c8)
1*e4b86885SCheng Sean Ye /*
2*e4b86885SCheng Sean Ye  * CDDL HEADER START
3*e4b86885SCheng Sean Ye  *
4*e4b86885SCheng Sean Ye  * The contents of this file are subject to the terms of the
5*e4b86885SCheng Sean Ye  * Common Development and Distribution License (the "License").
6*e4b86885SCheng Sean Ye  * You may not use this file except in compliance with the License.
7*e4b86885SCheng Sean Ye  *
8*e4b86885SCheng Sean Ye  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*e4b86885SCheng Sean Ye  * or http://www.opensolaris.org/os/licensing.
10*e4b86885SCheng Sean Ye  * See the License for the specific language governing permissions
11*e4b86885SCheng Sean Ye  * and limitations under the License.
12*e4b86885SCheng Sean Ye  *
13*e4b86885SCheng Sean Ye  * When distributing Covered Code, include this CDDL HEADER in each
14*e4b86885SCheng Sean Ye  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*e4b86885SCheng Sean Ye  * If applicable, add the following below this CDDL HEADER, with the
16*e4b86885SCheng Sean Ye  * fields enclosed by brackets "[]" replaced with your own identifying
17*e4b86885SCheng Sean Ye  * information: Portions Copyright [yyyy] [name of copyright owner]
18*e4b86885SCheng Sean Ye  *
19*e4b86885SCheng Sean Ye  * CDDL HEADER END
20*e4b86885SCheng Sean Ye  */
21*e4b86885SCheng Sean Ye /*
22*e4b86885SCheng Sean Ye  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*e4b86885SCheng Sean Ye  * Use is subject to license terms.
24*e4b86885SCheng Sean Ye  */
25*e4b86885SCheng Sean Ye 
26*e4b86885SCheng Sean Ye #include <sys/stat.h>
27*e4b86885SCheng Sean Ye #include <sys/types.h>
28*e4b86885SCheng Sean Ye #include <sys/param.h>
29*e4b86885SCheng Sean Ye #include <sys/cred.h>
30*e4b86885SCheng Sean Ye #include <sys/policy.h>
31*e4b86885SCheng Sean Ye #include <sys/file.h>
32*e4b86885SCheng Sean Ye #include <sys/errno.h>
33*e4b86885SCheng Sean Ye #include <sys/modctl.h>
34*e4b86885SCheng Sean Ye #include <sys/ddi.h>
35*e4b86885SCheng Sean Ye #include <sys/sunddi.h>
36*e4b86885SCheng Sean Ye #include <sys/conf.h>
37*e4b86885SCheng Sean Ye #include <sys/debug.h>
38*e4b86885SCheng Sean Ye #include <sys/systeminfo.h>
39*e4b86885SCheng Sean Ye 
40*e4b86885SCheng Sean Ye #include <sys/fm/protocol.h>
41*e4b86885SCheng Sean Ye #include <sys/devfm.h>
42*e4b86885SCheng Sean Ye 
43*e4b86885SCheng Sean Ye extern int fm_get_paddr(nvlist_t *, uint64_t *);
44*e4b86885SCheng Sean Ye #if defined(__x86)
45*e4b86885SCheng Sean Ye extern int fm_ioctl_physcpu_info(int, nvlist_t *, nvlist_t **);
46*e4b86885SCheng Sean Ye extern int fm_ioctl_cpu_retire(int, nvlist_t *, nvlist_t **);
47*e4b86885SCheng Sean Ye #endif /* __x86 */
48*e4b86885SCheng Sean Ye 
49*e4b86885SCheng Sean Ye static int fm_ioctl_versions(int, nvlist_t *, nvlist_t **);
50*e4b86885SCheng Sean Ye static int fm_ioctl_page_retire(int, nvlist_t *, nvlist_t **);
51*e4b86885SCheng Sean Ye 
52*e4b86885SCheng Sean Ye /*
53*e4b86885SCheng Sean Ye  * The driver's capabilities are strictly versioned, allowing userland patching
54*e4b86885SCheng Sean Ye  * without a reboot.  The userland should start with a FM_VERSIONS ioctl to
55*e4b86885SCheng Sean Ye  * query the versions of the kernel interfaces, then it's all userland's
56*e4b86885SCheng Sean Ye  * responsibility to prepare arguments etc to match the current kenrel.
57*e4b86885SCheng Sean Ye  * The version of FM_VERSIONS itself is FM_DRV_VERSION.
58*e4b86885SCheng Sean Ye  */
59*e4b86885SCheng Sean Ye typedef struct fm_version {
60*e4b86885SCheng Sean Ye 	char		*interface;	/* interface name */
61*e4b86885SCheng Sean Ye 	uint32_t	version;	/* interface version */
62*e4b86885SCheng Sean Ye } fm_vers_t;
63*e4b86885SCheng Sean Ye 
64*e4b86885SCheng Sean Ye typedef struct fm_subroutine {
65*e4b86885SCheng Sean Ye 	int		cmd;		/* ioctl cmd */
66*e4b86885SCheng Sean Ye 	boolean_t	priv;		/* require privilege */
67*e4b86885SCheng Sean Ye 	char		*version;	/* version name */
68*e4b86885SCheng Sean Ye 	int		(*func)(int, nvlist_t *, nvlist_t **);	/* handler */
69*e4b86885SCheng Sean Ye } fm_subr_t;
70*e4b86885SCheng Sean Ye 
71*e4b86885SCheng Sean Ye static const fm_vers_t fm_versions[] = {
72*e4b86885SCheng Sean Ye 	{ FM_VERSIONS_VERSION, FM_DRV_VERSION },
73*e4b86885SCheng Sean Ye 	{ FM_PAGE_OP_VERSION, 1 },
74*e4b86885SCheng Sean Ye 	{ FM_CPU_OP_VERSION, 1 },
75*e4b86885SCheng Sean Ye 	{ FM_CPU_INFO_VERSION, 1 },
76*e4b86885SCheng Sean Ye 	{ NULL, 0 }
77*e4b86885SCheng Sean Ye };
78*e4b86885SCheng Sean Ye 
79*e4b86885SCheng Sean Ye static const fm_subr_t fm_subrs[] = {
80*e4b86885SCheng Sean Ye 	{ FM_IOC_VERSIONS, B_FALSE, FM_VERSIONS_VERSION, fm_ioctl_versions },
81*e4b86885SCheng Sean Ye 	{ FM_IOC_PAGE_RETIRE, B_TRUE, FM_PAGE_OP_VERSION,
82*e4b86885SCheng Sean Ye 	    fm_ioctl_page_retire },
83*e4b86885SCheng Sean Ye 	{ FM_IOC_PAGE_STATUS, B_FALSE, FM_PAGE_OP_VERSION,
84*e4b86885SCheng Sean Ye 	    fm_ioctl_page_retire },
85*e4b86885SCheng Sean Ye 	{ FM_IOC_PAGE_UNRETIRE, B_TRUE, FM_PAGE_OP_VERSION,
86*e4b86885SCheng Sean Ye 	    fm_ioctl_page_retire },
87*e4b86885SCheng Sean Ye #if defined(__x86)
88*e4b86885SCheng Sean Ye 	{ FM_IOC_PHYSCPU_INFO, B_FALSE, FM_CPU_INFO_VERSION,
89*e4b86885SCheng Sean Ye 	    fm_ioctl_physcpu_info },
90*e4b86885SCheng Sean Ye 	{ FM_IOC_CPU_RETIRE, B_TRUE, FM_CPU_OP_VERSION,
91*e4b86885SCheng Sean Ye 	    fm_ioctl_cpu_retire },
92*e4b86885SCheng Sean Ye 	{ FM_IOC_CPU_STATUS, B_FALSE, FM_CPU_OP_VERSION,
93*e4b86885SCheng Sean Ye 	    fm_ioctl_cpu_retire },
94*e4b86885SCheng Sean Ye 	{ FM_IOC_CPU_UNRETIRE, B_TRUE, FM_CPU_OP_VERSION,
95*e4b86885SCheng Sean Ye 	    fm_ioctl_cpu_retire },
96*e4b86885SCheng Sean Ye #endif	/* __x86 */
97*e4b86885SCheng Sean Ye 	{ -1, B_FALSE, NULL, NULL },
98*e4b86885SCheng Sean Ye };
99*e4b86885SCheng Sean Ye 
100*e4b86885SCheng Sean Ye static dev_info_t *fm_dip;
101*e4b86885SCheng Sean Ye static boolean_t is_i86xpv;
102*e4b86885SCheng Sean Ye static nvlist_t *fm_vers_nvl;
103*e4b86885SCheng Sean Ye 
104*e4b86885SCheng Sean Ye static int
105*e4b86885SCheng Sean Ye fm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
106*e4b86885SCheng Sean Ye {
107*e4b86885SCheng Sean Ye 	switch (cmd) {
108*e4b86885SCheng Sean Ye 	case DDI_ATTACH:
109*e4b86885SCheng Sean Ye 		if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR,
110*e4b86885SCheng Sean Ye 		    ddi_get_instance(dip), DDI_PSEUDO, 0) != DDI_SUCCESS) {
111*e4b86885SCheng Sean Ye 			ddi_remove_minor_node(dip, NULL);
112*e4b86885SCheng Sean Ye 			return (DDI_FAILURE);
113*e4b86885SCheng Sean Ye 		}
114*e4b86885SCheng Sean Ye 		fm_dip = dip;
115*e4b86885SCheng Sean Ye 		is_i86xpv = (strcmp(platform, "i86xpv") == 0);
116*e4b86885SCheng Sean Ye 		break;
117*e4b86885SCheng Sean Ye 	case DDI_RESUME:
118*e4b86885SCheng Sean Ye 		break;
119*e4b86885SCheng Sean Ye 	default:
120*e4b86885SCheng Sean Ye 		return (DDI_FAILURE);
121*e4b86885SCheng Sean Ye 	}
122*e4b86885SCheng Sean Ye 	return (DDI_SUCCESS);
123*e4b86885SCheng Sean Ye }
124*e4b86885SCheng Sean Ye 
125*e4b86885SCheng Sean Ye static int
126*e4b86885SCheng Sean Ye fm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
127*e4b86885SCheng Sean Ye {
128*e4b86885SCheng Sean Ye 	int ret = DDI_SUCCESS;
129*e4b86885SCheng Sean Ye 
130*e4b86885SCheng Sean Ye 	switch (cmd) {
131*e4b86885SCheng Sean Ye 	case DDI_DETACH:
132*e4b86885SCheng Sean Ye 		ddi_remove_minor_node(dip, NULL);
133*e4b86885SCheng Sean Ye 		fm_dip = NULL;
134*e4b86885SCheng Sean Ye 		break;
135*e4b86885SCheng Sean Ye 	default:
136*e4b86885SCheng Sean Ye 		ret = DDI_FAILURE;
137*e4b86885SCheng Sean Ye 	}
138*e4b86885SCheng Sean Ye 	return (ret);
139*e4b86885SCheng Sean Ye }
140*e4b86885SCheng Sean Ye 
141*e4b86885SCheng Sean Ye /*ARGSUSED*/
142*e4b86885SCheng Sean Ye static int
143*e4b86885SCheng Sean Ye fm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
144*e4b86885SCheng Sean Ye {
145*e4b86885SCheng Sean Ye 	int error;
146*e4b86885SCheng Sean Ye 
147*e4b86885SCheng Sean Ye 	switch (infocmd) {
148*e4b86885SCheng Sean Ye 	case DDI_INFO_DEVT2DEVINFO:
149*e4b86885SCheng Sean Ye 		*result = fm_dip;
150*e4b86885SCheng Sean Ye 		error = DDI_SUCCESS;
151*e4b86885SCheng Sean Ye 		break;
152*e4b86885SCheng Sean Ye 	case DDI_INFO_DEVT2INSTANCE:
153*e4b86885SCheng Sean Ye 		*result = NULL;
154*e4b86885SCheng Sean Ye 		error = DDI_SUCCESS;
155*e4b86885SCheng Sean Ye 		break;
156*e4b86885SCheng Sean Ye 	default:
157*e4b86885SCheng Sean Ye 		error = DDI_FAILURE;
158*e4b86885SCheng Sean Ye 	}
159*e4b86885SCheng Sean Ye 	return (error);
160*e4b86885SCheng Sean Ye }
161*e4b86885SCheng Sean Ye 
162*e4b86885SCheng Sean Ye /*ARGSUSED1*/
163*e4b86885SCheng Sean Ye static int
164*e4b86885SCheng Sean Ye fm_open(dev_t *devp, int flag, int typ, struct cred *cred)
165*e4b86885SCheng Sean Ye {
166*e4b86885SCheng Sean Ye 	if (typ != OTYP_CHR)
167*e4b86885SCheng Sean Ye 		return (EINVAL);
168*e4b86885SCheng Sean Ye 	if (getminor(*devp) != 0)
169*e4b86885SCheng Sean Ye 		return (ENXIO);
170*e4b86885SCheng Sean Ye 
171*e4b86885SCheng Sean Ye 	return (0);
172*e4b86885SCheng Sean Ye }
173*e4b86885SCheng Sean Ye 
174*e4b86885SCheng Sean Ye /*ARGSUSED*/
175*e4b86885SCheng Sean Ye static int
176*e4b86885SCheng Sean Ye fm_ioctl_versions(int cmd, nvlist_t *invl, nvlist_t **onvlp)
177*e4b86885SCheng Sean Ye {
178*e4b86885SCheng Sean Ye 	nvlist_t *nvl;
179*e4b86885SCheng Sean Ye 	int err;
180*e4b86885SCheng Sean Ye 
181*e4b86885SCheng Sean Ye 	if ((err = nvlist_dup(fm_vers_nvl, &nvl, KM_SLEEP)) == 0)
182*e4b86885SCheng Sean Ye 		*onvlp = nvl;
183*e4b86885SCheng Sean Ye 
184*e4b86885SCheng Sean Ye 	return (err);
185*e4b86885SCheng Sean Ye }
186*e4b86885SCheng Sean Ye 
187*e4b86885SCheng Sean Ye /*
188*e4b86885SCheng Sean Ye  * Given a mem-scheme FMRI for a page, execute the given page retire
189*e4b86885SCheng Sean Ye  * command on it.
190*e4b86885SCheng Sean Ye  */
191*e4b86885SCheng Sean Ye /*ARGSUSED*/
192*e4b86885SCheng Sean Ye static int
193*e4b86885SCheng Sean Ye fm_ioctl_page_retire(int cmd, nvlist_t *invl, nvlist_t **onvlp)
194*e4b86885SCheng Sean Ye {
195*e4b86885SCheng Sean Ye 	uint64_t pa;
196*e4b86885SCheng Sean Ye 	nvlist_t *fmri;
197*e4b86885SCheng Sean Ye 	int err;
198*e4b86885SCheng Sean Ye 
199*e4b86885SCheng Sean Ye 	if (is_i86xpv)
200*e4b86885SCheng Sean Ye 		return (ENOTSUP);
201*e4b86885SCheng Sean Ye 
202*e4b86885SCheng Sean Ye 	if ((err = nvlist_lookup_nvlist(invl, FM_PAGE_RETIRE_FMRI, &fmri))
203*e4b86885SCheng Sean Ye 	    != 0)
204*e4b86885SCheng Sean Ye 		return (err);
205*e4b86885SCheng Sean Ye 
206*e4b86885SCheng Sean Ye 	if ((err = fm_get_paddr(fmri, &pa)) != 0)
207*e4b86885SCheng Sean Ye 		return (err);
208*e4b86885SCheng Sean Ye 
209*e4b86885SCheng Sean Ye 	switch (cmd) {
210*e4b86885SCheng Sean Ye 	case FM_IOC_PAGE_STATUS:
211*e4b86885SCheng Sean Ye 		return (page_retire_check(pa, NULL));
212*e4b86885SCheng Sean Ye 
213*e4b86885SCheng Sean Ye 	case FM_IOC_PAGE_RETIRE:
214*e4b86885SCheng Sean Ye 		return (page_retire(pa, PR_FMA));
215*e4b86885SCheng Sean Ye 
216*e4b86885SCheng Sean Ye 	case FM_IOC_PAGE_UNRETIRE:
217*e4b86885SCheng Sean Ye 		return (page_unretire(pa));
218*e4b86885SCheng Sean Ye 	}
219*e4b86885SCheng Sean Ye 
220*e4b86885SCheng Sean Ye 	return (ENOTTY);
221*e4b86885SCheng Sean Ye }
222*e4b86885SCheng Sean Ye 
223*e4b86885SCheng Sean Ye /*ARGSUSED*/
224*e4b86885SCheng Sean Ye static int
225*e4b86885SCheng Sean Ye fm_ioctl(dev_t dev, int cmd, intptr_t data, int flag, cred_t *cred, int *rvalp)
226*e4b86885SCheng Sean Ye {
227*e4b86885SCheng Sean Ye 	char *buf;
228*e4b86885SCheng Sean Ye 	int err;
229*e4b86885SCheng Sean Ye 	uint_t model;
230*e4b86885SCheng Sean Ye 	const fm_subr_t *subr;
231*e4b86885SCheng Sean Ye 	uint32_t vers;
232*e4b86885SCheng Sean Ye 	fm_ioc_data_t fid;
233*e4b86885SCheng Sean Ye 	nvlist_t *invl = NULL, *onvl = NULL;
234*e4b86885SCheng Sean Ye #ifdef _MULTI_DATAMODEL
235*e4b86885SCheng Sean Ye 	fm_ioc_data32_t fid32;
236*e4b86885SCheng Sean Ye #endif
237*e4b86885SCheng Sean Ye 
238*e4b86885SCheng Sean Ye 	if (getminor(dev) != 0)
239*e4b86885SCheng Sean Ye 		return (ENXIO);
240*e4b86885SCheng Sean Ye 
241*e4b86885SCheng Sean Ye 	for (subr = fm_subrs; subr->cmd != cmd; subr++)
242*e4b86885SCheng Sean Ye 		if (subr->cmd == -1)
243*e4b86885SCheng Sean Ye 			return (ENOTTY);
244*e4b86885SCheng Sean Ye 
245*e4b86885SCheng Sean Ye 	if (subr->priv && (flag & FWRITE) == 0 &&
246*e4b86885SCheng Sean Ye 	    secpolicy_sys_config(CRED(), 0) != 0)
247*e4b86885SCheng Sean Ye 		return (EPERM);
248*e4b86885SCheng Sean Ye 
249*e4b86885SCheng Sean Ye 	model = ddi_model_convert_from(flag & FMODELS);
250*e4b86885SCheng Sean Ye 
251*e4b86885SCheng Sean Ye 	switch (model) {
252*e4b86885SCheng Sean Ye #ifdef _MULTI_DATAMODEL
253*e4b86885SCheng Sean Ye 	case DDI_MODEL_ILP32:
254*e4b86885SCheng Sean Ye 		if (ddi_copyin((void *)data, &fid32,
255*e4b86885SCheng Sean Ye 		    sizeof (fm_ioc_data32_t), flag) != 0)
256*e4b86885SCheng Sean Ye 			return (EFAULT);
257*e4b86885SCheng Sean Ye 		fid.fid_version = fid32.fid_version;
258*e4b86885SCheng Sean Ye 		fid.fid_insz = fid32.fid_insz;
259*e4b86885SCheng Sean Ye 		fid.fid_inbuf = (caddr_t)(uintptr_t)fid32.fid_inbuf;
260*e4b86885SCheng Sean Ye 		fid.fid_outsz = fid32.fid_outsz;
261*e4b86885SCheng Sean Ye 		fid.fid_outbuf = (caddr_t)(uintptr_t)fid32.fid_outbuf;
262*e4b86885SCheng Sean Ye 		break;
263*e4b86885SCheng Sean Ye #endif /* _MULTI_DATAMODEL */
264*e4b86885SCheng Sean Ye 	case DDI_MODEL_NONE:
265*e4b86885SCheng Sean Ye 	default:
266*e4b86885SCheng Sean Ye 		if (ddi_copyin((void *)data, &fid, sizeof (fm_ioc_data_t),
267*e4b86885SCheng Sean Ye 		    flag) != 0)
268*e4b86885SCheng Sean Ye 			return (EFAULT);
269*e4b86885SCheng Sean Ye 	}
270*e4b86885SCheng Sean Ye 
271*e4b86885SCheng Sean Ye 	if (nvlist_lookup_uint32(fm_vers_nvl, subr->version, &vers) != 0 ||
272*e4b86885SCheng Sean Ye 	    fid.fid_version != vers)
273*e4b86885SCheng Sean Ye 		return (ENOTSUP);
274*e4b86885SCheng Sean Ye 
275*e4b86885SCheng Sean Ye 	if (fid.fid_insz > FM_IOC_MAXBUFSZ)
276*e4b86885SCheng Sean Ye 		return (ENAMETOOLONG);
277*e4b86885SCheng Sean Ye 	if (fid.fid_outsz > FM_IOC_MAXBUFSZ)
278*e4b86885SCheng Sean Ye 		return (EINVAL);
279*e4b86885SCheng Sean Ye 
280*e4b86885SCheng Sean Ye 	/*
281*e4b86885SCheng Sean Ye 	 * Copy in and unpack the input nvlist.
282*e4b86885SCheng Sean Ye 	 */
283*e4b86885SCheng Sean Ye 	if (fid.fid_insz != 0 && fid.fid_inbuf != (caddr_t)0) {
284*e4b86885SCheng Sean Ye 		buf = kmem_alloc(fid.fid_insz, KM_SLEEP);
285*e4b86885SCheng Sean Ye 		if (ddi_copyin(fid.fid_inbuf, buf, fid.fid_insz, flag) != 0) {
286*e4b86885SCheng Sean Ye 			kmem_free(buf, fid.fid_insz);
287*e4b86885SCheng Sean Ye 			return (EFAULT);
288*e4b86885SCheng Sean Ye 		}
289*e4b86885SCheng Sean Ye 		err = nvlist_unpack(buf, fid.fid_insz, &invl, KM_SLEEP);
290*e4b86885SCheng Sean Ye 		kmem_free(buf, fid.fid_insz);
291*e4b86885SCheng Sean Ye 		if (err != 0)
292*e4b86885SCheng Sean Ye 			return (err);
293*e4b86885SCheng Sean Ye 	}
294*e4b86885SCheng Sean Ye 
295*e4b86885SCheng Sean Ye 	err = subr->func(cmd, invl, &onvl);
296*e4b86885SCheng Sean Ye 
297*e4b86885SCheng Sean Ye 	if (invl != NULL)
298*e4b86885SCheng Sean Ye 		nvlist_free(invl);
299*e4b86885SCheng Sean Ye 
300*e4b86885SCheng Sean Ye 	if (err != 0) {
301*e4b86885SCheng Sean Ye 		if (onvl != NULL)
302*e4b86885SCheng Sean Ye 			nvlist_free(onvl);
303*e4b86885SCheng Sean Ye 		return (err);
304*e4b86885SCheng Sean Ye 	}
305*e4b86885SCheng Sean Ye 
306*e4b86885SCheng Sean Ye 	/*
307*e4b86885SCheng Sean Ye 	 * If the output nvlist contains any data, pack it and copyout.
308*e4b86885SCheng Sean Ye 	 */
309*e4b86885SCheng Sean Ye 	if (onvl != NULL) {
310*e4b86885SCheng Sean Ye 		size_t sz;
311*e4b86885SCheng Sean Ye 
312*e4b86885SCheng Sean Ye 		if ((err = nvlist_size(onvl, &sz, NV_ENCODE_NATIVE)) != 0) {
313*e4b86885SCheng Sean Ye 			nvlist_free(onvl);
314*e4b86885SCheng Sean Ye 			return (err);
315*e4b86885SCheng Sean Ye 		}
316*e4b86885SCheng Sean Ye 		if (sz > fid.fid_outsz) {
317*e4b86885SCheng Sean Ye 			nvlist_free(onvl);
318*e4b86885SCheng Sean Ye 			return (ENAMETOOLONG);
319*e4b86885SCheng Sean Ye 		}
320*e4b86885SCheng Sean Ye 
321*e4b86885SCheng Sean Ye 		buf = kmem_alloc(sz, KM_SLEEP);
322*e4b86885SCheng Sean Ye 		if ((err = nvlist_pack(onvl, &buf, &sz, NV_ENCODE_NATIVE,
323*e4b86885SCheng Sean Ye 		    KM_SLEEP)) != 0) {
324*e4b86885SCheng Sean Ye 			kmem_free(buf, sz);
325*e4b86885SCheng Sean Ye 			nvlist_free(onvl);
326*e4b86885SCheng Sean Ye 			return (err);
327*e4b86885SCheng Sean Ye 		}
328*e4b86885SCheng Sean Ye 		nvlist_free(onvl);
329*e4b86885SCheng Sean Ye 		if (ddi_copyout(buf, fid.fid_outbuf, sz, flag) != 0) {
330*e4b86885SCheng Sean Ye 			kmem_free(buf, sz);
331*e4b86885SCheng Sean Ye 			return (EFAULT);
332*e4b86885SCheng Sean Ye 		}
333*e4b86885SCheng Sean Ye 		kmem_free(buf, sz);
334*e4b86885SCheng Sean Ye 		fid.fid_outsz = sz;
335*e4b86885SCheng Sean Ye 
336*e4b86885SCheng Sean Ye 		switch (model) {
337*e4b86885SCheng Sean Ye #ifdef _MULTI_DATAMODEL
338*e4b86885SCheng Sean Ye 		case DDI_MODEL_ILP32:
339*e4b86885SCheng Sean Ye 			fid32.fid_outsz = (size32_t)fid.fid_outsz;
340*e4b86885SCheng Sean Ye 			if (ddi_copyout(&fid32, (void *)data,
341*e4b86885SCheng Sean Ye 			    sizeof (fm_ioc_data32_t), flag) != 0)
342*e4b86885SCheng Sean Ye 				return (EFAULT);
343*e4b86885SCheng Sean Ye 			break;
344*e4b86885SCheng Sean Ye #endif /* _MULTI_DATAMODEL */
345*e4b86885SCheng Sean Ye 		case DDI_MODEL_NONE:
346*e4b86885SCheng Sean Ye 		default:
347*e4b86885SCheng Sean Ye 			if (ddi_copyout(&fid, (void *)data,
348*e4b86885SCheng Sean Ye 			    sizeof (fm_ioc_data_t), flag) != 0)
349*e4b86885SCheng Sean Ye 				return (EFAULT);
350*e4b86885SCheng Sean Ye 		}
351*e4b86885SCheng Sean Ye 	}
352*e4b86885SCheng Sean Ye 
353*e4b86885SCheng Sean Ye 	return (err);
354*e4b86885SCheng Sean Ye }
355*e4b86885SCheng Sean Ye 
356*e4b86885SCheng Sean Ye static struct cb_ops fm_cb_ops = {
357*e4b86885SCheng Sean Ye 	fm_open,		/* open */
358*e4b86885SCheng Sean Ye 	nulldev,		/* close */
359*e4b86885SCheng Sean Ye 	nodev,			/* strategy */
360*e4b86885SCheng Sean Ye 	nodev,			/* print */
361*e4b86885SCheng Sean Ye 	nodev,			/* dump */
362*e4b86885SCheng Sean Ye 	nodev,			/* read */
363*e4b86885SCheng Sean Ye 	nodev,			/* write */
364*e4b86885SCheng Sean Ye 	fm_ioctl,		/* ioctl */
365*e4b86885SCheng Sean Ye 	nodev,			/* devmap */
366*e4b86885SCheng Sean Ye 	nodev,			/* mmap */
367*e4b86885SCheng Sean Ye 	nodev,			/* segmap */
368*e4b86885SCheng Sean Ye 	nochpoll,		/* poll */
369*e4b86885SCheng Sean Ye 	ddi_prop_op,		/* prop_op */
370*e4b86885SCheng Sean Ye 	NULL,			/* streamtab  */
371*e4b86885SCheng Sean Ye 	D_NEW | D_MP | D_64BIT | D_U64BIT
372*e4b86885SCheng Sean Ye };
373*e4b86885SCheng Sean Ye 
374*e4b86885SCheng Sean Ye static struct dev_ops fm_ops = {
375*e4b86885SCheng Sean Ye 	DEVO_REV,		/* devo_rev, */
376*e4b86885SCheng Sean Ye 	0,			/* refcnt  */
377*e4b86885SCheng Sean Ye 	fm_info,		/* get_dev_info */
378*e4b86885SCheng Sean Ye 	nulldev,		/* identify */
379*e4b86885SCheng Sean Ye 	nulldev,		/* probe */
380*e4b86885SCheng Sean Ye 	fm_attach,		/* attach */
381*e4b86885SCheng Sean Ye 	fm_detach,		/* detach */
382*e4b86885SCheng Sean Ye 	nodev,			/* reset */
383*e4b86885SCheng Sean Ye 	&fm_cb_ops,		/* driver operations */
384*e4b86885SCheng Sean Ye 	(struct bus_ops *)0	/* bus operations */
385*e4b86885SCheng Sean Ye };
386*e4b86885SCheng Sean Ye 
387*e4b86885SCheng Sean Ye static struct modldrv modldrv = {
388*e4b86885SCheng Sean Ye 	&mod_driverops, "fault management driver", &fm_ops,
389*e4b86885SCheng Sean Ye };
390*e4b86885SCheng Sean Ye 
391*e4b86885SCheng Sean Ye static struct modlinkage modlinkage = {
392*e4b86885SCheng Sean Ye 	MODREV_1, &modldrv, NULL
393*e4b86885SCheng Sean Ye };
394*e4b86885SCheng Sean Ye 
395*e4b86885SCheng Sean Ye int
396*e4b86885SCheng Sean Ye _init(void)
397*e4b86885SCheng Sean Ye {
398*e4b86885SCheng Sean Ye 	const fm_vers_t *p;
399*e4b86885SCheng Sean Ye 	int ret;
400*e4b86885SCheng Sean Ye 
401*e4b86885SCheng Sean Ye 
402*e4b86885SCheng Sean Ye 	if ((ret = mod_install(&modlinkage)) == 0) {
403*e4b86885SCheng Sean Ye 		(void) nvlist_alloc(&fm_vers_nvl, NV_UNIQUE_NAME, KM_SLEEP);
404*e4b86885SCheng Sean Ye 		for (p = fm_versions; p->interface != NULL; p++)
405*e4b86885SCheng Sean Ye 			(void) nvlist_add_uint32(fm_vers_nvl, p->interface,
406*e4b86885SCheng Sean Ye 			    p->version);
407*e4b86885SCheng Sean Ye 	}
408*e4b86885SCheng Sean Ye 
409*e4b86885SCheng Sean Ye 	return (ret);
410*e4b86885SCheng Sean Ye }
411*e4b86885SCheng Sean Ye 
412*e4b86885SCheng Sean Ye int
413*e4b86885SCheng Sean Ye _info(struct modinfo *modinfop)
414*e4b86885SCheng Sean Ye {
415*e4b86885SCheng Sean Ye 	return (mod_info(&modlinkage, modinfop));
416*e4b86885SCheng Sean Ye }
417*e4b86885SCheng Sean Ye 
418*e4b86885SCheng Sean Ye int
419*e4b86885SCheng Sean Ye _fini(void)
420*e4b86885SCheng Sean Ye {
421*e4b86885SCheng Sean Ye 	int ret;
422*e4b86885SCheng Sean Ye 
423*e4b86885SCheng Sean Ye 	if ((ret = mod_remove(&modlinkage)) == 0) {
424*e4b86885SCheng Sean Ye 		if (fm_vers_nvl != NULL)
425*e4b86885SCheng Sean Ye 			nvlist_free(fm_vers_nvl);
426*e4b86885SCheng Sean Ye 	}
427*e4b86885SCheng Sean Ye 
428*e4b86885SCheng Sean Ye 	return (ret);
429*e4b86885SCheng Sean Ye }
430