1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <ctype.h>
30 #include <string.h>
31 #include <limits.h>
32 #include <fm/topo_mod.h>
33 #include <sys/fm/protocol.h>
34 #include <topo_alloc.h>
35 #include <topo_error.h>
36 #include <topo_hc.h>
37 #include <topo_method.h>
38 #include <topo_subr.h>
39 #include <topo_string.h>
40 
41 /*
42  * Topology node properties and method operations may be accessed by FMRI.
43  * The FMRI used to perform property look-ups and method operations is
44  * the FMRI contained in the matching topology node's protocol property
45  * grouping for the resource property. The full range of fmd(1M)
46  * scheme plugin operations are supported as long as a backend method is
47  * supplied by a scheme-specific enumerator or the enumerator module that
48  * created the matching topology node.  Support for fmd scheme operations
49  * include:
50  *
51  *	- expand
52  *	- present
53  *	- contains
54  *	- unusable
55  *	- nvl2str
56  *
57  * In addition, the following operations are supported per-FMRI:
58  *
59  *	- str2nvl: convert string-based FMRI to nvlist
60  *	- compare: compare two FMRIs
61  *	- asru: lookup associated ASRU property by FMRI
62  *	- fru: lookup associated FRU by FMRI
63  *	- create: an FMRI nvlist by scheme type
64  *	- propery lookup
65  *
66  * These routines may only be called by consumers of a topology snapshot.
67  * They may not be called by libtopo enumerator or method modules.
68  */
69 
70 /*ARGSUSED*/
71 static int
72 set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
73 {
74 	if (nvlp != NULL)
75 		nvlist_free(nvlp);
76 
77 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
78 	    topo_strerror(err));
79 
80 	*errp = err;
81 	return (-1);
82 }
83 
84 /*ARGSUSED*/
85 static nvlist_t *
86 set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
87 {
88 	if (nvlp != NULL)
89 		nvlist_free(nvlp);
90 
91 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
92 	    topo_strerror(err));
93 
94 	*errp = err;
95 	return (NULL);
96 }
97 
98 int
99 topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err)
100 {
101 	char *scheme, *str;
102 	nvlist_t *out = NULL;
103 	tnode_t *rnode;
104 
105 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
106 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
107 		    TOPO_METH_NVL2STR, out));
108 
109 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
110 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
111 		    TOPO_METH_NVL2STR, out));
112 
113 	if (topo_method_invoke(rnode, TOPO_METH_NVL2STR,
114 	    TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0)
115 		return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out));
116 
117 	if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0)
118 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
119 		    TOPO_METH_NVL2STR, out));
120 
121 	if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL)
122 		return (set_error(thp, ETOPO_NOMEM, err,
123 		    TOPO_METH_NVL2STR, out));
124 
125 	nvlist_free(out);
126 
127 	return (0);
128 }
129 
130 int
131 topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
132     int *err)
133 {
134 	char *f, buf[PATH_MAX];
135 	nvlist_t *out = NULL, *in = NULL;
136 	tnode_t *rnode;
137 
138 	(void) strlcpy(buf, fmristr, sizeof (buf));
139 	if ((f = strchr(buf, ':')) == NULL)
140 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
141 		    TOPO_METH_STR2NVL, in));
142 
143 	*f = '\0'; /* strip trailing FMRI path */
144 
145 	if ((rnode = topo_hdl_root(thp, buf)) == NULL)
146 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
147 		    TOPO_METH_STR2NVL, in));
148 
149 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
150 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
151 		    in));
152 
153 	if (nvlist_add_string(in, "fmri-string", fmristr) != 0)
154 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
155 		    in));
156 
157 	if (topo_method_invoke(rnode, TOPO_METH_STR2NVL,
158 	    TOPO_METH_STR2NVL_VERSION, in, &out, err) != 0)
159 		return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in));
160 
161 	nvlist_free(in);
162 
163 	if (out == NULL ||
164 	    topo_hdl_nvdup(thp, out, fmri) != 0)
165 		return (set_error(thp, ETOPO_FMRI_NVL, err,
166 		    TOPO_METH_STR2NVL, out));
167 
168 	nvlist_free(out);
169 
170 	return (0);
171 }
172 
173 int
174 topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err)
175 {
176 	uint32_t present = 0;
177 	char *scheme;
178 	nvlist_t *out = NULL;
179 	tnode_t *rnode;
180 
181 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
182 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
183 		    TOPO_METH_PRESENT, out));
184 
185 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
186 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
187 		    TOPO_METH_PRESENT, out));
188 
189 	if (topo_method_invoke(rnode, TOPO_METH_PRESENT,
190 	    TOPO_METH_PRESENT_VERSION, fmri, &out, err) < 0) {
191 		(void) set_error(thp, *err, err, TOPO_METH_PRESENT, out);
192 		return (present);
193 	}
194 
195 	(void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present);
196 	nvlist_free(out);
197 
198 	return (present);
199 }
200 
201 int
202 topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err)
203 {
204 	uint32_t contains;
205 	char *scheme;
206 	nvlist_t *in = NULL, *out = NULL;
207 	tnode_t *rnode;
208 
209 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
210 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
211 		    TOPO_METH_CONTAINS, NULL));
212 
213 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
214 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
215 		    TOPO_METH_CONTAINS, NULL));
216 
217 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
218 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
219 		    NULL));
220 
221 	if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, fmri) != 0 ||
222 	    nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, subfmri) != 0)
223 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
224 		    in));
225 
226 	if (topo_hdl_nvalloc(thp, &out, NV_UNIQUE_NAME) != 0)
227 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
228 		    out));
229 
230 	if (topo_method_invoke(rnode, TOPO_METH_CONTAINS,
231 	    TOPO_METH_CONTAINS_VERSION, in, &out, err) < 0) {
232 		nvlist_free(out);
233 		return (set_error(thp, *err, err, TOPO_METH_CONTAINS, in));
234 	}
235 
236 	(void) nvlist_lookup_uint32(out, TOPO_METH_CONTAINS_RET, &contains);
237 	nvlist_free(in);
238 	nvlist_free(out);
239 
240 	return (contains);
241 }
242 
243 int
244 topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err)
245 {
246 	char *scheme;
247 	uint32_t unusable = 0;
248 	nvlist_t *out = NULL;
249 	tnode_t *rnode;
250 
251 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
252 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
253 		    TOPO_METH_UNUSABLE, out));
254 
255 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
256 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
257 		    TOPO_METH_UNUSABLE, out));
258 
259 	if (topo_method_invoke(rnode, TOPO_METH_UNUSABLE,
260 	    TOPO_METH_UNUSABLE_VERSION, fmri, &out, err) < 0)
261 		return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out));
262 
263 	(void) nvlist_lookup_uint32(out, TOPO_METH_UNUSABLE_RET, &unusable);
264 	nvlist_free(out);
265 
266 	return (unusable);
267 }
268 
269 int
270 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err)
271 {
272 	char *scheme;
273 	nvlist_t *out = NULL;
274 	tnode_t *rnode;
275 
276 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
277 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
278 		    TOPO_METH_EXPAND, out));
279 
280 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
281 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
282 		    TOPO_METH_EXPAND, out));
283 
284 	if (topo_method_invoke(rnode, TOPO_METH_EXPAND,
285 	    TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0)
286 		return (set_error(thp, *err, err, TOPO_METH_EXPAND, out));
287 
288 	return (0);
289 }
290 
291 static int
292 fmri_prop(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
293     const char *pname, nvlist_t *args, nvlist_t **prop,
294     int *err)
295 {
296 	int rv;
297 	nvlist_t *in = NULL;
298 	tnode_t *rnode;
299 	char *scheme;
300 
301 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
302 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
303 		    TOPO_METH_PROP_GET, in));
304 
305 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
306 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
307 		    TOPO_METH_PROP_GET, in));
308 
309 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
310 		return (set_error(thp, ETOPO_FMRI_NVL, err,
311 		    TOPO_METH_PROP_GET, in));
312 
313 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
314 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
315 	rv |= nvlist_add_string(in, TOPO_PROP_VAL_NAME, pname);
316 	if (args != NULL)
317 		rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
318 	if (rv != 0)
319 		return (set_error(thp, ETOPO_FMRI_NVL, err,
320 		    TOPO_METH_PROP_GET, in));
321 
322 	*prop = NULL;
323 	rv = topo_method_invoke(rnode, TOPO_METH_PROP_GET,
324 	    TOPO_METH_PROP_GET_VERSION, in, prop, err);
325 
326 	nvlist_free(in);
327 
328 	if (rv != 0)
329 		return (-1); /* *err is set for us */
330 
331 	if (*prop == NULL)
332 		return (set_error(thp, ETOPO_PROP_NOENT, err,
333 		    TOPO_METH_PROP_GET, NULL));
334 	return (0);
335 }
336 
337 int
338 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
339 {
340 	nvlist_t *ap, *prop = NULL;
341 
342 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU,
343 	    nvl, &prop, err) < 0)
344 		return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
345 
346 	if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &ap) != 0)
347 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_asru",
348 		    prop));
349 
350 	if (topo_hdl_nvdup(thp, ap, asru) < 0)
351 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_asru",
352 		    prop));
353 
354 	nvlist_free(prop);
355 
356 	return (0);
357 }
358 
359 int
360 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err)
361 {
362 	nvlist_t *fp, *prop = NULL;
363 
364 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU,
365 	    nvl, &prop, err) < 0)
366 		return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
367 
368 	if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0)
369 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_fru",
370 		    prop));
371 
372 	if (topo_hdl_nvdup(thp, fp, fru) < 0)
373 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_fru",
374 		    prop));
375 
376 	nvlist_free(prop);
377 
378 	return (0);
379 }
380 
381 int
382 topo_fmri_label(topo_hdl_t *thp, nvlist_t *nvl, char **label, int *err)
383 {
384 	nvlist_t *prop = NULL;
385 	char *lp;
386 
387 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL,
388 	    NULL, &prop, err) < 0)
389 		return (set_error(thp, *err, err, "topo_fmri_label", NULL));
390 
391 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &lp) != 0)
392 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
393 		    prop));
394 
395 	if ((*label = topo_hdl_strdup(thp, lp)) == NULL)
396 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_label",
397 		    prop));
398 
399 	nvlist_free(prop);
400 
401 	return (0);
402 }
403 
404 int
405 topo_fmri_serial(topo_hdl_t *thp, nvlist_t *nvl, char **serial, int *err)
406 {
407 	nvlist_t *prop = NULL;
408 	char *sp;
409 
410 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, FM_FMRI_HC_SERIAL_ID,
411 	    NULL, &prop, err) < 0)
412 		return (set_error(thp, *err, err, "topo_fmri_serial", NULL));
413 
414 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &sp) != 0)
415 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_serial",
416 		    prop));
417 
418 	if ((*serial = topo_hdl_strdup(thp, sp)) == NULL)
419 		return (set_error(thp, ETOPO_PROP_NOMEM, err,
420 		    "topo_fmri_serial", prop));
421 
422 	nvlist_free(prop);
423 
424 	return (0);
425 }
426 
427 int topo_fmri_getprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
428     const char *pname, nvlist_t *args,  nvlist_t **prop,
429     int *err)
430 {
431 	*prop = NULL;
432 
433 	return (fmri_prop(thp, nvl, pg, pname, args, prop, err));
434 }
435 
436 int topo_fmri_setprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
437     nvlist_t *prop, int flag, nvlist_t *args, int *err)
438 {
439 	int rv;
440 	nvlist_t *in = NULL, *out = NULL;
441 	tnode_t *rnode;
442 	char *scheme;
443 
444 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0)
445 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
446 		    TOPO_METH_PROP_SET, in));
447 
448 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
449 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
450 		    TOPO_METH_PROP_SET, in));
451 
452 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
453 		return (set_error(thp, ETOPO_FMRI_NVL, err,
454 		    TOPO_METH_PROP_SET, in));
455 
456 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, nvl);
457 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pg);
458 	rv |= nvlist_add_nvlist(in, TOPO_PROP_VAL, prop);
459 	rv |= nvlist_add_int32(in, TOPO_PROP_FLAG, (int32_t)flag);
460 	if (args != NULL)
461 		rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
462 	if (rv != 0)
463 		return (set_error(thp, ETOPO_FMRI_NVL, err,
464 		    TOPO_METH_PROP_SET, in));
465 
466 	rv = topo_method_invoke(rnode, TOPO_METH_PROP_SET,
467 	    TOPO_METH_PROP_SET_VERSION, in, &out, err);
468 
469 	nvlist_free(in);
470 
471 	/* no return values */
472 	if (out != NULL)
473 		nvlist_free(out);
474 
475 	if (rv)
476 		return (-1);
477 
478 	return (0);
479 
480 }
481 
482 int
483 topo_fmri_getpgrp(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
484     nvlist_t **pgroup, int *err)
485 {
486 	int rv;
487 	nvlist_t *in = NULL;
488 	tnode_t *rnode;
489 	char *scheme;
490 
491 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
492 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
493 		    TOPO_METH_PROP_GET, in));
494 
495 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
496 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
497 		    TOPO_METH_PROP_GET, in));
498 
499 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
500 		return (set_error(thp, ETOPO_FMRI_NVL, err,
501 		    TOPO_METH_PROP_GET, in));
502 
503 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
504 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
505 	if (rv != 0)
506 		return (set_error(thp, ETOPO_FMRI_NVL, err,
507 		    TOPO_METH_PROP_GET, in));
508 
509 	*pgroup = NULL;
510 	rv = topo_method_invoke(rnode, TOPO_METH_PGRP_GET,
511 	    TOPO_METH_PGRP_GET_VERSION, in, pgroup, err);
512 
513 	nvlist_free(in);
514 
515 	if (rv != 0)
516 		return (-1); /* *err is set for us */
517 
518 	if (*pgroup == NULL)
519 		return (set_error(thp, ETOPO_PROP_NOENT, err,
520 		    TOPO_METH_PROP_GET, NULL));
521 	return (0);
522 }
523 
524 int
525 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
526 {
527 	uint32_t compare;
528 	char *scheme1, *scheme2;
529 	nvlist_t *in;
530 	nvlist_t *out = NULL;
531 	tnode_t *rnode;
532 
533 	if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
534 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
535 		    TOPO_METH_COMPARE, NULL));
536 	if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
537 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
538 		    TOPO_METH_COMPARE, NULL));
539 
540 	if (strcmp(scheme1, scheme2) != 0)
541 		return (0);
542 
543 	if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
544 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
545 		    TOPO_METH_COMPARE, NULL));
546 
547 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
548 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
549 		    NULL));
550 
551 	if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV1, f1) != 0 ||
552 	    nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV2, f2) != 0)
553 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
554 		    in));
555 
556 	if (topo_method_invoke(rnode, TOPO_METH_COMPARE,
557 	    TOPO_METH_COMPARE_VERSION, in, &out, err) < 0)
558 		return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
559 
560 	(void) nvlist_lookup_uint32(out, TOPO_METH_COMPARE_RET, &compare);
561 	nvlist_free(out);
562 	nvlist_free(in);
563 
564 	return (compare);
565 }
566 
567 /*
568  * topo_fmri_create
569  *
570  *	If possible, creates an FMRI of the requested version in the
571  *	requested scheme.  Args are passed as part of the inputs to the
572  *	fmri-create method of the scheme.
573  */
574 nvlist_t *
575 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
576     topo_instance_t inst, nvlist_t *nvl, int *err)
577 {
578 	nvlist_t *ins;
579 	nvlist_t *out;
580 	tnode_t *rnode;
581 
582 	ins = out = NULL;
583 
584 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
585 		return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
586 		    TOPO_METH_FMRI, NULL));
587 
588 	if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
589 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
590 		    TOPO_METH_FMRI, NULL));
591 
592 	if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
593 	    nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
594 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
595 		    TOPO_METH_FMRI, ins));
596 	}
597 
598 	if (nvl != NULL &&
599 	    nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
600 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
601 		    TOPO_METH_FMRI, ins));
602 	}
603 	if (topo_method_invoke(rnode,
604 	    TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
605 		return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
606 	}
607 	nvlist_free(ins);
608 	return (out);
609 }
610 
611 /*
612  * These private utility functions are used by fmd to maintain its resource
613  * cache.  Because hc instance numbers are not guaranteed, it's possible to
614  * have two different FMRI strings represent the same logical entity.  These
615  * functions hide this implementation detail from unknowing consumers such as
616  * fmd.
617  *
618  * Ideally, we'd like to do a str2nvl() and then a full FMRI hash and
619  * comparison, but these functions are designed to be fast and efficient.
620  * Given that there is only a single hc node that has this property
621  * (ses-enclosure), we hard-code this behavior here.  If there are more
622  * instances of this behavior in the future, this function could be made more
623  * generic.
624  */
625 static ulong_t
626 topo_fmri_strhash_one(const char *fmri, size_t len)
627 {
628 	ulong_t g, h = 0;
629 	size_t i;
630 
631 	for (i = 0; i < len; i++) {
632 		h = (h << 4) + fmri[i];
633 
634 		if ((g = (h & 0xf0000000)) != 0) {
635 			h ^= (g >> 24);
636 			h ^= g;
637 		}
638 	}
639 
640 	return (h);
641 }
642 
643 /*ARGSUSED*/
644 ulong_t
645 topo_fmri_strhash(topo_hdl_t *thp, const char *fmri)
646 {
647 	char *e;
648 	ulong_t h;
649 
650 	if (strncmp(fmri, "hc://", 5) != 0 ||
651 	    (e = strstr(fmri, SES_ENCLOSURE)) == NULL)
652 		return (topo_fmri_strhash_one(fmri, strlen(fmri)));
653 
654 	h = topo_fmri_strhash_one(fmri, e - fmri);
655 	e += sizeof (SES_ENCLOSURE);
656 
657 	while (isdigit(*e))
658 		e++;
659 
660 	h += topo_fmri_strhash_one(e, strlen(e));
661 
662 	return (h);
663 }
664 
665 /*ARGSUSED*/
666 boolean_t
667 topo_fmri_strcmp(topo_hdl_t *thp, const char *a, const char *b)
668 {
669 	char *ea, *eb;
670 
671 	if (strncmp(a, "hc://", 5) != 0 ||
672 	    strncmp(b, "hc://", 5) != 0 ||
673 	    (ea = strstr(a, SES_ENCLOSURE)) == NULL ||
674 	    (eb = strstr(b, SES_ENCLOSURE)) == NULL)
675 		return (strcmp(a, b) == 0);
676 
677 	if ((ea - a) != (eb - b))
678 		return (B_FALSE);
679 
680 	if (strncmp(a, b, ea - a) != 0)
681 		return (B_FALSE);
682 
683 	ea += sizeof (SES_ENCLOSURE);
684 	eb += sizeof (SES_ENCLOSURE);
685 
686 	while (isdigit(*ea))
687 		ea++;
688 	while (isdigit(*eb))
689 		eb++;
690 
691 	return (strcmp(ea, eb) == 0);
692 }
693