xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_xml.c (revision a92282e44f968185a6bba094d1e5fece2da819cf)
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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright 2020 Joyent, Inc.
25  */
26 
27 #include <libxml/parser.h>
28 #include <libxml/xinclude.h>
29 #include <sys/fm/protocol.h>
30 #include <assert.h>
31 #include <string.h>
32 #include <strings.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <limits.h>
36 #include <fm/libtopo.h>
37 #include <unistd.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <topo_file.h>
41 #include <topo_mod.h>
42 #include <topo_subr.h>
43 #include <topo_alloc.h>
44 #include <topo_parse.h>
45 #include <topo_error.h>
46 
47 static tf_rdata_t *topo_xml_walk(topo_mod_t *, tf_info_t *, xmlNodePtr,
48     tnode_t *);
49 static tf_edata_t *enum_attributes_process(topo_mod_t *, xmlNodePtr);
50 static int enum_run(topo_mod_t *, tf_rdata_t *);
51 static int fac_enum_run(topo_mod_t *, tnode_t *, const char *);
52 static int fac_process(topo_mod_t *, xmlNodePtr, tf_rdata_t *, tnode_t *);
53 static int fac_enum_process(topo_mod_t *, xmlNodePtr, tnode_t *);
54 static int decorate_nodes(topo_mod_t *, tf_rdata_t *, xmlNodePtr, tnode_t *,
55     tf_pad_t **);
56 
57 
58 int
59 xmlattr_to_stab(topo_mod_t *mp, xmlNodePtr n, const char *stabname,
60     topo_stability_t *rs)
61 {
62 	xmlChar *str;
63 	int rv = 0;
64 
65 	if (n == NULL) {
66 		/* If there is no Stability defined, we default to private */
67 		*rs = TOPO_STABILITY_PRIVATE;
68 		return (0);
69 	}
70 	if ((str = xmlGetProp(n, (xmlChar *)stabname)) == NULL) {
71 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
72 		    "attribute to stability:\n");
73 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
74 	}
75 
76 	if (xmlStrcmp(str, (xmlChar *)Internal) == 0) {
77 		*rs = TOPO_STABILITY_INTERNAL;
78 	} else if (xmlStrcmp(str, (xmlChar *)Private) == 0) {
79 		*rs = TOPO_STABILITY_PRIVATE;
80 	} else if (xmlStrcmp(str, (xmlChar *)Obsolete) == 0) {
81 		*rs = TOPO_STABILITY_OBSOLETE;
82 	} else if (xmlStrcmp(str, (xmlChar *)External) == 0) {
83 		*rs = TOPO_STABILITY_EXTERNAL;
84 	} else if (xmlStrcmp(str, (xmlChar *)Unstable) == 0) {
85 		*rs = TOPO_STABILITY_UNSTABLE;
86 	} else if (xmlStrcmp(str, (xmlChar *)Evolving) == 0) {
87 		*rs = TOPO_STABILITY_EVOLVING;
88 	} else if (xmlStrcmp(str, (xmlChar *)Stable) == 0) {
89 		*rs = TOPO_STABILITY_STABLE;
90 	} else if (xmlStrcmp(str, (xmlChar *)Standard) == 0) {
91 		*rs = TOPO_STABILITY_STANDARD;
92 	} else {
93 		xmlFree(str);
94 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADSTAB));
95 	}
96 	xmlFree(str);
97 	return (rv);
98 }
99 
100 int
101 xmlattr_to_int(topo_mod_t *mp,
102     xmlNodePtr n, const char *propname, uint64_t *value)
103 {
104 	xmlChar *str;
105 	xmlChar *estr;
106 
107 	if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL) {
108 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
109 		    "%s: failed to lookup %s attribute", __func__, propname);
110 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
111 	}
112 	errno = 0;
113 	*value = strtoull((char *)str, (char **)&estr, 0);
114 	if (errno != 0 || *estr != '\0') {
115 		/* no conversion was done */
116 		xmlFree(str);
117 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
118 		    "%s: failed to convert %s attribute", __func__, propname);
119 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
120 	}
121 	xmlFree(str);
122 	return (0);
123 }
124 
125 int
126 xmlattr_to_double(topo_mod_t *mp,
127     xmlNodePtr n, const char *propname, double *value)
128 {
129 	xmlChar *str;
130 	xmlChar *estr;
131 
132 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
133 	    "xmlattr_to_double(propname=%s)\n", propname);
134 	if ((str = xmlGetProp(n, (xmlChar *)propname)) == NULL)
135 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
136 
137 	errno = 0;
138 	*value = strtold((char *)str, (char **)&estr);
139 	if (errno != 0 || *estr != '\0') {
140 		/* full or partial conversion failure */
141 		xmlFree(str);
142 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADNUM));
143 	}
144 	xmlFree(str);
145 	return (0);
146 }
147 
148 static int
149 xmlattr_to_fmri(topo_mod_t *mp,
150     xmlNodePtr xn, const char *propname, nvlist_t **rnvl)
151 {
152 	xmlChar *str;
153 
154 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlattr_to_fmri(propname=%s)\n",
155 	    propname);
156 	if ((str = xmlGetProp(xn, (xmlChar *)propname)) == NULL)
157 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
158 	if (topo_mod_str2nvl(mp, (const char *)str, rnvl) < 0) {
159 		xmlFree(str);
160 		return (-1);
161 	}
162 	xmlFree(str);
163 	return (0);
164 }
165 
166 static topo_type_t
167 xmlattr_to_type(topo_mod_t *mp, xmlNodePtr xn, xmlChar *attr)
168 {
169 	topo_type_t rv;
170 	xmlChar *str;
171 	if ((str = xmlGetProp(xn, (xmlChar *)attr)) == NULL) {
172 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "%s attribute missing",
173 		    attr);
174 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
175 		return (TOPO_TYPE_INVALID);
176 	}
177 	if (xmlStrcmp(str, (xmlChar *)Int32) == 0) {
178 		rv = TOPO_TYPE_INT32;
179 	} else if (xmlStrcmp(str, (xmlChar *)UInt32) == 0) {
180 		rv = TOPO_TYPE_UINT32;
181 	} else if (xmlStrcmp(str, (xmlChar *)Int64) == 0) {
182 		rv = TOPO_TYPE_INT64;
183 	} else if (xmlStrcmp(str, (xmlChar *)UInt64) == 0) {
184 		rv = TOPO_TYPE_UINT64;
185 	} else if (xmlStrcmp(str, (xmlChar *)FMRI) == 0) {
186 		rv = TOPO_TYPE_FMRI;
187 	} else if (xmlStrcmp(str, (xmlChar *)String) == 0) {
188 		rv = TOPO_TYPE_STRING;
189 	} else if (xmlStrcmp(str, (xmlChar *)Double) == 0) {
190 		rv = TOPO_TYPE_DOUBLE;
191 	} else if (xmlStrcmp(str, (xmlChar *)Int32_Arr) == 0) {
192 		rv = TOPO_TYPE_INT32_ARRAY;
193 	} else if (xmlStrcmp(str, (xmlChar *)UInt32_Arr) == 0) {
194 		rv = TOPO_TYPE_UINT32_ARRAY;
195 	} else if (xmlStrcmp(str, (xmlChar *)Int64_Arr) == 0) {
196 		rv = TOPO_TYPE_INT64_ARRAY;
197 	} else if (xmlStrcmp(str, (xmlChar *)UInt64_Arr) == 0) {
198 		rv = TOPO_TYPE_UINT64_ARRAY;
199 	} else if (xmlStrcmp(str, (xmlChar *)String_Arr) == 0) {
200 		rv = TOPO_TYPE_STRING_ARRAY;
201 	} else if (xmlStrcmp(str, (xmlChar *)FMRI_Arr) == 0) {
202 		rv = TOPO_TYPE_FMRI_ARRAY;
203 	} else {
204 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
205 		    "Unrecognized type attribute value '%s'.\n", str);
206 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
207 		xmlFree(str);
208 		return (TOPO_TYPE_INVALID);
209 	}
210 	xmlFree(str);
211 	return (rv);
212 }
213 
214 static int
215 xlate_common(topo_mod_t *mp, xmlNodePtr xn, topo_type_t ptype, nvlist_t *nvl,
216     const char *name)
217 {
218 	int rv;
219 	uint64_t ui;
220 	double dbl;
221 	uint_t i = 0, nelems = 0;
222 	nvlist_t *fmri;
223 	xmlChar *str;
224 	char **strarrbuf;
225 	void *arrbuf;
226 	nvlist_t **nvlarrbuf;
227 	xmlNodePtr cn;
228 
229 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xlate_common(name=%s)\n", name);
230 	switch (ptype) {
231 	case TOPO_TYPE_INT32:
232 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
233 			return (-1);
234 		rv = nvlist_add_int32(nvl, name, (int32_t)ui);
235 		break;
236 	case TOPO_TYPE_UINT32:
237 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
238 			return (-1);
239 		rv = nvlist_add_uint32(nvl, name, (uint32_t)ui);
240 		break;
241 	case TOPO_TYPE_INT64:
242 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
243 			return (-1);
244 		rv = nvlist_add_int64(nvl, name, (int64_t)ui);
245 		break;
246 	case TOPO_TYPE_UINT64:
247 		if (xmlattr_to_int(mp, xn, Value, &ui) < 0)
248 			return (-1);
249 		rv = nvlist_add_uint64(nvl, name, ui);
250 		break;
251 	case TOPO_TYPE_DOUBLE:
252 		if (xmlattr_to_double(mp, xn, Value, &dbl) < 0)
253 			return (-1);
254 		rv = nvlist_add_double(nvl, name, dbl);
255 		break;
256 	case TOPO_TYPE_FMRI:
257 		if (xmlattr_to_fmri(mp, xn, Value, &fmri) < 0)
258 			return (-1);
259 		rv = nvlist_add_nvlist(nvl, name, fmri);
260 		nvlist_free(fmri);
261 		break;
262 	case TOPO_TYPE_STRING:
263 		if ((str = xmlGetProp(xn, (xmlChar *)Value)) == NULL)
264 			return (-1);
265 		rv = nvlist_add_string(nvl, name, (char *)str);
266 		xmlFree(str);
267 		break;
268 	case TOPO_TYPE_INT32_ARRAY:
269 	case TOPO_TYPE_UINT32_ARRAY:
270 	case TOPO_TYPE_INT64_ARRAY:
271 	case TOPO_TYPE_UINT64_ARRAY:
272 	case TOPO_TYPE_STRING_ARRAY:
273 	case TOPO_TYPE_FMRI_ARRAY:
274 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next)
275 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
276 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0))
277 				nelems++;
278 
279 		if (nelems < 1) {
280 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "No <propitem> "
281 			    "or <argitem> elements found for array val");
282 			return (-1);
283 		}
284 		break;
285 	default:
286 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
287 		    "Unrecognized type attribute (ptype = %d)\n", ptype);
288 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE));
289 	}
290 
291 	switch (ptype) {
292 	case TOPO_TYPE_INT32_ARRAY:
293 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int32_t))))
294 		    == NULL)
295 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
296 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
297 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
298 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
299 
300 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
301 					return (-1);
302 				((int32_t *)arrbuf)[i++] = (int32_t)ui;
303 			}
304 		}
305 
306 		rv = nvlist_add_int32_array(nvl, name, (int32_t *)arrbuf,
307 		    nelems);
308 		topo_mod_free(mp, arrbuf, (nelems * sizeof (int32_t)));
309 		break;
310 	case TOPO_TYPE_UINT32_ARRAY:
311 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint32_t))))
312 		    == NULL)
313 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
314 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
315 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
316 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
317 
318 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
319 					return (-1);
320 				((uint32_t *)arrbuf)[i++] = (uint32_t)ui;
321 			}
322 		}
323 
324 		rv = nvlist_add_uint32_array(nvl, name, (uint32_t *)arrbuf,
325 		    nelems);
326 		topo_mod_free(mp, arrbuf, (nelems * sizeof (uint32_t)));
327 		break;
328 	case TOPO_TYPE_INT64_ARRAY:
329 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (int64_t))))
330 		    == NULL)
331 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
332 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
333 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
334 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
335 
336 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
337 					return (-1);
338 				((int64_t *)arrbuf)[i++] = (int64_t)ui;
339 			}
340 		}
341 
342 		rv = nvlist_add_int64_array(nvl, name, (int64_t *)arrbuf,
343 		    nelems);
344 		topo_mod_free(mp, arrbuf, (nelems * sizeof (int64_t)));
345 		break;
346 	case TOPO_TYPE_UINT64_ARRAY:
347 		if ((arrbuf = topo_mod_alloc(mp, (nelems * sizeof (uint64_t))))
348 		    == NULL)
349 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
350 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
351 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
352 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
353 
354 				if (xmlattr_to_int(mp, cn, Value, &ui) < 0)
355 					return (-1);
356 				((uint64_t *)arrbuf)[i++] = ui;
357 			}
358 		}
359 
360 		rv = nvlist_add_uint64_array(nvl, name, arrbuf,
361 		    nelems);
362 		topo_mod_free(mp, arrbuf, (nelems * sizeof (uint64_t)));
363 		break;
364 	case TOPO_TYPE_STRING_ARRAY:
365 		if ((strarrbuf = topo_mod_alloc(mp, (nelems * sizeof (char *))))
366 		    == NULL)
367 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
368 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
369 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
370 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
371 
372 				if ((str = xmlGetProp(cn, (xmlChar *)Value))
373 				    == NULL)
374 					return (-1);
375 
376 				strarrbuf[i++] =
377 				    topo_mod_strdup(mp, (const char *)str);
378 				xmlFree(str);
379 			}
380 		}
381 
382 		rv = nvlist_add_string_array(nvl, name, strarrbuf, nelems);
383 		topo_mod_strfreev(mp, strarrbuf, nelems);
384 		break;
385 	case TOPO_TYPE_FMRI_ARRAY:
386 		if ((nvlarrbuf = topo_mod_alloc(mp, (nelems *
387 		    sizeof (nvlist_t *)))) == NULL)
388 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
389 		for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
390 			if ((xmlStrcmp(cn->name, (xmlChar *)Propitem) == 0) ||
391 			    (xmlStrcmp(cn->name, (xmlChar *)Argitem) == 0)) {
392 
393 				if ((str = xmlGetProp(cn, (xmlChar *)Value))
394 				    == NULL)
395 					return (-1);
396 
397 				if (topo_mod_str2nvl(mp, (const char *)str,
398 				    &(nvlarrbuf[i++])) < 0) {
399 					xmlFree(str);
400 					return (-1);
401 				}
402 				xmlFree(str);
403 			}
404 		}
405 
406 		rv = nvlist_add_nvlist_array(nvl, name, nvlarrbuf,
407 		    nelems);
408 		topo_mod_free(mp, nvlarrbuf, (nelems * sizeof (nvlist_t *)));
409 		break;
410 	}
411 
412 	if (rv != 0) {
413 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
414 		    "Nvlist construction failed.\n");
415 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
416 	} else
417 		return (0);
418 }
419 
420 static int
421 xmlprop_xlate(topo_mod_t *mp, xmlNodePtr xn, nvlist_t *nvl)
422 {
423 	topo_type_t ptype;
424 	xmlChar *str;
425 
426 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "xmlprop_xlate\n");
427 	if ((str = xmlGetProp(xn, (xmlChar *)Immutable)) != NULL) {
428 		if (xmlStrcmp(str, (xmlChar *)False) == 0)
429 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
430 			    B_FALSE);
431 		else
432 			(void) nvlist_add_boolean_value(nvl, INV_IMMUTE,
433 			    B_TRUE);
434 		xmlFree(str);
435 	} else {
436 		(void) nvlist_add_boolean_value(nvl, INV_IMMUTE, B_TRUE);
437 	}
438 
439 	if ((ptype = xmlattr_to_type(mp, xn, (xmlChar *)Type))
440 	    == TOPO_TYPE_INVALID)
441 		return (-1);
442 
443 	if (nvlist_add_int32(nvl, INV_PVALTYPE, ptype) != 0)
444 		return (-1);
445 
446 	return (xlate_common(mp, xn, ptype, nvl, INV_PVAL));
447 }
448 
449 static int
450 dependent_create(topo_mod_t *mp,
451     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr dxn, tnode_t *ptn)
452 {
453 	tf_rdata_t *rp, *pp, *np;
454 	xmlChar *grptype;
455 	int sibs = 0;
456 
457 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependent_create\n");
458 	if ((grptype = xmlGetProp(dxn, (xmlChar *)Grouping)) == NULL) {
459 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
460 		    "Dependents missing grouping attribute");
461 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
462 	}
463 
464 	pp = NULL;
465 	if (xmlStrcmp(grptype, (xmlChar *)Siblings) == 0) {
466 		rp = pad->tpad_sibs;
467 		sibs++;
468 	} else if (xmlStrcmp(grptype, (xmlChar *)Children) == 0) {
469 		rp = pad->tpad_child;
470 	} else {
471 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
472 		    "Dependents have bogus grouping attribute");
473 		xmlFree(grptype);
474 		return (topo_mod_seterrno(mp, ETOPO_PRSR_BADGRP));
475 	}
476 	xmlFree(grptype);
477 	/* Add processed dependents to the tail of the list */
478 	while (rp != NULL) {
479 		pp = rp;
480 		rp = rp->rd_next;
481 	}
482 	if ((np = topo_xml_walk(mp, xinfo, dxn, ptn)) == NULL) {
483 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
484 		    "error within dependent .xml topology: "
485 		    "%s\n", topo_strerror(topo_mod_errno(mp)));
486 		return (-1);
487 	}
488 	if (pp != NULL)
489 		pp->rd_next = np;
490 	else if (sibs == 1)
491 		pad->tpad_sibs = np;
492 	else
493 		pad->tpad_child = np;
494 	return (0);
495 }
496 
497 static int
498 dependents_create(topo_mod_t *mp,
499     tf_info_t *xinfo, tf_pad_t *pad, xmlNodePtr pxn, tnode_t *ptn)
500 {
501 	xmlNodePtr cn;
502 
503 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "dependents_create\n");
504 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
505 		if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0) {
506 			if (dependent_create(mp, xinfo, pad, cn, ptn) < 0)
507 				return (-1);
508 		}
509 	}
510 	return (0);
511 }
512 
513 static int
514 prop_create(topo_mod_t *mp,
515     nvlist_t *pfmri, tnode_t *ptn, const char *gnm, const char *pnm,
516     topo_type_t ptype, int flag)
517 {
518 	nvlist_t *fmri, **fmriarr;
519 	uint32_t ui32, *ui32arr;
520 	uint64_t ui64, *ui64arr;
521 	int32_t i32, *i32arr;
522 	int64_t i64, *i64arr;
523 	double dbl;
524 	uint_t nelem;
525 	char *str, **strarr;
526 	int err, e;
527 
528 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "prop_create(pgrp = %s, "
529 	    "prop = %s)\n", gnm, pnm);
530 	switch (ptype) {
531 	case TOPO_TYPE_INT32:
532 		e = nvlist_lookup_int32(pfmri, INV_PVAL, &i32);
533 		break;
534 	case TOPO_TYPE_UINT32:
535 		e = nvlist_lookup_uint32(pfmri, INV_PVAL, &ui32);
536 		break;
537 	case TOPO_TYPE_INT64:
538 		e = nvlist_lookup_int64(pfmri, INV_PVAL, &i64);
539 		break;
540 	case TOPO_TYPE_UINT64:
541 		e = nvlist_lookup_uint64(pfmri, INV_PVAL, &ui64);
542 		break;
543 	case TOPO_TYPE_DOUBLE:
544 		e = nvlist_lookup_double(pfmri, INV_PVAL, &dbl);
545 		break;
546 	case TOPO_TYPE_FMRI:
547 		e = nvlist_lookup_nvlist(pfmri, INV_PVAL, &fmri);
548 		break;
549 	case TOPO_TYPE_STRING:
550 		e = nvlist_lookup_string(pfmri, INV_PVAL, &str);
551 		break;
552 	case TOPO_TYPE_INT32_ARRAY:
553 		e = nvlist_lookup_int32_array(pfmri, INV_PVAL, &i32arr, &nelem);
554 		break;
555 	case TOPO_TYPE_UINT32_ARRAY:
556 		e = nvlist_lookup_uint32_array(pfmri, INV_PVAL, &ui32arr,
557 		    &nelem);
558 		break;
559 	case TOPO_TYPE_INT64_ARRAY:
560 		e = nvlist_lookup_int64_array(pfmri, INV_PVAL, &i64arr,
561 		    &nelem);
562 		break;
563 	case TOPO_TYPE_UINT64_ARRAY:
564 		e = nvlist_lookup_uint64_array(pfmri, INV_PVAL, &ui64arr,
565 		    &nelem);
566 		break;
567 	case TOPO_TYPE_STRING_ARRAY:
568 		e = nvlist_lookup_string_array(pfmri, INV_PVAL, &strarr,
569 		    &nelem);
570 		break;
571 	case TOPO_TYPE_FMRI_ARRAY:
572 		e = nvlist_lookup_nvlist_array(pfmri, INV_PVAL, &fmriarr,
573 		    &nelem);
574 		break;
575 	default:
576 		e = ETOPO_PRSR_BADTYPE;
577 	}
578 	if (e != 0) {
579 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
580 		    "prop_create: prop value lookup failed.\n");
581 		return (topo_mod_seterrno(mp, e));
582 	}
583 	switch (ptype) {
584 	case TOPO_TYPE_INT32:
585 		e = topo_prop_set_int32(ptn, gnm, pnm, flag, i32, &err);
586 		break;
587 	case TOPO_TYPE_UINT32:
588 		e = topo_prop_set_uint32(ptn, gnm, pnm, flag, ui32, &err);
589 		break;
590 	case TOPO_TYPE_INT64:
591 		e = topo_prop_set_int64(ptn, gnm, pnm, flag, i64, &err);
592 		break;
593 	case TOPO_TYPE_UINT64:
594 		e = topo_prop_set_uint64(ptn, gnm, pnm, flag, ui64, &err);
595 		break;
596 	case TOPO_TYPE_DOUBLE:
597 		e = topo_prop_set_double(ptn, gnm, pnm, flag, dbl, &err);
598 		break;
599 	case TOPO_TYPE_FMRI:
600 		e = topo_prop_set_fmri(ptn, gnm, pnm, flag, fmri, &err);
601 		break;
602 	case TOPO_TYPE_STRING:
603 		e = topo_prop_set_string(ptn, gnm, pnm, flag, str, &err);
604 		break;
605 	case TOPO_TYPE_INT32_ARRAY:
606 		e = topo_prop_set_int32_array(ptn, gnm, pnm, flag, i32arr,
607 		    nelem, &err);
608 		break;
609 	case TOPO_TYPE_UINT32_ARRAY:
610 		e = topo_prop_set_uint32_array(ptn, gnm, pnm, flag, ui32arr,
611 		    nelem, &err);
612 		break;
613 	case TOPO_TYPE_INT64_ARRAY:
614 		e = topo_prop_set_int64_array(ptn, gnm, pnm, flag, i64arr,
615 		    nelem, &err);
616 		break;
617 	case TOPO_TYPE_UINT64_ARRAY:
618 		e = topo_prop_set_uint64_array(ptn, gnm, pnm, flag, ui64arr,
619 		    nelem, &err);
620 		break;
621 	case TOPO_TYPE_STRING_ARRAY:
622 		e = topo_prop_set_string_array(ptn, gnm, pnm, flag,
623 		    (const char **)strarr, nelem, &err);
624 		break;
625 	case TOPO_TYPE_FMRI_ARRAY:
626 		e = topo_prop_set_fmri_array(ptn, gnm, pnm, flag,
627 		    (const nvlist_t **)fmriarr, nelem, &err);
628 		break;
629 	}
630 	if (e != 0 && err != ETOPO_PROP_DEFD) {
631 
632 		/*
633 		 * Some properties may have already been set
634 		 * in topo_node_bind() or topo_prop_inherit if we are
635 		 * enumerating from a static .xml file
636 		 */
637 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "prop set "
638 		    "failed %s/%s:%s\n", gnm, pnm, topo_strerror(err));
639 		return (topo_mod_seterrno(mp, err));
640 	}
641 	return (0);
642 }
643 
644 static int
645 props_create(topo_mod_t *mp,
646     tnode_t *ptn, const char *gnm, nvlist_t **props, int nprops)
647 {
648 	topo_type_t ptype;
649 	boolean_t pim;
650 	char *pnm;
651 	int32_t i32;
652 	int flag;
653 	int pn;
654 	int e;
655 
656 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "props_create(pgrp = %s)\n",
657 	    gnm);
658 	for (pn = 0; pn < nprops; pn++) {
659 		e = nvlist_lookup_string(props[pn], INV_PNAME, &pnm);
660 		if (e != 0) {
661 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
662 			    "props create lookup (%s) failure: %s",
663 			    INV_PNAME, strerror(e));
664 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
665 		}
666 		e = nvlist_lookup_boolean_value(props[pn], INV_IMMUTE, &pim);
667 		if (e != 0) {
668 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
669 			    "props create lookup (%s) failure: %s",
670 			    INV_IMMUTE, strerror(e));
671 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
672 		}
673 		flag = (pim == B_TRUE) ?
674 		    TOPO_PROP_IMMUTABLE : TOPO_PROP_MUTABLE;
675 
676 		e = nvlist_lookup_int32(props[pn], INV_PVALTYPE, &i32);
677 		if (e != 0) {
678 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
679 			    "props create lookup (%s) failure: %s",
680 			    INV_PVALTYPE, strerror(e));
681 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
682 		}
683 		ptype = (topo_type_t)i32;
684 		if (prop_create(mp, props[pn], ptn, gnm, pnm, ptype, flag) < 0)
685 			return (-1);
686 	}
687 	return (0);
688 }
689 
690 static int
691 pgroups_create(topo_mod_t *mp, tf_pad_t *pad, tnode_t *ptn)
692 {
693 	topo_pgroup_info_t pgi;
694 	nvlist_t **props;
695 	char *gnm;
696 	char *nmstab, *dstab;
697 	uint32_t rnprops, nprops;
698 	uint32_t gv;
699 	int pg;
700 	int e;
701 
702 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_create: %s=%d\n",
703 	    topo_node_name(ptn), topo_node_instance(ptn));
704 	for (pg = 0; pg < pad->tpad_pgcnt; pg++) {
705 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
706 		    INV_PGRP_NAME, &gnm);
707 		if (e != 0) {
708 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
709 			    "pad lookup (%s) failed (%s).\n",
710 			    INV_PGRP_NAME, strerror(errno));
711 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
712 		}
713 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
714 		    INV_PGRP_NMSTAB, &nmstab);
715 		if (e != 0) {
716 			if (e != ENOENT) {
717 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
718 				    "pad lookup (%s) "
719 				    "failed.\n", INV_PGRP_NMSTAB);
720 				return (topo_mod_seterrno(mp,
721 				    ETOPO_PRSR_NVPROP));
722 			} else {
723 				nmstab = TOPO_STABSTR_PRIVATE;
724 			}
725 		}
726 		e = nvlist_lookup_string(pad->tpad_pgs[pg],
727 		    INV_PGRP_DSTAB, &dstab);
728 		if (e != 0) {
729 			if (e != ENOENT) {
730 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
731 				    "pad lookup (%s) failed.\n",
732 				    INV_PGRP_DSTAB);
733 				return (topo_mod_seterrno(mp,
734 				    ETOPO_PRSR_NVPROP));
735 			} else {
736 				dstab = TOPO_STABSTR_PRIVATE;
737 			}
738 		}
739 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
740 		    INV_PGRP_VER, &gv);
741 		if (e != 0) {
742 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
743 			    "pad lookup (%s) failed.\n",
744 			    INV_PGRP_VER);
745 			return (topo_mod_seterrno(mp, ETOPO_PRSR_NVPROP));
746 		}
747 		pgi.tpi_name = gnm;
748 		pgi.tpi_namestab = topo_name2stability(nmstab);
749 		pgi.tpi_datastab = topo_name2stability(dstab);
750 		pgi.tpi_version = gv;
751 		if (topo_pgroup_create(ptn, &pgi, &e) != 0) {
752 			if (e != ETOPO_PROP_DEFD) {
753 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
754 				    "pgroups create failure: %s\n",
755 				    topo_strerror(e));
756 				return (-1);
757 			}
758 		}
759 		e = nvlist_lookup_uint32(pad->tpad_pgs[pg],
760 		    INV_PGRP_NPROP, &rnprops);
761 		/*
762 		 * The number of properties could be zero if the property
763 		 * group only contains propmethod declarations
764 		 */
765 		if (rnprops > 0) {
766 			e |= nvlist_lookup_nvlist_array(pad->tpad_pgs[pg],
767 			    INV_PGRP_ALLPROPS, &props, &nprops);
768 			if (rnprops != nprops) {
769 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
770 				    "recorded number of props %d does not "
771 				    "match number of props recorded %d.\n",
772 				    rnprops, nprops);
773 			}
774 			if (props_create(mp, ptn, gnm, props, nprops) < 0)
775 				return (-1);
776 		}
777 	}
778 	return (0);
779 }
780 
781 static nvlist_t *
782 pval_record(topo_mod_t *mp, xmlNodePtr xn)
783 {
784 	nvlist_t *pnvl = NULL;
785 	xmlChar *pname;
786 
787 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pval_record\n");
788 	if ((pname = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
789 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
790 		    "propval lacks a name\n");
791 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
792 		return (NULL);
793 	}
794 	if (topo_mod_nvalloc(mp, &pnvl, NV_UNIQUE_NAME) < 0) {
795 		xmlFree(pname);
796 		return (NULL);
797 	}
798 	if (nvlist_add_string(pnvl, INV_PNAME, (char *)pname) < 0) {
799 		xmlFree(pname);
800 		nvlist_free(pnvl);
801 		return (NULL);
802 	}
803 	xmlFree(pname);
804 	/* FMXXX stability of the property name */
805 
806 	if (xmlprop_xlate(mp, xn, pnvl) < 0) {
807 		nvlist_free(pnvl);
808 		return (NULL);
809 	}
810 	return (pnvl);
811 }
812 
813 
814 struct propmeth_data {
815 	const char *pg_name;
816 	const char *prop_name;
817 	topo_type_t prop_type;
818 	const char *meth_name;
819 	topo_version_t meth_ver;
820 	nvlist_t *arg_nvl;
821 };
822 
823 static int
824 register_method(topo_mod_t *mp, tnode_t *ptn, struct propmeth_data *meth)
825 {
826 	int err;
827 
828 	if (topo_prop_method_version_register(ptn, meth->pg_name,
829 	    meth->prop_name, meth->prop_type, meth->meth_name, meth->meth_ver,
830 	    meth->arg_nvl, &err) != 0) {
831 
832 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "failed to register "
833 		    "propmethod %s for property \"%s\" in propgrp %s on node "
834 		    "%s=%d (%s)\n",
835 		    meth->meth_name, meth->prop_name, meth->pg_name,
836 		    topo_node_name(ptn), topo_node_instance(ptn),
837 		    topo_strerror(err));
838 		return (-1);
839 	}
840 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
841 	    "registered method %s on %s=%d\n",
842 	    meth->meth_name, topo_node_name(ptn), topo_node_instance(ptn));
843 
844 	return (0);
845 }
846 
847 static int
848 pmeth_record(topo_mod_t *mp, const char *pg_name, xmlNodePtr xn, tnode_t *tn,
849     const char *rname, const char *ppgrp_name)
850 {
851 	nvlist_t *arg_nvl = NULL;
852 	xmlNodePtr cn;
853 	xmlChar *meth_name = NULL, *prop_name = NULL;
854 	xmlChar *arg_name = NULL;
855 	uint64_t meth_ver, is_mutable = 0, is_nonvolatile = 0;
856 	topo_type_t prop_type;
857 	struct propmeth_data meth;
858 	int ret = 0, err;
859 	topo_type_t ptype;
860 	tnode_t *tmp;
861 
862 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pmeth_record: %s=%d "
863 	    "(pgrp=%s)\n", topo_node_name(tn), topo_node_instance(tn), pg_name);
864 
865 	/*
866 	 * Get propmethod attribute values
867 	 */
868 	if ((meth_name = xmlGetProp(xn, (xmlChar *)Name)) == NULL) {
869 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
870 		    "propmethod element lacks a name attribute\n");
871 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
872 	}
873 	if (xmlattr_to_int(mp, xn, Version, &meth_ver) < 0) {
874 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
875 		    "propmethod element lacks version attribute\n");
876 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
877 		goto pmr_done;
878 	}
879 	/*
880 	 * The "mutable" and "nonvoltile" attributes are optional.  If not
881 	 * specified we default to false (0)
882 	 */
883 	(void) xmlattr_to_int(mp, xn, Mutable, &is_mutable);
884 	(void) xmlattr_to_int(mp, xn, Nonvolatile, &is_nonvolatile);
885 
886 	if ((prop_name = xmlGetProp(xn, (xmlChar *)Propname)) == NULL) {
887 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
888 		    "propmethod element lacks propname attribute\n");
889 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
890 		goto pmr_done;
891 	}
892 	if ((prop_type = xmlattr_to_type(mp, xn, (xmlChar *)Proptype))
893 	    == TOPO_TYPE_INVALID) {
894 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
895 		    "error decoding proptype attribute\n");
896 		ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
897 		goto pmr_done;
898 	}
899 
900 	/*
901 	 * Allocate method argument nvlist
902 	 */
903 	if (topo_mod_nvalloc(mp, &arg_nvl, NV_UNIQUE_NAME) < 0) {
904 		ret = topo_mod_seterrno(mp, ETOPO_NOMEM);
905 		goto pmr_done;
906 	}
907 
908 	/*
909 	 * Iterate through the argval nodes and build the argval nvlist
910 	 */
911 	for (cn = xn->xmlChildrenNode; cn != NULL; cn = cn->next) {
912 		if (xmlStrcmp(cn->name, (xmlChar *)Argval) == 0) {
913 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
914 			    "found argval element\n");
915 			if ((arg_name = xmlGetProp(cn, (xmlChar *)Name))
916 			    == NULL) {
917 				topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
918 				    "argval element lacks a name attribute\n");
919 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
920 				goto pmr_done;
921 			}
922 			if ((ptype = xmlattr_to_type(mp, cn, (xmlChar *)Type))
923 			    == TOPO_TYPE_INVALID) {
924 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
925 				xmlFree(arg_name);
926 				break;
927 			}
928 			if (xlate_common(mp, cn, ptype, arg_nvl,
929 			    (const char *)arg_name) != 0) {
930 				ret = topo_mod_seterrno(mp, ETOPO_PRSR_BADTYPE);
931 				xmlFree(arg_name);
932 				break;
933 			}
934 		}
935 		if (arg_name) {
936 			xmlFree(arg_name);
937 			arg_name = NULL;
938 		}
939 	}
940 
941 	if (ret != 0)
942 		goto pmr_done;
943 
944 	/*
945 	 * Register the prop method for all of the nodes in our range
946 	 */
947 	meth.pg_name = (const char *)pg_name;
948 	meth.prop_name = (const char *)prop_name;
949 	meth.prop_type = prop_type;
950 	meth.meth_name = (const char *)meth_name;
951 	meth.meth_ver = meth_ver;
952 	meth.arg_nvl = arg_nvl;
953 
954 	/*
955 	 * If the propgroup element is under a range element, we'll apply
956 	 * the method to all of the topo nodes at this level with the same
957 	 * range name.
958 	 *
959 	 * Otherwise, if the propgroup element is under a node element
960 	 * then we'll simply register the method for this node.
961 	 */
962 	if (strcmp(ppgrp_name, Range) == 0) {
963 		for (tmp = tn; tmp != NULL; tmp = topo_child_next(NULL, tmp)) {
964 			if (strcmp(rname, topo_node_name(tmp)) == 0) {
965 				if (register_method(mp, tmp, &meth) != 0) {
966 					ret = topo_mod_seterrno(mp,
967 					    ETOPO_PRSR_REGMETH);
968 					goto pmr_done;
969 				}
970 				if (is_mutable) {
971 					if (topo_prop_setmutable(tmp,
972 					    meth.pg_name, meth.prop_name, &err)
973 					    != 0) {
974 						ret = topo_mod_seterrno(mp,
975 						    ETOPO_PRSR_REGMETH);
976 						goto pmr_done;
977 					}
978 				}
979 				if (is_nonvolatile) {
980 					if (topo_prop_setnonvolatile(tmp,
981 					    meth.pg_name, meth.prop_name, &err)
982 					    != 0) {
983 						ret = topo_mod_seterrno(mp,
984 						    ETOPO_PRSR_REGMETH);
985 						goto pmr_done;
986 					}
987 				}
988 			}
989 		}
990 	} else {
991 		if (register_method(mp, tn, &meth) != 0) {
992 			ret = topo_mod_seterrno(mp, ETOPO_PRSR_REGMETH);
993 			goto pmr_done;
994 		}
995 		if (is_mutable) {
996 			if (topo_prop_setmutable(tn, meth.pg_name,
997 			    meth.prop_name, &err) != 0) {
998 				ret = topo_mod_seterrno(mp,
999 				    ETOPO_PRSR_REGMETH);
1000 				goto pmr_done;
1001 			}
1002 		}
1003 		if (is_nonvolatile) {
1004 			if (topo_prop_setnonvolatile(tn, meth.pg_name,
1005 			    meth.prop_name, &err) != 0) {
1006 				ret = topo_mod_seterrno(mp,
1007 				    ETOPO_PRSR_REGMETH);
1008 				goto pmr_done;
1009 			}
1010 		}
1011 	}
1012 
1013 pmr_done:
1014 	if (meth_name)
1015 		xmlFree(meth_name);
1016 	if (prop_name)
1017 		xmlFree(prop_name);
1018 	nvlist_free(arg_nvl);
1019 	return (ret);
1020 }
1021 
1022 
1023 static int
1024 pgroup_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
1025     tf_pad_t *rpad, int pi, const char *ppgrp_name)
1026 {
1027 	topo_stability_t nmstab, dstab;
1028 	uint64_t ver;
1029 	xmlNodePtr cn;
1030 	xmlChar *name;
1031 	nvlist_t **apl = NULL;
1032 	nvlist_t *pgnvl = NULL;
1033 	int pcnt = 0;
1034 	int ai = 0;
1035 	int e;
1036 
1037 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup_record\n");
1038 	if ((name = xmlGetProp(pxn, (xmlChar *)Name)) == NULL) {
1039 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1040 		    "propgroup lacks a name\n");
1041 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1042 	}
1043 	if (xmlattr_to_int(mp, pxn, Version, &ver) < 0) {
1044 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1045 		    "propgroup lacks a version\n");
1046 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1047 	}
1048 	if (xmlattr_to_stab(mp, pxn, Namestab, &nmstab) < 0) {
1049 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1050 		    "propgroup lacks name-stability\n");
1051 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1052 	}
1053 	if (xmlattr_to_stab(mp, pxn, Datastab, &dstab) < 0) {
1054 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1055 		    "propgroup lacks data-stability\n");
1056 		return (topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR));
1057 	}
1058 
1059 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroup %s\n", (char *)name);
1060 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1061 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0)
1062 			pcnt++;
1063 	}
1064 
1065 	if (topo_mod_nvalloc(mp, &pgnvl, NV_UNIQUE_NAME) < 0) {
1066 		xmlFree(name);
1067 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1068 		    "failed to allocate propgroup nvlist\n");
1069 		return (topo_mod_seterrno(mp, ETOPO_NOMEM));
1070 	}
1071 
1072 	e = nvlist_add_string(pgnvl, INV_PGRP_NAME, (char *)name);
1073 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NMSTAB, nmstab);
1074 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_DSTAB, dstab);
1075 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_VER, ver);
1076 	e |= nvlist_add_uint32(pgnvl, INV_PGRP_NPROP, pcnt);
1077 	if (pcnt > 0)
1078 		if (e != 0 ||
1079 		    (apl = topo_mod_zalloc(mp, pcnt * sizeof (nvlist_t *)))
1080 		    == NULL) {
1081 			xmlFree(name);
1082 			nvlist_free(pgnvl);
1083 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1084 			    "failed to allocate nvlist array for properties"
1085 			    "(e=%d)\n", e);
1086 			return (topo_mod_seterrno(mp, ETOPO_NOMEM));
1087 		}
1088 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1089 		if (xmlStrcmp(cn->name, (xmlChar *)Propval) == 0) {
1090 			if (ai < pcnt) {
1091 				if ((apl[ai] = pval_record(mp, cn)) == NULL)
1092 					break;
1093 			}
1094 			ai++;
1095 		} else if (xmlStrcmp(cn->name, (xmlChar *)Prop_meth) == 0) {
1096 			if (pmeth_record(mp, (const char *)name, cn, tn, rname,
1097 			    ppgrp_name) < 0)
1098 				break;
1099 		}
1100 	}
1101 	xmlFree(name);
1102 	if (pcnt > 0) {
1103 		e |= (ai != pcnt);
1104 		e |= nvlist_add_nvlist_array(pgnvl, INV_PGRP_ALLPROPS, apl,
1105 		    pcnt);
1106 		for (ai = 0; ai < pcnt; ai++)
1107 			nvlist_free(apl[ai]);
1108 		topo_mod_free(mp, apl, pcnt * sizeof (nvlist_t *));
1109 		if (e != 0) {
1110 			nvlist_free(pgnvl);
1111 			return (-1);
1112 		}
1113 	}
1114 	rpad->tpad_pgs[pi] = pgnvl;
1115 	return (0);
1116 }
1117 
1118 static int
1119 pgroups_record(topo_mod_t *mp, xmlNodePtr pxn, tnode_t *tn, const char *rname,
1120     tf_pad_t *rpad, const char *ppgrp)
1121 {
1122 	xmlNodePtr cn;
1123 	int pi = 0;
1124 
1125 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "pgroups_record: pxn->name=%s\n",
1126 	    pxn->name);
1127 	for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1128 		if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0) {
1129 			if (pgroup_record(mp, cn, tn, rname, rpad, pi++, ppgrp)
1130 			    < 0)
1131 				return (-1);
1132 		}
1133 	}
1134 	return (0);
1135 }
1136 
1137 /*
1138  * psn:	pointer to a "set" XML node
1139  * key: string to search the set for
1140  *
1141  * returns: 1, if the set contains key
1142  *          0, otherwise
1143  */
1144 static int
1145 set_contains(topo_mod_t *mp, char *key, char *set)
1146 {
1147 	char *prod;
1148 	int rv = 0;
1149 
1150 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "set_contains(key = %s, "
1151 	    "setlist = %s)\n", key, set);
1152 
1153 	prod = strtok((char *)set, "|");
1154 	if (prod && (strcmp(key, prod) == 0))
1155 		return (1);
1156 
1157 	while ((prod = strtok(NULL, "|")))
1158 		if (strcmp(key, prod) == 0)
1159 			return (1);
1160 
1161 	return (rv);
1162 }
1163 
1164 
1165 /*
1166  * Process the property group and dependents xmlNode children of
1167  * parent xmlNode pxn.
1168  */
1169 static int
1170 pad_process(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1171     tf_pad_t **rpad)
1172 {
1173 	xmlNodePtr cn, gcn, psn, ecn, target;
1174 	xmlNodePtr def_set = NULL;
1175 	tnode_t *ct;
1176 	tf_pad_t *new = *rpad;
1177 	tf_rdata_t tmp_rd;
1178 	int pgcnt = 0;
1179 	int dcnt = 0;
1180 	int ecnt = 0;
1181 	int joined_set = 0, inst;
1182 	xmlChar *set;
1183 	char *key;
1184 
1185 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1186 	    "pad_process beneath %s=%d\n", topo_node_name(ptn),
1187 	    topo_node_instance(ptn));
1188 	if (new == NULL) {
1189 		for (cn = pxn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1190 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1191 			    "cn->name is %s \n", (char *)cn->name);
1192 			/*
1193 			 * We're iterating through the XML children looking for
1194 			 * four types of elements:
1195 			 *   1) dependents elements
1196 			 *   2) unconstrained pgroup elements
1197 			 *   3) pgroup elements constrained by set elements
1198 			 *   4) enum-method elements for the case that we want
1199 			 *	to post-process a statically defined node
1200 			 */
1201 			if (xmlStrcmp(cn->name, (xmlChar *)Dependents) == 0)
1202 				dcnt++;
1203 			else if (xmlStrcmp(cn->name, (xmlChar *)Propgrp) == 0)
1204 				pgcnt++;
1205 			else if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth)
1206 			    == 0) {
1207 				ecn = cn;
1208 				ecnt++;
1209 			} else if (xmlStrcmp(cn->name, (xmlChar *)Set) == 0) {
1210 				if (joined_set)
1211 					continue;
1212 				set = xmlGetProp(cn, (xmlChar *)Setlist);
1213 
1214 				if (mp->tm_hdl->th_product)
1215 					key = mp->tm_hdl->th_product;
1216 				else
1217 					key = mp->tm_hdl->th_platform;
1218 
1219 				/*
1220 				 * If it's the default set then we'll store
1221 				 * a pointer to it so that if none of the other
1222 				 * sets apply to our product we can fall
1223 				 * back to this one.
1224 				 */
1225 				if (strcmp((char *)set, "default") == 0)
1226 					def_set = cn;
1227 				else if (set_contains(mp, key, (char *)set)) {
1228 					psn = cn;
1229 					joined_set = 1;
1230 					for (gcn = cn->xmlChildrenNode;
1231 					    gcn != NULL; gcn = gcn->next) {
1232 						if (xmlStrcmp(gcn->name,
1233 						    (xmlChar *)Propgrp) == 0)
1234 							pgcnt++;
1235 					}
1236 				}
1237 				xmlFree(set);
1238 			}
1239 		}
1240 		/*
1241 		 * If we haven't found a set that contains our product AND
1242 		 * a default set exists, then we'll process it.
1243 		 */
1244 		if (!joined_set && def_set) {
1245 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1246 			    "Falling back to default set\n");
1247 			joined_set = 1;
1248 			psn = def_set;
1249 			for (gcn = psn->xmlChildrenNode; gcn != NULL;
1250 			    gcn = gcn->next) {
1251 				if (xmlStrcmp(gcn->name, (xmlChar *)Propgrp)
1252 				    == 0)
1253 					pgcnt++;
1254 			}
1255 		}
1256 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1257 		    "pad_process: dcnt=%d, pgcnt=%d, ecnt=%d, joined_set=%d\n",
1258 		    dcnt, pgcnt, ecnt, joined_set);
1259 		/*
1260 		 * If an enum-method element was found, AND we're a child of a
1261 		 * node element, then we invoke the enumerator so that it can do
1262 		 * post-processing of the node.
1263 		 */
1264 		if (ecnt && (strcmp((const char *)pxn->name, Node) == 0)) {
1265 			if ((tmp_rd.rd_einfo = enum_attributes_process(mp, ecn))
1266 			    == NULL)
1267 				return (-1);
1268 			tmp_rd.rd_mod = mp;
1269 			tmp_rd.rd_name = rd->rd_name;
1270 			tmp_rd.rd_min = rd->rd_min;
1271 			tmp_rd.rd_max = rd->rd_max;
1272 			tmp_rd.rd_pn = ptn;
1273 			if (enum_run(mp, &tmp_rd) < 0) {
1274 				/*
1275 				 * Note the failure but continue on
1276 				 */
1277 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1278 				    "pad_process: enumeration failed.\n");
1279 			}
1280 			tf_edata_free(mp, tmp_rd.rd_einfo);
1281 		}
1282 		/*
1283 		 * Here we allocate an element in an intermediate data structure
1284 		 * which keeps track property groups and dependents of the range
1285 		 * currently being processed.
1286 		 *
1287 		 * This structure is referenced in pgroups_record() to create
1288 		 * the actual property groups in the topo tree
1289 		 */
1290 		if ((new = tf_pad_new(mp, pgcnt, dcnt)) == NULL)
1291 			return (-1);
1292 
1293 		if (pgcnt > 0) {
1294 			new->tpad_pgs =
1295 			    topo_mod_zalloc(mp, pgcnt * sizeof (nvlist_t *));
1296 			if (new->tpad_pgs == NULL) {
1297 				tf_pad_free(mp, new);
1298 				return (-1);
1299 			}
1300 		}
1301 		/*
1302 		 * If the property groups are contained within a set
1303 		 * then they will be one level lower in the XML tree.
1304 		 */
1305 		if (joined_set)
1306 			target = psn;
1307 		else
1308 			target = pxn;
1309 
1310 		/*
1311 		 * If there is no "node" element under the "range"
1312 		 * element, then we need to attach the facility node to
1313 		 * each node in this range.
1314 		 *
1315 		 * Otherwise we only attach it to the current node
1316 		 */
1317 		if (xmlStrcmp(target->name, (xmlChar *)Range) == 0 ||
1318 		    xmlStrcmp(target->name, (xmlChar *)Set) == 0) {
1319 			for (ct = topo_child_first(rd->rd_pn);
1320 			    ct != NULL;
1321 			    ct = topo_child_next(rd->rd_pn, ct)) {
1322 
1323 				if (strcmp(topo_node_name(ct),
1324 				    rd->rd_name) != 0)
1325 					continue;
1326 
1327 				inst = topo_node_instance(ct);
1328 				if (inst < rd->rd_min || inst > rd->rd_max)
1329 					continue;
1330 
1331 				if (fac_enum_process(mp, target, ct) < 0)
1332 					return (-1);
1333 
1334 				if (fac_process(mp, target, rd, ct) < 0)
1335 					return (-1);
1336 			}
1337 		} else {
1338 			if (fac_enum_process(mp, target, ptn) < 0)
1339 				return (-1);
1340 			if (fac_process(mp, target, rd, ptn) < 0)
1341 				return (-1);
1342 		}
1343 		if (pgcnt > 0 && pgroups_record(mp, target, ptn, rd->rd_name,
1344 		    new, (const char *)pxn->name) < 0) {
1345 			tf_pad_free(mp, new);
1346 			return (-1);
1347 		}
1348 		*rpad = new;
1349 	}
1350 
1351 	/*
1352 	 * We need to process the property groups before enumerating any
1353 	 * dependents as that enuemration can itself have dependencies on
1354 	 * properties set on the parent node.
1355 	 */
1356 	if (new->tpad_pgcnt > 0)
1357 		if (pgroups_create(mp, new, ptn) < 0)
1358 			return (-1);
1359 
1360 	if (new->tpad_dcnt > 0)
1361 		if (dependents_create(mp, rd->rd_finfo, new, pxn, ptn) < 0)
1362 			return (-1);
1363 
1364 	return (0);
1365 }
1366 
1367 
1368 static int
1369 fac_enum_process(topo_mod_t *mp, xmlNodePtr pn, tnode_t *ptn)
1370 {
1371 	xmlNodePtr cn;
1372 	xmlChar *fprov = NULL;
1373 	int rv = 0;
1374 
1375 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1376 	    "fac_enum_process() called for %s=%d\n", topo_node_name(ptn),
1377 	    topo_node_instance(ptn));
1378 
1379 	for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1380 
1381 		if (xmlStrcmp(cn->name, (xmlChar *)"fac-enum") != 0)
1382 			continue;
1383 
1384 		if ((fprov = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1385 			goto fenumdone;
1386 		/*
1387 		 * Invoke enum entry point in facility provider which will
1388 		 * cause the facility enumeration node method to be
1389 		 * registered.
1390 		 */
1391 		if (fac_enum_run(mp, ptn, (const char *)fprov) != 0) {
1392 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1393 			    "fac_enum_process: enum entry point failed!\n");
1394 			goto fenumdone;
1395 		}
1396 		xmlFree(fprov);
1397 	}
1398 	return (0);
1399 fenumdone:
1400 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac-enum processing failed\n");
1401 
1402 	if (fprov != NULL)
1403 		xmlFree(fprov);
1404 
1405 	return (rv);
1406 }
1407 
1408 
1409 static int
1410 fac_process(topo_mod_t *mp, xmlNodePtr pn, tf_rdata_t *rd, tnode_t *ptn)
1411 {
1412 	xmlNodePtr cn;
1413 	xmlChar *fname = NULL, *ftype = NULL, *provider = NULL;
1414 	tnode_t *ntn = NULL;
1415 	tf_idata_t *newi;
1416 	int err;
1417 	topo_pgroup_info_t pgi;
1418 
1419 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1420 	    "fac_process() called for %s=%d\n", topo_node_name(ptn),
1421 	    topo_node_instance(ptn));
1422 
1423 	for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1424 
1425 		if (xmlStrcmp(cn->name, (xmlChar *)Facility) != 0)
1426 			continue;
1427 
1428 		if ((fname = xmlGetProp(cn, (xmlChar *)Name)) == NULL)
1429 			goto facdone;
1430 
1431 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1432 		    "processing facility node '%s'\n", fname);
1433 
1434 		if ((ftype = xmlGetProp(cn, (xmlChar *)Type)) == NULL)
1435 			goto facdone;
1436 
1437 		if ((provider = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1438 			goto facdone;
1439 
1440 		if (xmlStrcmp(ftype, (xmlChar *)Sensor) != 0 &&
1441 		    xmlStrcmp(ftype, (xmlChar *)Indicator) != 0)
1442 			goto facdone;
1443 
1444 		if ((ntn = topo_node_facbind(mp, ptn, (char *)fname,
1445 		    (char *)ftype)) == NULL)
1446 			goto facdone;
1447 
1448 		pgi.tpi_name = TOPO_PGROUP_FACILITY;
1449 		pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1450 		pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1451 		pgi.tpi_version = 1;
1452 		if (topo_pgroup_create(ntn, &pgi, &err) != 0) {
1453 			if (err != ETOPO_PROP_DEFD) {
1454 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1455 				    "pgroups create failure: %s\n",
1456 				    topo_strerror(err));
1457 				return (-1);
1458 			}
1459 		}
1460 		/*
1461 		 * Invoke enum entry point in the facility provider module,
1462 		 * which will cause the provider methods to be registered on
1463 		 * this node
1464 		 */
1465 		if (fac_enum_run(mp, ntn, (const char *)provider) != 0) {
1466 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_process: "
1467 			    "enum entry point failed for provider %s!\n",
1468 			    provider);
1469 			goto facdone;
1470 		}
1471 
1472 		if ((newi = tf_idata_new(mp, 0, ntn)) == NULL)
1473 			goto facdone;
1474 
1475 		if (tf_idata_insert(&rd->rd_instances, newi) < 0)
1476 			goto facdone;
1477 
1478 		if (pad_process(mp, rd, cn, ntn, &newi->ti_pad) < 0)
1479 			goto facdone;
1480 
1481 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with "
1482 		    "facility %s=%s.\n", ftype, fname);
1483 
1484 		xmlFree(ftype);
1485 		xmlFree(fname);
1486 		xmlFree(provider);
1487 	}
1488 
1489 	return (0);
1490 
1491 facdone:
1492 	topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "facility processing failed\n");
1493 
1494 	if (ftype != NULL)
1495 		xmlFree(ftype);
1496 	if (fname != NULL)
1497 		xmlFree(fname);
1498 	if (provider != NULL)
1499 		xmlFree(provider);
1500 	if (ntn != NULL)
1501 		topo_node_unbind(ntn);
1502 
1503 	return (0);
1504 }
1505 
1506 static int
1507 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd)
1508 {
1509 	xmlChar *str;
1510 	topo_instance_t inst;
1511 	tf_idata_t *newi;
1512 	tnode_t *ntn;
1513 	uint64_t ui;
1514 	int rv = -1;
1515 	int s = 0;
1516 
1517 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1518 	    "node_process %s\n", rd->rd_name);
1519 
1520 	if (xmlattr_to_int(mp, nn, Instance, &ui) < 0)
1521 		goto nodedone;
1522 	inst = (topo_instance_t)ui;
1523 
1524 	if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) {
1525 		if (xmlStrcmp(str, (xmlChar *)True) == 0)
1526 			s = 1;
1527 		xmlFree(str);
1528 	}
1529 
1530 	if (s == 0) {
1531 		if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn,
1532 		    rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst,
1533 		    s == 1 ? &s : NULL) < 0)
1534 			goto nodedone;
1535 	}
1536 	ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
1537 
1538 	if (ntn == NULL) {
1539 
1540 		/*
1541 		 * If this is a static node declaration, we can
1542 		 * ignore the lookup failure and continue
1543 		 * processing.  Otherwise, something
1544 		 * went wrong during enumeration
1545 		 */
1546 		if (s == 1)
1547 			rv = 0;
1548 		goto nodedone;
1549 	}
1550 	if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
1551 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1552 		    "node_process: tf_idata_new failed.\n");
1553 		goto nodedone;
1554 	}
1555 	if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
1556 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1557 		    "node_process: tf_idata_insert failed.\n");
1558 		goto nodedone;
1559 	}
1560 	if (pad_process(mp, rd, nn, ntn, &newi->ti_pad) < 0)
1561 		goto nodedone;
1562 	if (fac_process(mp, nn, rd, ntn) < 0)
1563 		goto nodedone;
1564 	rv = 0;
1565 nodedone:
1566 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
1567 	    rd->rd_name);
1568 	return (rv);
1569 }
1570 
1571 static tf_edata_t *
1572 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
1573 {
1574 	tf_edata_t *einfo;
1575 	uint64_t ui;
1576 
1577 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_attributes_process\n");
1578 	if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
1579 		(void) topo_mod_seterrno(mp, ETOPO_NOMEM);
1580 		return (NULL);
1581 	}
1582 	einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
1583 	if (einfo->te_name == NULL) {
1584 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1585 		    "Enumerator name attribute missing.\n");
1586 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
1587 		goto enodedone;
1588 	}
1589 
1590 	/*
1591 	 * Check for recursive enumeration
1592 	 */
1593 	if (strcmp(einfo->te_name, mp->tm_name) == 0) {
1594 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1595 		    "Recursive enumeration detected for %s\n",
1596 		    einfo->te_name);
1597 		(void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS);
1598 		goto enodedone;
1599 	}
1600 	if (xmlattr_to_int(mp, en, Version, &ui) < 0)
1601 		goto enodedone;
1602 	einfo->te_vers = (int)ui;
1603 
1604 	return (einfo);
1605 
1606 enodedone:
1607 	if (einfo->te_name != NULL)
1608 		xmlFree(einfo->te_name);
1609 	topo_mod_free(mp, einfo, sizeof (tf_edata_t));
1610 	return (NULL);
1611 }
1612 
1613 static int
1614 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
1615 {
1616 	topo_hdl_t *thp = mp->tm_hdl;
1617 	int e = -1;
1618 
1619 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_run\n");
1620 	/*
1621 	 * Check if the enumerator module is already loaded.
1622 	 * Module loading is single-threaded at this point so there's
1623 	 * no need to worry about the module going away or bumping the
1624 	 * ref count.
1625 	 */
1626 	if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
1627 	    0)) == NULL) {
1628 		if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
1629 		    rd->rd_einfo->te_vers)) == NULL) {
1630 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1631 			    "enum_run: mod_load of %s failed: %s.\n",
1632 			    rd->rd_einfo->te_name,
1633 			    topo_strerror(topo_mod_errno(mp)));
1634 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1635 			return (e);
1636 		}
1637 	}
1638 	/*
1639 	 * We're live, so let's enumerate.
1640 	 */
1641 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
1642 	    rd->rd_einfo->te_name);
1643 	e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
1644 	    rd->rd_name, rd->rd_min, rd->rd_max, NULL);
1645 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
1646 	    e);
1647 	if (e != 0) {
1648 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1649 		    "Enumeration failed (%s)\n",
1650 		    topo_strerror(topo_mod_errno(mp)));
1651 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1652 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1653 	}
1654 	return (e);
1655 }
1656 
1657 static int
1658 fac_enum_run(topo_mod_t *mp, tnode_t *node, const char *name)
1659 {
1660 	topo_hdl_t *thp = mp->tm_hdl;
1661 	topo_mod_t *fmod;
1662 	int e = -1;
1663 
1664 	topo_dprintf(thp, TOPO_DBG_XML, "fac_enum_run\n");
1665 	/*
1666 	 * Check if the enumerator module is already loaded.
1667 	 * Module loading is single-threaded at this point so there's
1668 	 * no need to worry about the module going away or bumping the
1669 	 * ref count.
1670 	 */
1671 	if ((fmod = topo_mod_lookup(thp, name, 0)) == NULL) {
1672 		if ((fmod = topo_mod_load(mp, name, TOPO_VERSION)) == NULL) {
1673 			topo_dprintf(thp, TOPO_DBG_ERR,
1674 			    "fac_enum_run: mod_load of %s failed: %s.\n",
1675 			    name, topo_strerror(topo_mod_errno(mp)));
1676 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1677 			return (e);
1678 		}
1679 	}
1680 	/*
1681 	 * We're live, so let's enumerate.
1682 	 */
1683 	topo_dprintf(thp, TOPO_DBG_XML, "fac enumerate request. (%s)\n", name);
1684 	e = topo_mod_enumerate(fmod, node, name, name, 0, 0, NULL);
1685 	topo_dprintf(thp, TOPO_DBG_XML, "back from enumeration. %d\n", e);
1686 	if (e != 0) {
1687 		topo_dprintf(thp, TOPO_DBG_ERR,
1688 		    "Facility provider enumeration failed (%s)\n",
1689 		    topo_strerror(topo_mod_errno(mp)));
1690 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1691 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1692 	}
1693 	return (e);
1694 }
1695 
1696 int
1697 decorate_nodes(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1698     tf_pad_t **rpad)
1699 {
1700 	tnode_t *ctn;
1701 
1702 	ctn = topo_child_first(ptn);
1703 	while (ctn != NULL) {
1704 		/* Only care about instances within the range */
1705 		if (strcmp(topo_node_name(ctn), rd->rd_name) != 0) {
1706 			ctn = topo_child_next(ptn, ctn);
1707 			continue;
1708 		}
1709 		if (pad_process(mp, rd, pxn, ctn, rpad) < 0)
1710 			return (-1);
1711 		if (decorate_nodes(mp, rd, pxn, ctn, rpad) < 0)
1712 			return (-1);
1713 		ctn = topo_child_next(ptn, ctn);
1714 	}
1715 	return (0);
1716 }
1717 
1718 int
1719 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
1720 {
1721 	/*
1722 	 * The range may have several children xmlNodes, that may
1723 	 * represent the enumeration method, property groups,
1724 	 * dependents, nodes or services.
1725 	 */
1726 	xmlNodePtr cn, enum_node = NULL, pmap_node = NULL;
1727 	xmlChar *pmap_name;
1728 	tnode_t *ct;
1729 	int e, ccnt = 0;
1730 
1731 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process\n"
1732 	    "process %s range beneath %s\n", rd->rd_name,
1733 	    topo_node_name(rd->rd_pn));
1734 
1735 	e = topo_node_range_create(mp,
1736 	    rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
1737 	if (e != 0 && topo_mod_errno(mp) != EMOD_NODE_DUP) {
1738 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1739 		    "Range create failed due to %s.\n",
1740 		    topo_strerror(topo_mod_errno(mp)));
1741 		return (-1);
1742 	}
1743 
1744 	/*
1745 	 * Before we process any of the other child xmlNodes, we iterate through
1746 	 * the children and looking for either enum-method or propmap elements.
1747 	 */
1748 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
1749 		if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
1750 			enum_node = cn;
1751 		else if (xmlStrcmp(cn->name, (xmlChar *)Propmap) == 0)
1752 			pmap_node = cn;
1753 
1754 	/*
1755 	 * If we found an enum-method element, process it first
1756 	 */
1757 	if (enum_node != NULL) {
1758 		if ((rd->rd_einfo = enum_attributes_process(mp, enum_node))
1759 		    == NULL)
1760 			return (-1);
1761 		if (enum_run(mp, rd) < 0) {
1762 			/*
1763 			 * Note the failure but continue on
1764 			 */
1765 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1766 			    "Enumeration failed.\n");
1767 		}
1768 	}
1769 
1770 	/*
1771 	 * Next, check if a propmap element was found and if so, load it in
1772 	 * and parse it.
1773 	 */
1774 	if (pmap_node != NULL) {
1775 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found a propmap "
1776 		    "element\n");
1777 		if ((pmap_name = xmlGetProp(pmap_node, (xmlChar *)Name))
1778 		    == NULL) {
1779 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1780 			    "propmap element missing name attribute.\n");
1781 		} else {
1782 			if (topo_file_load(mp, rd->rd_pn,
1783 			    (const char *)pmap_name,
1784 			    rd->rd_finfo->tf_scheme, 1) < 0) {
1785 
1786 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1787 				    "topo_xml_range_process: topo_file_load"
1788 				    "failed: %s.\n",
1789 				    topo_strerror(topo_mod_errno(mp)));
1790 			}
1791 			xmlFree(pmap_name);
1792 		}
1793 	}
1794 
1795 	/* Now look for nodes, i.e., hard instances */
1796 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1797 		if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0) {
1798 			if (node_process(mp, cn, rd) < 0) {
1799 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1800 				    "node processing failed: %s.\n",
1801 				    topo_strerror(topo_mod_errno(mp)));
1802 				return (topo_mod_seterrno(mp,
1803 				    EMOD_PARTIAL_ENUM));
1804 			}
1805 			ccnt++;
1806 		}
1807 	}
1808 
1809 	/*
1810 	 * Finally, process the property groups and dependents
1811 	 *
1812 	 * If the TF_PROPMAP flag is set for the XML file we're currently
1813 	 * processing, then this XML file was loaded via propmap.  In that case
1814 	 * we call a special routine to recursively apply the propgroup settings
1815 	 * to all of nodes in this range
1816 	 */
1817 	if (rd->rd_finfo->tf_flags & TF_PROPMAP)
1818 		(void) decorate_nodes(mp, rd, rn, rd->rd_pn, &rd->rd_pad);
1819 	else {
1820 		ct = topo_child_first(rd->rd_pn);
1821 		while (ct != NULL) {
1822 			/* Only care about instances within the range */
1823 			if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
1824 				ct = topo_child_next(rd->rd_pn, ct);
1825 				continue;
1826 			}
1827 			if (pad_process(mp, rd, rn, ct, &rd->rd_pad)
1828 			    < 0)
1829 				return (-1);
1830 
1831 			if (fac_process(mp, rn, rd, ct) < 0)
1832 				return (-1);
1833 
1834 			ct = topo_child_next(rd->rd_pn, ct);
1835 			ccnt++;
1836 		}
1837 	}
1838 
1839 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process: end "
1840 	    "range process %s\n", rd->rd_name);
1841 
1842 	return (0);
1843 }
1844 
1845 static tf_rdata_t *
1846 topo_xml_walk(topo_mod_t *mp,
1847     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
1848 {
1849 	xmlNodePtr curr, def_set = NULL;
1850 	tf_rdata_t *rr, *pr, *rdp;
1851 	xmlChar *set;
1852 	char *key;
1853 	int joined_set = 0;
1854 
1855 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
1856 	rr = pr = NULL;
1857 	/*
1858 	 * First iterate through all the XML nodes at this level to look for
1859 	 * set nodes.
1860 	 */
1861 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1862 		if (curr->name == NULL) {
1863 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1864 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1865 			continue;
1866 		}
1867 		if (xmlStrcmp(curr->name, (xmlChar *)Set) == 0) {
1868 			if (joined_set)
1869 				continue;
1870 
1871 			set = xmlGetProp(curr, (xmlChar *)Setlist);
1872 
1873 			if (mp->tm_hdl->th_product)
1874 				key = mp->tm_hdl->th_product;
1875 			else
1876 				key = mp->tm_hdl->th_platform;
1877 
1878 			/*
1879 			 * If it's the default set then we'll store
1880 			 * a pointer to it so that if none of the other
1881 			 * sets apply to our product we can fall
1882 			 * back to this one.
1883 			 */
1884 			if (strcmp((char *)set, "default") == 0)
1885 				def_set = curr;
1886 			else if (set_contains(mp, key, (char *)set)) {
1887 				joined_set = 1;
1888 				if ((rdp = topo_xml_walk(mp, xinfo, curr,
1889 				    troot)) == NULL) {
1890 					topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1891 					    "topo_xml_walk: failed1\n");
1892 				} else {
1893 					if (pr == NULL) {
1894 						rr = pr = rdp;
1895 					} else {
1896 						pr->rd_next = rdp;
1897 						pr = rdp;
1898 					}
1899 					rr->rd_cnt++;
1900 				}
1901 			}
1902 			xmlFree(set);
1903 		}
1904 	}
1905 	/*
1906 	 * If we haven't found a set that contains our product AND a default set
1907 	 * exists, then we'll process it.
1908 	 */
1909 	if (!joined_set && def_set) {
1910 		if ((rdp = topo_xml_walk(mp, xinfo, def_set, troot)) == NULL) {
1911 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1912 			    "topo_xml_walk: failed2\n");
1913 		}
1914 		if (pr == NULL) {
1915 			rr = pr = rdp;
1916 		} else {
1917 			pr->rd_next = rdp;
1918 			pr = rdp;
1919 		}
1920 		rr->rd_cnt++;
1921 	}
1922 	/*
1923 	 * Now we're interested in children xmlNodes of croot tagged
1924 	 * as 'ranges'.  These define what topology nodes may exist, and need
1925 	 * to be verified.
1926 	 */
1927 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1928 		if (curr->name == NULL) {
1929 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1930 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1931 			continue;
1932 		}
1933 		if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0)
1934 			continue;
1935 		if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
1936 			/*
1937 			 * Range processing error, continue walk
1938 			 */
1939 			continue;
1940 		}
1941 		if (pr == NULL) {
1942 			rr = pr = rdp;
1943 		} else {
1944 			pr->rd_next = rdp;
1945 			pr = rdp;
1946 		}
1947 		rr->rd_cnt++;
1948 	}
1949 
1950 	return (rr);
1951 }
1952 
1953 /*
1954  *  Convert parsed xml topology description into topology nodes
1955  */
1956 int
1957 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
1958 {
1959 	xmlNodePtr xroot;
1960 
1961 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "topo_xml_enum\n");
1962 
1963 	if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
1964 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1965 		    "Couldn't get root xmlNode.\n");
1966 		return (-1);
1967 	}
1968 	if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
1969 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1970 		    "error within .xml topology: %s\n",
1971 		    topo_strerror(topo_mod_errno(tmp)));
1972 		return (-1);
1973 	}
1974 	return (0);
1975 }
1976 
1977 /*
1978  * Load an XML tree from filename and read it into a DOM parse tree.
1979  */
1980 static tf_info_t *
1981 txml_file_parse(topo_mod_t *tmp,
1982     int fd, const char *filenm, const char *escheme)
1983 {
1984 	xmlValidCtxtPtr vcp;
1985 	xmlNodePtr cursor;
1986 	xmlDocPtr document;
1987 	xmlDtdPtr dtd = NULL;
1988 	xmlChar *scheme = NULL;
1989 	char *dtdpath = NULL;
1990 	int readflags = 0;
1991 	tf_info_t *r;
1992 	int e, validate = 0;
1993 
1994 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML,
1995 	    "txml_file_parse(filenm=%s, escheme=%s)\n", filenm, escheme);
1996 
1997 	/*
1998 	 * Since topologies can XInclude other topologies, and libxml2
1999 	 * doesn't do DTD-based validation with XInclude, by default
2000 	 * we don't validate topology files.  One can force
2001 	 * validation, though, by creating a TOPOXML_VALIDATE
2002 	 * environment variable and creating a TOPO_DTD environment
2003 	 * variable with the path to the DTD against which to validate.
2004 	 */
2005 	if (getenv("TOPOXML_VALIDATE") != NULL) {
2006 		dtdpath = getenv("TOPO_DTD");
2007 		if (dtdpath != NULL)
2008 			xmlLoadExtDtdDefaultValue = 0;
2009 		validate = 1;
2010 	}
2011 
2012 	/*
2013 	 * Splat warnings and errors related to parsing the topology
2014 	 * file if the TOPOXML_PERROR environment variable exists.
2015 	 */
2016 	if (getenv("TOPOXML_PERROR") == NULL)
2017 		readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
2018 
2019 	if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
2020 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2021 		    "txml_file_parse: couldn't parse document.\n");
2022 		return (NULL);
2023 	}
2024 
2025 	/*
2026 	 * Verify that this is a document type we understand.
2027 	 */
2028 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
2029 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2030 		    "document has no DTD.\n");
2031 		xmlFreeDoc(document);
2032 		return (NULL);
2033 	}
2034 
2035 	if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
2036 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2037 		    "document DTD unknown; bad topology file\n");
2038 		xmlFreeDoc(document);
2039 		return (NULL);
2040 	}
2041 
2042 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
2043 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
2044 		xmlFreeDoc(document);
2045 		return (NULL);
2046 	}
2047 
2048 	/*
2049 	 * Make sure we're looking at a topology description in the
2050 	 * expected scheme.
2051 	 */
2052 	if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
2053 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2054 		    "document is not a topology description.\n");
2055 		xmlFreeDoc(document);
2056 		return (NULL);
2057 	}
2058 	if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
2059 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2060 		    "topology lacks a scheme.\n");
2061 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
2062 		xmlFreeDoc(document);
2063 		return (NULL);
2064 	}
2065 	if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
2066 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2067 		    "topology in unrecognized scheme, %s, expecting %s\n",
2068 		    scheme, escheme);
2069 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
2070 		xmlFree(scheme);
2071 		xmlFreeDoc(document);
2072 		return (NULL);
2073 	}
2074 
2075 	if (dtdpath != NULL) {
2076 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
2077 		if (dtd == NULL) {
2078 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2079 			    "Could not parse DTD \"%s\".\n",
2080 			    dtdpath);
2081 			xmlFree(scheme);
2082 			xmlFreeDoc(document);
2083 			return (NULL);
2084 		}
2085 
2086 		if (document->extSubset != NULL)
2087 			xmlFreeDtd(document->extSubset);
2088 
2089 		document->extSubset = dtd;
2090 	}
2091 
2092 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
2093 		xmlFree(scheme);
2094 		xmlFreeDoc(document);
2095 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2096 		    "couldn't handle XInclude statements in document\n");
2097 		return (NULL);
2098 	}
2099 
2100 	if (validate) {
2101 		if ((vcp = xmlNewValidCtxt()) == NULL) {
2102 			xmlFree(scheme);
2103 			xmlFreeDoc(document);
2104 			return (NULL);
2105 		}
2106 		vcp->warning = xmlParserValidityWarning;
2107 		vcp->error = xmlParserValidityError;
2108 
2109 		e = xmlValidateDocument(vcp, document);
2110 
2111 		xmlFreeValidCtxt(vcp);
2112 
2113 		if (e == 0)
2114 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2115 			    "Document is not valid.\n");
2116 	}
2117 
2118 	if ((r = tf_info_new(tmp, document, scheme)) == NULL) {
2119 		xmlFree(scheme);
2120 		xmlFreeDoc(document);
2121 		return (NULL);
2122 	}
2123 
2124 	xmlFree(scheme);
2125 	scheme = NULL;
2126 	return (r);
2127 }
2128 
2129 tf_info_t *
2130 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
2131 {
2132 	int fd;
2133 	tf_info_t *tip;
2134 
2135 	if ((fd = open(path, O_RDONLY)) < 0) {
2136 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
2137 		    "failed to open %s for reading\n", path);
2138 		return (NULL);
2139 	}
2140 	tip = txml_file_parse(tmp, fd, path, escheme);
2141 	(void) close(fd);
2142 	return (tip);
2143 }
2144