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