xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_fmri.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <ctype.h>
28 #include <string.h>
29 #include <limits.h>
30 #include <fm/topo_mod.h>
31 #include <fm/fmd_fmri.h>
32 #include <sys/fm/protocol.h>
33 #include <topo_alloc.h>
34 #include <topo_error.h>
35 #include <topo_hc.h>
36 #include <topo_method.h>
37 #include <topo_subr.h>
38 #include <topo_string.h>
39 
40 /*
41  * Topology node properties and method operations may be accessed by FMRI.
42  * The FMRI used to perform property look-ups and method operations is
43  * the FMRI contained in the matching topology node's protocol property
44  * grouping for the resource property. The full range of fmd(1M)
45  * scheme plugin operations are supported as long as a backend method is
46  * supplied by a scheme-specific enumerator or the enumerator module that
47  * created the matching topology node.  Support for fmd scheme operations
48  * include:
49  *
50  *	- expand
51  *	- present
52  *	- replaced
53  *	- contains
54  *	- unusable
55  *	- service_state
56  *	- nvl2str
57  *	- retire
58  *	- unretire
59  *
60  * In addition, the following operations are supported per-FMRI:
61  *
62  *	- str2nvl: convert string-based FMRI to nvlist
63  *	- compare: compare two FMRIs
64  *	- asru: lookup associated ASRU property by FMRI
65  *	- fru: lookup associated FRU by FMRI
66  *	- create: an FMRI nvlist by scheme type
67  *	- propery lookup
68  *
69  * These routines may only be called by consumers of a topology snapshot.
70  * They may not be called by libtopo enumerator or method modules.
71  */
72 
73 /*ARGSUSED*/
74 static int
75 set_error(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
76 {
77 	if (nvlp != NULL)
78 		nvlist_free(nvlp);
79 
80 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
81 	    topo_strerror(err));
82 
83 	*errp = err;
84 	return (-1);
85 }
86 
87 /*ARGSUSED*/
88 static nvlist_t *
89 set_nverror(topo_hdl_t *thp, int err, int *errp, char *method, nvlist_t *nvlp)
90 {
91 	if (nvlp != NULL)
92 		nvlist_free(nvlp);
93 
94 	topo_dprintf(thp, TOPO_DBG_ERR, "%s failed: %s\n", method,
95 	    topo_strerror(err));
96 
97 	*errp = err;
98 	return (NULL);
99 }
100 
101 int
102 topo_fmri_nvl2str(topo_hdl_t *thp, nvlist_t *fmri, char **fmristr, int *err)
103 {
104 	char *scheme, *str;
105 	nvlist_t *out = NULL;
106 	tnode_t *rnode;
107 
108 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
109 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
110 		    TOPO_METH_NVL2STR, out));
111 
112 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
113 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
114 		    TOPO_METH_NVL2STR, out));
115 
116 	if (topo_method_invoke(rnode, TOPO_METH_NVL2STR,
117 	    TOPO_METH_NVL2STR_VERSION, fmri, &out, err) != 0)
118 		return (set_error(thp, *err, err, TOPO_METH_NVL2STR, out));
119 
120 	if (out == NULL || nvlist_lookup_string(out, "fmri-string", &str) != 0)
121 		return (set_error(thp, ETOPO_METHOD_INVAL, err,
122 		    TOPO_METH_NVL2STR, out));
123 
124 	if ((*fmristr = topo_hdl_strdup(thp, str)) == NULL)
125 		return (set_error(thp, ETOPO_NOMEM, err,
126 		    TOPO_METH_NVL2STR, out));
127 
128 	nvlist_free(out);
129 
130 	return (0);
131 }
132 
133 int
134 topo_fmri_str2nvl(topo_hdl_t *thp, const char *fmristr, nvlist_t **fmri,
135     int *err)
136 {
137 	char *f, buf[PATH_MAX];
138 	nvlist_t *out = NULL, *in = NULL;
139 	tnode_t *rnode;
140 
141 	(void) strlcpy(buf, fmristr, sizeof (buf));
142 	if ((f = strchr(buf, ':')) == NULL)
143 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
144 		    TOPO_METH_STR2NVL, in));
145 
146 	*f = '\0'; /* strip trailing FMRI path */
147 
148 	if ((rnode = topo_hdl_root(thp, buf)) == NULL)
149 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
150 		    TOPO_METH_STR2NVL, in));
151 
152 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
153 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
154 		    in));
155 
156 	if (nvlist_add_string(in, "fmri-string", fmristr) != 0)
157 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_STR2NVL,
158 		    in));
159 
160 	if (topo_method_invoke(rnode, TOPO_METH_STR2NVL,
161 	    TOPO_METH_STR2NVL_VERSION, in, &out, err) != 0)
162 		return (set_error(thp, *err, err, TOPO_METH_STR2NVL, in));
163 
164 	nvlist_free(in);
165 
166 	if (out == NULL ||
167 	    topo_hdl_nvdup(thp, out, fmri) != 0)
168 		return (set_error(thp, ETOPO_FMRI_NVL, err,
169 		    TOPO_METH_STR2NVL, out));
170 
171 	nvlist_free(out);
172 
173 	return (0);
174 }
175 
176 int
177 topo_fmri_present(topo_hdl_t *thp, nvlist_t *fmri, int *err)
178 {
179 	uint32_t present = 0;
180 	char *scheme;
181 	nvlist_t *out = NULL;
182 	tnode_t *rnode;
183 
184 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
185 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
186 		    TOPO_METH_PRESENT, out));
187 
188 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
189 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
190 		    TOPO_METH_PRESENT, out));
191 
192 	if (topo_method_invoke(rnode, TOPO_METH_PRESENT,
193 	    TOPO_METH_PRESENT_VERSION, fmri, &out, err) < 0) {
194 		(void) set_error(thp, *err, err, TOPO_METH_PRESENT, out);
195 		return (present);
196 	}
197 
198 	(void) nvlist_lookup_uint32(out, TOPO_METH_PRESENT_RET, &present);
199 	nvlist_free(out);
200 
201 	return (present);
202 }
203 
204 int
205 topo_fmri_replaced(topo_hdl_t *thp, nvlist_t *fmri, int *err)
206 {
207 	uint32_t replaced = FMD_OBJ_STATE_NOT_PRESENT;
208 	char *scheme;
209 	nvlist_t *out = NULL;
210 	tnode_t *rnode;
211 
212 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
213 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
214 		    TOPO_METH_REPLACED, out));
215 
216 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
217 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
218 		    TOPO_METH_REPLACED, out));
219 
220 	if (topo_method_invoke(rnode, TOPO_METH_REPLACED,
221 	    TOPO_METH_REPLACED_VERSION, fmri, &out, err) < 0) {
222 		(void) set_error(thp, *err, err, TOPO_METH_REPLACED, out);
223 		return (FMD_OBJ_STATE_UNKNOWN);
224 	}
225 
226 	(void) nvlist_lookup_uint32(out, TOPO_METH_REPLACED_RET, &replaced);
227 	nvlist_free(out);
228 
229 	return (replaced);
230 }
231 
232 int
233 topo_fmri_contains(topo_hdl_t *thp, nvlist_t *fmri, nvlist_t *subfmri, int *err)
234 {
235 	uint32_t contains;
236 	char *scheme;
237 	nvlist_t *in = NULL, *out = NULL;
238 	tnode_t *rnode;
239 
240 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
241 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
242 		    TOPO_METH_CONTAINS, NULL));
243 
244 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
245 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
246 		    TOPO_METH_CONTAINS, NULL));
247 
248 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
249 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
250 		    NULL));
251 
252 	if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_FMRI, fmri) != 0 ||
253 	    nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_SUBFMRI, subfmri) != 0)
254 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_CONTAINS,
255 		    in));
256 
257 	if (topo_method_invoke(rnode, TOPO_METH_CONTAINS,
258 	    TOPO_METH_CONTAINS_VERSION, in, &out, err) < 0)
259 		return (set_error(thp, *err, err, TOPO_METH_CONTAINS, in));
260 
261 	(void) nvlist_lookup_uint32(out, TOPO_METH_CONTAINS_RET, &contains);
262 	nvlist_free(in);
263 	nvlist_free(out);
264 
265 	return (contains);
266 }
267 
268 int
269 topo_fmri_unusable(topo_hdl_t *thp, nvlist_t *fmri, int *err)
270 {
271 	char *scheme;
272 	uint32_t unusable = 0;
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_UNUSABLE, out));
279 
280 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
281 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
282 		    TOPO_METH_UNUSABLE, out));
283 
284 	if (topo_method_invoke(rnode, TOPO_METH_UNUSABLE,
285 	    TOPO_METH_UNUSABLE_VERSION, fmri, &out, err) < 0)
286 		return (set_error(thp, *err, err, TOPO_METH_UNUSABLE, out));
287 
288 	(void) nvlist_lookup_uint32(out, TOPO_METH_UNUSABLE_RET, &unusable);
289 	nvlist_free(out);
290 
291 	return (unusable);
292 }
293 
294 int
295 topo_fmri_retire(topo_hdl_t *thp, nvlist_t *fmri, int *err)
296 {
297 	char *scheme;
298 	uint32_t status;
299 	nvlist_t *out = NULL;
300 	tnode_t *rnode;
301 
302 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
303 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
304 		    TOPO_METH_RETIRE, out));
305 
306 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
307 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
308 		    TOPO_METH_RETIRE, out));
309 
310 	if (topo_method_invoke(rnode, TOPO_METH_RETIRE,
311 	    TOPO_METH_RETIRE_VERSION, fmri, &out, err) < 0)
312 		return (set_error(thp, *err, err, TOPO_METH_RETIRE, out));
313 
314 	if (nvlist_lookup_uint32(out, TOPO_METH_RETIRE_RET, &status) != 0)
315 		return (set_error(thp, ETOPO_METHOD_FAIL, err,
316 		    TOPO_METH_RETIRE, out));
317 	nvlist_free(out);
318 
319 	return (status);
320 }
321 
322 int
323 topo_fmri_unretire(topo_hdl_t *thp, nvlist_t *fmri, int *err)
324 {
325 	char *scheme;
326 	uint32_t status;
327 	nvlist_t *out = NULL;
328 	tnode_t *rnode;
329 
330 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
331 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
332 		    TOPO_METH_UNRETIRE, out));
333 
334 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
335 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
336 		    TOPO_METH_UNRETIRE, out));
337 
338 	if (topo_method_invoke(rnode, TOPO_METH_UNRETIRE,
339 	    TOPO_METH_UNRETIRE_VERSION, fmri, &out, err) < 0)
340 		return (set_error(thp, *err, err, TOPO_METH_UNRETIRE, out));
341 
342 	if (nvlist_lookup_uint32(out, TOPO_METH_UNRETIRE_RET, &status) != 0) {
343 		nvlist_free(out);
344 		return (set_error(thp, ETOPO_METHOD_FAIL, err,
345 		    TOPO_METH_UNRETIRE, out));
346 	}
347 	nvlist_free(out);
348 
349 	return (status);
350 }
351 
352 int
353 topo_fmri_service_state(topo_hdl_t *thp, nvlist_t *fmri, int *err)
354 {
355 	char *scheme;
356 	uint32_t service_state = FMD_SERVICE_STATE_UNKNOWN;
357 	nvlist_t *out = NULL;
358 	tnode_t *rnode;
359 
360 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
361 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
362 		    TOPO_METH_SERVICE_STATE, out));
363 
364 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
365 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
366 		    TOPO_METH_SERVICE_STATE, out));
367 
368 	if (topo_method_invoke(rnode, TOPO_METH_SERVICE_STATE,
369 	    TOPO_METH_SERVICE_STATE_VERSION, fmri, &out, err) < 0)
370 		return (set_error(thp, *err, err, TOPO_METH_SERVICE_STATE,
371 		    out));
372 
373 	(void) nvlist_lookup_uint32(out, TOPO_METH_SERVICE_STATE_RET,
374 	    &service_state);
375 	nvlist_free(out);
376 
377 	return (service_state);
378 }
379 
380 int
381 topo_fmri_expand(topo_hdl_t *thp, nvlist_t *fmri, int *err)
382 {
383 	char *scheme;
384 	nvlist_t *out = NULL;
385 	tnode_t *rnode;
386 
387 	if (nvlist_lookup_string(fmri, FM_FMRI_SCHEME, &scheme) != 0)
388 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
389 		    TOPO_METH_EXPAND, out));
390 
391 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
392 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
393 		    TOPO_METH_EXPAND, out));
394 
395 	if (topo_method_invoke(rnode, TOPO_METH_EXPAND,
396 	    TOPO_METH_EXPAND_VERSION, fmri, &out, err) != 0)
397 		return (set_error(thp, *err, err, TOPO_METH_EXPAND, out));
398 
399 	return (0);
400 }
401 
402 static int
403 fmri_prop(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
404     const char *pname, nvlist_t *args, nvlist_t **prop,
405     int *err)
406 {
407 	int rv;
408 	nvlist_t *in = NULL;
409 	tnode_t *rnode;
410 	char *scheme;
411 
412 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
413 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
414 		    TOPO_METH_PROP_GET, in));
415 
416 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
417 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
418 		    TOPO_METH_PROP_GET, in));
419 
420 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
421 		return (set_error(thp, ETOPO_FMRI_NVL, err,
422 		    TOPO_METH_PROP_GET, in));
423 
424 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
425 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
426 	rv |= nvlist_add_string(in, TOPO_PROP_VAL_NAME, pname);
427 	if (args != NULL)
428 		rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
429 	if (rv != 0)
430 		return (set_error(thp, ETOPO_FMRI_NVL, err,
431 		    TOPO_METH_PROP_GET, in));
432 
433 	*prop = NULL;
434 	rv = topo_method_invoke(rnode, TOPO_METH_PROP_GET,
435 	    TOPO_METH_PROP_GET_VERSION, in, prop, err);
436 
437 	nvlist_free(in);
438 
439 	if (rv != 0)
440 		return (-1); /* *err is set for us */
441 
442 	if (*prop == NULL)
443 		return (set_error(thp, ETOPO_PROP_NOENT, err,
444 		    TOPO_METH_PROP_GET, NULL));
445 	return (0);
446 }
447 
448 int
449 topo_fmri_asru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **asru, int *err)
450 {
451 	nvlist_t *ap, *prop = NULL;
452 
453 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_ASRU,
454 	    nvl, &prop, err) < 0)
455 		return (set_error(thp, *err, err, "topo_fmri_asru", NULL));
456 
457 	if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &ap) != 0)
458 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_asru",
459 		    prop));
460 
461 	if (topo_hdl_nvdup(thp, ap, asru) < 0)
462 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_asru",
463 		    prop));
464 
465 	nvlist_free(prop);
466 
467 	return (0);
468 }
469 
470 int
471 topo_fmri_fru(topo_hdl_t *thp, nvlist_t *nvl, nvlist_t **fru, int *err)
472 {
473 	nvlist_t *fp, *prop = NULL;
474 
475 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_FRU,
476 	    nvl, &prop, err) < 0)
477 		return (set_error(thp, *err, err, "topo_fmri_fru", NULL));
478 
479 	if (nvlist_lookup_nvlist(prop, TOPO_PROP_VAL_VAL, &fp) != 0)
480 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_fru",
481 		    prop));
482 
483 	if (topo_hdl_nvdup(thp, fp, fru) < 0)
484 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_fru",
485 		    prop));
486 
487 	nvlist_free(prop);
488 
489 	return (0);
490 }
491 
492 int
493 topo_fmri_label(topo_hdl_t *thp, nvlist_t *nvl, char **label, int *err)
494 {
495 	nvlist_t *prop = NULL;
496 	char *lp;
497 
498 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, TOPO_PROP_LABEL,
499 	    NULL, &prop, err) < 0)
500 		return (set_error(thp, *err, err, "topo_fmri_label", NULL));
501 
502 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &lp) != 0)
503 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_label",
504 		    prop));
505 
506 	if ((*label = topo_hdl_strdup(thp, lp)) == NULL)
507 		return (set_error(thp, ETOPO_PROP_NOMEM, err, "topo_fmri_label",
508 		    prop));
509 
510 	nvlist_free(prop);
511 
512 	return (0);
513 }
514 
515 int
516 topo_fmri_serial(topo_hdl_t *thp, nvlist_t *nvl, char **serial, int *err)
517 {
518 	nvlist_t *prop = NULL;
519 	char *sp;
520 
521 	/*
522 	 * If there is a serial id in the resource fmri, then use that.
523 	 * Otherwise fall back to looking for a serial id property in the
524 	 * protocol group.
525 	 */
526 	if (nvlist_lookup_string(nvl, FM_FMRI_HC_SERIAL_ID, &sp) == 0) {
527 		if ((*serial = topo_hdl_strdup(thp, sp)) == NULL)
528 			return (set_error(thp, ETOPO_PROP_NOMEM, err,
529 			    "topo_fmri_serial", prop));
530 		else
531 			return (0);
532 	}
533 
534 	if (fmri_prop(thp, nvl, TOPO_PGROUP_PROTOCOL, FM_FMRI_HC_SERIAL_ID,
535 	    NULL, &prop, err) < 0)
536 		return (set_error(thp, *err, err, "topo_fmri_serial", NULL));
537 
538 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_VAL, &sp) != 0)
539 		return (set_error(thp, ETOPO_PROP_NVL, err, "topo_fmri_serial",
540 		    prop));
541 
542 	if ((*serial = topo_hdl_strdup(thp, sp)) == NULL)
543 		return (set_error(thp, ETOPO_PROP_NOMEM, err,
544 		    "topo_fmri_serial", prop));
545 
546 	nvlist_free(prop);
547 
548 	return (0);
549 }
550 
551 int topo_fmri_getprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
552     const char *pname, nvlist_t *args,  nvlist_t **prop,
553     int *err)
554 {
555 	*prop = NULL;
556 
557 	return (fmri_prop(thp, nvl, pg, pname, args, prop, err));
558 }
559 
560 int topo_fmri_setprop(topo_hdl_t *thp, nvlist_t *nvl, const char *pg,
561     nvlist_t *prop, int flag, nvlist_t *args, int *err)
562 {
563 	int rv;
564 	nvlist_t *in = NULL, *out = NULL;
565 	tnode_t *rnode;
566 	char *scheme;
567 
568 	if (nvlist_lookup_string(nvl, FM_FMRI_SCHEME, &scheme) != 0)
569 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
570 		    TOPO_METH_PROP_SET, in));
571 
572 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
573 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
574 		    TOPO_METH_PROP_SET, in));
575 
576 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
577 		return (set_error(thp, ETOPO_FMRI_NVL, err,
578 		    TOPO_METH_PROP_SET, in));
579 
580 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, nvl);
581 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pg);
582 	rv |= nvlist_add_nvlist(in, TOPO_PROP_VAL, prop);
583 	rv |= nvlist_add_int32(in, TOPO_PROP_FLAG, (int32_t)flag);
584 	if (args != NULL)
585 		rv |= nvlist_add_nvlist(in, TOPO_PROP_PARGS, args);
586 	if (rv != 0)
587 		return (set_error(thp, ETOPO_FMRI_NVL, err,
588 		    TOPO_METH_PROP_SET, in));
589 
590 	rv = topo_method_invoke(rnode, TOPO_METH_PROP_SET,
591 	    TOPO_METH_PROP_SET_VERSION, in, &out, err);
592 
593 	nvlist_free(in);
594 
595 	/* no return values */
596 	if (out != NULL)
597 		nvlist_free(out);
598 
599 	if (rv)
600 		return (-1);
601 
602 	return (0);
603 
604 }
605 
606 int
607 topo_fmri_getpgrp(topo_hdl_t *thp, nvlist_t *rsrc, const char *pgname,
608     nvlist_t **pgroup, int *err)
609 {
610 	int rv;
611 	nvlist_t *in = NULL;
612 	tnode_t *rnode;
613 	char *scheme;
614 
615 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
616 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
617 		    TOPO_METH_PROP_GET, in));
618 
619 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
620 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
621 		    TOPO_METH_PROP_GET, in));
622 
623 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
624 		return (set_error(thp, ETOPO_FMRI_NVL, err,
625 		    TOPO_METH_PROP_GET, in));
626 
627 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
628 	rv |= nvlist_add_string(in, TOPO_PROP_GROUP, pgname);
629 	if (rv != 0)
630 		return (set_error(thp, ETOPO_FMRI_NVL, err,
631 		    TOPO_METH_PROP_GET, in));
632 
633 	*pgroup = NULL;
634 	rv = topo_method_invoke(rnode, TOPO_METH_PGRP_GET,
635 	    TOPO_METH_PGRP_GET_VERSION, in, pgroup, err);
636 
637 	nvlist_free(in);
638 
639 	if (rv != 0)
640 		return (-1); /* *err is set for us */
641 
642 	if (*pgroup == NULL)
643 		return (set_error(thp, ETOPO_PROP_NOENT, err,
644 		    TOPO_METH_PROP_GET, NULL));
645 	return (0);
646 }
647 
648 int
649 topo_fmri_compare(topo_hdl_t *thp, nvlist_t *f1, nvlist_t *f2, int *err)
650 {
651 	uint32_t compare;
652 	char *scheme1, *scheme2;
653 	nvlist_t *in;
654 	nvlist_t *out = NULL;
655 	tnode_t *rnode;
656 
657 	if (nvlist_lookup_string(f1, FM_FMRI_SCHEME, &scheme1) != 0)
658 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
659 		    TOPO_METH_COMPARE, NULL));
660 	if (nvlist_lookup_string(f2, FM_FMRI_SCHEME, &scheme2) != 0)
661 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
662 		    TOPO_METH_COMPARE, NULL));
663 
664 	if (strcmp(scheme1, scheme2) != 0)
665 		return (0);
666 
667 	if ((rnode = topo_hdl_root(thp, scheme1)) == NULL)
668 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
669 		    TOPO_METH_COMPARE, NULL));
670 
671 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
672 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
673 		    NULL));
674 
675 	if (nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV1, f1) != 0 ||
676 	    nvlist_add_nvlist(in, TOPO_METH_FMRI_ARG_NV2, f2) != 0)
677 		return (set_error(thp, ETOPO_FMRI_NVL, err, TOPO_METH_COMPARE,
678 		    in));
679 
680 	if (topo_method_invoke(rnode, TOPO_METH_COMPARE,
681 	    TOPO_METH_COMPARE_VERSION, in, &out, err) < 0)
682 		return (set_error(thp, *err, err, TOPO_METH_COMPARE, in));
683 
684 	(void) nvlist_lookup_uint32(out, TOPO_METH_COMPARE_RET, &compare);
685 	nvlist_free(out);
686 	nvlist_free(in);
687 
688 	return (compare);
689 }
690 
691 /*
692  * topo_fmri_create
693  *
694  *	If possible, creates an FMRI of the requested version in the
695  *	requested scheme.  Args are passed as part of the inputs to the
696  *	fmri-create method of the scheme.
697  */
698 nvlist_t *
699 topo_fmri_create(topo_hdl_t *thp, const char *scheme, const char *name,
700     topo_instance_t inst, nvlist_t *nvl, int *err)
701 {
702 	nvlist_t *ins;
703 	nvlist_t *out;
704 	tnode_t *rnode;
705 
706 	ins = out = NULL;
707 
708 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
709 		return (set_nverror(thp, ETOPO_METHOD_NOTSUP, err,
710 		    TOPO_METH_FMRI, NULL));
711 
712 	if ((*err = topo_hdl_nvalloc(thp, &ins, NV_UNIQUE_NAME)) != 0)
713 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
714 		    TOPO_METH_FMRI, NULL));
715 
716 	if (nvlist_add_string(ins, TOPO_METH_FMRI_ARG_NAME, name) != 0 ||
717 	    nvlist_add_uint32(ins, TOPO_METH_FMRI_ARG_INST, inst) != 0) {
718 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
719 		    TOPO_METH_FMRI, ins));
720 	}
721 
722 	if (nvl != NULL &&
723 	    nvlist_add_nvlist(ins, TOPO_METH_FMRI_ARG_NVL, nvl) != 0) {
724 		return (set_nverror(thp, ETOPO_FMRI_NVL, err,
725 		    TOPO_METH_FMRI, ins));
726 	}
727 	if (topo_method_invoke(rnode,
728 	    TOPO_METH_FMRI, TOPO_METH_FMRI_VERSION, ins, &out, err) != 0) {
729 		return (set_nverror(thp, *err, err, TOPO_METH_FMRI, ins));
730 	}
731 	nvlist_free(ins);
732 	return (out);
733 }
734 
735 /*
736  * These private utility functions are used by fmd to maintain its resource
737  * cache.  Because hc instance numbers are not guaranteed, it's possible to
738  * have two different FMRI strings represent the same logical entity.  These
739  * functions hide this implementation detail from unknowing consumers such as
740  * fmd.
741  *
742  * Ideally, we'd like to do a str2nvl() and then a full FMRI hash and
743  * comparison, but these functions are designed to be fast and efficient.
744  * Given that there is only a single hc node that has this property
745  * (ses-enclosure), we hard-code this behavior here.  If there are more
746  * instances of this behavior in the future, this function could be made more
747  * generic.
748  *
749  * This code also handles changes in the server-id or revision fields of the hc
750  * FMRI, as these fields have no bearing on equivalence of FRUs.
751  */
752 static ulong_t
753 topo_fmri_strhash_one(const char *fmri, size_t len)
754 {
755 	ulong_t g, h = 0;
756 	size_t i;
757 
758 	for (i = 0; i < len; i++) {
759 		h = (h << 4) + fmri[i];
760 
761 		if ((g = (h & 0xf0000000)) != 0) {
762 			h ^= (g >> 24);
763 			h ^= g;
764 		}
765 	}
766 
767 	return (h);
768 }
769 
770 static const char *
771 topo_fmri_next_auth(const char *auth)
772 {
773 	const char *colon, *slash;
774 
775 	colon = strchr(auth + 1, ':');
776 	slash = strchr(auth, '/');
777 
778 	if (colon == NULL && slash == NULL)
779 		return (NULL);
780 
781 	if (colon == NULL)
782 		return (slash);
783 	else if (slash < colon)
784 		return (slash);
785 	else
786 		return (colon);
787 }
788 
789 /*
790  * List of authority information we care about.  Note that we explicitly ignore
791  * things that are properties of the chassis and not the resource itself:
792  *
793  * 	FM_FMRI_AUTH_PRODUCT_SN		"product-sn"
794  * 	FM_FMRI_AUTH_PRODUCT		"product-id"
795  * 	FM_FMRI_AUTH_DOMAIN		"domain-id"
796  * 	FM_FMRI_AUTH_SERVER		"server-id"
797  *	FM_FMRI_AUTH_HOST		"host-id"
798  *
799  * We also ignore the "revision" authority member, as that typically indicates
800  * the firmware revision and is not a static property of the FRU.  This leaves
801  * the following interesting members:
802  *
803  * 	FM_FMRI_AUTH_CHASSIS		"chassis-id"
804  *	FM_FMRI_HC_SERIAL_ID		"serial"
805  *	FM_FMRI_HC_PART			"part"
806  */
807 typedef enum {
808 	HC_AUTH_CHASSIS,
809 	HC_AUTH_SERIAL,
810 	HC_AUTH_PART,
811 	HC_AUTH_MAX
812 } hc_auth_type_t;
813 
814 static char *hc_auth_table[] = {
815 	FM_FMRI_AUTH_CHASSIS,
816 	FM_FMRI_HC_SERIAL_ID,
817 	FM_FMRI_HC_PART
818 };
819 
820 /*
821  * Takes an authority member, with leading ":" and trailing "=", and returns
822  * one of the above types if it's one of the things we care about.  If
823  * 'authlen' is specified, it is filled in with the length of the authority
824  * member, including leading and trailing characters.
825  */
826 static hc_auth_type_t
827 hc_auth_to_type(const char *auth, size_t *authlen)
828 {
829 	int i;
830 	size_t len;
831 
832 	if (auth[0] != ':')
833 		return (HC_AUTH_MAX);
834 
835 	for (i = 0; i < HC_AUTH_MAX; i++) {
836 		len = strlen(hc_auth_table[i]);
837 
838 		if (strncmp(auth + 1, hc_auth_table[i], len) == 0 &&
839 		    auth[len + 1] == '=') {
840 			if (authlen)
841 				*authlen = len + 2;
842 			break;
843 		}
844 	}
845 
846 	return (i);
847 }
848 
849 /*ARGSUSED*/
850 ulong_t
851 topo_fmri_strhash_internal(topo_hdl_t *thp, const char *fmri, boolean_t noauth)
852 {
853 	const char *auth, *next;
854 	const char *enclosure;
855 	ulong_t h;
856 	hc_auth_type_t type;
857 
858 	if (strncmp(fmri, "hc://", 5) != 0)
859 		return (topo_fmri_strhash_one(fmri, strlen(fmri)));
860 
861 	enclosure = strstr(fmri, SES_ENCLOSURE);
862 
863 	h = 0;
864 
865 	auth = next = fmri + 5;
866 	while (*next != '/') {
867 		auth = next;
868 
869 		if ((next = topo_fmri_next_auth(auth)) == NULL) {
870 			next = auth;
871 			break;
872 		}
873 
874 		if ((type = hc_auth_to_type(auth, NULL)) == HC_AUTH_MAX)
875 			continue;
876 
877 		if (!noauth || type == HC_AUTH_CHASSIS)
878 			h += topo_fmri_strhash_one(auth, next - auth);
879 	}
880 
881 	if (enclosure) {
882 		next = enclosure + sizeof (SES_ENCLOSURE);
883 		while (isdigit(*next))
884 			next++;
885 	}
886 
887 	h += topo_fmri_strhash_one(next, strlen(next));
888 
889 	return (h);
890 }
891 
892 /*ARGSUSED*/
893 ulong_t
894 topo_fmri_strhash(topo_hdl_t *thp, const char *fmri)
895 {
896 	return (topo_fmri_strhash_internal(thp, fmri, B_FALSE));
897 }
898 
899 /*ARGSUSED*/
900 ulong_t
901 topo_fmri_strhash_noauth(topo_hdl_t *thp, const char *fmri)
902 {
903 	return (topo_fmri_strhash_internal(thp, fmri, B_TRUE));
904 }
905 
906 
907 static void
908 topo_fmri_strcmp_parse_auth(const char *auth, const char *authtype[],
909     size_t authlen[])
910 {
911 	int i;
912 	const char *next;
913 	hc_auth_type_t type;
914 	size_t len;
915 
916 	for (i = 0; i < HC_AUTH_MAX; i++)
917 		authlen[i] = 0;
918 
919 	while (*auth != '/' &&
920 	    (next = topo_fmri_next_auth(auth)) != NULL) {
921 		if ((type = hc_auth_to_type(auth, &len)) == HC_AUTH_MAX) {
922 			auth = next;
923 			continue;
924 		}
925 
926 		authtype[type] = auth + len;
927 		authlen[type] = next - (auth + len);
928 		auth = next;
929 	}
930 }
931 
932 /*ARGSUSED*/
933 static boolean_t
934 topo_fmri_strcmp_internal(topo_hdl_t *thp, const char *a, const char *b,
935     boolean_t noauth)
936 {
937 	const char *fmria, *fmrib;
938 	const char *autha[HC_AUTH_MAX], *authb[HC_AUTH_MAX];
939 	size_t authlena[HC_AUTH_MAX], authlenb[HC_AUTH_MAX];
940 	int i;
941 
942 	/*
943 	 * For non-hc FMRIs, we don't do anything.
944 	 */
945 	if (strncmp(a, "hc://", 5) != 0 ||
946 	    strncmp(b, "hc://", 5) != 0)
947 		return (strcmp(a, b) == 0);
948 
949 	/*
950 	 * Get the portion of the FMRI independent of the authority
951 	 * information.
952 	 */
953 	fmria = strchr(a + 5, '/');
954 	fmrib = strchr(b + 5, '/');
955 	if (fmria == NULL || fmrib == NULL)
956 		return (strcmp(a, b));
957 	fmria++;
958 	fmrib++;
959 
960 	/*
961 	 * Comparing fmri authority information is a bit of a pain, because
962 	 * there may be a different number of members, and they can (but
963 	 * shouldn't be) in a different order.  We need to create a copy of the
964 	 * authority and parse it into pieces.  Because this function is
965 	 * intended to be fast (and not necessarily extensible), we hard-code
966 	 * the list of possible authority members in an enum and parse it into
967 	 * an array.
968 	 */
969 	topo_fmri_strcmp_parse_auth(a + 5, autha, authlena);
970 	topo_fmri_strcmp_parse_auth(b + 5, authb, authlenb);
971 
972 	for (i = 0; i < HC_AUTH_MAX; i++) {
973 		if (noauth && i != HC_AUTH_CHASSIS)
974 			continue;
975 
976 		if (authlena[i] == 0 && authlenb[i] == 0)
977 			continue;
978 
979 		if (authlena[i] != authlenb[i])
980 			return (B_FALSE);
981 
982 		if (strncmp(autha[i], authb[i], authlena[i]) != 0)
983 			return (B_FALSE);
984 	}
985 
986 	/*
987 	 * If this is rooted at a ses-enclosure node, skip past the instance
988 	 * number, as it has no meaning.
989 	 */
990 	if (strncmp(fmria, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0 &&
991 	    strncmp(fmrib, SES_ENCLOSURE, sizeof (SES_ENCLOSURE) - 1) == 0) {
992 		fmria += sizeof (SES_ENCLOSURE);
993 		fmrib += sizeof (SES_ENCLOSURE);
994 
995 		while (isdigit(*fmria))
996 			fmria++;
997 		while (isdigit(*fmrib))
998 			fmrib++;
999 	}
1000 
1001 	return (strcmp(fmria, fmrib) == 0);
1002 }
1003 
1004 /*ARGSUSED*/
1005 boolean_t
1006 topo_fmri_strcmp(topo_hdl_t *thp, const char *a, const char *b)
1007 {
1008 	return (topo_fmri_strcmp_internal(thp, a, b, B_FALSE));
1009 }
1010 
1011 /*ARGSUSED*/
1012 boolean_t
1013 topo_fmri_strcmp_noauth(topo_hdl_t *thp, const char *a, const char *b)
1014 {
1015 	return (topo_fmri_strcmp_internal(thp, a, b, B_TRUE));
1016 }
1017 
1018 int
1019 topo_fmri_facility(topo_hdl_t *thp, nvlist_t *rsrc, const char *fac_type,
1020     uint32_t fac_subtype, topo_walk_cb_t cb, void *cb_args, int *err)
1021 {
1022 	int rv;
1023 	nvlist_t *in = NULL, *out;
1024 	tnode_t *rnode;
1025 	char *scheme;
1026 
1027 	if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &scheme) != 0)
1028 		return (set_error(thp, ETOPO_FMRI_MALFORM, err,
1029 		    TOPO_METH_PROP_GET, in));
1030 
1031 	if ((rnode = topo_hdl_root(thp, scheme)) == NULL)
1032 		return (set_error(thp, ETOPO_METHOD_NOTSUP, err,
1033 		    TOPO_METH_PROP_GET, in));
1034 
1035 	if (topo_hdl_nvalloc(thp, &in, NV_UNIQUE_NAME) != 0)
1036 		return (set_error(thp, ETOPO_FMRI_NVL, err,
1037 		    TOPO_METH_PROP_GET, in));
1038 
1039 	rv = nvlist_add_nvlist(in, TOPO_PROP_RESOURCE, rsrc);
1040 	rv |= nvlist_add_string(in, FM_FMRI_FACILITY_TYPE, fac_type);
1041 	rv |= nvlist_add_uint32(in, "type", fac_subtype);
1042 #ifdef _LP64
1043 	rv |= nvlist_add_uint64(in, "callback", (uint64_t)cb);
1044 	rv |= nvlist_add_uint64(in, "callback-args", (uint64_t)cb_args);
1045 #else
1046 	rv |= nvlist_add_uint32(in, "callback", (uint32_t)cb);
1047 	rv |= nvlist_add_uint32(in, "callback-args", (uint32_t)cb_args);
1048 #endif
1049 	if (rv != 0)
1050 		return (set_error(thp, ETOPO_FMRI_NVL, err,
1051 		    TOPO_METH_PROP_GET, in));
1052 
1053 	rv = topo_method_invoke(rnode, TOPO_METH_FACILITY,
1054 	    TOPO_METH_FACILITY_VERSION, in, &out, err);
1055 
1056 	nvlist_free(in);
1057 
1058 	if (rv != 0)
1059 		return (-1); /* *err is set for us */
1060 
1061 	return (0);
1062 }
1063