xref: /illumos-gate/usr/src/uts/common/os/ddi_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 #include <sys/avl.h>
18508a0e8cSRob Johnston #include <sys/ddi_ufm.h>
19508a0e8cSRob Johnston #include <sys/ddi_ufm_impl.h>
20508a0e8cSRob Johnston #include <sys/debug.h>
21508a0e8cSRob Johnston #include <sys/kmem.h>
22508a0e8cSRob Johnston #include <sys/sunddi.h>
23508a0e8cSRob Johnston #include <sys/stddef.h>
248d55b806SRobert Mustacchi #include <sys/sunndi.h>
258d55b806SRobert Mustacchi #include <sys/file.h>
268d55b806SRobert Mustacchi #include <sys/sysmacros.h>
27508a0e8cSRob Johnston 
28508a0e8cSRob Johnston /*
29508a0e8cSRob Johnston  * The UFM subsystem tracks its internal state with respect to device
30508a0e8cSRob Johnston  * drivers that participate in the DDI UFM subsystem on a per-instance basis
31508a0e8cSRob Johnston  * via ddi_ufm_handle_t structures (see ddi_ufm_impl.h).  This is known as the
32508a0e8cSRob Johnston  * UFM handle.  The UFM handle contains a pointer to the driver's UFM ops,
33*bbf21555SRichard Lowe  * which the ufm(4D) pseudo driver uses to invoke the UFM entry points in
34508a0e8cSRob Johnston  * response to DDI UFM ioctls.  Additionally, the DDI UFM subsystem uses the
35508a0e8cSRob Johnston  * handle to maintain cached UFM image and slot data.
36508a0e8cSRob Johnston  *
37508a0e8cSRob Johnston  * In order to track and provide fast lookups of a driver instance's UFM
38508a0e8cSRob Johnston  * handle, the DDI UFM subsystem stores a pointer to the handle in a global AVL
39508a0e8cSRob Johnston  * tree. UFM handles are added to the tree when a driver calls ddi_ufm_init(9E)
40508a0e8cSRob Johnston  * and removed from the tree when a driver calls ddi_ufm_fini(9E).
41508a0e8cSRob Johnston  *
42508a0e8cSRob Johnston  * Some notes on the locking strategy/rules.
43508a0e8cSRob Johnston  *
44508a0e8cSRob Johnston  * All access to the tree is serialized via the mutex, ufm_lock.
45508a0e8cSRob Johnston  * Additionally, each UFM handle is protected by a per-handle mutex.
46508a0e8cSRob Johnston  *
47508a0e8cSRob Johnston  * Code must acquire ufm_lock in order to walk the tree.  Before reading or
48508a0e8cSRob Johnston  * modifying the state of any UFM handle, code must then acquire the
49508a0e8cSRob Johnston  * UFM handle lock.  Once the UFM handle lock has been acquired, ufm_lock
50508a0e8cSRob Johnston  * should be dropped.
51508a0e8cSRob Johnston  *
52508a0e8cSRob Johnston  * Only one UFM handle lock should be held at any time.
53508a0e8cSRob Johnston  * If a UFM handle lock is held, it must be released before attempting to
54508a0e8cSRob Johnston  * re-acquire ufm_lock.
55508a0e8cSRob Johnston  *
56508a0e8cSRob Johnston  * For example, the lock sequence for calling a UFM entry point and/or
57508a0e8cSRob Johnston  * reading/modifying UFM handle state would be as follows:
58508a0e8cSRob Johnston  * - acquire ufm_lock
59508a0e8cSRob Johnston  * - walk tree to find UFH handle
60508a0e8cSRob Johnston  * - acquire UFM handle lock
61508a0e8cSRob Johnston  * - release ufm_lock
62508a0e8cSRob Johnston  * - call entry point and/or access handle state
63508a0e8cSRob Johnston  *
64508a0e8cSRob Johnston  * Testing
65508a0e8cSRob Johnston  * -------
66508a0e8cSRob Johnston  * A set of automated tests for the DDI UFM subsystem exists at:
67508a0e8cSRob Johnston  * usr/src/test/os-tests/tests/ddi_ufm/
68508a0e8cSRob Johnston  *
69508a0e8cSRob Johnston  * These tests should be run whenever changes are made to the DDI UFM
70508a0e8cSRob Johnston  * subsystem or the ufm driver.
71508a0e8cSRob Johnston  */
728d55b806SRobert Mustacchi 
738d55b806SRobert Mustacchi /*
748d55b806SRobert Mustacchi  * Amount of data to read in one go (1 MiB).
758d55b806SRobert Mustacchi  */
768d55b806SRobert Mustacchi #define	UFM_READ_STRIDE	(1024 * 1024)
778d55b806SRobert Mustacchi 
78508a0e8cSRob Johnston static avl_tree_t ufm_handles;
79508a0e8cSRob Johnston static kmutex_t ufm_lock;
80508a0e8cSRob Johnston 
81508a0e8cSRob Johnston static int ufm_handle_compare(const void *, const void *);
82508a0e8cSRob Johnston 
83508a0e8cSRob Johnston static void
ufm_cache_invalidate(ddi_ufm_handle_t * ufmh)84508a0e8cSRob Johnston ufm_cache_invalidate(ddi_ufm_handle_t *ufmh)
85508a0e8cSRob Johnston {
86508a0e8cSRob Johnston 	ASSERT(MUTEX_HELD(&ufmh->ufmh_lock));
87508a0e8cSRob Johnston 
88508a0e8cSRob Johnston 	if (ufmh->ufmh_images == NULL)
89508a0e8cSRob Johnston 		return;
90508a0e8cSRob Johnston 
91508a0e8cSRob Johnston 	for (uint_t i = 0; i < ufmh->ufmh_nimages; i++) {
92508a0e8cSRob Johnston 		struct ddi_ufm_image *img = &ufmh->ufmh_images[i];
93508a0e8cSRob Johnston 
94508a0e8cSRob Johnston 		if (img->ufmi_slots == NULL)
95508a0e8cSRob Johnston 			continue;
96508a0e8cSRob Johnston 
97508a0e8cSRob Johnston 		for (uint_t s = 0; s < img->ufmi_nslots; s++) {
98508a0e8cSRob Johnston 			struct ddi_ufm_slot *slot = &img->ufmi_slots[s];
99508a0e8cSRob Johnston 
100508a0e8cSRob Johnston 			if (slot->ufms_version != NULL)
101508a0e8cSRob Johnston 				strfree(slot->ufms_version);
102508a0e8cSRob Johnston 			nvlist_free(slot->ufms_misc);
103508a0e8cSRob Johnston 		}
104508a0e8cSRob Johnston 		kmem_free(img->ufmi_slots,
105508a0e8cSRob Johnston 		    (img->ufmi_nslots * sizeof (ddi_ufm_slot_t)));
106508a0e8cSRob Johnston 		if (img->ufmi_desc != NULL)
107508a0e8cSRob Johnston 			strfree(img->ufmi_desc);
108508a0e8cSRob Johnston 		nvlist_free(img->ufmi_misc);
109508a0e8cSRob Johnston 	}
110508a0e8cSRob Johnston 
111508a0e8cSRob Johnston 	kmem_free(ufmh->ufmh_images,
112508a0e8cSRob Johnston 	    (ufmh->ufmh_nimages * sizeof (ddi_ufm_image_t)));
113508a0e8cSRob Johnston 	ufmh->ufmh_images = NULL;
114508a0e8cSRob Johnston 	ufmh->ufmh_nimages = 0;
115508a0e8cSRob Johnston 	ufmh->ufmh_caps = 0;
116508a0e8cSRob Johnston 	nvlist_free(ufmh->ufmh_report);
117508a0e8cSRob Johnston 	ufmh->ufmh_report = NULL;
118508a0e8cSRob Johnston }
119508a0e8cSRob Johnston 
120508a0e8cSRob Johnston static void
free_nvlist_array(nvlist_t ** nvlarr,uint_t nelems)121508a0e8cSRob Johnston free_nvlist_array(nvlist_t **nvlarr, uint_t nelems)
122508a0e8cSRob Johnston {
123508a0e8cSRob Johnston 	for (uint_t i = 0; i < nelems; i++) {
124508a0e8cSRob Johnston 		if (nvlarr[i] != NULL)
125508a0e8cSRob Johnston 			nvlist_free(nvlarr[i]);
126508a0e8cSRob Johnston 	}
127508a0e8cSRob Johnston 	kmem_free(nvlarr, nelems * sizeof (nvlist_t *));
128508a0e8cSRob Johnston }
129508a0e8cSRob Johnston 
130508a0e8cSRob Johnston int
ufm_cache_fill(ddi_ufm_handle_t * ufmh)131508a0e8cSRob Johnston ufm_cache_fill(ddi_ufm_handle_t *ufmh)
132508a0e8cSRob Johnston {
133508a0e8cSRob Johnston 	int ret;
134508a0e8cSRob Johnston 	uint_t nimgs;
135508a0e8cSRob Johnston 	ddi_ufm_cap_t caps;
136508a0e8cSRob Johnston 	nvlist_t **images = NULL, **slots = NULL;
137508a0e8cSRob Johnston 
138508a0e8cSRob Johnston 	ASSERT(MUTEX_HELD(&ufmh->ufmh_lock));
139508a0e8cSRob Johnston 
140508a0e8cSRob Johnston 	/*
141508a0e8cSRob Johnston 	 * Check whether we already have a cached report and if so, return
142508a0e8cSRob Johnston 	 * straight away.
143508a0e8cSRob Johnston 	 */
144508a0e8cSRob Johnston 	if (ufmh->ufmh_report != NULL)
145508a0e8cSRob Johnston 		return (0);
146508a0e8cSRob Johnston 
147508a0e8cSRob Johnston 	/*
148508a0e8cSRob Johnston 	 * First check which UFM caps this driver supports.  If it doesn't
149508a0e8cSRob Johnston 	 * support DDI_UFM_CAP_REPORT, then there's nothing to cache and we
150508a0e8cSRob Johnston 	 * can just return.
151508a0e8cSRob Johnston 	 */
152508a0e8cSRob Johnston 	ret = ufmh->ufmh_ops->ddi_ufm_op_getcaps(ufmh, ufmh->ufmh_arg, &caps);
153508a0e8cSRob Johnston 	if (ret != 0)
154508a0e8cSRob Johnston 		return (ret);
155508a0e8cSRob Johnston 
156508a0e8cSRob Johnston 	ufmh->ufmh_caps = caps;
157508a0e8cSRob Johnston 	if ((ufmh->ufmh_caps & DDI_UFM_CAP_REPORT) == 0)
158508a0e8cSRob Johnston 		return (ENOTSUP);
159508a0e8cSRob Johnston 
160508a0e8cSRob Johnston 	/*
161508a0e8cSRob Johnston 	 * Next, figure out how many UFM images the device has.  If a
162508a0e8cSRob Johnston 	 * ddi_ufm_op_nimages entry point wasn't specified, then we assume
163508a0e8cSRob Johnston 	 * that the device has a single image.
164508a0e8cSRob Johnston 	 */
165508a0e8cSRob Johnston 	if (ufmh->ufmh_ops->ddi_ufm_op_nimages != NULL) {
166508a0e8cSRob Johnston 		ret = ufmh->ufmh_ops->ddi_ufm_op_nimages(ufmh, ufmh->ufmh_arg,
167508a0e8cSRob Johnston 		    &nimgs);
168508a0e8cSRob Johnston 		if (ret == 0 && nimgs > 0)
169508a0e8cSRob Johnston 			ufmh->ufmh_nimages = nimgs;
170508a0e8cSRob Johnston 		else
171508a0e8cSRob Johnston 			goto cache_fail;
172508a0e8cSRob Johnston 	} else {
173508a0e8cSRob Johnston 		ufmh->ufmh_nimages = 1;
174508a0e8cSRob Johnston 	}
175508a0e8cSRob Johnston 
176508a0e8cSRob Johnston 	/*
177508a0e8cSRob Johnston 	 * Now that we know how many images we're dealing with, allocate space
178508a0e8cSRob Johnston 	 * for an appropriately-sized array of ddi_ufm_image_t structs and then
179508a0e8cSRob Johnston 	 * iterate through them calling the ddi_ufm_op_fill_image entry point
180508a0e8cSRob Johnston 	 * so that the driver can fill them in.
181508a0e8cSRob Johnston 	 */
182508a0e8cSRob Johnston 	ufmh->ufmh_images =
183508a0e8cSRob Johnston 	    kmem_zalloc((sizeof (ddi_ufm_image_t) * ufmh->ufmh_nimages),
184ca783257SDan McDonald 	    KM_NOSLEEP_LAZY);
185508a0e8cSRob Johnston 	if (ufmh->ufmh_images == NULL)
186508a0e8cSRob Johnston 		return (ENOMEM);
187508a0e8cSRob Johnston 
188508a0e8cSRob Johnston 	for (uint_t i = 0; i < ufmh->ufmh_nimages; i++) {
189508a0e8cSRob Johnston 		struct ddi_ufm_image *img = &ufmh->ufmh_images[i];
190508a0e8cSRob Johnston 
191508a0e8cSRob Johnston 		ret = ufmh->ufmh_ops->ddi_ufm_op_fill_image(ufmh,
192508a0e8cSRob Johnston 		    ufmh->ufmh_arg, i, img);
193508a0e8cSRob Johnston 
194508a0e8cSRob Johnston 		if (ret != 0)
195508a0e8cSRob Johnston 			goto cache_fail;
196508a0e8cSRob Johnston 
197e89be50aSRob Johnston 		if (img->ufmi_desc == NULL || img->ufmi_nslots == 0) {
198e89be50aSRob Johnston 			ret = EIO;
199e89be50aSRob Johnston 			goto cache_fail;
200e89be50aSRob Johnston 		}
201508a0e8cSRob Johnston 
202508a0e8cSRob Johnston 		img->ufmi_slots =
203508a0e8cSRob Johnston 		    kmem_zalloc((sizeof (ddi_ufm_slot_t) * img->ufmi_nslots),
204ca783257SDan McDonald 		    KM_NOSLEEP_LAZY);
205508a0e8cSRob Johnston 		if (img->ufmi_slots == NULL) {
206508a0e8cSRob Johnston 			ret = ENOMEM;
207508a0e8cSRob Johnston 			goto cache_fail;
208508a0e8cSRob Johnston 		}
209508a0e8cSRob Johnston 
210508a0e8cSRob Johnston 		for (uint_t s = 0; s < img->ufmi_nslots; s++) {
211508a0e8cSRob Johnston 			struct ddi_ufm_slot *slot = &img->ufmi_slots[s];
212508a0e8cSRob Johnston 
213508a0e8cSRob Johnston 			ret = ufmh->ufmh_ops->ddi_ufm_op_fill_slot(ufmh,
214508a0e8cSRob Johnston 			    ufmh->ufmh_arg, i, s, slot);
215508a0e8cSRob Johnston 
216508a0e8cSRob Johnston 			if (ret != 0)
217508a0e8cSRob Johnston 				goto cache_fail;
218508a0e8cSRob Johnston 
219508a0e8cSRob Johnston 			ASSERT(slot->ufms_attrs & DDI_UFM_ATTR_EMPTY ||
220508a0e8cSRob Johnston 			    slot->ufms_version != NULL);
221508a0e8cSRob Johnston 		}
222508a0e8cSRob Johnston 	}
223508a0e8cSRob Johnston 	images = kmem_zalloc(sizeof (nvlist_t *) * ufmh->ufmh_nimages,
224508a0e8cSRob Johnston 	    KM_SLEEP);
225508a0e8cSRob Johnston 	for (uint_t i = 0; i < ufmh->ufmh_nimages; i ++) {
226508a0e8cSRob Johnston 		ddi_ufm_image_t *img = &ufmh->ufmh_images[i];
227508a0e8cSRob Johnston 
228508a0e8cSRob Johnston 		images[i] = fnvlist_alloc();
229508a0e8cSRob Johnston 		fnvlist_add_string(images[i], DDI_UFM_NV_IMAGE_DESC,
230508a0e8cSRob Johnston 		    img->ufmi_desc);
231508a0e8cSRob Johnston 		if (img->ufmi_misc != NULL) {
232508a0e8cSRob Johnston 			fnvlist_add_nvlist(images[i], DDI_UFM_NV_IMAGE_MISC,
233508a0e8cSRob Johnston 			    img->ufmi_misc);
234508a0e8cSRob Johnston 		}
235508a0e8cSRob Johnston 
236508a0e8cSRob Johnston 		slots = kmem_zalloc(sizeof (nvlist_t *) * img->ufmi_nslots,
237508a0e8cSRob Johnston 		    KM_SLEEP);
238508a0e8cSRob Johnston 		for (uint_t s = 0; s < img->ufmi_nslots; s++) {
239508a0e8cSRob Johnston 			ddi_ufm_slot_t *slot = &img->ufmi_slots[s];
240508a0e8cSRob Johnston 
241508a0e8cSRob Johnston 			slots[s] = fnvlist_alloc();
242508a0e8cSRob Johnston 			fnvlist_add_uint32(slots[s], DDI_UFM_NV_SLOT_ATTR,
243508a0e8cSRob Johnston 			    slot->ufms_attrs);
244508a0e8cSRob Johnston 			if (slot->ufms_attrs & DDI_UFM_ATTR_EMPTY)
245508a0e8cSRob Johnston 				continue;
246508a0e8cSRob Johnston 
2478d55b806SRobert Mustacchi 			if (slot->ufms_imgsize != 0) {
2488d55b806SRobert Mustacchi 				fnvlist_add_uint64(slots[s],
2498d55b806SRobert Mustacchi 				    DDI_UFM_NV_SLOT_IMGSIZE,
2508d55b806SRobert Mustacchi 				    slot->ufms_imgsize);
2518d55b806SRobert Mustacchi 			}
2528d55b806SRobert Mustacchi 
253508a0e8cSRob Johnston 			fnvlist_add_string(slots[s], DDI_UFM_NV_SLOT_VERSION,
254508a0e8cSRob Johnston 			    slot->ufms_version);
255508a0e8cSRob Johnston 			if (slot->ufms_misc != NULL) {
256508a0e8cSRob Johnston 				fnvlist_add_nvlist(slots[s],
257508a0e8cSRob Johnston 				    DDI_UFM_NV_SLOT_MISC, slot->ufms_misc);
258508a0e8cSRob Johnston 			}
259508a0e8cSRob Johnston 		}
260508a0e8cSRob Johnston 		fnvlist_add_nvlist_array(images[i], DDI_UFM_NV_IMAGE_SLOTS,
261508a0e8cSRob Johnston 		    slots, img->ufmi_nslots);
262508a0e8cSRob Johnston 		free_nvlist_array(slots, img->ufmi_nslots);
263508a0e8cSRob Johnston 	}
264508a0e8cSRob Johnston 	ufmh->ufmh_report = fnvlist_alloc();
265508a0e8cSRob Johnston 	fnvlist_add_nvlist_array(ufmh->ufmh_report, DDI_UFM_NV_IMAGES, images,
266508a0e8cSRob Johnston 	    ufmh->ufmh_nimages);
267508a0e8cSRob Johnston 	free_nvlist_array(images, ufmh->ufmh_nimages);
268508a0e8cSRob Johnston 
269508a0e8cSRob Johnston 	return (0);
270508a0e8cSRob Johnston 
271508a0e8cSRob Johnston cache_fail:
272508a0e8cSRob Johnston 	ufm_cache_invalidate(ufmh);
273508a0e8cSRob Johnston 	return (ret);
274508a0e8cSRob Johnston }
275508a0e8cSRob Johnston 
2768d55b806SRobert Mustacchi int
ufm_read_img(ddi_ufm_handle_t * ufmh,uint_t img,uint_t slot,uint64_t len,uint64_t off,uintptr_t uaddr,uint64_t * nreadp,int copyflags)2778d55b806SRobert Mustacchi ufm_read_img(ddi_ufm_handle_t *ufmh, uint_t img, uint_t slot, uint64_t len,
2788d55b806SRobert Mustacchi     uint64_t off, uintptr_t uaddr, uint64_t *nreadp, int copyflags)
2798d55b806SRobert Mustacchi {
2808d55b806SRobert Mustacchi 	int ret = 0;
2818d55b806SRobert Mustacchi 	ddi_ufm_cap_t caps;
2828d55b806SRobert Mustacchi 	void *buf;
2838d55b806SRobert Mustacchi 	uint64_t nread;
2848d55b806SRobert Mustacchi 
2858d55b806SRobert Mustacchi 	ret = ufmh->ufmh_ops->ddi_ufm_op_getcaps(ufmh, ufmh->ufmh_arg, &caps);
2868d55b806SRobert Mustacchi 	if (ret != 0) {
2878d55b806SRobert Mustacchi 		return (ret);
2888d55b806SRobert Mustacchi 	}
2898d55b806SRobert Mustacchi 
2908d55b806SRobert Mustacchi 	if ((caps & DDI_UFM_CAP_READIMG) == 0 ||
2918d55b806SRobert Mustacchi 	    ufmh->ufmh_ops->ddi_ufm_op_readimg == NULL) {
2928d55b806SRobert Mustacchi 		return (ENOTSUP);
2938d55b806SRobert Mustacchi 	}
2948d55b806SRobert Mustacchi 
2958d55b806SRobert Mustacchi 	if (off + len < MAX(off, len)) {
2968d55b806SRobert Mustacchi 		return (EOVERFLOW);
2978d55b806SRobert Mustacchi 	}
2988d55b806SRobert Mustacchi 
2998d55b806SRobert Mustacchi 	buf = kmem_zalloc(UFM_READ_STRIDE, KM_SLEEP);
3008d55b806SRobert Mustacchi 	nread = 0;
3018d55b806SRobert Mustacchi 	while (len > 0) {
3028d55b806SRobert Mustacchi 		uint64_t toread = MIN(len, UFM_READ_STRIDE);
3038d55b806SRobert Mustacchi 		uint64_t iter;
3048d55b806SRobert Mustacchi 
3058d55b806SRobert Mustacchi 		ret = ufmh->ufmh_ops->ddi_ufm_op_readimg(ufmh, ufmh->ufmh_arg,
3068d55b806SRobert Mustacchi 		    img, slot, toread, off + nread, buf, &iter);
3078d55b806SRobert Mustacchi 		if (ret != 0) {
3088d55b806SRobert Mustacchi 			break;
3098d55b806SRobert Mustacchi 		}
3108d55b806SRobert Mustacchi 
3118d55b806SRobert Mustacchi 		if (ddi_copyout(buf, (void *)(uintptr_t)(uaddr + nread), iter,
3128d55b806SRobert Mustacchi 		    copyflags & FKIOCTL) != 0) {
3138d55b806SRobert Mustacchi 			ret = EFAULT;
3148d55b806SRobert Mustacchi 			break;
3158d55b806SRobert Mustacchi 		}
3168d55b806SRobert Mustacchi 
3178d55b806SRobert Mustacchi 		nread += iter;
3188d55b806SRobert Mustacchi 		len -= iter;
3198d55b806SRobert Mustacchi 	}
3208d55b806SRobert Mustacchi 
3218d55b806SRobert Mustacchi 	*nreadp = nread;
3228d55b806SRobert Mustacchi 	kmem_free(buf, UFM_READ_STRIDE);
3238d55b806SRobert Mustacchi 	return (ret);
3248d55b806SRobert Mustacchi }
3258d55b806SRobert Mustacchi 
326508a0e8cSRob Johnston /*
327508a0e8cSRob Johnston  * This gets called early in boot by setup_ddi().
328508a0e8cSRob Johnston  */
329508a0e8cSRob Johnston void
ufm_init(void)330508a0e8cSRob Johnston ufm_init(void)
331508a0e8cSRob Johnston {
332508a0e8cSRob Johnston 	mutex_init(&ufm_lock, NULL, MUTEX_DEFAULT, NULL);
333508a0e8cSRob Johnston 
334508a0e8cSRob Johnston 	avl_create(&ufm_handles, ufm_handle_compare,
335508a0e8cSRob Johnston 	    sizeof (ddi_ufm_handle_t),
336508a0e8cSRob Johnston 	    offsetof(ddi_ufm_handle_t, ufmh_link));
337508a0e8cSRob Johnston }
338508a0e8cSRob Johnston 
339508a0e8cSRob Johnston static int
ufm_handle_compare(const void * a1,const void * a2)340508a0e8cSRob Johnston ufm_handle_compare(const void *a1, const void *a2)
341508a0e8cSRob Johnston {
342508a0e8cSRob Johnston 	const struct ddi_ufm_handle *hdl1, *hdl2;
343508a0e8cSRob Johnston 	int cmp;
344508a0e8cSRob Johnston 
345508a0e8cSRob Johnston 	hdl1 = (struct ddi_ufm_handle *)a1;
346508a0e8cSRob Johnston 	hdl2 = (struct ddi_ufm_handle *)a2;
347508a0e8cSRob Johnston 
348508a0e8cSRob Johnston 	cmp = strcmp(hdl1->ufmh_devpath, hdl2->ufmh_devpath);
349508a0e8cSRob Johnston 
350508a0e8cSRob Johnston 	if (cmp > 0)
351508a0e8cSRob Johnston 		return (1);
352508a0e8cSRob Johnston 	else if (cmp < 0)
353508a0e8cSRob Johnston 		return (-1);
354508a0e8cSRob Johnston 	else
355508a0e8cSRob Johnston 		return (0);
356508a0e8cSRob Johnston }
357508a0e8cSRob Johnston 
358508a0e8cSRob Johnston /*
359508a0e8cSRob Johnston  * This is used by the ufm driver to lookup the UFM handle associated with a
360508a0e8cSRob Johnston  * particular devpath.
361508a0e8cSRob Johnston  *
362508a0e8cSRob Johnston  * On success, this function returns the reqested UFH handle, with its lock
363508a0e8cSRob Johnston  * held.  Caller is responsible to dropping the lock when it is done with the
364508a0e8cSRob Johnston  * handle.
365508a0e8cSRob Johnston  */
366508a0e8cSRob Johnston struct ddi_ufm_handle *
ufm_find(const char * devpath)367508a0e8cSRob Johnston ufm_find(const char *devpath)
368508a0e8cSRob Johnston {
369508a0e8cSRob Johnston 	struct ddi_ufm_handle find = { 0 }, *ufmh;
370508a0e8cSRob Johnston 
371508a0e8cSRob Johnston 	(void) strlcpy(find.ufmh_devpath, devpath, MAXPATHLEN);
372508a0e8cSRob Johnston 
373508a0e8cSRob Johnston 	mutex_enter(&ufm_lock);
374508a0e8cSRob Johnston 	ufmh = avl_find(&ufm_handles, &find, NULL);
375508a0e8cSRob Johnston 	if (ufmh != NULL)
376508a0e8cSRob Johnston 		mutex_enter(&ufmh->ufmh_lock);
377508a0e8cSRob Johnston 	mutex_exit(&ufm_lock);
378508a0e8cSRob Johnston 
379508a0e8cSRob Johnston 	return (ufmh);
380508a0e8cSRob Johnston }
381508a0e8cSRob Johnston 
382508a0e8cSRob Johnston int
ddi_ufm_init(dev_info_t * dip,uint_t version,ddi_ufm_ops_t * ufmops,ddi_ufm_handle_t ** ufmh,void * arg)383508a0e8cSRob Johnston ddi_ufm_init(dev_info_t *dip, uint_t version, ddi_ufm_ops_t *ufmops,
384508a0e8cSRob Johnston     ddi_ufm_handle_t **ufmh, void *arg)
385508a0e8cSRob Johnston {
386508a0e8cSRob Johnston 	ddi_ufm_handle_t *old_ufmh;
387508a0e8cSRob Johnston 	char devpath[MAXPATHLEN];
388508a0e8cSRob Johnston 
389508a0e8cSRob Johnston 	VERIFY(version != 0 && ufmops != NULL);
390508a0e8cSRob Johnston 	VERIFY(ufmops->ddi_ufm_op_fill_image != NULL &&
391508a0e8cSRob Johnston 	    ufmops->ddi_ufm_op_fill_slot != NULL &&
392508a0e8cSRob Johnston 	    ufmops->ddi_ufm_op_getcaps != NULL);
393508a0e8cSRob Johnston 
394508a0e8cSRob Johnston 	if (version < DDI_UFM_VERSION_ONE || version > DDI_UFM_CURRENT_VERSION)
395508a0e8cSRob Johnston 		return (ENOTSUP);
396508a0e8cSRob Johnston 
397508a0e8cSRob Johnston 	/*
398508a0e8cSRob Johnston 	 * First we check if we already have a UFM handle for this device
399508a0e8cSRob Johnston 	 * instance.  This can happen if the module got unloaded or the driver
400508a0e8cSRob Johnston 	 * was suspended after previously registering with the UFM subsystem.
401508a0e8cSRob Johnston 	 *
402508a0e8cSRob Johnston 	 * If we find an old handle then we simply reset its state and hand it
403508a0e8cSRob Johnston 	 * back to the driver.
404508a0e8cSRob Johnston 	 *
405508a0e8cSRob Johnston 	 * If we don't find an old handle then this is a new registration, so
406508a0e8cSRob Johnston 	 * we allocate and initialize a new handle.
407508a0e8cSRob Johnston 	 *
408508a0e8cSRob Johnston 	 * In either case, we don't need to NULL-out the other fields (like
409508a0e8cSRob Johnston 	 * ufmh_report) as in order for them to be referenced, ufmh_state has to
410508a0e8cSRob Johnston 	 * first transition to DDI_UFM_STATE_READY.  The only way that can
411508a0e8cSRob Johnston 	 * happen is for the driver to call ddi_ufm_update(), which will call
412508a0e8cSRob Johnston 	 * ufm_cache_invalidate(), which in turn will take care of properly
413508a0e8cSRob Johnston 	 * cleaning up and reinitializing the other fields in the handle.
414508a0e8cSRob Johnston 	 */
415508a0e8cSRob Johnston 	(void) ddi_pathname(dip, devpath);
416508a0e8cSRob Johnston 	if ((old_ufmh = ufm_find(devpath)) != NULL) {
417508a0e8cSRob Johnston 		*ufmh = old_ufmh;
418508a0e8cSRob Johnston 	} else {
419508a0e8cSRob Johnston 		*ufmh = kmem_zalloc(sizeof (ddi_ufm_handle_t), KM_SLEEP);
420508a0e8cSRob Johnston 		(void) strlcpy((*ufmh)->ufmh_devpath, devpath, MAXPATHLEN);
421508a0e8cSRob Johnston 		mutex_init(&(*ufmh)->ufmh_lock, NULL, MUTEX_DEFAULT, NULL);
422508a0e8cSRob Johnston 	}
423508a0e8cSRob Johnston 	(*ufmh)->ufmh_ops = ufmops;
424508a0e8cSRob Johnston 	(*ufmh)->ufmh_arg = arg;
425508a0e8cSRob Johnston 	(*ufmh)->ufmh_version = version;
426508a0e8cSRob Johnston 	(*ufmh)->ufmh_state = DDI_UFM_STATE_INIT;
427508a0e8cSRob Johnston 
428508a0e8cSRob Johnston 	/*
429508a0e8cSRob Johnston 	 * If this is a new registration, add the UFM handle to the global AVL
430508a0e8cSRob Johnston 	 * tree of handles.
431508a0e8cSRob Johnston 	 *
432508a0e8cSRob Johnston 	 * Otherwise, if it's an old registration then ufm_find() will have
433508a0e8cSRob Johnston 	 * returned the old handle with the lock already held, so we need to
434508a0e8cSRob Johnston 	 * release it before returning.
435508a0e8cSRob Johnston 	 */
436508a0e8cSRob Johnston 	if (old_ufmh == NULL) {
437508a0e8cSRob Johnston 		mutex_enter(&ufm_lock);
438508a0e8cSRob Johnston 		avl_add(&ufm_handles, *ufmh);
439508a0e8cSRob Johnston 		mutex_exit(&ufm_lock);
440508a0e8cSRob Johnston 	} else {
441508a0e8cSRob Johnston 		mutex_exit(&old_ufmh->ufmh_lock);
442508a0e8cSRob Johnston 	}
443508a0e8cSRob Johnston 
4448d55b806SRobert Mustacchi 	/*
4458d55b806SRobert Mustacchi 	 * Give a hint in the devinfo tree that this device supports UFM
4468d55b806SRobert Mustacchi 	 * capabilities.
4478d55b806SRobert Mustacchi 	 */
4488d55b806SRobert Mustacchi 	(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip, "ddi-ufm-capable");
4498d55b806SRobert Mustacchi 
450508a0e8cSRob Johnston 	return (DDI_SUCCESS);
451508a0e8cSRob Johnston }
452508a0e8cSRob Johnston 
453508a0e8cSRob Johnston void
ddi_ufm_fini(ddi_ufm_handle_t * ufmh)454508a0e8cSRob Johnston ddi_ufm_fini(ddi_ufm_handle_t *ufmh)
455508a0e8cSRob Johnston {
456508a0e8cSRob Johnston 	VERIFY(ufmh != NULL);
457508a0e8cSRob Johnston 
458508a0e8cSRob Johnston 	mutex_enter(&ufmh->ufmh_lock);
459508a0e8cSRob Johnston 	ufmh->ufmh_state |= DDI_UFM_STATE_SHUTTING_DOWN;
460508a0e8cSRob Johnston 	ufm_cache_invalidate(ufmh);
461508a0e8cSRob Johnston 	mutex_exit(&ufmh->ufmh_lock);
462508a0e8cSRob Johnston }
463508a0e8cSRob Johnston 
464508a0e8cSRob Johnston void
ddi_ufm_update(ddi_ufm_handle_t * ufmh)465508a0e8cSRob Johnston ddi_ufm_update(ddi_ufm_handle_t *ufmh)
466508a0e8cSRob Johnston {
467508a0e8cSRob Johnston 	VERIFY(ufmh != NULL);
468508a0e8cSRob Johnston 
469508a0e8cSRob Johnston 	mutex_enter(&ufmh->ufmh_lock);
470508a0e8cSRob Johnston 	if (ufmh->ufmh_state & DDI_UFM_STATE_SHUTTING_DOWN) {
471508a0e8cSRob Johnston 		mutex_exit(&ufmh->ufmh_lock);
472508a0e8cSRob Johnston 		return;
473508a0e8cSRob Johnston 	}
474508a0e8cSRob Johnston 	ufm_cache_invalidate(ufmh);
475508a0e8cSRob Johnston 	ufmh->ufmh_state |= DDI_UFM_STATE_READY;
476508a0e8cSRob Johnston 	mutex_exit(&ufmh->ufmh_lock);
477508a0e8cSRob Johnston }
478508a0e8cSRob Johnston 
479508a0e8cSRob Johnston void
ddi_ufm_image_set_desc(ddi_ufm_image_t * uip,const char * desc)480508a0e8cSRob Johnston ddi_ufm_image_set_desc(ddi_ufm_image_t *uip, const char *desc)
481508a0e8cSRob Johnston {
482508a0e8cSRob Johnston 	VERIFY(uip != NULL && desc != NULL);
483508a0e8cSRob Johnston 	if (uip->ufmi_desc != NULL)
484508a0e8cSRob Johnston 		strfree(uip->ufmi_desc);
485508a0e8cSRob Johnston 
486508a0e8cSRob Johnston 	uip->ufmi_desc = ddi_strdup(desc, KM_SLEEP);
487508a0e8cSRob Johnston }
488508a0e8cSRob Johnston 
489508a0e8cSRob Johnston void
ddi_ufm_image_set_nslots(ddi_ufm_image_t * uip,uint_t nslots)490508a0e8cSRob Johnston ddi_ufm_image_set_nslots(ddi_ufm_image_t *uip, uint_t nslots)
491508a0e8cSRob Johnston {
492508a0e8cSRob Johnston 	VERIFY(uip != NULL);
493508a0e8cSRob Johnston 	uip->ufmi_nslots = nslots;
494508a0e8cSRob Johnston }
495508a0e8cSRob Johnston 
496508a0e8cSRob Johnston void
ddi_ufm_image_set_misc(ddi_ufm_image_t * uip,nvlist_t * misc)497508a0e8cSRob Johnston ddi_ufm_image_set_misc(ddi_ufm_image_t *uip, nvlist_t *misc)
498508a0e8cSRob Johnston {
499508a0e8cSRob Johnston 	VERIFY(uip != NULL && misc != NULL);
500508a0e8cSRob Johnston 	nvlist_free(uip->ufmi_misc);
501508a0e8cSRob Johnston 	uip->ufmi_misc = misc;
502508a0e8cSRob Johnston }
503508a0e8cSRob Johnston 
504508a0e8cSRob Johnston void
ddi_ufm_slot_set_version(ddi_ufm_slot_t * usp,const char * version)505508a0e8cSRob Johnston ddi_ufm_slot_set_version(ddi_ufm_slot_t *usp, const char *version)
506508a0e8cSRob Johnston {
507508a0e8cSRob Johnston 	VERIFY(usp != NULL && version != NULL);
508508a0e8cSRob Johnston 	if (usp->ufms_version != NULL)
509508a0e8cSRob Johnston 		strfree(usp->ufms_version);
510508a0e8cSRob Johnston 
511508a0e8cSRob Johnston 	usp->ufms_version = ddi_strdup(version, KM_SLEEP);
512508a0e8cSRob Johnston }
513508a0e8cSRob Johnston 
514508a0e8cSRob Johnston void
ddi_ufm_slot_set_attrs(ddi_ufm_slot_t * usp,ddi_ufm_attr_t attr)515508a0e8cSRob Johnston ddi_ufm_slot_set_attrs(ddi_ufm_slot_t *usp, ddi_ufm_attr_t attr)
516508a0e8cSRob Johnston {
517508a0e8cSRob Johnston 	VERIFY(usp != NULL && attr <= DDI_UFM_ATTR_MAX);
518508a0e8cSRob Johnston 	usp->ufms_attrs = attr;
519508a0e8cSRob Johnston }
520508a0e8cSRob Johnston 
521508a0e8cSRob Johnston void
ddi_ufm_slot_set_misc(ddi_ufm_slot_t * usp,nvlist_t * misc)522508a0e8cSRob Johnston ddi_ufm_slot_set_misc(ddi_ufm_slot_t *usp, nvlist_t *misc)
523508a0e8cSRob Johnston {
524508a0e8cSRob Johnston 	VERIFY(usp != NULL && misc != NULL);
525508a0e8cSRob Johnston 	nvlist_free(usp->ufms_misc);
526508a0e8cSRob Johnston 	usp->ufms_misc = misc;
527508a0e8cSRob Johnston }
5288d55b806SRobert Mustacchi 
5298d55b806SRobert Mustacchi void
ddi_ufm_slot_set_imgsize(ddi_ufm_slot_t * usp,uint64_t size)5308d55b806SRobert Mustacchi ddi_ufm_slot_set_imgsize(ddi_ufm_slot_t *usp, uint64_t size)
5318d55b806SRobert Mustacchi {
5328d55b806SRobert Mustacchi 	VERIFY3P(usp, !=, NULL);
5338d55b806SRobert Mustacchi 	usp->ufms_imgsize = size;
5348d55b806SRobert Mustacchi }
535