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