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