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  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <strings.h>
29 #include <assert.h>
30 #include <fm/libtopo.h>
31 #include <topo_prop.h>
32 #include <topo_string.h>
33 #include <topo_alloc.h>
34 #include <topo_error.h>
35 #include <topo_method.h>
36 
37 /*
38  * Topology nodes are permitted to contain property information.
39  * Property information is organized according to property grouping.
40  * Each property group defines a name, a stability level for that name,
41  * a stability level for all underlying property data (name, type, values),
42  * a version for the property group definition and and a list of uniquely
43  * defined properties.  Property group versions are incremented when one of
44  * the following changes occurs:
45  *	- a property name changes
46  *	- a property type changes
47  *	- a property definition is removed from the group
48  * Compatible changes such as new property definitions in the group do
49  * not require version changes.
50  *
51  * Each property defines a unique (within the group) name, a type and
52  * a value.  Properties may be statically defined as int32, uint32, int64,
53  * uint64, fmri, string or arrays of each type.  Properties may also be
54  * dynamically exported via module registered methods.  For example, a module
55  * may register a method to export an ASRU property that is dynamically
56  * contructed when a call to topo_node_fmri() is invoked for a particular
57  * topology node.
58  *
59  * Static properties are persistently attached to topology nodes during
60  * enumeration by an enumeration module or as part of XML statements in a
61  * toplogy map file using the topo_prop_set* family of routines.  Similarly,
62  * property methods are registered during enumeration or as part of
63  * statements in topololgy map files.  Set-up of property methods is performed
64  * by calling topo_prop_method_register().
65  *
66  * All properties, whether statically persisted in a snapshot or dynamically
67  * obtained, may be read via the topo_prop_get* family of interfaces.
68  * Callers wishing to receive all property groups and properties for a given
69  * node may use topo_prop_getall().  This routine returns a nested nvlist
70  * of all groupings and property (name, type, value) sets.  Groupings
71  * are defined by TOPO_PROP_GROUP (name, data stability, name stability and
72  * version) and a nested nvlist of properties (TOPO_PROP_VAL).  Each property
73  * value is defined by its name, type and value.
74  */
75 static void topo_propval_destroy(topo_propval_t *);
76 
77 static topo_pgroup_t *
78 pgroup_get(tnode_t *node, const char *pgname)
79 {
80 	topo_pgroup_t *pg;
81 	/*
82 	 * Check for an existing pgroup
83 	 */
84 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
85 	    pg = topo_list_next(pg)) {
86 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
87 			return (pg);
88 		}
89 	}
90 
91 	return (NULL);
92 }
93 
94 static topo_propval_t *
95 propval_get(topo_pgroup_t *pg, const char *pname)
96 {
97 	topo_proplist_t *pvl;
98 
99 	if (pg == NULL)
100 		return (NULL);
101 
102 	for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
103 	    pvl = topo_list_next(pvl)) {
104 		if (strcmp(pvl->tp_pval->tp_name, pname) == 0)
105 			return (pvl->tp_pval);
106 	}
107 
108 	return (NULL);
109 }
110 
111 static int
112 method_geterror(nvlist_t *nvl, int err, int *errp)
113 {
114 	if (nvl != NULL)
115 		nvlist_free(nvl);
116 
117 	*errp = err;
118 
119 	return (-1);
120 }
121 
122 static int
123 prop_method_get(tnode_t *node, topo_propval_t *pv, topo_propmethod_t *pm,
124     nvlist_t *pargs, int *err)
125 {
126 	int ret;
127 	nvlist_t *args, *nvl;
128 	char *name;
129 	topo_type_t type;
130 
131 	if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0 ||
132 	    nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args) != 0)
133 		return (method_geterror(NULL, ETOPO_PROP_NVL, err));
134 
135 	if (pargs != NULL)
136 		if (nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs) != 0)
137 			return (method_geterror(args, ETOPO_PROP_NVL, err));
138 
139 	/* Now, get the latest value */
140 	if (topo_method_call(node, pm->tpm_name, pm->tpm_version,
141 	    args, &nvl, err) < 0)
142 		return (method_geterror(args, *err, err));
143 
144 	nvlist_free(args);
145 
146 	/* Verify the property contents */
147 	ret = nvlist_lookup_string(nvl, TOPO_PROP_VAL_NAME, &name);
148 	if (ret != 0 || strcmp(name, pv->tp_name) != 0)
149 		return (method_geterror(nvl, ETOPO_PROP_NAME, err));
150 
151 	ret = nvlist_lookup_uint32(nvl, TOPO_PROP_VAL_TYPE, (uint32_t *)&type);
152 	if (ret != 0 || type != pv->tp_type)
153 		return (method_geterror(nvl, ETOPO_PROP_TYPE, err));
154 
155 	/* Release the last value and re-assign to the new value */
156 	if (pv->tp_val != NULL)
157 		nvlist_free(pv->tp_val);
158 	pv->tp_val = nvl;
159 
160 	return (0);
161 }
162 
163 static topo_propval_t *
164 prop_get(tnode_t *node, const char *pgname, const char *pname, nvlist_t *pargs,
165     int *err)
166 {
167 	topo_propval_t *pv = NULL;
168 
169 	if ((pv = propval_get(pgroup_get(node, pgname), pname)) == NULL) {
170 		*err = ETOPO_PROP_NOENT;
171 		return (NULL);
172 	}
173 
174 	if (pv->tp_method != NULL) {
175 		if (prop_method_get(node, pv, pv->tp_method, pargs, err) < 0)
176 			return (NULL);
177 	}
178 
179 	return (pv);
180 }
181 
182 static int
183 get_properror(tnode_t *node, int *errp, int err)
184 {
185 	topo_node_unlock(node);
186 	*errp = err;
187 	return (-1);
188 }
189 
190 static int
191 prop_getval(tnode_t *node, const char *pgname, const char *pname, void *val,
192     topo_type_t type, uint_t *nelems, int *err)
193 {
194 	int i, j, ret = 0;
195 	topo_hdl_t *thp = node->tn_hdl;
196 	topo_propval_t *pv;
197 
198 	topo_node_lock(node);
199 	if ((pv = prop_get(node, pgname, pname, NULL, err))
200 	    == NULL)
201 		return (get_properror(node, err, *err));
202 
203 	if (pv->tp_type != type)
204 		return (get_properror(node, err, ETOPO_PROP_TYPE));
205 
206 	switch (type) {
207 		case TOPO_TYPE_INT32:
208 			ret = nvlist_lookup_int32(pv->tp_val, TOPO_PROP_VAL_VAL,
209 			    (int32_t *)val);
210 			break;
211 		case TOPO_TYPE_UINT32:
212 			ret = nvlist_lookup_uint32(pv->tp_val,
213 			    TOPO_PROP_VAL_VAL, (uint32_t *)val);
214 			break;
215 		case TOPO_TYPE_INT64:
216 			ret = nvlist_lookup_int64(pv->tp_val, TOPO_PROP_VAL_VAL,
217 			    (int64_t *)val);
218 			break;
219 		case TOPO_TYPE_UINT64:
220 			ret = nvlist_lookup_uint64(pv->tp_val,
221 			    TOPO_PROP_VAL_VAL, (uint64_t *)val);
222 			break;
223 		case TOPO_TYPE_STRING: {
224 			char *str;
225 
226 			ret = nvlist_lookup_string(pv->tp_val,
227 			    TOPO_PROP_VAL_VAL, &str);
228 			if (ret == 0) {
229 				char *s2;
230 				if ((s2 = topo_hdl_strdup(thp, str)) == NULL)
231 					ret = -1;
232 				else
233 					*(char **)val = s2;
234 			}
235 			break;
236 		}
237 		case TOPO_TYPE_FMRI: {
238 			nvlist_t *nvl;
239 
240 			ret = nvlist_lookup_nvlist(pv->tp_val,
241 			    TOPO_PROP_VAL_VAL, &nvl);
242 			if (ret == 0)
243 				ret = topo_hdl_nvdup(thp, nvl,
244 				    (nvlist_t **)val);
245 			break;
246 		}
247 		case TOPO_TYPE_INT32_ARRAY: {
248 			int32_t *a1, *a2;
249 
250 			if ((ret = nvlist_lookup_int32_array(pv->tp_val,
251 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
252 				break;
253 			if ((a1 = topo_hdl_alloc(thp, sizeof (int32_t) *
254 			    *nelems)) == NULL) {
255 				ret = ETOPO_NOMEM;
256 				break;
257 			}
258 			for (i = 0; i < *nelems; ++i)
259 				a1[i] = a2[i];
260 			*(int32_t **)val = a1;
261 			break;
262 		}
263 		case TOPO_TYPE_UINT32_ARRAY: {
264 			uint32_t *a1, *a2;
265 
266 			if ((ret = nvlist_lookup_uint32_array(pv->tp_val,
267 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
268 				break;
269 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint32_t) *
270 			    *nelems)) == NULL) {
271 				ret = ETOPO_NOMEM;
272 				break;
273 			}
274 			for (i = 0; i < *nelems; ++i)
275 				a1[i] = a2[i];
276 			*(uint32_t **)val = a1;
277 			break;
278 		}
279 		case TOPO_TYPE_INT64_ARRAY: {
280 			int64_t *a1, *a2;
281 
282 			if ((ret = nvlist_lookup_int64_array(pv->tp_val,
283 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
284 				break;
285 			if ((a1 = topo_hdl_alloc(thp, sizeof (int64_t) *
286 			    *nelems)) == NULL) {
287 				ret = ETOPO_NOMEM;
288 				break;
289 			}
290 			for (i = 0; i < *nelems; ++i)
291 				a1[i] = a2[i];
292 			*(int64_t **)val = a1;
293 			break;
294 		}
295 		case TOPO_TYPE_UINT64_ARRAY: {
296 			uint64_t *a1, *a2;
297 
298 			if ((ret = nvlist_lookup_uint64_array(pv->tp_val,
299 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
300 				break;
301 			if ((a1 = topo_hdl_alloc(thp, sizeof (uint64_t) *
302 			    *nelems)) == NULL) {
303 				ret = ETOPO_NOMEM;
304 				break;
305 			}
306 			for (i = 0; i < *nelems; ++i)
307 				a1[i] = a2[i];
308 			*(uint64_t **)val = a1;
309 			break;
310 		}
311 		case TOPO_TYPE_STRING_ARRAY: {
312 			char **a1, **a2;
313 
314 			if ((ret = nvlist_lookup_string_array(pv->tp_val,
315 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
316 				break;
317 			if ((a1 = topo_hdl_alloc(thp, sizeof (char *) *
318 			    *nelems)) == NULL) {
319 				ret = ETOPO_NOMEM;
320 				break;
321 			}
322 			for (i = 0; i < *nelems; ++i) {
323 				if ((a1[i] = topo_hdl_strdup(thp, a2[i]))
324 				    == NULL) {
325 					for (j = 0; j < i; ++j)
326 						topo_hdl_free(thp, a1[j],
327 						    sizeof (char *));
328 					topo_hdl_free(thp, a1,
329 					    sizeof (char *) * *nelems);
330 					break;
331 				}
332 			}
333 			*(char ***)val = a1;
334 			break;
335 		}
336 		case TOPO_TYPE_FMRI_ARRAY: {
337 			nvlist_t **a1, **a2;
338 
339 			if ((ret = nvlist_lookup_nvlist_array(pv->tp_val,
340 			    TOPO_PROP_VAL_VAL, &a2, nelems)) != 0)
341 				break;
342 			if ((a1 = topo_hdl_alloc(thp, sizeof (nvlist_t *) *
343 			    *nelems)) == NULL) {
344 				ret = ETOPO_NOMEM;
345 				break;
346 			}
347 			for (i = 0; i < *nelems; ++i) {
348 				if (topo_hdl_nvdup(thp, a2[i], &a1[i]) < 0) {
349 					for (j = 0; j < i; ++j)
350 						nvlist_free(a1[j]);
351 					topo_hdl_free(thp, a1,
352 					    sizeof (nvlist_t *) * *nelems);
353 					break;
354 				}
355 			}
356 			*(nvlist_t ***)val = a1;
357 			break;
358 		}
359 		default:
360 			ret = ETOPO_PROP_NOENT;
361 	}
362 
363 	if (ret != 0)
364 		if (ret == ENOENT)
365 			return (get_properror(node, err, ETOPO_PROP_NOENT));
366 		else if (ret < ETOPO_UNKNOWN)
367 			return (get_properror(node, err, ETOPO_PROP_NVL));
368 		else
369 			return (get_properror(node, err, ret));
370 
371 	topo_node_unlock(node);
372 	return (0);
373 }
374 
375 int
376 topo_prop_get_int32(tnode_t *node, const char *pgname, const char *pname,
377     int32_t *val, int *err)
378 {
379 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT32,
380 	    NULL, err));
381 }
382 
383 int
384 topo_prop_get_uint32(tnode_t *node, const char *pgname, const char *pname,
385     uint32_t *val, int *err)
386 {
387 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT32,
388 	    NULL, err));
389 }
390 
391 int
392 topo_prop_get_int64(tnode_t *node, const char *pgname, const char *pname,
393     int64_t *val, int *err)
394 {
395 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_INT64,
396 	    NULL, err));
397 }
398 
399 int
400 topo_prop_get_uint64(tnode_t *node, const char *pgname, const char *pname,
401     uint64_t *val, int *err)
402 {
403 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_UINT64,
404 	    NULL, err));
405 }
406 
407 int
408 topo_prop_get_string(tnode_t *node, const char *pgname, const char *pname,
409     char **val, int *err)
410 {
411 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_STRING,
412 	    NULL, err));
413 }
414 
415 int
416 topo_prop_get_fmri(tnode_t *node, const char *pgname, const char *pname,
417     nvlist_t **val, int *err)
418 {
419 	return (prop_getval(node, pgname, pname, (void *)val, TOPO_TYPE_FMRI,
420 	    NULL, err));
421 }
422 
423 int
424 topo_prop_get_int32_array(tnode_t *node, const char *pgname, const char *pname,
425     int32_t **val, uint_t *nelem, int *err)
426 {
427 	return (prop_getval(node, pgname, pname, (void *)val,
428 	    TOPO_TYPE_INT32_ARRAY, nelem, err));
429 }
430 
431 int
432 topo_prop_get_uint32_array(tnode_t *node, const char *pgname, const char *pname,
433     uint32_t **val, uint_t *nelem, int *err)
434 {
435 	return (prop_getval(node, pgname, pname, (void *)val,
436 	    TOPO_TYPE_UINT32_ARRAY, nelem, err));
437 }
438 
439 int
440 topo_prop_get_int64_array(tnode_t *node, const char *pgname, const char *pname,
441     int64_t **val, uint_t *nelem, int *err)
442 {
443 	return (prop_getval(node, pgname, pname, (void *)val,
444 	    TOPO_TYPE_INT64_ARRAY, nelem, err));
445 }
446 
447 int
448 topo_prop_get_uint64_array(tnode_t *node, const char *pgname, const char *pname,
449     uint64_t **val, uint_t *nelem, int *err)
450 {
451 	return (prop_getval(node, pgname, pname, (void *)val,
452 	    TOPO_TYPE_UINT64_ARRAY, nelem, err));
453 }
454 
455 int
456 topo_prop_get_string_array(tnode_t *node, const char *pgname, const char *pname,
457     char ***val, uint_t *nelem, int *err)
458 {
459 	return (prop_getval(node, pgname, pname, (void *)val,
460 	    TOPO_TYPE_STRING_ARRAY, nelem, err));
461 }
462 
463 int
464 topo_prop_get_fmri_array(tnode_t *node, const char *pgname, const char *pname,
465     nvlist_t ***val, uint_t *nelem, int *err)
466 {
467 	return (prop_getval(node, pgname, pname, (void *)val,
468 	    TOPO_TYPE_FMRI_ARRAY, nelem, err));
469 }
470 
471 static topo_propval_t *
472 set_seterror(tnode_t *node, topo_proplist_t *pvl, int *errp, int err)
473 {
474 	topo_hdl_t *thp = node->tn_hdl;
475 	topo_propval_t *pv;
476 
477 	if (pvl != NULL) {
478 		pv = pvl->tp_pval;
479 		topo_propval_destroy(pv);
480 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
481 	}
482 
483 	topo_node_unlock(node);
484 	*errp = err;
485 
486 	return (NULL);
487 }
488 
489 static topo_propval_t *
490 prop_create(tnode_t *node, const char *pgname, const char *pname,
491     topo_type_t type, int flag, int *err)
492 {
493 	topo_hdl_t *thp = node->tn_hdl;
494 	topo_pgroup_t *pg;
495 	topo_propval_t *pv;
496 	topo_proplist_t *pvl;
497 
498 	/*
499 	 * Replace existing prop value with new one
500 	 */
501 	if ((pg = pgroup_get(node, pgname)) == NULL) {
502 		topo_node_unlock(node);
503 		*err = ETOPO_PROP_NOENT;
504 		return (NULL);
505 	}
506 
507 	if ((pv = propval_get(pg, pname)) != NULL) {
508 		if (pv->tp_type != type)
509 			return (set_seterror(node, NULL, err, ETOPO_PROP_TYPE));
510 		else if (pv->tp_flag == TOPO_PROP_IMMUTABLE)
511 			return (set_seterror(node, NULL, err, ETOPO_PROP_DEFD));
512 
513 		nvlist_free(pv->tp_val);
514 		pv->tp_val = NULL;
515 	} else {
516 		if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
517 		    == NULL)
518 			return (set_seterror(node, NULL, err, ETOPO_NOMEM));
519 
520 		if ((pv = topo_hdl_zalloc(thp, sizeof (topo_propval_t)))
521 		    == NULL)
522 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
523 
524 		pv->tp_hdl = thp;
525 		pvl->tp_pval = pv;
526 
527 		if ((pv->tp_name = topo_hdl_strdup(thp, pname))
528 		    == NULL)
529 			return (set_seterror(node, pvl, err, ETOPO_NOMEM));
530 		pv->tp_flag = flag;
531 		pv->tp_type = type;
532 		topo_prop_hold(pv);
533 		topo_list_append(&pg->tpg_pvals, pvl);
534 	}
535 
536 	return (pv);
537 }
538 
539 static int
540 topo_prop_set(tnode_t *node, const char *pgname, const char *pname,
541     topo_type_t type, int flag, void *val, int nelems, int *err)
542 {
543 	int ret;
544 	topo_hdl_t *thp = node->tn_hdl;
545 	nvlist_t *nvl;
546 	topo_propval_t *pv;
547 
548 	if (topo_hdl_nvalloc(thp, &nvl, NV_UNIQUE_NAME) < 0) {
549 		*err = ETOPO_PROP_NVL;
550 		return (-1);
551 	}
552 
553 	ret = nvlist_add_string(nvl, TOPO_PROP_VAL_NAME, pname);
554 	ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_TYPE, type);
555 	switch (type) {
556 		case TOPO_TYPE_INT32:
557 			ret |= nvlist_add_int32(nvl, TOPO_PROP_VAL_VAL,
558 			    *(int32_t *)val);
559 			break;
560 		case TOPO_TYPE_UINT32:
561 			ret |= nvlist_add_uint32(nvl, TOPO_PROP_VAL_VAL,
562 			    *(uint32_t *)val);
563 			break;
564 		case TOPO_TYPE_INT64:
565 			ret |= nvlist_add_int64(nvl, TOPO_PROP_VAL_VAL,
566 			    *(int64_t *)val);
567 			break;
568 		case TOPO_TYPE_UINT64:
569 			ret |= nvlist_add_uint64(nvl, TOPO_PROP_VAL_VAL,
570 			    *(uint64_t *)val);
571 			break;
572 		case TOPO_TYPE_STRING:
573 			ret |= nvlist_add_string(nvl, TOPO_PROP_VAL_VAL,
574 			    (char *)val);
575 			break;
576 		case TOPO_TYPE_FMRI:
577 			ret |= nvlist_add_nvlist(nvl, TOPO_PROP_VAL_VAL,
578 			    (nvlist_t *)val);
579 			break;
580 		case TOPO_TYPE_INT32_ARRAY:
581 			ret |= nvlist_add_int32_array(nvl,
582 			    TOPO_PROP_VAL_VAL, (int32_t *)val, nelems);
583 			break;
584 		case TOPO_TYPE_UINT32_ARRAY:
585 			ret |= nvlist_add_uint32_array(nvl,
586 			    TOPO_PROP_VAL_VAL, (uint32_t *)val, nelems);
587 			break;
588 		case TOPO_TYPE_INT64_ARRAY:
589 			ret |= nvlist_add_int64_array(nvl,
590 			    TOPO_PROP_VAL_VAL, (int64_t *)val, nelems);
591 			break;
592 		case TOPO_TYPE_UINT64_ARRAY:
593 			ret |= nvlist_add_uint64_array(nvl,
594 			    TOPO_PROP_VAL_VAL, (uint64_t *)val, nelems);
595 			break;
596 		case TOPO_TYPE_STRING_ARRAY:
597 			ret |= nvlist_add_string_array(nvl,
598 			    TOPO_PROP_VAL_VAL, (char **)val, nelems);
599 			break;
600 		case TOPO_TYPE_FMRI_ARRAY:
601 			ret |= nvlist_add_nvlist_array(nvl,
602 			    TOPO_PROP_VAL_VAL, (nvlist_t **)val, nelems);
603 			break;
604 		default:
605 			*err = ETOPO_PROP_TYPE;
606 			return (-1);
607 	}
608 
609 	if (ret != 0) {
610 		nvlist_free(nvl);
611 		if (ret == ENOMEM) {
612 			*err = ETOPO_PROP_NOMEM;
613 			return (-1);
614 		} else {
615 			*err = ETOPO_PROP_NVL;
616 			return (-1);
617 		}
618 	}
619 
620 	topo_node_lock(node);
621 	if ((pv = prop_create(node, pgname, pname, type, flag, err)) == NULL) {
622 		nvlist_free(nvl);
623 		return (-1); /* unlocked and err set */
624 	}
625 
626 	pv->tp_val = nvl;
627 
628 	topo_node_unlock(node);
629 
630 	return (ret);
631 }
632 
633 int
634 topo_prop_set_int32(tnode_t *node, const char *pgname, const char *pname,
635     int flag, int32_t val, int *err)
636 {
637 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32, flag,
638 	    &val, 1, err));
639 }
640 
641 int
642 topo_prop_set_uint32(tnode_t *node, const char *pgname, const char *pname,
643     int flag, uint32_t val, int *err)
644 {
645 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32, flag,
646 	    &val, 1, err));
647 }
648 
649 int
650 topo_prop_set_int64(tnode_t *node, const char *pgname, const char *pname,
651     int flag, int64_t val, int *err)
652 {
653 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64, flag,
654 	    &val, 1, err));
655 }
656 
657 int
658 topo_prop_set_uint64(tnode_t *node, const char *pgname, const char *pname,
659     int flag, uint64_t val, int *err)
660 {
661 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64, flag,
662 	    &val, 1, err));
663 }
664 
665 int
666 topo_prop_set_string(tnode_t *node, const char *pgname, const char *pname,
667     int flag, const char *val, int *err)
668 {
669 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING, flag,
670 	    (void *)val, 1, err));
671 }
672 
673 int
674 topo_prop_set_fmri(tnode_t *node, const char *pgname, const char *pname,
675     int flag, const nvlist_t *fmri, int *err)
676 {
677 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI, flag,
678 	    (void *)fmri, 1, err));
679 }
680 
681 int
682 topo_prop_set_int32_array(tnode_t *node, const char *pgname, const char *pname,
683     int flag, int32_t *val, uint_t nelems, int *err)
684 {
685 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT32_ARRAY, flag,
686 	    val, nelems, err));
687 }
688 
689 int
690 topo_prop_set_uint32_array(tnode_t *node, const char *pgname, const char *pname,
691     int flag, uint32_t *val, uint_t nelems, int *err)
692 {
693 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT32_ARRAY, flag,
694 	    val, nelems, err));
695 }
696 
697 int
698 topo_prop_set_int64_array(tnode_t *node, const char *pgname, const char *pname,
699     int flag, int64_t *val, uint_t nelems, int *err)
700 {
701 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_INT64_ARRAY, flag,
702 	    val, nelems, err));
703 }
704 
705 int
706 topo_prop_set_uint64_array(tnode_t *node, const char *pgname, const char *pname,
707     int flag, uint64_t *val, uint_t nelems, int *err)
708 {
709 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_UINT64_ARRAY, flag,
710 	    val, nelems, err));
711 }
712 
713 int
714 topo_prop_set_string_array(tnode_t *node, const char *pgname, const char *pname,
715     int flag, const char **val, uint_t nelems, int *err)
716 {
717 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_STRING_ARRAY, flag,
718 	    (void *)val, nelems, err));
719 }
720 
721 int
722 topo_prop_set_fmri_array(tnode_t *node, const char *pgname, const char *pname,
723     int flag, const nvlist_t **fmri, uint_t nelems, int *err)
724 {
725 	return (topo_prop_set(node, pgname, pname, TOPO_TYPE_FMRI_ARRAY, flag,
726 	    (void *)fmri, nelems, err));
727 }
728 
729 /*
730  * topo_prop_setprop() is a private project function for fmtopo
731  */
732 int
733 topo_prop_setprop(tnode_t *node, const char *pgname, nvlist_t *prop,
734     int flag, nvlist_t *pargs, int *err)
735 {
736 	int ret;
737 	topo_hdl_t *thp = node->tn_hdl;
738 	topo_propval_t *pv;
739 	nvlist_t *nvl, *args;
740 	char *name;
741 	topo_type_t type;
742 
743 	if (nvlist_lookup_string(prop, TOPO_PROP_VAL_NAME, &name) != 0) {
744 		*err = ETOPO_PROP_NAME;
745 		return (-1);
746 	}
747 	if (nvlist_lookup_uint32(prop, TOPO_PROP_VAL_TYPE, (uint32_t *)&type)
748 	    != 0) {
749 		*err = ETOPO_PROP_TYPE;
750 		return (-1);
751 	}
752 
753 	topo_node_lock(node);
754 	if ((pv = prop_create(node, pgname, name, type, flag, err)) == NULL)
755 		return (-1); /* unlocked and err set */
756 
757 	/*
758 	 * Set by method or set to new prop value.  If we fail, leave
759 	 * property in list with old value.
760 	 */
761 	if (pv->tp_method != NULL) {
762 		topo_propmethod_t *pm = pv->tp_method;
763 
764 		if (topo_hdl_nvalloc(pv->tp_hdl, &args, NV_UNIQUE_NAME) < 0) {
765 			topo_node_unlock(node);
766 			*err = ETOPO_PROP_NOMEM;
767 			return (-1);
768 		}
769 		ret = nvlist_add_nvlist(args, TOPO_PROP_ARGS, pm->tpm_args);
770 		if (pargs != NULL)
771 			ret |= nvlist_add_nvlist(args, TOPO_PROP_PARGS, pargs);
772 
773 		if (ret != 0) {
774 			topo_node_unlock(node);
775 			nvlist_free(args);
776 			*err = ETOPO_PROP_NVL;
777 			return (-1);
778 		}
779 
780 		ret = topo_method_call(node, pm->tpm_name, pm->tpm_version,
781 		    args, &nvl, err);
782 		nvlist_free(args);
783 	} else {
784 		if ((ret = topo_hdl_nvdup(thp, prop, &nvl)) != 0)
785 			*err = ETOPO_PROP_NOMEM;
786 	}
787 
788 	if (ret != 0) {
789 		topo_node_unlock(node);
790 		return (-1);
791 	}
792 
793 	pv->tp_val = nvl;
794 	topo_node_unlock(node);
795 	return (0);
796 }
797 
798 static int
799 register_methoderror(tnode_t *node, topo_propmethod_t *pm, int *errp, int l,
800     int err)
801 {
802 	topo_hdl_t *thp = node->tn_hdl;
803 
804 	if (pm != NULL) {
805 		if (pm->tpm_name != NULL)
806 			topo_hdl_strfree(thp, pm->tpm_name);
807 		if (pm->tpm_args != NULL)
808 			nvlist_free(pm->tpm_args);
809 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
810 	}
811 
812 	*errp = err;
813 
814 	if (l != 0)
815 		topo_node_unlock(node);
816 
817 	return (-1);
818 }
819 
820 int
821 prop_method_register(tnode_t *node, const char *pgname, const char *pname,
822     topo_type_t ptype, const char *mname, topo_version_t version,
823     const nvlist_t *args, int *err)
824 {
825 	topo_hdl_t *thp = node->tn_hdl;
826 	topo_propmethod_t *pm = NULL;
827 	topo_propval_t *pv = NULL;
828 
829 	if ((pm = topo_hdl_zalloc(thp, sizeof (topo_propmethod_t))) == NULL)
830 		return (register_methoderror(node, pm, err, 1,
831 		    ETOPO_PROP_NOMEM));
832 
833 	if ((pm->tpm_name = topo_hdl_strdup(thp, mname)) == NULL)
834 		return (register_methoderror(node, pm, err, 1,
835 		    ETOPO_PROP_NOMEM));
836 
837 	pm->tpm_version = version;
838 
839 	if (topo_hdl_nvdup(thp, (nvlist_t *)args, &pm->tpm_args) != 0)
840 		return (register_methoderror(node, pm, err, 1,
841 		    ETOPO_PROP_NOMEM));
842 
843 	if ((pv = prop_create(node, pgname, pname, ptype, TOPO_PROP_MUTABLE,
844 	    err)) == NULL) {
845 		/* node unlocked */
846 		return (register_methoderror(node, pm, err, 0, *err));
847 	}
848 
849 	if (pv->tp_method != NULL) {
850 		topo_node_unlock(node);
851 		return (register_methoderror(node, pm, err, 1,
852 		    ETOPO_METHOD_DEFD));
853 	}
854 
855 	pv->tp_val = NULL;
856 	pv->tp_method = pm;
857 
858 	topo_node_unlock(node);
859 
860 	return (0);
861 }
862 
863 int
864 topo_prop_method_register(tnode_t *node, const char *pgname, const char *pname,
865     topo_type_t ptype, const char *mname, const nvlist_t *args, int *err)
866 {
867 	topo_imethod_t *mp;
868 
869 	topo_node_lock(node);
870 
871 	if ((mp = topo_method_lookup(node, mname)) == NULL)
872 		return (register_methoderror(node, NULL, err, 1,
873 		    ETOPO_METHOD_NOTSUP));
874 
875 	return (prop_method_register(node, pgname, pname, ptype, mname,
876 	    mp->tim_version, args, err)); /* err set and node unlocked */
877 }
878 
879 int
880 topo_prop_method_version_register(tnode_t *node, const char *pgname,
881     const char *pname, topo_type_t ptype, const char *mname,
882     topo_version_t version, const nvlist_t *args, int *err)
883 {
884 	topo_imethod_t *mp;
885 
886 	topo_node_lock(node);
887 
888 	if ((mp = topo_method_lookup(node, mname)) == NULL)
889 		return (register_methoderror(node, NULL, err, 1,
890 		    ETOPO_METHOD_NOTSUP));
891 
892 	if (version < mp->tim_version)
893 		return (register_methoderror(node, NULL, err, 1,
894 		    ETOPO_METHOD_VEROLD));
895 	if (version > mp->tim_version)
896 		return (register_methoderror(node, NULL, err, 1,
897 		    ETOPO_METHOD_VERNEW));
898 
899 	return (prop_method_register(node, pgname, pname, ptype, mname,
900 	    version, args, err)); /* err set and node unlocked */
901 }
902 
903 void
904 topo_prop_method_unregister(tnode_t *node, const char *pgname,
905     const char *pname)
906 {
907 	topo_propval_t *pv;
908 	topo_pgroup_t *pg;
909 	topo_proplist_t *pvl;
910 	topo_hdl_t *thp = node->tn_hdl;
911 
912 	topo_node_lock(node);
913 
914 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
915 	    pg = topo_list_next(pg)) {
916 		if (strcmp(pg->tpg_info->tpi_name, pgname) == 0) {
917 			break;
918 		}
919 	}
920 
921 	if (pg == NULL) {
922 		topo_node_unlock(node);
923 		return;
924 	}
925 
926 	for (pvl = topo_list_next(&pg->tpg_list); pvl != NULL;
927 	    pvl = topo_list_next(pvl)) {
928 		pv = pvl->tp_pval;
929 		if (strcmp(pv->tp_name, pname) == 0) {
930 			topo_list_delete(&pg->tpg_pvals, pvl);
931 			assert(pv->tp_refs == 1);
932 			topo_prop_rele(pv);
933 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
934 			break;
935 		}
936 	}
937 
938 	topo_node_unlock(node);
939 }
940 
941 static int
942 inherit_seterror(tnode_t *node, int *errp, int err)
943 {
944 	topo_node_unlock(node);
945 	topo_node_unlock(node->tn_parent);
946 
947 	*errp = err;
948 
949 	return (-1);
950 }
951 
952 int
953 topo_prop_inherit(tnode_t *node, const char *pgname, const char *name, int *err)
954 {
955 	topo_hdl_t *thp = node->tn_hdl;
956 	tnode_t *pnode = node->tn_parent;
957 	topo_pgroup_t *pg;
958 	topo_propval_t *pv;
959 	topo_proplist_t *pvl;
960 
961 	topo_node_lock(pnode);
962 	topo_node_lock(node);
963 	/*
964 	 * Check for an existing property group and prop val
965 	 */
966 	if ((pv = propval_get(pgroup_get(pnode, pgname), name)) == NULL)
967 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
968 
969 	/*
970 	 * Can this propval be inherited?
971 	 */
972 	if (pv->tp_flag != TOPO_PROP_IMMUTABLE)
973 		return (inherit_seterror(node, err, ETOPO_PROP_NOINHERIT));
974 
975 	/*
976 	 * Property group should already exist: bump the ref count for this
977 	 * propval and add it to the node's property group
978 	 */
979 	if ((pg = pgroup_get(node, pgname)) == NULL)
980 		return (inherit_seterror(node, err, ETOPO_PROP_NOENT));
981 
982 	if ((pvl = topo_hdl_zalloc(thp, sizeof (topo_proplist_t)))
983 	    == NULL)
984 		return (inherit_seterror(node, err, ETOPO_NOMEM));
985 
986 	topo_prop_hold(pv);
987 	pvl->tp_pval = pv;
988 	topo_list_append(&pg->tpg_pvals, pvl);
989 
990 	topo_node_unlock(node);
991 	topo_node_unlock(pnode);
992 
993 	return (0);
994 }
995 
996 topo_pgroup_info_t *
997 topo_pgroup_info(tnode_t *node, const char *pgname, int *err)
998 {
999 	topo_hdl_t *thp = node->tn_hdl;
1000 	topo_pgroup_t *pg;
1001 	topo_ipgroup_info_t *pip;
1002 	topo_pgroup_info_t *info;
1003 
1004 	topo_node_lock(node);
1005 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1006 	    pg = topo_list_next(pg)) {
1007 		if (strcmp(pgname, pg->tpg_info->tpi_name) == 0) {
1008 			if ((info = topo_hdl_alloc(thp,
1009 			    sizeof (topo_pgroup_info_t))) == NULL)
1010 				return (NULL);
1011 
1012 			pip = pg->tpg_info;
1013 			if ((info->tpi_name =
1014 			    topo_hdl_strdup(thp, pip->tpi_name)) == NULL) {
1015 				*err = ETOPO_PROP_NOMEM;
1016 				topo_hdl_free(thp, info,
1017 				    sizeof (topo_pgroup_info_t));
1018 				topo_node_unlock(node);
1019 				return (NULL);
1020 			}
1021 			info->tpi_namestab = pip->tpi_namestab;
1022 			info->tpi_datastab = pip->tpi_datastab;
1023 			info->tpi_version = pip->tpi_version;
1024 			topo_node_unlock(node);
1025 			return (info);
1026 		}
1027 	}
1028 
1029 	*err = ETOPO_PROP_NOENT;
1030 	topo_node_unlock(node);
1031 	return (NULL);
1032 }
1033 
1034 static int
1035 pgroup_seterr(tnode_t *node, topo_pgroup_t *pg, topo_ipgroup_info_t *pip,
1036     int *err)
1037 {
1038 	topo_hdl_t *thp = node->tn_hdl;
1039 
1040 	if (pip != NULL) {
1041 		if (pip->tpi_name != NULL)
1042 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1043 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1044 	}
1045 
1046 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1047 	*err = ETOPO_NOMEM;
1048 
1049 	topo_node_unlock(node);
1050 
1051 	return (-1);
1052 }
1053 
1054 int
1055 topo_pgroup_create(tnode_t *node, const topo_pgroup_info_t *pinfo, int *err)
1056 {
1057 	topo_pgroup_t *pg;
1058 	topo_ipgroup_info_t *pip;
1059 	topo_hdl_t *thp = node->tn_hdl;
1060 
1061 	*err = 0;
1062 
1063 	topo_node_lock(node);
1064 	/*
1065 	 * Check for an existing pgroup
1066 	 */
1067 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1068 	    pg = topo_list_next(pg)) {
1069 		if (strcmp(pg->tpg_info->tpi_name, pinfo->tpi_name) == 0) {
1070 			*err = ETOPO_PROP_DEFD;
1071 			topo_node_unlock(node);
1072 			return (-1);
1073 		}
1074 	}
1075 
1076 	if ((pg = topo_hdl_zalloc(thp, sizeof (topo_pgroup_t))) == NULL) {
1077 		*err = ETOPO_NOMEM;
1078 		topo_node_unlock(node);
1079 		return (-1);
1080 	}
1081 
1082 	if ((pip = topo_hdl_zalloc(thp, sizeof (topo_ipgroup_info_t)))
1083 	    == NULL)
1084 		return (pgroup_seterr(node, pg, pip, err));
1085 
1086 	if ((pip->tpi_name = topo_hdl_strdup(thp, pinfo->tpi_name))
1087 	    == NULL)
1088 		return (pgroup_seterr(node, pg, pip, err));
1089 
1090 	pip->tpi_namestab = pinfo->tpi_namestab;
1091 	pip->tpi_datastab = pinfo->tpi_datastab;
1092 	pip->tpi_version = pinfo->tpi_version;
1093 
1094 	pg->tpg_info = pip;
1095 
1096 	topo_list_append(&node->tn_pgroups, pg);
1097 	topo_node_unlock(node);
1098 
1099 	return (0);
1100 }
1101 
1102 void
1103 topo_pgroup_destroy(tnode_t *node, const char *pname)
1104 {
1105 	topo_hdl_t *thp = node->tn_hdl;
1106 	topo_pgroup_t *pg;
1107 	topo_proplist_t *pvl;
1108 	topo_ipgroup_info_t *pip;
1109 
1110 	topo_node_lock(node);
1111 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1112 	    pg = topo_list_next(pg)) {
1113 		if (strcmp(pg->tpg_info->tpi_name, pname) == 0) {
1114 			break;
1115 		}
1116 	}
1117 
1118 	if (pg == NULL) {
1119 		topo_node_unlock(node);
1120 		return;
1121 	}
1122 
1123 	while ((pvl = topo_list_next(&pg->tpg_list)) != NULL) {
1124 		topo_list_delete(&pg->tpg_pvals, pvl);
1125 		topo_prop_rele(pvl->tp_pval);
1126 		topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1127 	}
1128 
1129 	topo_list_delete(&node->tn_pgroups, pg);
1130 	topo_node_unlock(node);
1131 
1132 	pip = pg->tpg_info;
1133 	if (pip != NULL) {
1134 		if (pip->tpi_name != NULL)
1135 			topo_hdl_strfree(thp, (char *)pip->tpi_name);
1136 		topo_hdl_free(thp, pip, sizeof (topo_ipgroup_info_t));
1137 	}
1138 
1139 	topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1140 }
1141 
1142 void
1143 topo_pgroup_destroy_all(tnode_t *node)
1144 {
1145 	topo_hdl_t *thp = node->tn_hdl;
1146 	topo_pgroup_t *pg;
1147 	topo_proplist_t *pvl;
1148 	topo_ipgroup_info_t *pip;
1149 
1150 	topo_node_lock(node);
1151 	while ((pg = topo_list_next(&node->tn_pgroups)) != NULL) {
1152 		while ((pvl = topo_list_next(&pg->tpg_pvals)) != NULL) {
1153 			topo_list_delete(&pg->tpg_pvals, pvl);
1154 			topo_prop_rele(pvl->tp_pval);
1155 			topo_hdl_free(thp, pvl, sizeof (topo_proplist_t));
1156 		}
1157 
1158 		topo_list_delete(&node->tn_pgroups, pg);
1159 
1160 		pip = pg->tpg_info;
1161 		if (pip != NULL) {
1162 			if (pip->tpi_name != NULL)
1163 				topo_hdl_strfree(thp, (char *)pip->tpi_name);
1164 			topo_hdl_free(thp, pip, sizeof (topo_pgroup_info_t));
1165 		}
1166 
1167 		topo_hdl_free(thp, pg, sizeof (topo_pgroup_t));
1168 	}
1169 	topo_node_unlock(node);
1170 }
1171 
1172 static void
1173 propmethod_destroy(topo_hdl_t *thp, topo_propval_t *pv)
1174 {
1175 	topo_propmethod_t *pm;
1176 
1177 	pm = pv->tp_method;
1178 	if (pm != NULL) {
1179 		if (pm->tpm_name != NULL)
1180 			topo_hdl_strfree(thp, pm->tpm_name);
1181 		if (pm->tpm_args != NULL)
1182 			nvlist_free(pm->tpm_args);
1183 		topo_hdl_free(thp, pm, sizeof (topo_propmethod_t));
1184 		pv->tp_method = NULL;
1185 	}
1186 }
1187 
1188 static void
1189 topo_propval_destroy(topo_propval_t *pv)
1190 {
1191 	topo_hdl_t *thp;
1192 
1193 	if (pv == NULL)
1194 		return;
1195 
1196 	thp = pv->tp_hdl;
1197 
1198 	if (pv->tp_name != NULL)
1199 		topo_hdl_strfree(thp, pv->tp_name);
1200 
1201 	if (pv->tp_val != NULL)
1202 		nvlist_free(pv->tp_val);
1203 
1204 	propmethod_destroy(thp, pv);
1205 
1206 	topo_hdl_free(thp, pv, sizeof (topo_propval_t));
1207 }
1208 
1209 void
1210 topo_prop_hold(topo_propval_t *pv)
1211 {
1212 	pv->tp_refs++;
1213 }
1214 
1215 void
1216 topo_prop_rele(topo_propval_t *pv)
1217 {
1218 	pv->tp_refs--;
1219 
1220 	assert(pv->tp_refs >= 0);
1221 
1222 	if (pv->tp_refs == 0)
1223 		topo_propval_destroy(pv);
1224 }
1225 
1226 /*
1227  * topo_prop_getprop() and topo_prop_getprops() are private project functions
1228  * for fmtopo
1229  */
1230 int
1231 topo_prop_getprop(tnode_t *node, const char *pgname, const char *pname,
1232     nvlist_t *args, nvlist_t **prop, int *err)
1233 {
1234 	topo_hdl_t *thp = node->tn_hdl;
1235 	topo_propval_t *pv;
1236 
1237 	topo_node_lock(node);
1238 	if ((pv = prop_get(node, pgname, pname, args, err)) == NULL) {
1239 		topo_node_unlock(node);
1240 		(void) get_properror(node, err, *err);
1241 		return (-1);
1242 	}
1243 
1244 	if (topo_hdl_nvdup(thp, pv->tp_val, prop) != 0) {
1245 		topo_node_unlock(node);
1246 		(void) get_properror(node, err, ETOPO_NOMEM);
1247 		return (-1);
1248 	}
1249 	topo_node_unlock(node);
1250 
1251 	return (0);
1252 }
1253 
1254 static int
1255 prop_val_add(tnode_t *node, nvlist_t **nvl, topo_propval_t *pv, int *err)
1256 {
1257 	if (pv->tp_method != NULL)
1258 		if (prop_method_get(node, pv, pv->tp_method, NULL, err) < 0)
1259 			return (-1);
1260 
1261 	if (pv->tp_val == NULL) {
1262 		*err = ETOPO_PROP_NOENT;
1263 		return (-1);
1264 	}
1265 
1266 	if (topo_hdl_nvdup(pv->tp_hdl, pv->tp_val, nvl) != 0) {
1267 		*err = ETOPO_PROP_NOMEM;
1268 		return (-1);
1269 	}
1270 
1271 	return (0);
1272 }
1273 
1274 static int
1275 get_pgrp_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1276 {
1277 	topo_node_unlock(node);
1278 
1279 	if (nvl != NULL)
1280 		nvlist_free(nvl);
1281 
1282 	*errp = err;
1283 
1284 	return (-1);
1285 }
1286 
1287 int
1288 topo_prop_getpgrp(tnode_t *node, const char *pgname, nvlist_t **pgrp,
1289     int *err)
1290 {
1291 	int ret;
1292 	topo_hdl_t *thp = node->tn_hdl;
1293 	nvlist_t *nvl, *pvnvl;
1294 	topo_pgroup_t *pg;
1295 	topo_propval_t *pv;
1296 	topo_proplist_t *pvl;
1297 
1298 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1299 		*err = ETOPO_NOMEM;
1300 		return (-1);
1301 	}
1302 
1303 	topo_node_lock(node);
1304 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1305 	    pg = topo_list_next(pg)) {
1306 
1307 		if (strcmp(pgname, pg->tpg_info->tpi_name) != 0)
1308 			continue;
1309 
1310 		if (nvlist_add_string(nvl, TOPO_PROP_GROUP_NAME,
1311 		    pg->tpg_info->tpi_name) != 0 ||
1312 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_NSTAB,
1313 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1314 		    nvlist_add_string(nvl, TOPO_PROP_GROUP_DSTAB,
1315 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1316 		    nvlist_add_int32(nvl, TOPO_PROP_GROUP_VERSION,
1317 		    pg->tpg_info->tpi_version) != 0)
1318 			return (get_pgrp_seterror(node, nvl, err,
1319 			    ETOPO_PROP_NVL));
1320 
1321 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1322 		    pvl = topo_list_next(pvl)) {
1323 
1324 			pv = pvl->tp_pval;
1325 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1326 				return (get_pgrp_seterror(node, nvl, err,
1327 				    *err));
1328 			}
1329 			if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_VAL,
1330 			    pvnvl)) != 0) {
1331 				nvlist_free(pvnvl);
1332 				return (get_pgrp_seterror(node, nvl, err, ret));
1333 			}
1334 
1335 			nvlist_free(pvnvl);
1336 		}
1337 		topo_node_unlock(node);
1338 		*pgrp = nvl;
1339 		return (0);
1340 	}
1341 
1342 	topo_node_unlock(node);
1343 	*err = ETOPO_PROP_NOENT;
1344 	return (-1);
1345 }
1346 
1347 static nvlist_t *
1348 get_all_seterror(tnode_t *node, nvlist_t *nvl, int *errp, int err)
1349 {
1350 	topo_node_unlock(node);
1351 
1352 	if (nvl != NULL)
1353 		nvlist_free(nvl);
1354 
1355 	*errp = err;
1356 
1357 	return (NULL);
1358 }
1359 
1360 nvlist_t *
1361 topo_prop_getprops(tnode_t *node, int *err)
1362 {
1363 	int ret;
1364 	topo_hdl_t *thp = node->tn_hdl;
1365 	nvlist_t *nvl, *pgnvl, *pvnvl;
1366 	topo_pgroup_t *pg;
1367 	topo_propval_t *pv;
1368 	topo_proplist_t *pvl;
1369 
1370 	topo_node_lock(node);
1371 	if (topo_hdl_nvalloc(thp, &nvl, 0) != 0) {
1372 		return (get_all_seterror(node, NULL, err, ETOPO_NOMEM));
1373 	}
1374 
1375 	for (pg = topo_list_next(&node->tn_pgroups); pg != NULL;
1376 	    pg = topo_list_next(pg)) {
1377 		if (topo_hdl_nvalloc(thp, &pgnvl, 0) != 0)
1378 			return (get_all_seterror(node, nvl, err, ETOPO_NOMEM));
1379 
1380 		if (nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NAME,
1381 		    pg->tpg_info->tpi_name) != 0 ||
1382 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_NSTAB,
1383 		    topo_stability2name(pg->tpg_info->tpi_namestab)) != 0 ||
1384 		    nvlist_add_string(pgnvl, TOPO_PROP_GROUP_DSTAB,
1385 		    topo_stability2name(pg->tpg_info->tpi_datastab)) != 0 ||
1386 		    nvlist_add_int32(pgnvl, TOPO_PROP_GROUP_VERSION,
1387 		    pg->tpg_info->tpi_version) != 0)
1388 			return (get_all_seterror(node, nvl, err,
1389 			    ETOPO_PROP_NVL));
1390 
1391 		for (pvl = topo_list_next(&pg->tpg_pvals); pvl != NULL;
1392 		    pvl = topo_list_next(pvl)) {
1393 
1394 			pv = pvl->tp_pval;
1395 			if (prop_val_add(node, &pvnvl, pv, err) < 0) {
1396 				nvlist_free(pgnvl);
1397 				return (get_all_seterror(node, nvl, err, *err));
1398 			}
1399 			if ((ret = nvlist_add_nvlist(pgnvl, TOPO_PROP_VAL,
1400 			    pvnvl)) != 0) {
1401 				nvlist_free(pgnvl);
1402 				nvlist_free(pvnvl);
1403 				return (get_all_seterror(node, nvl, err, ret));
1404 			}
1405 
1406 			nvlist_free(pvnvl);
1407 		}
1408 		if ((ret = nvlist_add_nvlist(nvl, TOPO_PROP_GROUP, pgnvl))
1409 		    != 0) {
1410 			nvlist_free(pgnvl);
1411 			return (get_all_seterror(node, nvl, err, ret));
1412 		}
1413 
1414 		nvlist_free(pgnvl);
1415 	}
1416 
1417 	topo_node_unlock(node);
1418 
1419 	return (nvl);
1420 }
1421