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