xref: /illumos-gate/usr/src/uts/common/io/ufm.c (revision bbf21555)
1508a0e8cSRob Johnston /*
2508a0e8cSRob Johnston  * This file and its contents are supplied under the terms of the
3508a0e8cSRob Johnston  * Common Development and Distribution License ("CDDL"), version 1.0.
4508a0e8cSRob Johnston  * You may only use this file in accordance with the terms of version
5508a0e8cSRob Johnston  * 1.0 of the CDDL.
6508a0e8cSRob Johnston  *
7508a0e8cSRob Johnston  * A full copy of the text of the CDDL should have accompanied this
8508a0e8cSRob Johnston  * source.  A copy of the CDDL is also available via the Internet at
9508a0e8cSRob Johnston  * http://www.illumos.org/license/CDDL.
10508a0e8cSRob Johnston  */
11508a0e8cSRob Johnston 
12508a0e8cSRob Johnston /*
13508a0e8cSRob Johnston  * Copyright 2019 Joyent, Inc.
148d55b806SRobert Mustacchi  * Copyright 2020 Oxide Computer Company
15508a0e8cSRob Johnston  */
16508a0e8cSRob Johnston 
17508a0e8cSRob Johnston /*
18*bbf21555SRichard Lowe  * The ufm(4D) pseudo driver provides an ioctl interface for DDI UFM
19508a0e8cSRob Johnston  * information.  See ddi_ufm.h.
20508a0e8cSRob Johnston  */
21508a0e8cSRob Johnston #include <sys/ddi.h>
22508a0e8cSRob Johnston #include <sys/sunddi.h>
23508a0e8cSRob Johnston #include <sys/esunddi.h>
24508a0e8cSRob Johnston #include <sys/ddi_ufm.h>
25508a0e8cSRob Johnston #include <sys/ddi_ufm_impl.h>
26508a0e8cSRob Johnston #include <sys/conf.h>
27508a0e8cSRob Johnston #include <sys/debug.h>
28508a0e8cSRob Johnston #include <sys/file.h>
29508a0e8cSRob Johnston #include <sys/kmem.h>
30508a0e8cSRob Johnston #include <sys/stat.h>
318d55b806SRobert Mustacchi #include <sys/sysmacros.h>
328d55b806SRobert Mustacchi 
338d55b806SRobert Mustacchi #define	UFM_READ_SIZE		(1 * 1024 * 1024)
34508a0e8cSRob Johnston 
35508a0e8cSRob Johnston #define	UFMTEST_IOC		('u' << 24) | ('f' << 16) | ('t' << 8)
36508a0e8cSRob Johnston #define	UFMTEST_IOC_SETFW	(UFMTEST_IOC | 1)
37508a0e8cSRob Johnston 
38508a0e8cSRob Johnston static dev_info_t *ufm_devi = NULL;
39508a0e8cSRob Johnston 
40508a0e8cSRob Johnston static int ufm_open(dev_t *, int, int, cred_t *);
41508a0e8cSRob Johnston static int ufm_close(dev_t, int, int, cred_t *);
42508a0e8cSRob Johnston static int ufm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
43508a0e8cSRob Johnston 
44508a0e8cSRob Johnston static struct cb_ops ufm_cb_ops = {
45508a0e8cSRob Johnston 	.cb_open =	ufm_open,
46508a0e8cSRob Johnston 	.cb_close =	ufm_close,
47508a0e8cSRob Johnston 	.cb_strategy =	nodev,
48508a0e8cSRob Johnston 	.cb_print =	nodev,
49508a0e8cSRob Johnston 	.cb_dump =	nodev,
50508a0e8cSRob Johnston 	.cb_read =	nodev,
51508a0e8cSRob Johnston 	.cb_write =	nodev,
52508a0e8cSRob Johnston 	.cb_ioctl =	ufm_ioctl,
53508a0e8cSRob Johnston 	.cb_devmap =	nodev,
54508a0e8cSRob Johnston 	.cb_mmap =	nodev,
55508a0e8cSRob Johnston 	.cb_segmap =	nodev,
56508a0e8cSRob Johnston 	.cb_chpoll =	nochpoll,
57508a0e8cSRob Johnston 	.cb_prop_op =	ddi_prop_op,
58508a0e8cSRob Johnston 	.cb_str =	NULL,
59508a0e8cSRob Johnston 	.cb_flag =	D_NEW | D_MP,
60508a0e8cSRob Johnston 	.cb_rev =	CB_REV,
61508a0e8cSRob Johnston 	.cb_aread =	nodev,
62508a0e8cSRob Johnston 	.cb_awrite =	nodev
63508a0e8cSRob Johnston };
64508a0e8cSRob Johnston 
65508a0e8cSRob Johnston static int ufm_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
66508a0e8cSRob Johnston static int ufm_attach(dev_info_t *, ddi_attach_cmd_t);
67508a0e8cSRob Johnston static int ufm_detach(dev_info_t *, ddi_detach_cmd_t);
68508a0e8cSRob Johnston 
69508a0e8cSRob Johnston static struct dev_ops ufm_ops = {
70508a0e8cSRob Johnston 	.devo_rev =		DEVO_REV,
71508a0e8cSRob Johnston 	.devo_refcnt =		0,
72508a0e8cSRob Johnston 	.devo_getinfo =		ufm_info,
73508a0e8cSRob Johnston 	.devo_identify =	nulldev,
74508a0e8cSRob Johnston 	.devo_probe =		nulldev,
75508a0e8cSRob Johnston 	.devo_attach =		ufm_attach,
76508a0e8cSRob Johnston 	.devo_detach =		ufm_detach,
77508a0e8cSRob Johnston 	.devo_reset =		nodev,
78508a0e8cSRob Johnston 	.devo_cb_ops =		&ufm_cb_ops,
79508a0e8cSRob Johnston 	.devo_bus_ops =		NULL,
80508a0e8cSRob Johnston 	.devo_power =		NULL,
81508a0e8cSRob Johnston 	.devo_quiesce =		ddi_quiesce_not_needed
82508a0e8cSRob Johnston };
83508a0e8cSRob Johnston 
84508a0e8cSRob Johnston static struct modldrv modldrv = {
85508a0e8cSRob Johnston 	.drv_modops =		&mod_driverops,
86508a0e8cSRob Johnston 	.drv_linkinfo =		"Upgradeable FW Module driver",
87508a0e8cSRob Johnston 	.drv_dev_ops =		&ufm_ops
88508a0e8cSRob Johnston };
89508a0e8cSRob Johnston 
90508a0e8cSRob Johnston static struct modlinkage modlinkage = {
91508a0e8cSRob Johnston 	.ml_rev =		MODREV_1,
92508a0e8cSRob Johnston 	.ml_linkage =		{ (void *)&modldrv, NULL }
93508a0e8cSRob Johnston };
94508a0e8cSRob Johnston 
95508a0e8cSRob Johnston int
_init(void)96508a0e8cSRob Johnston _init(void)
97508a0e8cSRob Johnston {
98508a0e8cSRob Johnston 	return (mod_install(&modlinkage));
99508a0e8cSRob Johnston }
100508a0e8cSRob Johnston 
101508a0e8cSRob Johnston int
_fini(void)102508a0e8cSRob Johnston _fini(void)
103508a0e8cSRob Johnston {
104508a0e8cSRob Johnston 	return (mod_remove(&modlinkage));
105508a0e8cSRob Johnston }
106508a0e8cSRob Johnston 
107508a0e8cSRob Johnston int
_info(struct modinfo * modinfop)108508a0e8cSRob Johnston _info(struct modinfo *modinfop)
109508a0e8cSRob Johnston {
110508a0e8cSRob Johnston 	return (mod_info(&modlinkage, modinfop));
111508a0e8cSRob Johnston }
112508a0e8cSRob Johnston 
113508a0e8cSRob Johnston static int
ufm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)114508a0e8cSRob Johnston ufm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
115508a0e8cSRob Johnston {
116508a0e8cSRob Johnston 	switch (infocmd) {
117508a0e8cSRob Johnston 	case DDI_INFO_DEVT2DEVINFO:
118508a0e8cSRob Johnston 		*result = ufm_devi;
119508a0e8cSRob Johnston 		return (DDI_SUCCESS);
120508a0e8cSRob Johnston 	case DDI_INFO_DEVT2INSTANCE:
121508a0e8cSRob Johnston 		*result = 0;
122508a0e8cSRob Johnston 		return (DDI_SUCCESS);
123508a0e8cSRob Johnston 	}
124508a0e8cSRob Johnston 	return (DDI_FAILURE);
125508a0e8cSRob Johnston }
126508a0e8cSRob Johnston 
127508a0e8cSRob Johnston static int
ufm_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)128508a0e8cSRob Johnston ufm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
129508a0e8cSRob Johnston {
130508a0e8cSRob Johnston 	if (cmd != DDI_ATTACH || ufm_devi != NULL)
131508a0e8cSRob Johnston 		return (DDI_FAILURE);
132508a0e8cSRob Johnston 
133508a0e8cSRob Johnston 	if (ddi_create_minor_node(devi, "ufm", S_IFCHR, 0, DDI_PSEUDO, 0) ==
134508a0e8cSRob Johnston 	    DDI_FAILURE) {
135508a0e8cSRob Johnston 		ddi_remove_minor_node(devi, NULL);
136508a0e8cSRob Johnston 		return (DDI_FAILURE);
137508a0e8cSRob Johnston 	}
138508a0e8cSRob Johnston 
139508a0e8cSRob Johnston 	ufm_devi = devi;
140508a0e8cSRob Johnston 	return (DDI_SUCCESS);
141508a0e8cSRob Johnston }
142508a0e8cSRob Johnston 
143508a0e8cSRob Johnston static int
ufm_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)144508a0e8cSRob Johnston ufm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
145508a0e8cSRob Johnston {
146508a0e8cSRob Johnston 	if (cmd != DDI_DETACH)
147508a0e8cSRob Johnston 		return (DDI_FAILURE);
148508a0e8cSRob Johnston 
149508a0e8cSRob Johnston 	if (devi != NULL)
150508a0e8cSRob Johnston 		ddi_remove_minor_node(devi, NULL);
151508a0e8cSRob Johnston 
1528d55b806SRobert Mustacchi 	ufm_devi = NULL;
153508a0e8cSRob Johnston 	return (DDI_SUCCESS);
154508a0e8cSRob Johnston }
155508a0e8cSRob Johnston 
156508a0e8cSRob Johnston static int
ufm_open(dev_t * devp,int flag,int otyp,cred_t * credp)157508a0e8cSRob Johnston ufm_open(dev_t *devp, int flag, int otyp, cred_t *credp)
158508a0e8cSRob Johnston {
159508a0e8cSRob Johnston 	const int inv_flags = FWRITE | FEXCL | FNDELAY | FNONBLOCK;
160508a0e8cSRob Johnston 
161508a0e8cSRob Johnston 	if (otyp != OTYP_CHR)
162508a0e8cSRob Johnston 		return (EINVAL);
163508a0e8cSRob Johnston 
164508a0e8cSRob Johnston 	if (flag & inv_flags)
165508a0e8cSRob Johnston 		return (EINVAL);
166508a0e8cSRob Johnston 
167508a0e8cSRob Johnston 	if (drv_priv(credp) != 0)
168508a0e8cSRob Johnston 		return (EPERM);
169508a0e8cSRob Johnston 
170508a0e8cSRob Johnston 	return (0);
171508a0e8cSRob Johnston }
172508a0e8cSRob Johnston 
173508a0e8cSRob Johnston static int
ufm_close(dev_t dev,int flag,int otyp,cred_t * credp)174508a0e8cSRob Johnston ufm_close(dev_t dev, int flag, int otyp, cred_t *credp)
175508a0e8cSRob Johnston {
176508a0e8cSRob Johnston 	return (0);
177508a0e8cSRob Johnston }
178508a0e8cSRob Johnston 
179508a0e8cSRob Johnston static boolean_t
ufm_driver_ready(ddi_ufm_handle_t * ufmh)180508a0e8cSRob Johnston ufm_driver_ready(ddi_ufm_handle_t *ufmh)
181508a0e8cSRob Johnston {
182508a0e8cSRob Johnston 	VERIFY(ufmh != NULL);
183508a0e8cSRob Johnston 
184508a0e8cSRob Johnston 	if (ufmh->ufmh_state & DDI_UFM_STATE_SHUTTING_DOWN ||
185508a0e8cSRob Johnston 	    !(ufmh->ufmh_state & DDI_UFM_STATE_READY)) {
186508a0e8cSRob Johnston 		return (B_FALSE);
187508a0e8cSRob Johnston 	}
188508a0e8cSRob Johnston 	return (B_TRUE);
189508a0e8cSRob Johnston }
190508a0e8cSRob Johnston 
191508a0e8cSRob Johnston static int
ufm_do_getcaps(intptr_t data,int mode)192508a0e8cSRob Johnston ufm_do_getcaps(intptr_t data, int mode)
193508a0e8cSRob Johnston {
194508a0e8cSRob Johnston 	ddi_ufm_handle_t *ufmh;
195508a0e8cSRob Johnston 	ddi_ufm_cap_t caps;
196508a0e8cSRob Johnston 	ufm_ioc_getcaps_t ugc;
197508a0e8cSRob Johnston 	dev_info_t *dip;
198508a0e8cSRob Johnston 	int ret;
199508a0e8cSRob Johnston 	char devpath[MAXPATHLEN];
200508a0e8cSRob Johnston 
201508a0e8cSRob Johnston 	if (ddi_copyin((void *)data, &ugc, sizeof (ufm_ioc_getcaps_t),
202508a0e8cSRob Johnston 	    mode) != 0)
203508a0e8cSRob Johnston 		return (EFAULT);
204508a0e8cSRob Johnston 
205508a0e8cSRob Johnston 	if (strlcpy(devpath, ugc.ufmg_devpath, MAXPATHLEN) >= MAXPATHLEN)
206508a0e8cSRob Johnston 		return (EOVERFLOW);
207508a0e8cSRob Johnston 
208508a0e8cSRob Johnston 	if ((dip = e_ddi_hold_devi_by_path(devpath, 0)) == NULL) {
209508a0e8cSRob Johnston 		return (ENOTSUP);
210508a0e8cSRob Johnston 	}
211508a0e8cSRob Johnston 	if ((ufmh = ufm_find(devpath)) == NULL) {
212508a0e8cSRob Johnston 		ddi_release_devi(dip);
213508a0e8cSRob Johnston 		return (ENOTSUP);
214508a0e8cSRob Johnston 	}
215508a0e8cSRob Johnston 	ASSERT(MUTEX_HELD(&ufmh->ufmh_lock));
216508a0e8cSRob Johnston 
217508a0e8cSRob Johnston 	if (!ufm_driver_ready(ufmh)) {
218508a0e8cSRob Johnston 		ddi_release_devi(dip);
219508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
220508a0e8cSRob Johnston 		return (EAGAIN);
221508a0e8cSRob Johnston 	}
222508a0e8cSRob Johnston 
223508a0e8cSRob Johnston 	if (ugc.ufmg_version != ufmh->ufmh_version) {
224508a0e8cSRob Johnston 		ddi_release_devi(dip);
225508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
226508a0e8cSRob Johnston 		return (ENOTSUP);
227508a0e8cSRob Johnston 	}
228508a0e8cSRob Johnston 
229508a0e8cSRob Johnston 	if ((ret = ufm_cache_fill(ufmh)) != 0) {
230508a0e8cSRob Johnston 		ddi_release_devi(dip);
231508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
232508a0e8cSRob Johnston 		return (ret);
233508a0e8cSRob Johnston 	}
234508a0e8cSRob Johnston 
235508a0e8cSRob Johnston 	ret = ufmh->ufmh_ops->ddi_ufm_op_getcaps(ufmh, ufmh->ufmh_arg, &caps);
236508a0e8cSRob Johnston 	mutex_exit(&ufmh->ufmh_lock);
237508a0e8cSRob Johnston 	ddi_release_devi(dip);
238508a0e8cSRob Johnston 
239508a0e8cSRob Johnston 	if (ret != 0)
240508a0e8cSRob Johnston 		return (ret);
241508a0e8cSRob Johnston 
242508a0e8cSRob Johnston 	ugc.ufmg_caps = caps;
243508a0e8cSRob Johnston 
244508a0e8cSRob Johnston 	if (ddi_copyout(&ugc, (void *)data, sizeof (ufm_ioc_getcaps_t),
245508a0e8cSRob Johnston 	    mode) != 0)
246508a0e8cSRob Johnston 		return (EFAULT);
247508a0e8cSRob Johnston 
248508a0e8cSRob Johnston 	return (0);
249508a0e8cSRob Johnston }
250508a0e8cSRob Johnston 
251508a0e8cSRob Johnston static int
ufm_do_reportsz(intptr_t data,int mode)252508a0e8cSRob Johnston ufm_do_reportsz(intptr_t data, int mode)
253508a0e8cSRob Johnston {
254508a0e8cSRob Johnston 	ddi_ufm_handle_t *ufmh;
255508a0e8cSRob Johnston 	dev_info_t *dip;
256508a0e8cSRob Johnston 	uint_t model;
257508a0e8cSRob Johnston 	size_t sz;
258508a0e8cSRob Johnston 	int ret;
259508a0e8cSRob Johnston 	char devpath[MAXPATHLEN];
260508a0e8cSRob Johnston 	ufm_ioc_bufsz_t ufbz;
261508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
262508a0e8cSRob Johnston 	ufm_ioc_bufsz32_t ufbz32;
263508a0e8cSRob Johnston #endif
264508a0e8cSRob Johnston 
265508a0e8cSRob Johnston 	model = ddi_model_convert_from(mode);
266508a0e8cSRob Johnston 
267508a0e8cSRob Johnston 	switch (model) {
268508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
269508a0e8cSRob Johnston 	case DDI_MODEL_ILP32:
270508a0e8cSRob Johnston 		if (ddi_copyin((void *)data, &ufbz32,
271508a0e8cSRob Johnston 		    sizeof (ufm_ioc_bufsz32_t), mode) != 0)
272508a0e8cSRob Johnston 			return (EFAULT);
273508a0e8cSRob Johnston 		ufbz.ufbz_version = ufbz32.ufbz_version;
274508a0e8cSRob Johnston 		if (strlcpy(ufbz.ufbz_devpath, ufbz32.ufbz_devpath,
275508a0e8cSRob Johnston 		    MAXPATHLEN) >= MAXPATHLEN) {
276508a0e8cSRob Johnston 			return (EOVERFLOW);
277508a0e8cSRob Johnston 		}
278508a0e8cSRob Johnston 		break;
279508a0e8cSRob Johnston #endif /* _MULTI_DATAMODEL */
280508a0e8cSRob Johnston 	case DDI_MODEL_NONE:
281508a0e8cSRob Johnston 	default:
282508a0e8cSRob Johnston 		if (ddi_copyin((void *)data, &ufbz,
283508a0e8cSRob Johnston 		    sizeof (ufm_ioc_bufsz_t), mode) != 0)
284508a0e8cSRob Johnston 			return (EFAULT);
285508a0e8cSRob Johnston 	}
286508a0e8cSRob Johnston 
287508a0e8cSRob Johnston 	if (strlcpy(devpath, ufbz.ufbz_devpath, MAXPATHLEN) >= MAXPATHLEN)
288508a0e8cSRob Johnston 		return (EOVERFLOW);
289508a0e8cSRob Johnston 
290508a0e8cSRob Johnston 	if ((dip = e_ddi_hold_devi_by_path(devpath, 0)) == NULL) {
291508a0e8cSRob Johnston 		return (ENOTSUP);
292508a0e8cSRob Johnston 	}
293508a0e8cSRob Johnston 	if ((ufmh = ufm_find(devpath)) == NULL) {
294508a0e8cSRob Johnston 		ddi_release_devi(dip);
295508a0e8cSRob Johnston 		return (ENOTSUP);
296508a0e8cSRob Johnston 	}
297508a0e8cSRob Johnston 	ASSERT(MUTEX_HELD(&ufmh->ufmh_lock));
298508a0e8cSRob Johnston 
299508a0e8cSRob Johnston 	if (!ufm_driver_ready(ufmh)) {
300508a0e8cSRob Johnston 		ddi_release_devi(dip);
301508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
302508a0e8cSRob Johnston 		return (EAGAIN);
303508a0e8cSRob Johnston 	}
304508a0e8cSRob Johnston 
305508a0e8cSRob Johnston 	if (ufbz.ufbz_version != ufmh->ufmh_version) {
306508a0e8cSRob Johnston 		ddi_release_devi(dip);
307508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
308508a0e8cSRob Johnston 		return (ENOTSUP);
309508a0e8cSRob Johnston 	}
310508a0e8cSRob Johnston 
311508a0e8cSRob Johnston 	/*
312508a0e8cSRob Johnston 	 * Note - ufm_cache_fill() also takes care of verifying that the driver
313508a0e8cSRob Johnston 	 * supports the DDI_UFM_CAP_REPORT capability and will return non-zero,
314508a0e8cSRob Johnston 	 * if not supported.
315508a0e8cSRob Johnston 	 */
316508a0e8cSRob Johnston 	if ((ret = ufm_cache_fill(ufmh)) != 0) {
317508a0e8cSRob Johnston 		ddi_release_devi(dip);
318508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
319508a0e8cSRob Johnston 		return (ret);
320508a0e8cSRob Johnston 	}
321508a0e8cSRob Johnston 	ddi_release_devi(dip);
322508a0e8cSRob Johnston 
323508a0e8cSRob Johnston 	ret = nvlist_size(ufmh->ufmh_report, &sz, NV_ENCODE_NATIVE);
324508a0e8cSRob Johnston 	mutex_exit(&ufmh->ufmh_lock);
325508a0e8cSRob Johnston 	if (ret != 0)
326508a0e8cSRob Johnston 		return (ret);
327508a0e8cSRob Johnston 
328508a0e8cSRob Johnston 	switch (model) {
329508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
330508a0e8cSRob Johnston 	case DDI_MODEL_ILP32:
331508a0e8cSRob Johnston 		ufbz32.ufbz_size = sz;
332508a0e8cSRob Johnston 		if (ddi_copyout(&ufbz32, (void *)data,
333508a0e8cSRob Johnston 		    sizeof (ufm_ioc_bufsz32_t), mode) != 0)
334508a0e8cSRob Johnston 			return (EFAULT);
335508a0e8cSRob Johnston 		break;
336508a0e8cSRob Johnston #endif /* _MULTI_DATAMODEL */
337508a0e8cSRob Johnston 	case DDI_MODEL_NONE:
338508a0e8cSRob Johnston 	default:
339508a0e8cSRob Johnston 		ufbz.ufbz_size = sz;
340508a0e8cSRob Johnston 		if (ddi_copyout(&ufbz, (void *)data,
341508a0e8cSRob Johnston 		    sizeof (ufm_ioc_bufsz_t), mode) != 0)
342508a0e8cSRob Johnston 			return (EFAULT);
343508a0e8cSRob Johnston 	}
344508a0e8cSRob Johnston 	return (0);
345508a0e8cSRob Johnston }
346508a0e8cSRob Johnston 
347508a0e8cSRob Johnston static int
ufm_do_report(intptr_t data,int mode)348508a0e8cSRob Johnston ufm_do_report(intptr_t data, int mode)
349508a0e8cSRob Johnston {
350508a0e8cSRob Johnston 	ddi_ufm_handle_t *ufmh;
351508a0e8cSRob Johnston 	uint_t model;
352508a0e8cSRob Johnston 	int ret = 0;
353508a0e8cSRob Johnston 	char *buf;
354508a0e8cSRob Johnston 	size_t sz;
355508a0e8cSRob Johnston 	dev_info_t *dip;
356508a0e8cSRob Johnston 	char devpath[MAXPATHLEN];
357508a0e8cSRob Johnston 	ufm_ioc_report_t ufmr;
358508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
359508a0e8cSRob Johnston 	ufm_ioc_report32_t ufmr32;
360508a0e8cSRob Johnston #endif
361508a0e8cSRob Johnston 
362508a0e8cSRob Johnston 	model = ddi_model_convert_from(mode);
363508a0e8cSRob Johnston 
364508a0e8cSRob Johnston 	switch (model) {
365508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
366508a0e8cSRob Johnston 	case DDI_MODEL_ILP32:
367508a0e8cSRob Johnston 		if (ddi_copyin((void *)data, &ufmr32,
368508a0e8cSRob Johnston 		    sizeof (ufm_ioc_report32_t), mode) != 0)
369508a0e8cSRob Johnston 			return (EFAULT);
370508a0e8cSRob Johnston 		ufmr.ufmr_version = ufmr32.ufmr_version;
371508a0e8cSRob Johnston 		if (strlcpy(ufmr.ufmr_devpath, ufmr32.ufmr_devpath,
372508a0e8cSRob Johnston 		    MAXPATHLEN) >= MAXPATHLEN) {
373508a0e8cSRob Johnston 			return (EOVERFLOW);
374508a0e8cSRob Johnston 		}
375508a0e8cSRob Johnston 		ufmr.ufmr_bufsz = ufmr32.ufmr_bufsz;
376508a0e8cSRob Johnston 		ufmr.ufmr_buf = (caddr_t)(uintptr_t)ufmr32.ufmr_buf;
377508a0e8cSRob Johnston 		break;
378508a0e8cSRob Johnston #endif /* _MULTI_DATAMODEL */
379508a0e8cSRob Johnston 	case DDI_MODEL_NONE:
380508a0e8cSRob Johnston 	default:
381508a0e8cSRob Johnston 		if (ddi_copyin((void *)data, &ufmr,
382508a0e8cSRob Johnston 		    sizeof (ufm_ioc_report_t), mode) != 0)
383508a0e8cSRob Johnston 			return (EFAULT);
384508a0e8cSRob Johnston 	}
385508a0e8cSRob Johnston 
386508a0e8cSRob Johnston 	if (strlcpy(devpath, ufmr.ufmr_devpath, MAXPATHLEN) >= MAXPATHLEN)
387508a0e8cSRob Johnston 		return (EOVERFLOW);
388508a0e8cSRob Johnston 
389508a0e8cSRob Johnston 	if ((dip = e_ddi_hold_devi_by_path(devpath, 0)) == NULL) {
390508a0e8cSRob Johnston 			return (ENOTSUP);
391508a0e8cSRob Johnston 	}
392508a0e8cSRob Johnston 	if ((ufmh = ufm_find(devpath)) == NULL) {
393508a0e8cSRob Johnston 		ddi_release_devi(dip);
394508a0e8cSRob Johnston 		return (ENOTSUP);
395508a0e8cSRob Johnston 	}
396508a0e8cSRob Johnston 	ASSERT(MUTEX_HELD(&ufmh->ufmh_lock));
397508a0e8cSRob Johnston 
398508a0e8cSRob Johnston 	if (!ufm_driver_ready(ufmh)) {
399508a0e8cSRob Johnston 		ddi_release_devi(dip);
400508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
401508a0e8cSRob Johnston 		return (EAGAIN);
402508a0e8cSRob Johnston 	}
403508a0e8cSRob Johnston 
404508a0e8cSRob Johnston 	if (ufmr.ufmr_version != ufmh->ufmh_version) {
405508a0e8cSRob Johnston 		ddi_release_devi(dip);
406508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
407508a0e8cSRob Johnston 		return (ENOTSUP);
408508a0e8cSRob Johnston 	}
409508a0e8cSRob Johnston 
410508a0e8cSRob Johnston 	/*
411508a0e8cSRob Johnston 	 * Note - ufm_cache_fill() also takes care of verifying that the driver
412508a0e8cSRob Johnston 	 * supports the DDI_UFM_CAP_REPORT capability and will return non-zero,
413508a0e8cSRob Johnston 	 * if not supported.
414508a0e8cSRob Johnston 	 */
415508a0e8cSRob Johnston 	if ((ret = ufm_cache_fill(ufmh)) != 0) {
416508a0e8cSRob Johnston 		ddi_release_devi(dip);
417508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
418508a0e8cSRob Johnston 		return (ret);
419508a0e8cSRob Johnston 	}
420508a0e8cSRob Johnston 	ddi_release_devi(dip);
421508a0e8cSRob Johnston 
422508a0e8cSRob Johnston 	if ((ret = nvlist_size(ufmh->ufmh_report, &sz, NV_ENCODE_NATIVE)) !=
423508a0e8cSRob Johnston 	    0) {
424508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
425508a0e8cSRob Johnston 		return (ret);
426508a0e8cSRob Johnston 	}
427508a0e8cSRob Johnston 	if (sz > ufmr.ufmr_bufsz) {
428508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
429508a0e8cSRob Johnston 		return (EOVERFLOW);
430508a0e8cSRob Johnston 	}
431508a0e8cSRob Johnston 
432508a0e8cSRob Johnston 	buf = fnvlist_pack(ufmh->ufmh_report, &sz);
433508a0e8cSRob Johnston 	mutex_exit(&ufmh->ufmh_lock);
434508a0e8cSRob Johnston 
435508a0e8cSRob Johnston 	if (ddi_copyout(buf, ufmr.ufmr_buf, sz, mode) != 0) {
436508a0e8cSRob Johnston 		kmem_free(buf, sz);
437508a0e8cSRob Johnston 		return (EFAULT);
438508a0e8cSRob Johnston 	}
439508a0e8cSRob Johnston 	kmem_free(buf, sz);
440508a0e8cSRob Johnston 
441508a0e8cSRob Johnston 	switch (model) {
442508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
443508a0e8cSRob Johnston 	case DDI_MODEL_ILP32:
444508a0e8cSRob Johnston 		ufmr32.ufmr_bufsz = sz;
445508a0e8cSRob Johnston 		if (ddi_copyout(&ufmr32, (void *)data,
446508a0e8cSRob Johnston 		    sizeof (ufm_ioc_report32_t), mode) != 0)
447508a0e8cSRob Johnston 			return (EFAULT);
448508a0e8cSRob Johnston 		break;
449508a0e8cSRob Johnston #endif /* _MULTI_DATAMODEL */
450508a0e8cSRob Johnston 	case DDI_MODEL_NONE:
451508a0e8cSRob Johnston 	default:
452508a0e8cSRob Johnston 		ufmr.ufmr_bufsz = sz;
453508a0e8cSRob Johnston 		if (ddi_copyout(&ufmr, (void *)data,
454508a0e8cSRob Johnston 		    sizeof (ufm_ioc_report_t), mode) != 0)
455508a0e8cSRob Johnston 			return (EFAULT);
456508a0e8cSRob Johnston 	}
457508a0e8cSRob Johnston 
458508a0e8cSRob Johnston 	return (0);
459508a0e8cSRob Johnston }
460508a0e8cSRob Johnston 
4618d55b806SRobert Mustacchi static int
ufm_do_readimg(intptr_t data,int mode)4628d55b806SRobert Mustacchi ufm_do_readimg(intptr_t data, int mode)
4638d55b806SRobert Mustacchi {
4648d55b806SRobert Mustacchi 	int ret;
4658d55b806SRobert Mustacchi 	uint_t model;
4668d55b806SRobert Mustacchi 	ufm_ioc_readimg_t ufri;
4678d55b806SRobert Mustacchi 	char devpath[MAXPATHLEN];
4688d55b806SRobert Mustacchi 	ddi_ufm_handle_t *ufmh;
4698d55b806SRobert Mustacchi 	dev_info_t *dip;
4708d55b806SRobert Mustacchi #ifdef _MULTI_DATAMODEL
4718d55b806SRobert Mustacchi 	ufm_ioc_readimg32_t ufri32;
4728d55b806SRobert Mustacchi #endif
4738d55b806SRobert Mustacchi 
4748d55b806SRobert Mustacchi 	model = ddi_model_convert_from(mode);
4758d55b806SRobert Mustacchi 	switch (model) {
4768d55b806SRobert Mustacchi #ifdef _MULTI_DATAMODEL
4778d55b806SRobert Mustacchi 	case DDI_MODEL_ILP32:
4788d55b806SRobert Mustacchi 		if (ddi_copyin((void *)data, &ufri32, sizeof (ufri32),
4798d55b806SRobert Mustacchi 		    mode) != 0) {
4808d55b806SRobert Mustacchi 			return (EFAULT);
4818d55b806SRobert Mustacchi 		}
4828d55b806SRobert Mustacchi 		ufri.ufri_version = ufri32.ufri_version;
4838d55b806SRobert Mustacchi 		ufri.ufri_imageno = ufri32.ufri_imageno;
4848d55b806SRobert Mustacchi 		ufri.ufri_slotno = ufri32.ufri_slotno;
4858d55b806SRobert Mustacchi 		ufri.ufri_offset = ufri32.ufri_offset;
4868d55b806SRobert Mustacchi 		ufri.ufri_len = ufri32.ufri_len;
4878d55b806SRobert Mustacchi 		ufri.ufri_nread = ufri32.ufri_nread;
4888d55b806SRobert Mustacchi 
4898d55b806SRobert Mustacchi 		if (strlcpy(ufri.ufri_devpath, ufri32.ufri_devpath,
4908d55b806SRobert Mustacchi 		    MAXPATHLEN) >= MAXPATHLEN) {
4918d55b806SRobert Mustacchi 			return (EOVERFLOW);
4928d55b806SRobert Mustacchi 		}
4938d55b806SRobert Mustacchi 		ufri.ufri_buf = (caddr_t)(uintptr_t)ufri32.ufri_buf;
4948d55b806SRobert Mustacchi 		break;
4958d55b806SRobert Mustacchi #endif /* _MULTI_DATAMODEL */
4968d55b806SRobert Mustacchi 	case DDI_MODEL_NONE:
4978d55b806SRobert Mustacchi 	default:
4988d55b806SRobert Mustacchi 		if (ddi_copyin((void *)data, &ufri, sizeof (ufri), mode) != 0) {
4998d55b806SRobert Mustacchi 			return (EFAULT);
5008d55b806SRobert Mustacchi 		}
5018d55b806SRobert Mustacchi 	}
5028d55b806SRobert Mustacchi 
5038d55b806SRobert Mustacchi 	if (strlcpy(devpath, ufri.ufri_devpath, MAXPATHLEN) >= MAXPATHLEN)
5048d55b806SRobert Mustacchi 		return (EOVERFLOW);
5058d55b806SRobert Mustacchi 
5068d55b806SRobert Mustacchi 	if ((dip = e_ddi_hold_devi_by_path(devpath, 0)) == NULL) {
5078d55b806SRobert Mustacchi 			return (ENOTSUP);
5088d55b806SRobert Mustacchi 	}
5098d55b806SRobert Mustacchi 	if ((ufmh = ufm_find(devpath)) == NULL) {
5108d55b806SRobert Mustacchi 		ddi_release_devi(dip);
5118d55b806SRobert Mustacchi 		return (ENOTSUP);
5128d55b806SRobert Mustacchi 	}
5138d55b806SRobert Mustacchi 	ASSERT(MUTEX_HELD(&ufmh->ufmh_lock));
5148d55b806SRobert Mustacchi 
5158d55b806SRobert Mustacchi 	if (!ufm_driver_ready(ufmh)) {
5168d55b806SRobert Mustacchi 		ret = EAGAIN;
5178d55b806SRobert Mustacchi 		goto out;
5188d55b806SRobert Mustacchi 	}
5198d55b806SRobert Mustacchi 
5208d55b806SRobert Mustacchi 	if (ufri.ufri_version != ufmh->ufmh_version) {
5218d55b806SRobert Mustacchi 		ret = ENOTSUP;
5228d55b806SRobert Mustacchi 		goto out;
5238d55b806SRobert Mustacchi 	}
5248d55b806SRobert Mustacchi 
5258d55b806SRobert Mustacchi 	ret = ufm_read_img(ufmh, ufri.ufri_imageno, ufri.ufri_slotno,
5268d55b806SRobert Mustacchi 	    ufri.ufri_len, ufri.ufri_offset, (uintptr_t)ufri.ufri_buf,
5278d55b806SRobert Mustacchi 	    &ufri.ufri_nread, mode);
5288d55b806SRobert Mustacchi 
5298d55b806SRobert Mustacchi out:
5308d55b806SRobert Mustacchi 	mutex_exit(&ufmh->ufmh_lock);
5318d55b806SRobert Mustacchi 	ddi_release_devi(dip);
5328d55b806SRobert Mustacchi 
5338d55b806SRobert Mustacchi 	if (ret == 0) {
5348d55b806SRobert Mustacchi 		switch (model) {
5358d55b806SRobert Mustacchi #ifdef _MULTI_DATAMODEL
5368d55b806SRobert Mustacchi 		case DDI_MODEL_ILP32:
5378d55b806SRobert Mustacchi 			ufri32.ufri_nread = ufri.ufri_nread;
5388d55b806SRobert Mustacchi 			if (ddi_copyout(&ufri32, (void *)data, sizeof (ufri32),
5398d55b806SRobert Mustacchi 			    mode) != 0) {
5408d55b806SRobert Mustacchi 				return (EFAULT);
5418d55b806SRobert Mustacchi 			}
5428d55b806SRobert Mustacchi 			break;
5438d55b806SRobert Mustacchi #endif /* _MULTI_DATAMODEL */
5448d55b806SRobert Mustacchi 		case DDI_MODEL_NONE:
5458d55b806SRobert Mustacchi 		default:
5468d55b806SRobert Mustacchi 			if (ddi_copyout(&ufri, (void *)data, sizeof (ufri),
5478d55b806SRobert Mustacchi 			    mode) != 0) {
5488d55b806SRobert Mustacchi 				return (EFAULT);
5498d55b806SRobert Mustacchi 			}
5508d55b806SRobert Mustacchi 		}
5518d55b806SRobert Mustacchi 	}
5528d55b806SRobert Mustacchi 
5538d55b806SRobert Mustacchi 	return (ret);
5548d55b806SRobert Mustacchi }
5558d55b806SRobert Mustacchi 
556508a0e8cSRob Johnston static int
ufm_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rvalp)557508a0e8cSRob Johnston ufm_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp,
558508a0e8cSRob Johnston     int *rvalp)
559508a0e8cSRob Johnston {
560508a0e8cSRob Johnston 	int ret = 0;
561508a0e8cSRob Johnston 
562508a0e8cSRob Johnston 	if (drv_priv(credp) != 0)
563508a0e8cSRob Johnston 		return (EPERM);
564508a0e8cSRob Johnston 
565508a0e8cSRob Johnston 	switch (cmd) {
566508a0e8cSRob Johnston 	case UFM_IOC_GETCAPS:
567508a0e8cSRob Johnston 		ret = ufm_do_getcaps(data, mode);
568508a0e8cSRob Johnston 		break;
569508a0e8cSRob Johnston 
570508a0e8cSRob Johnston 	case UFM_IOC_REPORTSZ:
571508a0e8cSRob Johnston 		ret = ufm_do_reportsz(data, mode);
572508a0e8cSRob Johnston 		break;
573508a0e8cSRob Johnston 
574508a0e8cSRob Johnston 	case UFM_IOC_REPORT:
575508a0e8cSRob Johnston 		ret = ufm_do_report(data, mode);
576508a0e8cSRob Johnston 		break;
5778d55b806SRobert Mustacchi 
5788d55b806SRobert Mustacchi 	case UFM_IOC_READIMG:
5798d55b806SRobert Mustacchi 		ret = ufm_do_readimg(data, mode);
5808d55b806SRobert Mustacchi 		break;
581508a0e8cSRob Johnston 	default:
582508a0e8cSRob Johnston 		return (ENOTTY);
583508a0e8cSRob Johnston 	}
584508a0e8cSRob Johnston 	return (ret);
585508a0e8cSRob Johnston 
586508a0e8cSRob Johnston }
587