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