xref: /illumos-gate/usr/src/uts/common/io/ufmtest.c (revision ca783257)
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.
14508a0e8cSRob Johnston  */
15508a0e8cSRob Johnston 
16508a0e8cSRob Johnston /*
17508a0e8cSRob Johnston  * This is a test driver used for exercising the DDI UFM subsystem.
18508a0e8cSRob Johnston  *
19508a0e8cSRob Johnston  * Most of the test cases depend on the ufmtest driver being loaded.
20508a0e8cSRob Johnston  * On SmartOS, this driver will need to be manually installed, as it is not
21508a0e8cSRob Johnston  * part of the platform image.
22508a0e8cSRob Johnston  */
23508a0e8cSRob Johnston #include <sys/ddi.h>
24508a0e8cSRob Johnston #include <sys/sunddi.h>
25508a0e8cSRob Johnston #include <sys/esunddi.h>
26508a0e8cSRob Johnston #include <sys/ddi_ufm.h>
27508a0e8cSRob Johnston #include <sys/conf.h>
28508a0e8cSRob Johnston #include <sys/debug.h>
29508a0e8cSRob Johnston #include <sys/file.h>
30508a0e8cSRob Johnston #include <sys/kmem.h>
31508a0e8cSRob Johnston #include <sys/stat.h>
32508a0e8cSRob Johnston #include <sys/zone.h>
33508a0e8cSRob Johnston 
34508a0e8cSRob Johnston #include "ufmtest.h"
35508a0e8cSRob Johnston 
36508a0e8cSRob Johnston typedef struct ufmtest {
37508a0e8cSRob Johnston 	dev_info_t		*ufmt_devi;
38508a0e8cSRob Johnston 	nvlist_t		*ufmt_nvl;
39508a0e8cSRob Johnston 	ddi_ufm_handle_t	*ufmt_ufmh;
40508a0e8cSRob Johnston 	uint32_t		ufmt_failflags;
41508a0e8cSRob Johnston } ufmtest_t;
42508a0e8cSRob Johnston 
43508a0e8cSRob Johnston static ufmtest_t ufmt = { 0 };
44508a0e8cSRob Johnston 
45508a0e8cSRob Johnston static int ufmtest_open(dev_t *, int, int, cred_t *);
46508a0e8cSRob Johnston static int ufmtest_close(dev_t, int, int, cred_t *);
47508a0e8cSRob Johnston static int ufmtest_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
48508a0e8cSRob Johnston 
49508a0e8cSRob Johnston static struct cb_ops ufmtest_cb_ops = {
50508a0e8cSRob Johnston 	.cb_open =	ufmtest_open,
51508a0e8cSRob Johnston 	.cb_close =	ufmtest_close,
52508a0e8cSRob Johnston 	.cb_strategy =	nodev,
53508a0e8cSRob Johnston 	.cb_print =	nodev,
54508a0e8cSRob Johnston 	.cb_dump =	nodev,
55508a0e8cSRob Johnston 	.cb_read =	nodev,
56508a0e8cSRob Johnston 	.cb_write =	nodev,
57508a0e8cSRob Johnston 	.cb_ioctl =	ufmtest_ioctl,
58508a0e8cSRob Johnston 	.cb_devmap =	nodev,
59508a0e8cSRob Johnston 	.cb_mmap =	nodev,
60508a0e8cSRob Johnston 	.cb_segmap =	nodev,
61508a0e8cSRob Johnston 	.cb_chpoll =	nochpoll,
62508a0e8cSRob Johnston 	.cb_prop_op =	ddi_prop_op,
63508a0e8cSRob Johnston 	.cb_str =	NULL,
64508a0e8cSRob Johnston 	.cb_flag =	D_NEW | D_MP,
65508a0e8cSRob Johnston 	.cb_rev =	CB_REV,
66508a0e8cSRob Johnston 	.cb_aread =	nodev,
67508a0e8cSRob Johnston 	.cb_awrite =	nodev
68508a0e8cSRob Johnston };
69508a0e8cSRob Johnston 
70508a0e8cSRob Johnston static int ufmtest_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
71508a0e8cSRob Johnston static int ufmtest_attach(dev_info_t *, ddi_attach_cmd_t);
72508a0e8cSRob Johnston static int ufmtest_detach(dev_info_t *, ddi_detach_cmd_t);
73508a0e8cSRob Johnston 
74508a0e8cSRob Johnston static struct dev_ops ufmtest_ops = {
75508a0e8cSRob Johnston 	.devo_rev =		DEVO_REV,
76508a0e8cSRob Johnston 	.devo_refcnt =		0,
77508a0e8cSRob Johnston 	.devo_getinfo =		ufmtest_info,
78508a0e8cSRob Johnston 	.devo_identify =	nulldev,
79508a0e8cSRob Johnston 	.devo_probe =		nulldev,
80508a0e8cSRob Johnston 	.devo_attach =		ufmtest_attach,
81508a0e8cSRob Johnston 	.devo_detach =		ufmtest_detach,
82508a0e8cSRob Johnston 	.devo_reset =		nodev,
83508a0e8cSRob Johnston 	.devo_cb_ops =		&ufmtest_cb_ops,
84508a0e8cSRob Johnston 	.devo_bus_ops =		NULL,
85508a0e8cSRob Johnston 	.devo_power =		NULL,
86508a0e8cSRob Johnston 	.devo_quiesce =		ddi_quiesce_not_needed
87508a0e8cSRob Johnston };
88508a0e8cSRob Johnston 
89508a0e8cSRob Johnston static struct modldrv modldrv = {
90508a0e8cSRob Johnston 	.drv_modops =		&mod_driverops,
91508a0e8cSRob Johnston 	.drv_linkinfo =		"DDI UFM test driver",
92508a0e8cSRob Johnston 	.drv_dev_ops =		&ufmtest_ops
93508a0e8cSRob Johnston };
94508a0e8cSRob Johnston 
95508a0e8cSRob Johnston static struct modlinkage modlinkage = {
96508a0e8cSRob Johnston 	.ml_rev =		MODREV_1,
97508a0e8cSRob Johnston 	.ml_linkage =		{ (void *)&modldrv, NULL }
98508a0e8cSRob Johnston };
99508a0e8cSRob Johnston 
100508a0e8cSRob Johnston static int ufmtest_nimages(ddi_ufm_handle_t *, void *, uint_t *);
101508a0e8cSRob Johnston static int ufmtest_fill_image(ddi_ufm_handle_t *, void *, uint_t,
102508a0e8cSRob Johnston     ddi_ufm_image_t *);
103508a0e8cSRob Johnston static int ufmtest_fill_slot(ddi_ufm_handle_t *, void *, uint_t, uint_t,
104508a0e8cSRob Johnston     ddi_ufm_slot_t *);
105508a0e8cSRob Johnston static int ufmtest_getcaps(ddi_ufm_handle_t *, void *, ddi_ufm_cap_t *);
106508a0e8cSRob Johnston 
107508a0e8cSRob Johnston static ddi_ufm_ops_t ufmtest_ufm_ops = {
108508a0e8cSRob Johnston 	ufmtest_nimages,
109508a0e8cSRob Johnston 	ufmtest_fill_image,
110508a0e8cSRob Johnston 	ufmtest_fill_slot,
111508a0e8cSRob Johnston 	ufmtest_getcaps
112508a0e8cSRob Johnston };
113508a0e8cSRob Johnston 
114508a0e8cSRob Johnston 
115508a0e8cSRob Johnston int
_init(void)116508a0e8cSRob Johnston _init(void)
117508a0e8cSRob Johnston {
118508a0e8cSRob Johnston 	return (mod_install(&modlinkage));
119508a0e8cSRob Johnston }
120508a0e8cSRob Johnston 
121508a0e8cSRob Johnston int
_fini(void)122508a0e8cSRob Johnston _fini(void)
123508a0e8cSRob Johnston {
124508a0e8cSRob Johnston 	return (mod_remove(&modlinkage));
125508a0e8cSRob Johnston }
126508a0e8cSRob Johnston 
127508a0e8cSRob Johnston int
_info(struct modinfo * modinfop)128508a0e8cSRob Johnston _info(struct modinfo *modinfop)
129508a0e8cSRob Johnston {
130508a0e8cSRob Johnston 	return (mod_info(&modlinkage, modinfop));
131508a0e8cSRob Johnston }
132508a0e8cSRob Johnston 
133508a0e8cSRob Johnston static int
ufmtest_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)134508a0e8cSRob Johnston ufmtest_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
135508a0e8cSRob Johnston {
136508a0e8cSRob Johnston 	switch (infocmd) {
137508a0e8cSRob Johnston 	case DDI_INFO_DEVT2DEVINFO:
138508a0e8cSRob Johnston 		*result = ufmt.ufmt_devi;
139508a0e8cSRob Johnston 		return (DDI_SUCCESS);
140508a0e8cSRob Johnston 	case DDI_INFO_DEVT2INSTANCE:
141508a0e8cSRob Johnston 		*result = (void *)(uintptr_t)ddi_get_instance(dip);
142508a0e8cSRob Johnston 		return (DDI_SUCCESS);
143508a0e8cSRob Johnston 	}
144508a0e8cSRob Johnston 	return (DDI_FAILURE);
145508a0e8cSRob Johnston }
146508a0e8cSRob Johnston 
147508a0e8cSRob Johnston static int
ufmtest_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)148508a0e8cSRob Johnston ufmtest_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
149508a0e8cSRob Johnston {
150508a0e8cSRob Johnston 	if (cmd != DDI_ATTACH || ufmt.ufmt_devi != NULL)
151508a0e8cSRob Johnston 		return (DDI_FAILURE);
152508a0e8cSRob Johnston 
153508a0e8cSRob Johnston 	if (ddi_create_minor_node(devi, "ufmtest", S_IFCHR, 0, DDI_PSEUDO,
154508a0e8cSRob Johnston 	    0) == DDI_FAILURE) {
155508a0e8cSRob Johnston 		ddi_remove_minor_node(devi, NULL);
156508a0e8cSRob Johnston 		return (DDI_FAILURE);
157508a0e8cSRob Johnston 	}
158508a0e8cSRob Johnston 
159508a0e8cSRob Johnston 	ufmt.ufmt_devi = devi;
160508a0e8cSRob Johnston 
161508a0e8cSRob Johnston 	if (ddi_ufm_init(ufmt.ufmt_devi, DDI_UFM_CURRENT_VERSION,
162508a0e8cSRob Johnston 	    &ufmtest_ufm_ops, &ufmt.ufmt_ufmh, NULL) != 0) {
163508a0e8cSRob Johnston 		dev_err(ufmt.ufmt_devi, CE_WARN, "failed to initialize UFM "
164508a0e8cSRob Johnston 		    "subsystem");
165508a0e8cSRob Johnston 		return (DDI_FAILURE);
166508a0e8cSRob Johnston 	}
167508a0e8cSRob Johnston 
168508a0e8cSRob Johnston 	return (DDI_SUCCESS);
169508a0e8cSRob Johnston }
170508a0e8cSRob Johnston 
171508a0e8cSRob Johnston static int
ufmtest_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)172508a0e8cSRob Johnston ufmtest_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
173508a0e8cSRob Johnston {
174508a0e8cSRob Johnston 	if (cmd != DDI_DETACH)
175508a0e8cSRob Johnston 		return (DDI_FAILURE);
176508a0e8cSRob Johnston 
177508a0e8cSRob Johnston 	if (devi != NULL)
178508a0e8cSRob Johnston 		ddi_remove_minor_node(devi, NULL);
179508a0e8cSRob Johnston 
180508a0e8cSRob Johnston 	ddi_ufm_fini(ufmt.ufmt_ufmh);
181508a0e8cSRob Johnston 	if (ufmt.ufmt_nvl != NULL) {
182508a0e8cSRob Johnston 		nvlist_free(ufmt.ufmt_nvl);
183508a0e8cSRob Johnston 		ufmt.ufmt_nvl = NULL;
184508a0e8cSRob Johnston 	}
185508a0e8cSRob Johnston 
186508a0e8cSRob Johnston 	return (DDI_SUCCESS);
187508a0e8cSRob Johnston }
188508a0e8cSRob Johnston 
189508a0e8cSRob Johnston static int
ufmtest_open(dev_t * devp,int flag,int otyp,cred_t * credp)190508a0e8cSRob Johnston ufmtest_open(dev_t *devp, int flag, int otyp, cred_t *credp)
191508a0e8cSRob Johnston {
192508a0e8cSRob Johnston 	const int inv_flags = FWRITE | FEXCL | FNDELAY | FNONBLOCK;
193508a0e8cSRob Johnston 
194508a0e8cSRob Johnston 	if (otyp != OTYP_CHR)
195508a0e8cSRob Johnston 		return (EINVAL);
196508a0e8cSRob Johnston 
197508a0e8cSRob Johnston 	if (flag & inv_flags)
198508a0e8cSRob Johnston 		return (EINVAL);
199508a0e8cSRob Johnston 
200508a0e8cSRob Johnston 	if (drv_priv(credp) != 0)
201508a0e8cSRob Johnston 		return (EPERM);
202508a0e8cSRob Johnston 
203508a0e8cSRob Johnston 	if (getzoneid() != GLOBAL_ZONEID)
204508a0e8cSRob Johnston 		return (EPERM);
205508a0e8cSRob Johnston 
206508a0e8cSRob Johnston 	return (0);
207508a0e8cSRob Johnston }
208508a0e8cSRob Johnston 
209508a0e8cSRob Johnston static int
ufmtest_close(dev_t dev,int flag,int otyp,cred_t * credp)210508a0e8cSRob Johnston ufmtest_close(dev_t dev, int flag, int otyp, cred_t *credp)
211508a0e8cSRob Johnston {
212508a0e8cSRob Johnston 	return (0);
213508a0e8cSRob Johnston }
214508a0e8cSRob Johnston 
215508a0e8cSRob Johnston /*
216508a0e8cSRob Johnston  * By default, this pseudo test driver contains no hardcoded UFM data to
217508a0e8cSRob Johnston  * report.  This ioctl takes a packed nvlist, representing a UFM report.
218508a0e8cSRob Johnston  * This data is then used as a source for firmware information by this
219508a0e8cSRob Johnston  * driver when it's UFM callback are called.
220508a0e8cSRob Johnston  *
221508a0e8cSRob Johnston  * External test programs can use this ioctl to effectively seed this
222508a0e8cSRob Johnston  * driver with arbitrary firmware information which it will report up to the
223508a0e8cSRob Johnston  * DDI UFM subsystem.
224508a0e8cSRob Johnston  */
225508a0e8cSRob Johnston static int
ufmtest_do_setfw(intptr_t data,int mode)226508a0e8cSRob Johnston ufmtest_do_setfw(intptr_t data, int mode)
227508a0e8cSRob Johnston {
228508a0e8cSRob Johnston 	int ret;
229508a0e8cSRob Johnston 	uint_t model;
230508a0e8cSRob Johnston 	ufmtest_ioc_setfw_t setfw;
231508a0e8cSRob Johnston 	char *nvlbuf = NULL;
232508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
233508a0e8cSRob Johnston 	ufmtest_ioc_setfw32_t setfw32;
234508a0e8cSRob Johnston #endif
235508a0e8cSRob Johnston 	model = ddi_model_convert_from(mode);
236508a0e8cSRob Johnston 
237508a0e8cSRob Johnston 	switch (model) {
238508a0e8cSRob Johnston #ifdef _MULTI_DATAMODEL
239508a0e8cSRob Johnston 	case DDI_MODEL_ILP32:
240508a0e8cSRob Johnston 		if (ddi_copyin((void *)data, &setfw32,
241508a0e8cSRob Johnston 		    sizeof (ufmtest_ioc_setfw32_t), mode) != 0)
242508a0e8cSRob Johnston 			return (EFAULT);
243508a0e8cSRob Johnston 		setfw.utsw_bufsz = setfw32.utsw_bufsz;
244508a0e8cSRob Johnston 		setfw.utsw_buf = (caddr_t)(uintptr_t)setfw32.utsw_buf;
245508a0e8cSRob Johnston 		break;
246508a0e8cSRob Johnston #endif /* _MULTI_DATAMODEL */
247508a0e8cSRob Johnston 	case DDI_MODEL_NONE:
248508a0e8cSRob Johnston 	default:
249508a0e8cSRob Johnston 		if (ddi_copyin((void *)data, &setfw,
250508a0e8cSRob Johnston 		    sizeof (ufmtest_ioc_setfw_t), mode) != 0)
251508a0e8cSRob Johnston 			return (EFAULT);
252508a0e8cSRob Johnston 	}
253508a0e8cSRob Johnston 
254508a0e8cSRob Johnston 	if (ufmt.ufmt_nvl != NULL) {
255508a0e8cSRob Johnston 		nvlist_free(ufmt.ufmt_nvl);
256508a0e8cSRob Johnston 		ufmt.ufmt_nvl = NULL;
257508a0e8cSRob Johnston 	}
258508a0e8cSRob Johnston 
259*ca783257SDan McDonald 	nvlbuf = kmem_zalloc(setfw.utsw_bufsz, KM_NOSLEEP_LAZY);
260508a0e8cSRob Johnston 	if (nvlbuf == NULL)
261508a0e8cSRob Johnston 		return (ENOMEM);
262508a0e8cSRob Johnston 
263508a0e8cSRob Johnston 	if (ddi_copyin(setfw.utsw_buf, nvlbuf, setfw.utsw_bufsz, mode) != 0) {
264508a0e8cSRob Johnston 		kmem_free(nvlbuf, setfw.utsw_bufsz);
265508a0e8cSRob Johnston 		return (EFAULT);
266508a0e8cSRob Johnston 	}
267508a0e8cSRob Johnston 
268508a0e8cSRob Johnston 	ret = nvlist_unpack(nvlbuf, setfw.utsw_bufsz, &ufmt.ufmt_nvl,
2690d1087e8SHans Rosenfeld 	    KM_NOSLEEP);
270508a0e8cSRob Johnston 	kmem_free(nvlbuf, setfw.utsw_bufsz);
271508a0e8cSRob Johnston 
272508a0e8cSRob Johnston 	if (ret != 0)
273508a0e8cSRob Johnston 		return (ret);
274508a0e8cSRob Johnston 
275508a0e8cSRob Johnston 	/*
276508a0e8cSRob Johnston 	 * Notify the UFM subsystem that our firmware information has changed.
277508a0e8cSRob Johnston 	 */
278508a0e8cSRob Johnston 	ddi_ufm_update(ufmt.ufmt_ufmh);
279508a0e8cSRob Johnston 
280508a0e8cSRob Johnston 	return (0);
281508a0e8cSRob Johnston }
282508a0e8cSRob Johnston 
283508a0e8cSRob Johnston static int
ufmtest_do_toggle_fails(intptr_t data,int mode)284508a0e8cSRob Johnston ufmtest_do_toggle_fails(intptr_t data, int mode)
285508a0e8cSRob Johnston {
286508a0e8cSRob Johnston 	ufmtest_ioc_fails_t fails;
287508a0e8cSRob Johnston 
288508a0e8cSRob Johnston 	if (ddi_copyin((void *)data, &fails, sizeof (ufmtest_ioc_fails_t),
289508a0e8cSRob Johnston 	    mode) != 0)
290508a0e8cSRob Johnston 		return (EFAULT);
291508a0e8cSRob Johnston 
292508a0e8cSRob Johnston 	if (fails.utfa_flags > UFMTEST_MAX_FAILFLAGS)
293508a0e8cSRob Johnston 		return (EINVAL);
294508a0e8cSRob Johnston 
295508a0e8cSRob Johnston 	ufmt.ufmt_failflags = fails.utfa_flags;
296508a0e8cSRob Johnston 
297508a0e8cSRob Johnston 	return (0);
298508a0e8cSRob Johnston }
299508a0e8cSRob Johnston 
300508a0e8cSRob Johnston /* ARGSUSED */
301508a0e8cSRob Johnston static int
ufmtest_ioctl(dev_t dev,int cmd,intptr_t data,int mode,cred_t * credp,int * rvalp)302508a0e8cSRob Johnston ufmtest_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp,
303508a0e8cSRob Johnston     int *rvalp)
304508a0e8cSRob Johnston {
305508a0e8cSRob Johnston 	int ret = 0;
306508a0e8cSRob Johnston 
307508a0e8cSRob Johnston 	if (drv_priv(credp) != 0)
308508a0e8cSRob Johnston 		return (EPERM);
309508a0e8cSRob Johnston 
310508a0e8cSRob Johnston 	switch (cmd) {
311508a0e8cSRob Johnston 	case UFMTEST_IOC_SET_FW:
312508a0e8cSRob Johnston 		ret = ufmtest_do_setfw(data, mode);
313508a0e8cSRob Johnston 		break;
314508a0e8cSRob Johnston 	case UFMTEST_IOC_TOGGLE_FAILS:
315508a0e8cSRob Johnston 		ret = ufmtest_do_toggle_fails(data, mode);
316508a0e8cSRob Johnston 		break;
317508a0e8cSRob Johnston 	case UFMTEST_IOC_DO_UPDATE:
318508a0e8cSRob Johnston 		ddi_ufm_update(ufmt.ufmt_ufmh);
319508a0e8cSRob Johnston 		break;
320508a0e8cSRob Johnston 	default:
321508a0e8cSRob Johnston 		return (ENOTTY);
322508a0e8cSRob Johnston 	}
323508a0e8cSRob Johnston 	return (ret);
324508a0e8cSRob Johnston }
325508a0e8cSRob Johnston 
326508a0e8cSRob Johnston static int
ufmtest_nimages(ddi_ufm_handle_t * ufmh,void * arg,uint_t * nimgs)327508a0e8cSRob Johnston ufmtest_nimages(ddi_ufm_handle_t *ufmh, void *arg, uint_t *nimgs)
328508a0e8cSRob Johnston {
329508a0e8cSRob Johnston 	nvlist_t **imgs;
330508a0e8cSRob Johnston 	uint_t ni;
331508a0e8cSRob Johnston 
332508a0e8cSRob Johnston 	if (ufmt.ufmt_failflags & UFMTEST_FAIL_NIMAGES ||
333508a0e8cSRob Johnston 	    ufmt.ufmt_nvl == NULL)
334508a0e8cSRob Johnston 		return (EINVAL);
335508a0e8cSRob Johnston 
336508a0e8cSRob Johnston 	if (nvlist_lookup_nvlist_array(ufmt.ufmt_nvl, DDI_UFM_NV_IMAGES, &imgs,
337508a0e8cSRob Johnston 	    &ni) != 0)
338508a0e8cSRob Johnston 		return (EINVAL);
339508a0e8cSRob Johnston 
340508a0e8cSRob Johnston 	*nimgs = ni;
341508a0e8cSRob Johnston 	return (0);
342508a0e8cSRob Johnston }
343508a0e8cSRob Johnston 
344508a0e8cSRob Johnston static int
ufmtest_fill_image(ddi_ufm_handle_t * ufmh,void * arg,uint_t imgno,ddi_ufm_image_t * img)345508a0e8cSRob Johnston ufmtest_fill_image(ddi_ufm_handle_t *ufmh, void *arg, uint_t imgno,
346508a0e8cSRob Johnston     ddi_ufm_image_t *img)
347508a0e8cSRob Johnston {
348508a0e8cSRob Johnston 	nvlist_t **images, *misc, *miscdup = NULL, **slots;
349508a0e8cSRob Johnston 	char *desc;
350508a0e8cSRob Johnston 	uint_t ni, ns;
351508a0e8cSRob Johnston 
352508a0e8cSRob Johnston 	if (ufmt.ufmt_failflags & UFMTEST_FAIL_FILLIMAGE ||
353508a0e8cSRob Johnston 	    ufmt.ufmt_nvl == NULL ||
354508a0e8cSRob Johnston 	    nvlist_lookup_nvlist_array(ufmt.ufmt_nvl, DDI_UFM_NV_IMAGES,
355508a0e8cSRob Johnston 	    &images, &ni) != 0)
356508a0e8cSRob Johnston 		goto err;
357508a0e8cSRob Johnston 
358508a0e8cSRob Johnston 	if (imgno >= ni)
359508a0e8cSRob Johnston 		goto err;
360508a0e8cSRob Johnston 
361508a0e8cSRob Johnston 	if (nvlist_lookup_string(images[imgno], DDI_UFM_NV_IMAGE_DESC,
362508a0e8cSRob Johnston 	    &desc) != 0 ||
363508a0e8cSRob Johnston 	    nvlist_lookup_nvlist_array(images[imgno], DDI_UFM_NV_IMAGE_SLOTS,
364508a0e8cSRob Johnston 	    &slots, &ns) != 0)
365508a0e8cSRob Johnston 		goto err;
366508a0e8cSRob Johnston 
367508a0e8cSRob Johnston 	ddi_ufm_image_set_desc(img, desc);
368508a0e8cSRob Johnston 	ddi_ufm_image_set_nslots(img, ns);
369508a0e8cSRob Johnston 
370508a0e8cSRob Johnston 	if (nvlist_lookup_nvlist(images[imgno], DDI_UFM_NV_IMAGE_MISC, &misc)
371508a0e8cSRob Johnston 	    == 0) {
372508a0e8cSRob Johnston 		if (nvlist_dup(misc, &miscdup, 0) != 0)
373508a0e8cSRob Johnston 			return (ENOMEM);
374508a0e8cSRob Johnston 
375508a0e8cSRob Johnston 		ddi_ufm_image_set_misc(img, miscdup);
376508a0e8cSRob Johnston 	}
377508a0e8cSRob Johnston 	return (0);
378508a0e8cSRob Johnston err:
379508a0e8cSRob Johnston 	return (EINVAL);
380508a0e8cSRob Johnston }
381508a0e8cSRob Johnston 
382508a0e8cSRob Johnston static int
ufmtest_fill_slot(ddi_ufm_handle_t * ufmh,void * arg,uint_t imgno,uint_t slotno,ddi_ufm_slot_t * slot)383508a0e8cSRob Johnston ufmtest_fill_slot(ddi_ufm_handle_t *ufmh, void *arg, uint_t imgno,
384508a0e8cSRob Johnston     uint_t slotno, ddi_ufm_slot_t *slot)
385508a0e8cSRob Johnston {
386508a0e8cSRob Johnston 	nvlist_t **images, *misc, *miscdup = NULL, **slots;
387508a0e8cSRob Johnston 	char *vers;
388508a0e8cSRob Johnston 	uint32_t attrs;
389508a0e8cSRob Johnston 	uint_t ni, ns;
390508a0e8cSRob Johnston 
391508a0e8cSRob Johnston 	if (ufmt.ufmt_failflags & UFMTEST_FAIL_FILLSLOT ||
392508a0e8cSRob Johnston 	    ufmt.ufmt_nvl == NULL ||
393508a0e8cSRob Johnston 	    nvlist_lookup_nvlist_array(ufmt.ufmt_nvl, DDI_UFM_NV_IMAGES,
394508a0e8cSRob Johnston 	    &images, &ni) != 0)
395508a0e8cSRob Johnston 		goto err;
396508a0e8cSRob Johnston 
397508a0e8cSRob Johnston 	if (imgno >= ni)
398508a0e8cSRob Johnston 		goto err;
399508a0e8cSRob Johnston 
400508a0e8cSRob Johnston 	if (nvlist_lookup_nvlist_array(images[imgno], DDI_UFM_NV_IMAGE_SLOTS,
401508a0e8cSRob Johnston 	    &slots, &ns) != 0)
402508a0e8cSRob Johnston 		goto err;
403508a0e8cSRob Johnston 
404508a0e8cSRob Johnston 	if (slotno >= ns)
405508a0e8cSRob Johnston 		goto err;
406508a0e8cSRob Johnston 
407508a0e8cSRob Johnston 	if (nvlist_lookup_uint32(slots[slotno], DDI_UFM_NV_SLOT_ATTR,
408508a0e8cSRob Johnston 	    &attrs) != 0)
409508a0e8cSRob Johnston 		goto err;
410508a0e8cSRob Johnston 
411508a0e8cSRob Johnston 	ddi_ufm_slot_set_attrs(slot, attrs);
412508a0e8cSRob Johnston 	if (attrs & DDI_UFM_ATTR_EMPTY)
413508a0e8cSRob Johnston 		return (0);
414508a0e8cSRob Johnston 
415508a0e8cSRob Johnston 	if (nvlist_lookup_string(slots[slotno], DDI_UFM_NV_SLOT_VERSION,
416508a0e8cSRob Johnston 	    &vers) != 0)
417508a0e8cSRob Johnston 		goto err;
418508a0e8cSRob Johnston 
419508a0e8cSRob Johnston 	ddi_ufm_slot_set_version(slot, vers);
420508a0e8cSRob Johnston 
421508a0e8cSRob Johnston 	if (nvlist_lookup_nvlist(slots[slotno], DDI_UFM_NV_SLOT_MISC, &misc) ==
422508a0e8cSRob Johnston 	    0) {
423508a0e8cSRob Johnston 		if (nvlist_dup(misc, &miscdup, 0) != 0)
424508a0e8cSRob Johnston 			return (ENOMEM);
425508a0e8cSRob Johnston 
426508a0e8cSRob Johnston 		ddi_ufm_slot_set_misc(slot, miscdup);
427508a0e8cSRob Johnston 	}
428508a0e8cSRob Johnston 	return (0);
429508a0e8cSRob Johnston err:
430508a0e8cSRob Johnston 	return (EINVAL);
431508a0e8cSRob Johnston }
432508a0e8cSRob Johnston 
433508a0e8cSRob Johnston static int
ufmtest_getcaps(ddi_ufm_handle_t * ufmh,void * arg,ddi_ufm_cap_t * caps)434508a0e8cSRob Johnston ufmtest_getcaps(ddi_ufm_handle_t *ufmh, void *arg, ddi_ufm_cap_t *caps)
435508a0e8cSRob Johnston {
436508a0e8cSRob Johnston 	if (ufmt.ufmt_failflags & UFMTEST_FAIL_GETCAPS)
437508a0e8cSRob Johnston 		return (EINVAL);
438508a0e8cSRob Johnston 
439508a0e8cSRob Johnston 	*caps = DDI_UFM_CAP_REPORT;
440508a0e8cSRob Johnston 
441508a0e8cSRob Johnston 	return (0);
442508a0e8cSRob Johnston }
443