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