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