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