xref: /illumos-gate/usr/src/lib/fm/topo/libtopo/common/topo_xml.c (revision bb0ade0978a02d3fe0b0165cd4725fdcb593fbfb)
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 (!rd->contains_node_ele) {
1061 				for (ct = topo_child_first(rd->rd_pn);
1062 				    ct != NULL;
1063 				    ct = topo_child_next(rd->rd_pn, ct)) {
1064 
1065 					if (strcmp(topo_node_name(ct),
1066 					    rd->rd_name) != 0)
1067 						continue;
1068 
1069 					if (fac_enum_process(mp, target,
1070 					    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 (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 		}
1088 		*rpad = new;
1089 	}
1090 
1091 	if (new->tpad_dcnt > 0)
1092 		if (dependents_create(mp, rd->rd_finfo, new, pxn, ptn) < 0)
1093 			return (-1);
1094 
1095 	if (new->tpad_pgcnt > 0)
1096 		if (pgroups_create(mp, new, ptn) < 0)
1097 			return (-1);
1098 
1099 	return (0);
1100 }
1101 
1102 
1103 static int
1104 fac_enum_process(topo_mod_t *mp, xmlNodePtr pn, tnode_t *ptn)
1105 {
1106 	xmlNodePtr cn;
1107 	xmlChar *fprov = NULL;
1108 	int rv = 0;
1109 
1110 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1111 	    "fac_enum_process() called for %s=%d\n", topo_node_name(ptn),
1112 	    topo_node_instance(ptn));
1113 
1114 	for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1115 
1116 		if (xmlStrcmp(cn->name, (xmlChar *)"fac-enum") != 0)
1117 			continue;
1118 
1119 		if ((fprov = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1120 			goto fenumdone;
1121 
1122 		if (xmlStrcmp(fprov, (xmlChar *)"fac_prov_ipmi") != 0) {
1123 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1124 			    "Invalid provider specified: %s\n", fprov);
1125 			goto fenumdone;
1126 		}
1127 
1128 		/*
1129 		 * Invoke enum entry point in fac provider which will cause the
1130 		 * facility enumeration node method to be registered.
1131 		 */
1132 		if (fac_enum_run(mp, ptn, (const char *)fprov) != 0) {
1133 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1134 			    "fac_enum_process: enum entry point failed!\n");
1135 			goto fenumdone;
1136 		}
1137 		xmlFree(fprov);
1138 	}
1139 	return (0);
1140 fenumdone:
1141 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac-enum processing failed\n");
1142 
1143 	if (fprov != NULL)
1144 		xmlFree(fprov);
1145 
1146 	return (rv);
1147 }
1148 
1149 
1150 static int
1151 fac_process(topo_mod_t *mp, xmlNodePtr pn, tf_rdata_t *rd, tnode_t *ptn)
1152 {
1153 	xmlNodePtr cn;
1154 	xmlChar *fname = NULL, *ftype = NULL, *provider = NULL;
1155 	tnode_t *ntn = NULL;
1156 	tf_idata_t *newi;
1157 	int err;
1158 	topo_pgroup_info_t pgi;
1159 
1160 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1161 	    "fac_process() called\n");
1162 
1163 	for (cn = pn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1164 
1165 		if (xmlStrcmp(cn->name, (xmlChar *)Facility) != 0)
1166 			continue;
1167 
1168 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1169 		    "facility processing\n");
1170 
1171 		if ((fname = xmlGetProp(cn, (xmlChar *)Name)) == NULL)
1172 			goto facdone;
1173 
1174 		if ((ftype = xmlGetProp(cn, (xmlChar *)Type)) == NULL)
1175 			goto facdone;
1176 
1177 		if ((provider = xmlGetProp(cn, (xmlChar *)Provider)) == NULL)
1178 			goto facdone;
1179 
1180 		if (xmlStrcmp(ftype, (xmlChar *)Sensor) != 0 &&
1181 		    xmlStrcmp(ftype, (xmlChar *)Indicator) != 0)
1182 			goto facdone;
1183 
1184 		if (xmlStrcmp(provider, (xmlChar *)"fac_prov_ipmi") != 0) {
1185 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "fac_process: "
1186 			    "Invalid provider attr value: %s\n", provider);
1187 			goto facdone;
1188 		}
1189 
1190 		if ((ntn = topo_node_facbind(mp, ptn, (char *)fname,
1191 		    (char *)ftype)) == NULL)
1192 			goto facdone;
1193 
1194 		pgi.tpi_name = TOPO_PGROUP_FACILITY;
1195 		pgi.tpi_namestab = TOPO_STABILITY_PRIVATE;
1196 		pgi.tpi_datastab = TOPO_STABILITY_PRIVATE;
1197 		pgi.tpi_version = 1;
1198 		if (topo_pgroup_create(ntn, &pgi, &err) != 0) {
1199 			if (err != ETOPO_PROP_DEFD) {
1200 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1201 				    "pgroups create failure: %s\n",
1202 				    topo_strerror(err));
1203 				return (-1);
1204 			}
1205 		}
1206 		/*
1207 		 * Invoke enum entry point in fac_prov_ipmi module, which will
1208 		 * cause the provider methods to be registered on this node
1209 		 */
1210 		if (fac_enum_run(mp, ntn, (const char *)provider) != 0) {
1211 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "fac_process: "
1212 			    "enum entry point failed for provider %s!\n",
1213 			    provider);
1214 			goto facdone;
1215 		}
1216 
1217 		if ((newi = tf_idata_new(mp, 0, ntn)) == NULL)
1218 			goto facdone;
1219 
1220 		if (tf_idata_insert(&rd->rd_instances, newi) < 0)
1221 			goto facdone;
1222 
1223 		if (pad_process(mp, rd, cn, ntn, &newi->ti_pad) < 0)
1224 			goto facdone;
1225 
1226 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with "
1227 		    "facility %s=%s.\n", ftype, fname);
1228 
1229 		xmlFree(ftype);
1230 		xmlFree(fname);
1231 		xmlFree(provider);
1232 	}
1233 
1234 	return (0);
1235 
1236 facdone:
1237 	topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR, "facility processing failed\n");
1238 
1239 	if (ftype != NULL)
1240 		xmlFree(ftype);
1241 	if (fname != NULL)
1242 		xmlFree(fname);
1243 	if (provider != NULL)
1244 		xmlFree(provider);
1245 	if (ntn != NULL)
1246 		topo_node_unbind(ntn);
1247 
1248 	return (0);
1249 }
1250 
1251 static int
1252 node_process(topo_mod_t *mp, xmlNodePtr nn, tf_rdata_t *rd)
1253 {
1254 	xmlChar *str;
1255 	topo_instance_t inst;
1256 	tf_idata_t *newi;
1257 	tnode_t *ntn;
1258 	uint64_t ui;
1259 	int rv = -1;
1260 	int s = 0;
1261 
1262 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1263 	    "node_process %s\n", rd->rd_name);
1264 
1265 	rd->contains_node_ele = 1;
1266 	if (xmlattr_to_int(mp, nn, Instance, &ui) < 0)
1267 		goto nodedone;
1268 	inst = (topo_instance_t)ui;
1269 
1270 	if ((str = xmlGetProp(nn, (xmlChar *)Static)) != NULL) {
1271 		if (xmlStrcmp(str, (xmlChar *)True) == 0)
1272 			s = 1;
1273 		xmlFree(str);
1274 	}
1275 
1276 	if (s == 0) {
1277 		if (topo_mod_enumerate(rd->rd_mod, rd->rd_pn,
1278 		    rd->rd_finfo->tf_scheme, rd->rd_name, inst, inst,
1279 		    s == 1 ? &s : NULL) < 0)
1280 			goto nodedone;
1281 	}
1282 	ntn = topo_node_lookup(rd->rd_pn, rd->rd_name, inst);
1283 
1284 	if (ntn == NULL) {
1285 
1286 		/*
1287 		 * If this is a static node declaration, we can
1288 		 * ignore the lookup failure and continue
1289 		 * processing.  Otherwise, something
1290 		 * went wrong during enumeration
1291 		 */
1292 		if (s == 1)
1293 			rv = 0;
1294 		goto nodedone;
1295 	}
1296 	if ((newi = tf_idata_new(mp, inst, ntn)) == NULL) {
1297 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1298 		    "node_process: tf_idata_new failed.\n");
1299 		goto nodedone;
1300 	}
1301 	if (tf_idata_insert(&rd->rd_instances, newi) < 0) {
1302 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1303 		    "node_process: tf_idata_insert failed.\n");
1304 		goto nodedone;
1305 	}
1306 	if (pad_process(mp, rd, nn, ntn, &newi->ti_pad) < 0)
1307 		goto nodedone;
1308 	if (fac_process(mp, nn, rd, ntn) < 0)
1309 		goto nodedone;
1310 	rv = 0;
1311 nodedone:
1312 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "done with node %s.\n",
1313 	    rd->rd_name);
1314 	return (rv);
1315 }
1316 
1317 static tf_edata_t *
1318 enum_attributes_process(topo_mod_t *mp, xmlNodePtr en)
1319 {
1320 	tf_edata_t *einfo;
1321 	uint64_t ui;
1322 
1323 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_attributes_process\n");
1324 	if ((einfo = topo_mod_zalloc(mp, sizeof (tf_edata_t))) == NULL) {
1325 		(void) topo_mod_seterrno(mp, ETOPO_NOMEM);
1326 		return (NULL);
1327 	}
1328 	einfo->te_name = (char *)xmlGetProp(en, (xmlChar *)Name);
1329 	if (einfo->te_name == NULL) {
1330 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1331 		    "Enumerator name attribute missing.\n");
1332 		(void) topo_mod_seterrno(mp, ETOPO_PRSR_NOATTR);
1333 		goto enodedone;
1334 	}
1335 
1336 	/*
1337 	 * Check for recursive enumeration
1338 	 */
1339 	if (strcmp(einfo->te_name, mp->tm_name) == 0) {
1340 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1341 		    "Recursive enumeration detected for %s\n",
1342 		    einfo->te_name);
1343 		(void) topo_mod_seterrno(mp, ETOPO_ENUM_RECURS);
1344 		goto enodedone;
1345 	}
1346 	if (xmlattr_to_int(mp, en, Version, &ui) < 0)
1347 		goto enodedone;
1348 	einfo->te_vers = (int)ui;
1349 
1350 	return (einfo);
1351 
1352 enodedone:
1353 	if (einfo->te_name != NULL)
1354 		xmlFree(einfo->te_name);
1355 	topo_mod_free(mp, einfo, sizeof (tf_edata_t));
1356 	return (NULL);
1357 }
1358 
1359 static int
1360 enum_run(topo_mod_t *mp, tf_rdata_t *rd)
1361 {
1362 	topo_hdl_t *thp = mp->tm_hdl;
1363 	int e = -1;
1364 
1365 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enum_run\n");
1366 	/*
1367 	 * Check if the enumerator module is already loaded.
1368 	 * Module loading is single-threaded at this point so there's
1369 	 * no need to worry about the module going away or bumping the
1370 	 * ref count.
1371 	 */
1372 	if ((rd->rd_mod = topo_mod_lookup(thp, rd->rd_einfo->te_name,
1373 	    0)) == NULL) {
1374 		if ((rd->rd_mod = topo_mod_load(mp, rd->rd_einfo->te_name,
1375 		    rd->rd_einfo->te_vers)) == NULL) {
1376 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1377 			    "enum_run: mod_load of %s failed: %s.\n",
1378 			    rd->rd_einfo->te_name,
1379 			    topo_strerror(topo_mod_errno(mp)));
1380 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1381 			return (e);
1382 		}
1383 	}
1384 	/*
1385 	 * We're live, so let's enumerate.
1386 	 */
1387 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "enumerate request. (%s)\n",
1388 	    rd->rd_einfo->te_name);
1389 	e = topo_mod_enumerate(rd->rd_mod, rd->rd_pn, rd->rd_einfo->te_name,
1390 	    rd->rd_name, rd->rd_min, rd->rd_max, NULL);
1391 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "back from enumeration. %d\n",
1392 	    e);
1393 	if (e != 0) {
1394 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1395 		    "Enumeration failed (%s)\n",
1396 		    topo_strerror(topo_mod_errno(mp)));
1397 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1398 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1399 	}
1400 	return (e);
1401 }
1402 
1403 static int
1404 fac_enum_run(topo_mod_t *mp, tnode_t *node, const char *name)
1405 {
1406 	topo_hdl_t *thp = mp->tm_hdl;
1407 	topo_mod_t *fmod;
1408 	int e = -1;
1409 
1410 	topo_dprintf(thp, TOPO_DBG_XML, "fac_enum_run\n");
1411 	/*
1412 	 * Check if the enumerator module is already loaded.
1413 	 * Module loading is single-threaded at this point so there's
1414 	 * no need to worry about the module going away or bumping the
1415 	 * ref count.
1416 	 */
1417 	if ((fmod = topo_mod_lookup(thp, name, 0)) == NULL) {
1418 		if ((fmod = topo_mod_load(mp, name, TOPO_VERSION)) == NULL) {
1419 			topo_dprintf(thp, TOPO_DBG_ERR,
1420 			    "fac_enum_run: mod_load of %s failed: %s.\n",
1421 			    name, topo_strerror(topo_mod_errno(mp)));
1422 			(void) topo_hdl_seterrno(thp, topo_mod_errno(mp));
1423 			return (e);
1424 		}
1425 	}
1426 	/*
1427 	 * We're live, so let's enumerate.
1428 	 */
1429 	topo_dprintf(thp, TOPO_DBG_XML, "fac enumerate request. (%s)\n", name);
1430 	e = topo_mod_enumerate(fmod, node, name, name, 0, 0, NULL);
1431 	topo_dprintf(thp, TOPO_DBG_XML, "back from enumeration. %d\n", e);
1432 	if (e != 0) {
1433 		topo_dprintf(thp, TOPO_DBG_ERR,
1434 		    "Facility provider enumeration failed (%s)\n",
1435 		    topo_strerror(topo_mod_errno(mp)));
1436 		(void) topo_hdl_seterrno(thp, EMOD_PARTIAL_ENUM);
1437 		return (topo_mod_seterrno(mp, EMOD_PARTIAL_ENUM));
1438 	}
1439 	return (e);
1440 }
1441 
1442 int
1443 decorate_nodes(topo_mod_t *mp, tf_rdata_t *rd, xmlNodePtr pxn, tnode_t *ptn,
1444     tf_pad_t **rpad)
1445 {
1446 	tnode_t *ctn;
1447 
1448 	ctn = topo_child_first(ptn);
1449 	while (ctn != NULL) {
1450 		/* Only care about instances within the range */
1451 		if (strcmp(topo_node_name(ctn), rd->rd_name) != 0) {
1452 			ctn = topo_child_next(ptn, ctn);
1453 			continue;
1454 		}
1455 		if (pad_process(mp, rd, pxn, ctn, rpad) < 0)
1456 			return (-1);
1457 		if (decorate_nodes(mp, rd, pxn, ctn, rpad) < 0)
1458 			return (-1);
1459 		ctn = topo_child_next(ptn, ctn);
1460 	}
1461 	return (0);
1462 }
1463 
1464 int
1465 topo_xml_range_process(topo_mod_t *mp, xmlNodePtr rn, tf_rdata_t *rd)
1466 {
1467 	/*
1468 	 * The range may have several children xmlNodes, that may
1469 	 * represent the enumeration method, property groups,
1470 	 * dependents, nodes or services.
1471 	 */
1472 	xmlNodePtr cn, enum_node = NULL, pmap_node = NULL;
1473 	xmlChar *pmap_name;
1474 	tnode_t *ct;
1475 	int e, ccnt = 0;
1476 
1477 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process\n"
1478 	    "process %s range beneath %s\n", rd->rd_name,
1479 	    topo_node_name(rd->rd_pn));
1480 
1481 	rd->contains_node_ele = 0;
1482 	e = topo_node_range_create(mp,
1483 	    rd->rd_pn, rd->rd_name, rd->rd_min, rd->rd_max);
1484 	if (e != 0 && topo_mod_errno(mp) != EMOD_NODE_DUP) {
1485 		topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1486 		    "Range create failed due to %s.\n",
1487 		    topo_strerror(topo_mod_errno(mp)));
1488 		return (-1);
1489 	}
1490 
1491 	/*
1492 	 * Before we process any of the other child xmlNodes, we iterate through
1493 	 * the children and looking for either enum-method or propmap elements.
1494 	 */
1495 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next)
1496 		if (xmlStrcmp(cn->name, (xmlChar *)Enum_meth) == 0)
1497 			enum_node = cn;
1498 		else if (xmlStrcmp(cn->name, (xmlChar *)Propmap) == 0)
1499 			pmap_node = cn;
1500 
1501 	/*
1502 	 * If we found an enum-method element, process it first
1503 	 */
1504 	if (enum_node != NULL) {
1505 		if ((rd->rd_einfo = enum_attributes_process(mp, enum_node))
1506 		    == NULL)
1507 			return (-1);
1508 		if (enum_run(mp, rd) < 0) {
1509 			/*
1510 			 * Note the failure but continue on
1511 			 */
1512 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1513 			    "Enumeration failed.\n");
1514 		}
1515 	}
1516 
1517 	/*
1518 	 * Next, check if a propmap element was found and if so, load it in
1519 	 * and parse it.
1520 	 */
1521 	if (pmap_node != NULL) {
1522 		topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "found a propmap "
1523 		    "element\n");
1524 		if ((pmap_name = xmlGetProp(pmap_node, (xmlChar *)Name))
1525 		    == NULL) {
1526 			topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1527 			    "propmap element missing name attribute.\n");
1528 		} else {
1529 			if (topo_file_load(mp, rd->rd_pn,
1530 			    (const char *)pmap_name,
1531 			    rd->rd_finfo->tf_scheme, 1) < 0) {
1532 
1533 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1534 				    "topo_xml_range_process: topo_file_load"
1535 				    "failed: %s.\n",
1536 				    topo_strerror(topo_mod_errno(mp)));
1537 			}
1538 			xmlFree(pmap_name);
1539 		}
1540 	}
1541 
1542 	/* Now look for nodes, i.e., hard instances */
1543 	for (cn = rn->xmlChildrenNode; cn != NULL; cn = cn->next) {
1544 		if (xmlStrcmp(cn->name, (xmlChar *)Node) == 0) {
1545 			if (node_process(mp, cn, rd) < 0) {
1546 				topo_dprintf(mp->tm_hdl, TOPO_DBG_ERR,
1547 				    "node processing failed: %s.\n",
1548 				    topo_strerror(topo_mod_errno(mp)));
1549 				return (topo_mod_seterrno(mp,
1550 				    EMOD_PARTIAL_ENUM));
1551 			}
1552 			ccnt++;
1553 		}
1554 	}
1555 
1556 	/*
1557 	 * Finally, process the property groups and dependents
1558 	 *
1559 	 * If the TF_PROPMAP flag is set for the XML file we're currently
1560 	 * processing, then this XML file was loaded via propmap.  In that case
1561 	 * we call a special routine to recursively apply the propgroup settings
1562 	 * to all of nodes in this range
1563 	 */
1564 	if (rd->rd_finfo->tf_flags & TF_PROPMAP)
1565 		(void) decorate_nodes(mp, rd, rn, rd->rd_pn, &rd->rd_pad);
1566 	else {
1567 		ct = topo_child_first(rd->rd_pn);
1568 		while (ct != NULL) {
1569 			/* Only care about instances within the range */
1570 			if (strcmp(topo_node_name(ct), rd->rd_name) != 0) {
1571 				ct = topo_child_next(rd->rd_pn, ct);
1572 				continue;
1573 			}
1574 			if (pad_process(mp, rd, rn, ct, &rd->rd_pad)
1575 			    < 0)
1576 				return (-1);
1577 
1578 			if (fac_process(mp, rn, rd, ct) < 0)
1579 				return (-1);
1580 
1581 			ct = topo_child_next(rd->rd_pn, ct);
1582 			ccnt++;
1583 		}
1584 	}
1585 
1586 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_range_process: end "
1587 	    "range process %s\n", rd->rd_name);
1588 
1589 	return (0);
1590 }
1591 
1592 static tf_rdata_t *
1593 topo_xml_walk(topo_mod_t *mp,
1594     tf_info_t *xinfo, xmlNodePtr croot, tnode_t *troot)
1595 {
1596 	xmlNodePtr curr, def_set = NULL;
1597 	tf_rdata_t *rr, *pr, *rdp;
1598 	xmlChar *set;
1599 	char *key;
1600 	int joined_set = 0;
1601 
1602 	topo_dprintf(mp->tm_hdl, TOPO_DBG_XML, "topo_xml_walk\n");
1603 	rr = pr = NULL;
1604 	/*
1605 	 * First iterate through all the XML nodes at this level to look for
1606 	 * set nodes.
1607 	 */
1608 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1609 		if (curr->name == NULL) {
1610 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1611 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1612 			continue;
1613 		}
1614 		if (xmlStrcmp(curr->name, (xmlChar *)Set) == 0) {
1615 			if (joined_set)
1616 				continue;
1617 
1618 			set = xmlGetProp(curr, (xmlChar *)Setlist);
1619 
1620 			if (mp->tm_hdl->th_product)
1621 				key = mp->tm_hdl->th_product;
1622 			else
1623 				key = mp->tm_hdl->th_platform;
1624 
1625 			/*
1626 			 * If it's the default set then we'll store
1627 			 * a pointer to it so that if none of the other
1628 			 * sets apply to our product we can fall
1629 			 * back to this one.
1630 			 */
1631 			if (strcmp((char *)set, "default") == 0)
1632 				def_set = curr;
1633 			else if (set_contains(mp, key, (char *)set)) {
1634 				joined_set = 1;
1635 				if ((rdp = topo_xml_walk(mp, xinfo, curr,
1636 				    troot)) == NULL) {
1637 					topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1638 					    "topo_xml_walk: failed1\n");
1639 				}
1640 				if (pr == NULL) {
1641 					rr = pr = rdp;
1642 				} else {
1643 					pr->rd_next = rdp;
1644 					pr = rdp;
1645 				}
1646 				rr->rd_cnt++;
1647 			}
1648 			xmlFree(set);
1649 		}
1650 	}
1651 	/*
1652 	 * If we haven't found a set that contains our product AND a default set
1653 	 * exists, then we'll process it.
1654 	 */
1655 	if (!joined_set && def_set)
1656 		if (topo_xml_walk(mp, xinfo, def_set, troot) == NULL) {
1657 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1658 			    "topo_xml_walk: failed2\n");
1659 		}
1660 
1661 	/*
1662 	 * Now we're interested in children xmlNodes of croot tagged
1663 	 * as 'ranges'.  These define what topology nodes may exist, and need
1664 	 * to be verified.
1665 	 */
1666 	for (curr = croot->xmlChildrenNode; curr != NULL; curr = curr->next) {
1667 		if (curr->name == NULL) {
1668 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1669 			    "topo_xml_walk: Ignoring nameless xmlnode\n");
1670 			continue;
1671 		}
1672 		if (xmlStrcmp(curr->name, (xmlChar *)Range) != 0) {
1673 			topo_dprintf(mp->tm_hdl, TOPO_DBG_XML,
1674 			    "topo_xml_walk: Ignoring non-range %s.\n",
1675 			    curr->name);
1676 			continue;
1677 		}
1678 		if ((rdp = tf_rdata_new(mp, xinfo, curr, troot)) == NULL) {
1679 			/*
1680 			 * Range processing error, continue walk
1681 			 */
1682 			continue;
1683 		}
1684 		if (pr == NULL) {
1685 			rr = pr = rdp;
1686 		} else {
1687 			pr->rd_next = rdp;
1688 			pr = rdp;
1689 		}
1690 		rr->rd_cnt++;
1691 	}
1692 
1693 	return (rr);
1694 }
1695 
1696 /*
1697  *  Convert parsed xml topology description into topology nodes
1698  */
1699 int
1700 topo_xml_enum(topo_mod_t *tmp, tf_info_t *xinfo, tnode_t *troot)
1701 {
1702 	xmlNodePtr xroot;
1703 
1704 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML, "topo_xml_enum\n");
1705 
1706 	if ((xroot = xmlDocGetRootElement(xinfo->tf_xdoc)) == NULL) {
1707 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1708 		    "Couldn't get root xmlNode.\n");
1709 		return (-1);
1710 	}
1711 	if ((xinfo->tf_rd = topo_xml_walk(tmp, xinfo, xroot, troot)) == NULL) {
1712 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1713 		    "error within .xml topology: %s\n",
1714 		    topo_strerror(topo_mod_errno(tmp)));
1715 		return (-1);
1716 	}
1717 	return (0);
1718 }
1719 
1720 /*
1721  * Load an XML tree from filename and read it into a DOM parse tree.
1722  */
1723 static tf_info_t *
1724 txml_file_parse(topo_mod_t *tmp,
1725     int fd, const char *filenm, const char *escheme)
1726 {
1727 	xmlValidCtxtPtr vcp;
1728 	xmlNodePtr cursor;
1729 	xmlDocPtr document;
1730 	xmlDtdPtr dtd = NULL;
1731 	xmlChar *scheme = NULL;
1732 	char *dtdpath = NULL;
1733 	int readflags = 0;
1734 	tf_info_t *r;
1735 	int e, validate = 0;
1736 
1737 	topo_dprintf(tmp->tm_hdl, TOPO_DBG_XML,
1738 	    "txml_file_parse(filenm=%s, escheme=%s)\n", filenm, escheme);
1739 
1740 	/*
1741 	 * Since topologies can XInclude other topologies, and libxml2
1742 	 * doesn't do DTD-based validation with XInclude, by default
1743 	 * we don't validate topology files.  One can force
1744 	 * validation, though, by creating a TOPOXML_VALIDATE
1745 	 * environment variable and creating a TOPO_DTD environment
1746 	 * variable with the path to the DTD against which to validate.
1747 	 */
1748 	if (getenv("TOPOXML_VALIDATE") != NULL) {
1749 		dtdpath = getenv("TOPO_DTD");
1750 		if (dtdpath != NULL)
1751 			xmlLoadExtDtdDefaultValue = 0;
1752 		validate = 1;
1753 	}
1754 
1755 	/*
1756 	 * Splat warnings and errors related to parsing the topology
1757 	 * file if the TOPOXML_PERROR environment variable exists.
1758 	 */
1759 	if (getenv("TOPOXML_PERROR") == NULL)
1760 		readflags = XML_PARSE_NOERROR | XML_PARSE_NOWARNING;
1761 
1762 	if ((document = xmlReadFd(fd, filenm, NULL, readflags)) == NULL) {
1763 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1764 		    "txml_file_parse: couldn't parse document.\n");
1765 		return (NULL);
1766 	}
1767 
1768 	/*
1769 	 * Verify that this is a document type we understand.
1770 	 */
1771 	if ((dtd = xmlGetIntSubset(document)) == NULL) {
1772 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1773 		    "document has no DTD.\n");
1774 		xmlFreeDoc(document);
1775 		return (NULL);
1776 	}
1777 
1778 	if (strcmp((const char *)dtd->SystemID, TOPO_DTD_PATH) != 0) {
1779 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1780 		    "document DTD unknown; bad topology file\n");
1781 		xmlFreeDoc(document);
1782 		return (NULL);
1783 	}
1784 
1785 	if ((cursor = xmlDocGetRootElement(document)) == NULL) {
1786 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR, "document is empty.\n");
1787 		xmlFreeDoc(document);
1788 		return (NULL);
1789 	}
1790 
1791 	/*
1792 	 * Make sure we're looking at a topology description in the
1793 	 * expected scheme.
1794 	 */
1795 	if (xmlStrcmp(cursor->name, (xmlChar *)Topology) != 0) {
1796 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1797 		    "document is not a topology description.\n");
1798 		xmlFreeDoc(document);
1799 		return (NULL);
1800 	}
1801 	if ((scheme = xmlGetProp(cursor, (xmlChar *)Scheme)) == NULL) {
1802 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1803 		    "topology lacks a scheme.\n");
1804 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_NOATTR);
1805 		xmlFreeDoc(document);
1806 		return (NULL);
1807 	}
1808 	if (xmlStrcmp(scheme, (xmlChar *)escheme) != 0) {
1809 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1810 		    "topology in unrecognized scheme, %s, expecting %s\n",
1811 		    scheme, escheme);
1812 		(void) topo_mod_seterrno(tmp, ETOPO_PRSR_BADSCH);
1813 		xmlFree(scheme);
1814 		xmlFreeDoc(document);
1815 		return (NULL);
1816 	}
1817 
1818 	if (dtdpath != NULL) {
1819 		dtd = xmlParseDTD(NULL, (xmlChar *)dtdpath);
1820 		if (dtd == NULL) {
1821 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1822 			    "Could not parse DTD \"%s\".\n",
1823 			    dtdpath);
1824 			xmlFree(scheme);
1825 			xmlFreeDoc(document);
1826 			return (NULL);
1827 		}
1828 
1829 		if (document->extSubset != NULL)
1830 			xmlFreeDtd(document->extSubset);
1831 
1832 		document->extSubset = dtd;
1833 	}
1834 
1835 	if (xmlXIncludeProcessFlags(document, XML_PARSE_XINCLUDE) == -1) {
1836 		xmlFree(scheme);
1837 		xmlFreeDoc(document);
1838 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1839 		    "couldn't handle XInclude statements in document\n");
1840 		return (NULL);
1841 	}
1842 
1843 	if (validate) {
1844 		if ((vcp = xmlNewValidCtxt()) == NULL) {
1845 			xmlFree(scheme);
1846 			xmlFreeDoc(document);
1847 			return (NULL);
1848 		}
1849 		vcp->warning = xmlParserValidityWarning;
1850 		vcp->error = xmlParserValidityError;
1851 
1852 		e = xmlValidateDocument(vcp, document);
1853 
1854 		xmlFreeValidCtxt(vcp);
1855 
1856 		if (e == 0)
1857 			topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1858 			    "Document is not valid.\n");
1859 	}
1860 
1861 	if ((r = tf_info_new(tmp, document, scheme)) == NULL) {
1862 		xmlFree(scheme);
1863 		xmlFreeDoc(document);
1864 		return (NULL);
1865 	}
1866 
1867 	xmlFree(scheme);
1868 	scheme = NULL;
1869 	return (r);
1870 }
1871 
1872 tf_info_t *
1873 topo_xml_read(topo_mod_t *tmp, const char *path, const char *escheme)
1874 {
1875 	int fd;
1876 	tf_info_t *tip;
1877 
1878 	if ((fd = open(path, O_RDONLY)) < 0) {
1879 		topo_dprintf(tmp->tm_hdl, TOPO_DBG_ERR,
1880 		    "failed to open %s for reading\n", path);
1881 		return (NULL);
1882 	}
1883 	tip = txml_file_parse(tmp, fd, path, escheme);
1884 	(void) close(fd);
1885 	return (tip);
1886 }
1887